2024-04-09 17:07:53 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2024-04-10 17:19:11 +00:00
|
|
|
"io"
|
2024-04-09 17:07:53 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2024-04-10 14:58:07 +00:00
|
|
|
"strconv"
|
2024-04-09 17:07:53 +00:00
|
|
|
|
2024-04-10 17:19:11 +00:00
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
2024-04-11 07:05:44 +00:00
|
|
|
"github.com/minoplhy/chibisafe-netproxy/src/handler"
|
2024-04-09 17:07:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// Check is already done in main()
|
|
|
|
Chibisafe_basepath := os.Getenv("CHIBISAFE_BASEPATH")
|
2024-04-10 14:58:07 +00:00
|
|
|
// Max Upload Size
|
|
|
|
// if Enviroment is failed, will fallback to 10MB
|
|
|
|
GetMaxUploadSize := os.Getenv("MAX_UPLOAD_SIZE")
|
|
|
|
maxUploadSize, err := strconv.Atoi(GetMaxUploadSize)
|
|
|
|
if err != nil {
|
|
|
|
maxUploadSize = 10 * 1024 * 1024 // 10 MB
|
|
|
|
}
|
2024-04-09 17:07:53 +00:00
|
|
|
|
|
|
|
if r.Method != "POST" {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusMethodNotAllowed, "Method not allowed"), http.StatusMethodNotAllowed)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, "Method not allowed")
|
2024-04-10 11:51:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for x-api-key header
|
|
|
|
API_Key := r.Header.Get("x-api-key")
|
|
|
|
if API_Key == "" {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusBadRequest, "X-api-key is empty!"), http.StatusBadRequest)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, "X-api-key is empty!")
|
2024-04-10 11:51:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-10 14:58:07 +00:00
|
|
|
// Validate x-api-key
|
|
|
|
if !handler.Check_API_Key(Chibisafe_basepath, API_Key) {
|
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusUnauthorized, "Failure to validate X-API-Key"), http.StatusUnauthorized)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, "Failure to validate X-API-Key")
|
2024-04-10 14:58:07 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-10 11:51:57 +00:00
|
|
|
r.Body = http.MaxBytesReader(w, r.Body, int64(maxUploadSize))
|
|
|
|
|
|
|
|
// ParseMultipartForm parses a request body as multipart/form-data
|
|
|
|
err = r.ParseMultipartForm(int64(maxUploadSize))
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "http: request body too large" {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusRequestEntityTooLarge, "Request Body is too large!"), http.StatusRequestEntityTooLarge)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, "Request Body is too large!")
|
2024-04-10 11:51:57 +00:00
|
|
|
return
|
|
|
|
}
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// truncated for brevity
|
|
|
|
|
|
|
|
// The argument to FormFile must match the name attribute
|
|
|
|
// of the file input on the frontend
|
|
|
|
file, fileHeader, err := r.FormFile("file")
|
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr}, "Received a successful POST")
|
2024-04-09 17:07:53 +00:00
|
|
|
tempfilepath := handler.GetTempFilename(fileHeader.Filename)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr, tempfilepath}, "Successfully obtained temporary Filename")
|
2024-04-09 17:07:53 +00:00
|
|
|
handler.SaveFile(tempfilepath, file)
|
|
|
|
|
|
|
|
PostData := handler.UploadPostMeta{
|
|
|
|
ContentType: fileHeader.Header.Get("Content-Type"),
|
|
|
|
Name: fileHeader.Filename,
|
|
|
|
FileSize: fileHeader.Size,
|
|
|
|
}
|
|
|
|
|
2024-04-11 11:38:54 +00:00
|
|
|
UploadHeaders := map[string]string{
|
|
|
|
"X-Api-Key": API_Key,
|
|
|
|
"Content-Type": "application/json",
|
2024-04-11 13:27:57 +00:00
|
|
|
}
|
|
|
|
|
2024-04-11 15:29:42 +00:00
|
|
|
// Check if client sent X-Real-IP Header
|
2024-04-11 14:15:21 +00:00
|
|
|
if r.Header.Get("X-Real-IP") != "" && handler.IsInternalIP(r.RemoteAddr) {
|
|
|
|
UploadHeaders["X-Real-IP"] = r.Header.Get("X-Real-IP")
|
2024-04-11 11:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
chibisafe_post, err := handler.UploadPost(Chibisafe_basepath, UploadHeaders, PostData)
|
2024-04-10 11:51:57 +00:00
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusBadRequest, "Something went wrong!"), http.StatusBadRequest)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr, tempfilepath}, err.Error())
|
2024-04-10 11:51:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-09 17:07:53 +00:00
|
|
|
var chibisafe_Response_Metadata handler.UploadResponseMeta
|
|
|
|
err = json.Unmarshal(chibisafe_post, &chibisafe_Response_Metadata)
|
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr, chibisafe_Response_Metadata.Identifier, tempfilepath}, "Successfully obtained PUT keys")
|
2024-04-09 17:07:53 +00:00
|
|
|
|
|
|
|
_, err = handler.NetworkStoragePut(chibisafe_Response_Metadata.URL, PostData.ContentType, tempfilepath)
|
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr, chibisafe_Response_Metadata.Identifier, tempfilepath}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr, chibisafe_Response_Metadata.Identifier, tempfilepath}, "Successfully PUT file to Network Storage")
|
2024-04-09 17:07:53 +00:00
|
|
|
|
2024-04-10 16:30:23 +00:00
|
|
|
// Build Struct for PostProcess Json
|
|
|
|
//
|
|
|
|
// Name -> original Filename
|
|
|
|
// ContentType -> original Content-Type
|
|
|
|
// Identifier -> File Identifier ID
|
2024-04-09 17:07:53 +00:00
|
|
|
PostProcessData := handler.UploadProcessMeta{
|
|
|
|
Name: fileHeader.Filename,
|
|
|
|
ContentType: fileHeader.Header.Get("Content-Type"),
|
|
|
|
Identifier: chibisafe_Response_Metadata.Identifier,
|
|
|
|
}
|
|
|
|
|
2024-04-11 11:38:54 +00:00
|
|
|
ProcessHeaders := map[string]string{
|
|
|
|
"X-Api-Key": API_Key,
|
|
|
|
"Content-Type": "application/json",
|
2024-04-11 13:27:57 +00:00
|
|
|
}
|
|
|
|
|
2024-04-11 15:29:42 +00:00
|
|
|
// Check if client sent X-Real-IP Header
|
2024-04-11 14:15:21 +00:00
|
|
|
if r.Header.Get("X-Real-IP") != "" && handler.IsInternalIP(r.RemoteAddr) {
|
|
|
|
ProcessHeaders["X-Real-IP"] = r.Header.Get("X-Real-IP")
|
2024-04-11 11:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PostProcess, err := handler.UploadProcessPost(Chibisafe_basepath, ProcessHeaders, PostProcessData)
|
2024-04-10 11:51:57 +00:00
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr, chibisafe_Response_Metadata.Identifier, tempfilepath}, err.Error())
|
2024-04-10 11:51:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-09 17:07:53 +00:00
|
|
|
var PostProcessResponse handler.UploadProcessResponseMeta
|
|
|
|
err = json.Unmarshal(PostProcess, &PostProcessResponse)
|
|
|
|
if err != nil {
|
2024-04-10 14:58:07 +00:00
|
|
|
http.Error(w, handler.ErrorResponseBuild(http.StatusInternalServerError, "Something went wrong!"), http.StatusInternalServerError)
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr, PostProcessResponse.Name, tempfilepath}, fmt.Sprintf("Successfully Processed Response with UUID: %s", PostProcessResponse.UUID))
|
2024-04-09 17:07:53 +00:00
|
|
|
|
|
|
|
err = handler.DeleteFile(tempfilepath)
|
|
|
|
if err != nil {
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.ErrorLogBuilder([]string{r.RemoteAddr, chibisafe_Response_Metadata.Identifier, tempfilepath}, err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
return
|
|
|
|
}
|
2024-04-10 17:19:11 +00:00
|
|
|
handler.InfoLogBuilder([]string{r.RemoteAddr, tempfilepath}, "Successfully Deleted Temporary file from local disk")
|
2024-04-10 11:51:57 +00:00
|
|
|
JsonResponse, _ := json.Marshal(PostProcessResponse)
|
|
|
|
fmt.Fprintf(w, "%s", JsonResponse)
|
2024-04-09 17:07:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2024-04-10 17:19:11 +00:00
|
|
|
// Open or create a file for appending logs
|
|
|
|
log_file, err := os.OpenFile("activity.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal().Msg(err.Error())
|
|
|
|
}
|
|
|
|
defer log_file.Close()
|
|
|
|
|
|
|
|
// Setup Logging Policy
|
|
|
|
// Multi level writer on logfile and console
|
|
|
|
//
|
|
|
|
// Format : Console -> Human Readable
|
|
|
|
// File -> Json
|
|
|
|
logger := zerolog.New(zerolog.MultiLevelWriter(log_file, os.Stdout)).With().Timestamp().Logger()
|
|
|
|
logger = logger.Output(io.MultiWriter(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "15:04:05"}, log_file))
|
|
|
|
// Set as Global logger :)
|
|
|
|
log.Logger = logger
|
|
|
|
|
2024-04-09 17:07:53 +00:00
|
|
|
Chibisafe_basepath := os.Getenv("CHIBISAFE_BASEPATH")
|
2024-04-10 14:58:07 +00:00
|
|
|
Max_Upload_Size := os.Getenv("MAX_UPLOAD_SIZE")
|
2024-04-09 17:07:53 +00:00
|
|
|
|
|
|
|
if Chibisafe_basepath == "" {
|
2024-04-10 17:19:11 +00:00
|
|
|
log.Fatal().Msg("CHIBISAFE_BASEPATH environment is not set!")
|
2024-04-09 17:07:53 +00:00
|
|
|
}
|
2024-04-10 14:58:07 +00:00
|
|
|
if Max_Upload_Size != "" {
|
|
|
|
_, err := strconv.Atoi(Max_Upload_Size)
|
|
|
|
if err != nil {
|
2024-04-10 17:19:11 +00:00
|
|
|
log.Fatal().Msg("MAX_UPLOAD_SIZE environment is invaild!")
|
2024-04-10 14:58:07 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-09 17:07:53 +00:00
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/api/v1/upload", uploadHandler)
|
|
|
|
|
2024-04-09 18:00:48 +00:00
|
|
|
if err := http.ListenAndServe(":4040", mux); err != nil {
|
2024-04-10 17:19:11 +00:00
|
|
|
log.Fatal().Msg(err.Error())
|
2024-04-09 17:07:53 +00:00
|
|
|
}
|
|
|
|
}
|