clearlove2.1
1// ClearLove 插件社区 · 云端服务 —— 单二进制部署
2// 入口:加载配置 -> 连接 SQLite -> 建表 -> 解析模板 -> 注册路由 -> 启动 HTTP 服务
3// 域名:https://clearlove.kazx.top(通过 Nginx 反代到本服务)
4package main
5
6import (
7 "embed"
8 "fmt"
9 "io/fs"
10 "log"
11 "net/http"
12 "os"
13 "path/filepath"
14
15 "clearlove-cloud/internal/config"
16 "clearlove-cloud/internal/database"
17 "clearlove-cloud/internal/handlers"
18 "clearlove-cloud/internal/middleware"
19)
20
21//go:embed all:web/templates all:web/static
22var webFS embed.FS
23
24func main() {
25 config.Load()
26 if err := ensureDirs(); err != nil {
27 log.Fatalf("目录初始化失败: %v\n提示:可通过 CLOUD_DATA_DIR / CLOUD_UPLOAD_DIR 指定可写目录", err)
28 }
29 if err := database.Connect(); err != nil {
30 log.Fatalf("数据库连接失败: %v", err)
31 }
32 if err := database.Migrate(); err != nil {
33 log.Fatalf("数据库迁移失败: %v", err)
34 }
35
36 handlers.SetupTemplates(webFS)
37 staticFS, _ := fs.Sub(webFS, "web/static")
38 handlers.StaticFS = staticFS
39
40 addr := ":16869"
41 if p := os.Getenv("PORT"); p != "" {
42 addr = ":" + p
43 } else if p := os.Getenv("CLOUD_PORT"); p != "" {
44 addr = ":" + p
45 }
46 log.Printf("ClearLove 插件社区 %s | 数据目录: %s | 对外地址: %s", config.Version, config.Cfg.DataDir, config.Cfg.BaseURL)
47 log.Printf("服务已启动: http://localhost%s (管理后台: /admin,开发者: /dev/login)", addr)
48 if err := http.ListenAndServe(addr, middleware.Use(buildMux())); err != nil {
49 log.Fatalf("HTTP 服务启动失败: %v", err)
50 }
51}
52
53// ensureDirs 创建并校验运行时目录可写
54func ensureDirs() error {
55 dirs := []string{
56 config.Cfg.DataDir,
57 config.Cfg.UploadDir,
58 filepath.Join(config.Cfg.UploadDir, "packages"),
59 }
60 for _, d := range dirs {
61 if err := os.MkdirAll(d, 0o755); err != nil {
62 return err
63 }
64 probe := filepath.Join(d, ".write_test")
65 if err := os.WriteFile(probe, []byte("ok"), 0o644); err != nil {
66 return fmt.Errorf("%s 不可写: %w", d, err)
67 }
68 _ = os.Remove(probe)
69 }
70 return nil
71}
72
73// buildMux 注册全部路由
74func buildMux() *http.ServeMux {
75 mux := http.NewServeMux()
76
77 // 静态资源
78 mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(handlers.StaticFS))))
79 mux.HandleFunc("GET /favicon.svg", handlers.Favicon)
80
81 // 官网
82 mux.HandleFunc("GET /{$}", handlers.Home)
83 mux.HandleFunc("GET /plugins", handlers.Plugins)
84 mux.HandleFunc("GET /plugins/{slug}", handlers.PluginDetail)
85 mux.HandleFunc("GET /docs", handlers.Docs)
86 mux.HandleFunc("GET /download", handlers.DownloadSite)
87 mux.HandleFunc("GET /dl/{slug}", handlers.DownloadLatest)
88
89 // 开发者
90 mux.HandleFunc("GET /dev/register", handlers.DevRegisterPage)
91 mux.HandleFunc("POST /dev/register", handlers.DevRegisterSubmit)
92 mux.HandleFunc("GET /dev/login", handlers.DevLoginPage)
93 mux.HandleFunc("POST /dev/login", handlers.DevLoginSubmit)
94 mux.HandleFunc("GET /dev/logout", handlers.DevLogout)
95 mux.HandleFunc("POST /api/dev/send-code", handlers.DevSendCode)
96 mux.HandleFunc("GET /dev", handlers.DevDash)
97 mux.HandleFunc("GET /dev/plugins", handlers.DevMyPlugins)
98 mux.HandleFunc("GET /dev/plugins/new", handlers.DevNewPage)
99 mux.HandleFunc("POST /dev/plugins/new", handlers.DevNewSubmit)
100 mux.HandleFunc("GET /dev/plugins/{slug}", handlers.DevPluginPage)
101 mux.HandleFunc("POST /dev/plugins/{slug}/update", handlers.DevPluginUpdate)
102 mux.HandleFunc("POST /dev/plugins/{slug}/version", handlers.DevPluginVersion)
103 mux.HandleFunc("POST /dev/plugins/{slug}/submit", handlers.DevPluginSubmit)
104 mux.HandleFunc("POST /dev/plugins/{slug}/delete", handlers.DevPluginDelete)
105 mux.HandleFunc("GET /dev/profile", handlers.DevProfile)
106 mux.HandleFunc("POST /dev/profile", handlers.DevProfileSave)
107
108 // 插件市场开放 API(供表白墙后台插件商店调用)
109 mux.HandleFunc("GET /api/v1/store/plugins", handlers.APIListPlugins)
110 mux.HandleFunc("GET /api/v1/store/plugins/{slug}", handlers.APIPluginDetail)
111 mux.HandleFunc("GET /api/v1/store/download/{slug}/{version}", handlers.APIDownload)
112 mux.HandleFunc("POST /api/v1/store/report", handlers.APIReportInstall)
113
114 // 管理后台
115 mux.HandleFunc("GET /admin/login", handlers.AdminLoginPage)
116 mux.HandleFunc("POST /admin/login", handlers.AdminLoginSubmit)
117 mux.HandleFunc("GET /admin/logout", handlers.AdminLogout)
118 mux.HandleFunc("GET /admin", handlers.AdminDash)
119 mux.HandleFunc("GET /admin/plugins", handlers.AdminPlugins)
120 mux.HandleFunc("POST /admin/plugins/status", handlers.AdminPluginStatus)
121 mux.HandleFunc("GET /admin/developers", handlers.AdminDevelopers)
122 mux.HandleFunc("POST /admin/developers/status", handlers.AdminDeveloperStatus)
123 mux.HandleFunc("GET /admin/settings", handlers.AdminSettings)
124 mux.HandleFunc("POST /admin/settings", handlers.AdminSettingsSave)
125 mux.HandleFunc("POST /admin/password", handlers.AdminPassword)
126
127 return mux
128}