285 lines
7.3 KiB
Go
285 lines
7.3 KiB
Go
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() {
|
|
// 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()
|
|
}
|
|
}
|
|
|
|
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),
|
|
})
|
|
}
|
|
}
|