webdesk-app-market-server/main.go

293 lines
7.6 KiB
Go
Raw Normal View History

2024-11-10 22:47:39 +01:00
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"
2024-11-10 22:47:39 +01:00
)
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"`
2024-11-10 22:47:39 +01:00
}
type AppMetadata struct {
Name string `json:"name"`
Ver string `json:"ver"`
Info string `json:"info"`
Pub string `json:"pub"`
2024-11-10 22:47:39 +01:00
}
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)
2024-11-10 22:47:39 +01:00
}
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()
}
2024-11-10 22:47:39 +01:00
}
func main() {
r := gin.Default()
// Serve static files
r.Static("/apps/files", "./apps/files")
// Handler function for getting apps list
getAppsHandler := 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)
}
// Serve apps list on root path
r.GET("/", getAppsHandler)
// Group routes for /apps
apps := r.Group("/apps")
{
apps.GET("", getAppsHandler)
// 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 struct {
AppMetadata
AppID string `json:"appid,omitempty"`
}
if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid metadata"})
return
}
appID := metadata.AppID
if appID == "" {
rand.Seed(time.Now().UnixNano())
appID = fmt.Sprintf("%012d", rand.Intn(1000000000000))
}
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)),
}
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
}
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) {
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
if file, err := c.FormFile("file"); err == nil {
os.Remove(oldFilePath)
newPath := fmt.Sprintf("/apps/files/%s_%s%s",
apps[i].Name, apps[i].Ver, filepath.Ext(file.Filename))
apps[i].Path = newPath
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
}
os.Remove(filePath)
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)
2024-11-10 22:47:39 +01:00
}
// 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),
})
}
}