package main import ( "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "os" "path/filepath" "strings" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/joho/godotenv" ) type App struct { Name string `json:"name"` Ver string `json:"ver"` AppID string `json:"appid"` Info string `json:"info"` Pub string `json:"pub"` Path string `json:"path"` } type AppMetadata struct { Name string `json:"name"` Ver string `json:"ver"` Info string `json:"info"` Pub string `json:"pub"` } func init() { // set production mode on gin, if you are facing issues then remove this line and see what is going on in debug mode gin.SetMode(gin.ReleaseMode) // Load .env file if err := godotenv.Load(); err != nil { // Create .env if it doesn't exist authToken := uuid.New().String() defaultPort := "8080" envContent := fmt.Sprintf("AUTH_TOKEN=%s\nPORT=%s", authToken, defaultPort) ioutil.WriteFile(".env", []byte(envContent), 0644) godotenv.Load() } websrvport := os.Getenv("PORT") if websrvport == "" { websrvport = "8080" } fmt.Printf("Starting web server on port %s\n", websrvport) } func authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") if authHeader == "" { c.JSON(http.StatusUnauthorized, gin.H{"error": "No authorization header"}) c.Abort() return } token := strings.Replace(authHeader, "Bearer ", "", 1) if token != os.Getenv("AUTH_TOKEN") { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) c.Abort() return } c.Next() } } func main() { r := gin.Default() // Serve static files r.Static("/apps/files", "./apps/files") // Group routes for /apps apps := r.Group("/apps") { apps.GET("", func(c *gin.Context) { data, err := ioutil.ReadFile("apps.json") if err != nil { c.JSON(http.StatusOK, []App{}) return } var apps []App json.Unmarshal(data, &apps) c.JSON(http.StatusOK, apps) }) // Handle other methods for /apps apps.Handle("POST", "", methodNotAllowedHandler("GET")) apps.Handle("PUT", "", methodNotAllowedHandler("GET")) apps.Handle("DELETE", "", methodNotAllowedHandler("GET")) apps.Handle("PATCH", "", methodNotAllowedHandler("GET")) } // Group routes for /uploadapp uploadapp := r.Group("/uploadapp") { uploadapp.POST("", authMiddleware(), func(c *gin.Context) { metadataStr := c.PostForm("metadata") file, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"}) return } var metadata AppMetadata if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid metadata"}) return } // Generate 12-digit app ID rand.Seed(time.Now().UnixNano()) appID := fmt.Sprintf("%012d", rand.Intn(1000000000000)) // Create new app entry newApp := App{ Name: metadata.Name, Ver: metadata.Ver, AppID: appID, Info: metadata.Info, Pub: metadata.Pub, Path: fmt.Sprintf("/apps/files/%s_%s%s", metadata.Name, metadata.Ver, filepath.Ext(file.Filename)), } // Save file if err := os.MkdirAll("apps/files", 0755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create directory"}) return } if err := c.SaveUploadedFile(file, "."+newApp.Path); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file"}) return } // Update apps.json var apps []App data, _ := ioutil.ReadFile("apps.json") json.Unmarshal(data, &apps) apps = append(apps, newApp) appsJSON, _ := json.Marshal(apps) ioutil.WriteFile("apps.json", appsJSON, 0644) c.JSON(http.StatusOK, newApp) }) uploadapp.Handle("GET", "", methodNotAllowedHandler("POST")) uploadapp.Handle("PUT", "", methodNotAllowedHandler("POST")) uploadapp.Handle("DELETE", "", methodNotAllowedHandler("POST")) uploadapp.Handle("PATCH", "", methodNotAllowedHandler("POST")) } // Group routes for /editapp editapp := r.Group("/editapp") { editapp.PUT("", authMiddleware(), func(c *gin.Context) { // Get metadata from form metadataStr := c.PostForm("metadata") if metadataStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Metadata is required"}) return } var updateData struct { AppID string `json:"appid"` App AppMetadata `json:"app"` } if err := json.Unmarshal([]byte(metadataStr), &updateData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid metadata format"}) return } var apps []App data, _ := ioutil.ReadFile("apps.json") json.Unmarshal(data, &apps) found := false var oldFilePath string for i := range apps { if apps[i].AppID == updateData.AppID { oldFilePath = "." + apps[i].Path apps[i].Name = updateData.App.Name apps[i].Ver = updateData.App.Ver apps[i].Info = updateData.App.Info // Handle file update if provided if file, err := c.FormFile("file"); err == nil { // Delete old file os.Remove(oldFilePath) // Generate new path newPath := fmt.Sprintf("/apps/files/%s_%s%s", apps[i].Name, apps[i].Ver, filepath.Ext(file.Filename)) apps[i].Path = newPath // Save new file if err := c.SaveUploadedFile(file, "."+newPath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save new file"}) return } } apps[i].Pub = updateData.App.Pub found = true break } } if !found { c.JSON(http.StatusNotFound, gin.H{"error": "App not found"}) return } appsJSON, _ := json.Marshal(apps) ioutil.WriteFile("apps.json", appsJSON, 0644) c.JSON(http.StatusOK, gin.H{"message": "App updated successfully"}) }) editapp.Handle("GET", "", methodNotAllowedHandler("PUT")) editapp.Handle("POST", "", methodNotAllowedHandler("PUT")) editapp.Handle("DELETE", "", methodNotAllowedHandler("PUT")) editapp.Handle("PATCH", "", methodNotAllowedHandler("PUT")) } // Group routes for /deleteapp deleteapp := r.Group("/deleteapp") { deleteapp.DELETE("", authMiddleware(), func(c *gin.Context) { var request struct { AppID string `json:"appid"` } if err := c.BindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) return } var apps []App data, _ := ioutil.ReadFile("apps.json") json.Unmarshal(data, &apps) found := false var filePath string for i := range apps { if apps[i].AppID == request.AppID { filePath = "." + apps[i].Path apps = append(apps[:i], apps[i+1:]...) found = true break } } if !found { c.JSON(http.StatusNotFound, gin.H{"error": "App not found"}) return } // Delete the file os.Remove(filePath) // Update apps.json appsJSON, _ := json.Marshal(apps) ioutil.WriteFile("apps.json", appsJSON, 0644) c.JSON(http.StatusOK, gin.H{"message": "App deleted successfully"}) }) deleteapp.Handle("GET", "", methodNotAllowedHandler("DELETE")) deleteapp.Handle("POST", "", methodNotAllowedHandler("DELETE")) deleteapp.Handle("PUT", "", methodNotAllowedHandler("DELETE")) deleteapp.Handle("PATCH", "", methodNotAllowedHandler("DELETE")) } port := os.Getenv("PORT") if port == "" { port = "8080" } r.Run(":" + port) } // Helper function to create method not allowed handlers func methodNotAllowedHandler(allowedMethod string) gin.HandlerFunc { return func(c *gin.Context) { c.JSON(http.StatusMethodNotAllowed, gin.H{ "error": fmt.Sprintf("Method not allowed. Only %s is supported for this endpoint.", allowedMethod), }) } }