package controller
import (
"archive/tar"
"compress/gzip"
"encoding/json"
"errors"
"github.com/go-chi/chi"
"github.com/goodrain/rainbond/db"
httputil "github.com/goodrain/rainbond/util/http"
"io"
"math/rand"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"strings"
)
type LongVersionStruct struct{}
const BaseUploadPath = "/grdata/lang"
const LSUploadPath = "/run/lang"
var allowedExtensions = map[string]bool{
".jar": true,
".tar.gz": true,
}
func (t *LongVersionStruct) UploadLongVersion(w http.ResponseWriter, r *http.Request) {
file, fileHeader, err := r.FormFile("file")
if err != nil {
sendResponse(w, http.StatusBadRequest, "failure", "get file failure.")
return
}
defer file.Close()
fileName := fileHeader.Filename
fileExtension := strings.ToLower(filepath.Ext(fileName))
if !allowedExtensions[fileExtension] && !(strings.HasSuffix(fileName, ".tar.gz")) {
sendResponse(w, http.StatusBadRequest, "failure", "file type not allowed. Only .jar and .tar.gz files are permitted.")
return
}
if fileExtension == ".tar.gz" || strings.HasSuffix(fileName, ".tar.gz") {
if err := validateTarGz(file); err != nil {
sendResponse(w, http.StatusBadRequest, "failure", "invalid tar.gz file: "+err.Error())
return
}
} else if fileExtension == ".jar" {
if err := validateJar(file); err != nil {
sendResponse(w, http.StatusBadRequest, "failure", "invalid jar file: "+err.Error())
return
}
}
eventID, err := generateRandomString(32)
if err != nil {
sendResponse(w, http.StatusInternalServerError, "failure", "failed to generate event ID: "+err.Error())
return
}
langPath := path.Join(LSUploadPath, eventID)
if _, err = os.Stat(langPath); os.IsNotExist(err) {
err := os.MkdirAll(langPath, os.ModePerm)
if err != nil {
sendResponse(w, http.StatusInternalServerError, "failure", "dir does not exist, mkdir failure")
return
}
}
newFile, err := os.Create(path.Join(langPath, fileHeader.Filename))
if err != nil {
sendResponse(w, http.StatusInternalServerError, "failure", "Create file error: "+err.Error())
return
}
defer newFile.Close()
if _, err = file.Seek(0, io.SeekStart); err != nil {
sendResponse(w, http.StatusInternalServerError, "failure", "failed to reset file pointer: "+err.Error())
return
}
_, err = io.Copy(newFile, file)
if err != nil {
sendResponse(w, http.StatusInternalServerError, "failure", "Failed to write file: "+err.Error())
return
}
long := struct {
EventID string `json:"event_id"`
FileName string `json:"file_name"`
}{
eventID,
fileHeader.Filename,
}
origin := r.Header.Get("Origin")
w.Header().Add("Access-Control-Allow-Origin", origin)
w.Header().Add("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS")
w.Header().Add("Access-Control-Allow-Credentials", "true")
w.Header().Add("Access-Control-Allow-Headers", "authorization,x_region_name,x_team_name,x-requested-with")
sendResponse(w, http.StatusOK, "successful", long)
}
func validateTarGz(file io.Reader) error {
gzr, err := gzip.NewReader(file)
if err != nil {
return err
}
defer gzr.Close()
tarReader := tar.NewReader(gzr)
_, err = tarReader.Next()
if err != nil {
return err
}
return nil
}
func validateJar(file io.Reader) error {
buffer := make([]byte, 4)
if _, err := file.Read(buffer); err != nil {
return err
}
if string(buffer) != "PK\x03\x04" {
return errors.New("invalid jar file header")
}
return nil
}
func (t *LongVersionStruct) DownloadLongVersion(w http.ResponseWriter, r *http.Request) {
language := strings.TrimSpace(chi.URLParam(r, "language"))
version := strings.TrimSpace(chi.URLParam(r, "version"))
ver, err := db.GetManager().LongVersionDao().GetVersionByLanguageAndVersion(language, version)
if err != nil {
sendResponse(w, http.StatusBadRequest, "failure", "get version failure"+err.Error())
return
}
http.ServeFile(w, r, path.Join(BaseUploadPath, ver.EventID, ver.FileName))
}
func (t *LongVersionStruct) OptionLongVersion(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
w.Header().Add("Access-Control-Allow-Origin", origin)
w.Header().Add("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS")
w.Header().Add("Access-Control-Allow-Credentials", "true")
w.Header().Add("Access-Control-Allow-Headers", "authorization,x_region_name,x_team_name,content-type,x-requested-with")
httputil.ReturnSuccess(r, w, nil)
return
}
func generateRandomString(n int) (string, error) {
const letters = "abcdefghijklmnopqrstuvwxyz0123456789"
bytes := make([]byte, n)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
for i, b := range bytes {
bytes[i] = letters[b%byte(len(letters))]
}
return string(bytes), nil
}
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Bean interface{} `json:"bean,omitempty"`
List interface{} `json:"list,omitempty"`
}
func sendResponse(w http.ResponseWriter, code int, message string, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
response := APIResponse{
Code: code,
Message: message,
}
switch reflect.TypeOf(data).Kind() {
case reflect.Slice, reflect.Array:
response.List = data
default:
response.Bean = data
}
json.NewEncoder(w).Encode(response)
}