package main import ( "crypto/rand" "encoding/hex" "encoding/json" "fmt" "io" "io/ioutil" "log" "mime/multipart" "net/http" "os" "path/filepath" "sync" "strings" "github.com/joho/godotenv" ) type App struct { Name string `json:"name"` Version string `json:"ver"` ID string `json:"appid"` Info string `json:"info"` Developer string `json:"pub"` Path string `json:"path"` } var ( apps []App appsFilePath = "apps.json" uploadDir = "uploads" bearerToken string mutex sync.Mutex ) func loadApps() error { data, err := ioutil.ReadFile(appsFilePath) if err != nil { return err } return json.Unmarshal(data, &apps) } func saveApps() error { data, err := json.MarshalIndent(apps, "", " ") if err != nil { return err } return ioutil.WriteFile(appsFilePath, data, 0644) } func generateToken() (string, error) { tokenBytes := make([]byte, 16) if _, err := rand.Read(tokenBytes); err != nil { return "", err } return hex.EncodeToString(tokenBytes), nil } func loadOrGenerateToken() error { if err := godotenv.Load(); err != nil { log.Println("No .env file found. Generating a new token.") } bearerToken = os.Getenv("TOKEN") if bearerToken == "" { var err error bearerToken, err = generateToken() if err != nil { return fmt.Errorf("failed to generate token: %v", err) } err = saveTokenToEnv(bearerToken) if err != nil { return fmt.Errorf("failed to save token to .env: %v", err) } log.Printf("Generated new token: %s\n", bearerToken) } port := os.Getenv("PORT") if port == "" { port = "8080" } return nil } func saveTokenToEnv(token string) error { return ioutil.WriteFile(".env", []byte("TOKEN="+token), 0644) } func listAppsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") mutex.Lock() defer mutex.Unlock() json.NewEncoder(w).Encode(apps) } func saveUploadedFile(customPath, fileName string, file multipart.File) (string, error) { appDir := filepath.Join(uploadDir, customPath) if err := os.MkdirAll(appDir, 0755); err != nil { return "", err } filePath := filepath.Join(appDir, fileName) dst, err := os.Create(filePath) if err != nil { return "", err } defer dst.Close() if _, err := io.Copy(dst, file); err != nil { return "", err } return fmt.Sprintf("/%s/%s/%s", uploadDir, customPath, fileName), nil } func uploadAppHandler(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token != "Bearer "+bearerToken { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if err := r.ParseMultipartForm(10 << 20); err != nil { http.Error(w, "Invalid form data", http.StatusBadRequest) return } metadata := r.FormValue("metadata") var newApp App if err := json.Unmarshal([]byte(metadata), &newApp); err != nil { http.Error(w, "Invalid metadata JSON", http.StatusBadRequest) return } file, handler, err := r.FormFile("file") if err != nil { http.Error(w, "File upload error", http.StatusBadRequest) return } defer file.Close() customPath := r.FormValue("customPath") if customPath == "" { customPath = strings.ReplaceAll(newApp.Name, " ", "_") } filePath, err := saveUploadedFile(customPath, handler.Filename, file) if err != nil { http.Error(w, "Failed to save file", http.StatusInternalServerError) return } newApp.Path = filePath mutex.Lock() apps = append(apps, newApp) if err := saveApps(); err != nil { mutex.Unlock() http.Error(w, "Failed to save app data", http.StatusInternalServerError) return } mutex.Unlock() w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newApp) } func deleteAppHandler(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token != "Bearer "+bearerToken { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } appID := r.URL.Query().Get("id") if appID == "" { http.Error(w, "App ID is required", http.StatusBadRequest) return } mutex.Lock() defer mutex.Unlock() for i, app := range apps { if app.ID == appID { // Remove the app's file if it exists if err := os.Remove(filepath.Join(".", app.Path)); err != nil { log.Printf("Failed to delete file: %v\n", err) } // Remove the app from the list apps = append(apps[:i], apps[i+1:]...) if err := saveApps(); err != nil { http.Error(w, "Failed to save apps", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("App deleted successfully")) return } } http.Error(w, "App not found", http.StatusNotFound) } func editAppHandler(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token != "Bearer "+bearerToken { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if err := r.ParseMultipartForm(10 << 20); err != nil { http.Error(w, "Invalid form data", http.StatusBadRequest) return } appID := r.FormValue("id") if appID == "" { http.Error(w, "App ID is required", http.StatusBadRequest) return } var updatedApp *App mutex.Lock() for i := range apps { if apps[i].ID == appID { updatedApp = &apps[i] break } } mutex.Unlock() if updatedApp == nil { http.Error(w, "App not found", http.StatusNotFound) return } if name := r.FormValue("name"); name != "" { updatedApp.Name = name } if info := r.FormValue("info"); info != "" { updatedApp.Info = info } // Check if a new file is uploaded file, handler, err := r.FormFile("file") if err == nil { defer file.Close() customPath := r.FormValue("customPath") if customPath == "" { customPath = strings.ReplaceAll(updatedApp.Name, " ", "_") } filePath, err := saveUploadedFile(customPath, handler.Filename, file) if err != nil { http.Error(w, "Failed to save file", http.StatusInternalServerError) return } // Remove the old file os.Remove(filepath.Join(".", updatedApp.Path)) updatedApp.Path = filePath } mutex.Lock() defer mutex.Unlock() if err := saveApps(); err != nil { http.Error(w, "Failed to save app data", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(updatedApp) } func main() { if err := loadOrGenerateToken(); err != nil { log.Fatalf("Error loading or generating token: %v", err) } if err := loadApps(); err != nil { if os.IsNotExist(err) { log.Println("apps.json not found. Starting with an empty app list.") } else { log.Fatalf("Failed to load apps.json: %v", err) } } http.Handle("/uploads/", http.StripPrefix("/uploads", http.FileServer(http.Dir(uploadDir)))) http.HandleFunc("/apps", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: listAppsHandler(w, r) case http.MethodPost: uploadAppHandler(w, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } }) http.HandleFunc("/delete", deleteAppHandler) http.HandleFunc("/editapp", editAppHandler) port := os.Getenv("PORT") fmt.Printf("Server starting on port %s\n", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }