更新项目架构与存储适配器,添加用户认证功能

本次提交包含以下主要更改:
1. 更新 `.gitignore` 文件,添加对 `node_modules` 和环境变量文件的忽略。
2. 修改 `.gitmodules` 文件,替换为新的子模块 `cloudflare-worker`。
3. 新增 `ARCHITECTURE.md` 和 `PROJECT_REFACTOR_PLAN.md` 文档,详细描述项目架构和改造计划。
4. 实现用户认证功能,添加 GitHub OAuth 处理逻辑,支持 JWT 生成与解析。
5. 引入新的存储接口 `CanvasStore`,并实现相应的存储逻辑,支持用户画布的增删改查。
6. 更新 `main.go` 文件,整合新的认证与存储逻辑,优化路由设置。

这些更改旨在提升项目的可扩展性与用户体验,支持多用户环境下的画布管理与存储。
This commit is contained in:
Yuzhong Zhang
2025-07-05 23:13:17 +08:00
parent 61abbc612b
commit 94953a5eac
26 changed files with 2078 additions and 293 deletions
+54 -25
View File
@@ -3,9 +3,12 @@ package main
import (
"embed"
_ "embed"
"excalidraw-complete/core"
"excalidraw-complete/handlers/api/documents"
"excalidraw-complete/handlers/api/firebase"
"excalidraw-complete/handlers/api/kv"
"excalidraw-complete/handlers/api/openai"
"excalidraw-complete/handlers/auth"
authMiddleware "excalidraw-complete/middleware"
"excalidraw-complete/stores"
"flag"
"fmt"
@@ -17,11 +20,11 @@ import (
"strings"
"syscall"
"github.com/sirupsen/logrus"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/joho/godotenv"
"github.com/sirupsen/logrus"
"github.com/zishang520/engine.io/v2/types"
"github.com/zishang520/engine.io/v2/utils"
socketio "github.com/zishang520/socket.io/v2/socket"
@@ -103,7 +106,7 @@ func handleUI() http.Handler {
})
}
func setupRouter(documentStore core.DocumentStore) *chi.Mux {
func setupRouter(store stores.Store) *chi.Mux {
r := chi.NewRouter()
r.Use(middleware.Logger)
@@ -121,13 +124,37 @@ func setupRouter(documentStore core.DocumentStore) *chi.Mux {
})
r.Route("/api/v2", func(r chi.Router) {
r.Post("/post/", documents.HandleCreate(documentStore))
// Route for canvases, protected by JWT auth
r.Group(func(r chi.Router) {
r.Use(authMiddleware.AuthJWT)
r.Route("/kv", func(r chi.Router) {
r.Get("/", kv.HandleListCanvases(store))
r.Route("/{key}", func(r chi.Router) {
r.Get("/", kv.HandleGetCanvas(store))
r.Put("/", kv.HandleSaveCanvas(store))
r.Delete("/", kv.HandleDeleteCanvas(store))
})
})
r.Route("/chat", func(r chi.Router) {
r.Post("/completions", openai.HandleChatCompletion())
})
})
// Old routes for anonymous document sharing
r.Post("/post/", documents.HandleCreate(store))
r.Route("/{id}", func(r chi.Router) {
r.Get("/", documents.HandleGet(documentStore))
r.Get("/", documents.HandleGet(store))
})
})
r.Route("/auth/github", func(r chi.Router) {
r.Get("/login", auth.HandleGitHubLogin)
r.Get("/callback", auth.HandleGitHubCallback)
})
return r
}
func setupSocketIO() *socketio.Server {
opts := socketio.DefaultServerOptions()
opts.SetMaxHttpBufferSize(5000000)
@@ -242,39 +269,41 @@ func waitForShutdown(ioo *socketio.Server) {
}
func main() {
// Define a log level flag
logLevel := flag.String("loglevel", "info", "Set the logging level: debug, info, warn, error, fatal, panic")
listenAddr := flag.String("listen", ":3002", "Set the server listen address")
// Load .env file
if err := godotenv.Load(); err != nil {
logrus.Info("No .env file found")
}
listenAddress := flag.String("listen", ":3002", "The address to listen on.")
logLevel := flag.String("loglevel", "info", "The log level (debug, info, warn, error).")
flag.Parse()
// Set the log level
level, err := logrus.ParseLevel(*logLevel)
if err != nil {
fmt.Fprintf(os.Stderr, "Invalid log level: %v\n", err)
os.Exit(1)
logrus.Fatalf("Invalid log level: %v", err)
}
logrus.SetLevel(level)
documentStore := stores.GetStore() // Make sure this is well-defined in your "stores" package
r := setupRouter(documentStore)
ioo := setupSocketIO()
r.Handle("/socket.io/", ioo.ServeHandler(nil))
r.Get("/ping", func(w http.ResponseWriter, _ *http.Request) {
_, err := w.Write([]byte("pong"))
if err != nil {
panic(err)
}
logrus.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
})
auth.Init()
openai.Init()
store := stores.GetStore()
r := setupRouter(store)
ioo := setupSocketIO()
r.Mount("/socket.io/", ioo.ServeHandler(nil))
r.Mount("/", handleUI())
logrus.WithField("addr", *listenAddr).Info("starting server")
logrus.WithField("addr", *listenAddress).Info("starting server")
go func() {
if err := http.ListenAndServe(*listenAddr, r); err != nil {
if err := http.ListenAndServe(*listenAddress, r); err != nil {
logrus.WithField("event", "start server").Fatal(err)
}
}()
logrus.Debug("Server is running in the background")
waitForShutdown(ioo)
}