diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..76495e2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,86 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 项目简介 + +Sun-Panel 是服务器/NAS 导航面板应用,前后端分离架构: + +- **前端**:Vue 3 + TypeScript + Vite + Naive UI + Pinia +- **后端**:Go + Gin + GORM,支持 SQLite(默认)或 MySQL,可选 Redis + +## 常用命令 + +### 前端 + +```bash +pnpm dev # 启动开发服务器,端口 1002 +pnpm build # 生产构建(类型检查 + Vite 打包) +pnpm lint # ESLint 检查 +pnpm lint:fix # 自动修复 +pnpm type-check # 仅 TypeScript 类型检查 +``` + +### 后端 + +```bash +cd service +go run main.go # 启动,默认端口 3002 +``` + +### Docker 全量构建 + +```bash +docker build -t sun-panel . +``` + +## 架构要点 + +### 前后端通信 + +开发时,Vite 将 `/api/*` 和 `/uploads/*` 代理到 `http://127.0.0.1:3002`(由 `.env` 中 `VITE_APP_API_BASE_URL` 控制)。生产部署时前后端同端口,Go 直接 serve 静态文件。 + +### 前端分层 + +``` +src/api/ → API 调用函数(按模块分:panel/ system/ 等) +src/store/ → Pinia 状态(auth/user/panel/admin/notice/moduleConfig/app) +src/views/ → 页面组件(home/login/exception) +src/components/ → UI 组件(apps/ common/ deskModule/) +src/utils/request/ → axios 封装,含 token 拦截器 +src/hooks/ → Composition API(useTheme/useLanguage/useBasicLayout/useIconRender) +src/locales/ → 国际化(zh-CN.json / en-US.json) +``` + +### 后端分层 + +``` +service/router/ → 路由注册,入口 A_ENTER.go +service/api/api_v1/ → Handler 层,中间件在 middleware/ +service/lib/ → 业务逻辑(user/cache/monitor/siteFavicon 等) +service/models/ → GORM 模型 +service/global/ → 全局变量(Db/Logger/Redis 等) +service/initialize/ → 启动初始化流程,入口 A_ENTER.go +``` + +后端每个子目录都以 `A_ENTER.go` 作为该模块的入口聚合文件。 + +### 路由分组(后端) + +- `/system/*` — 登录/用户/系统设置,需 LoginInterceptor +- `/panel/*` — 面板数据/图标管理,需 LoginInterceptor +- `/openness/*` — 无需认证的公开接口 + +### 配置文件 + +后端使用 INI 格式配置,示例见 `service/assets/conf.example.ini`。关键字段: +- `database_drive`:`sqlite`(默认)或 `mysql` +- `cache_drive` / `queue_drive`:`memory`(默认)或 `redis` +- `source_path`:上传文件存储路径,默认 `./uploads` + +## 开发注意 + +- 前端路径别名 `@` 指向 `src/` +- 提交前 Husky + lint-staged 会自动对 `.ts/.tsx/.vue` 运行 `eslint --fix` +- TypeScript 开启 `strict` 和 `noUnusedLocals`,避免引入 `any` +- 国际化新增文案需同时更新 `zh-CN.json` 和 `en-US.json` diff --git a/Dockerfile b/Dockerfile index 4e6ea91..9fc8699 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM node AS web_image # 华为源 # RUN npm config set registry https://repo.huaweicloud.com/repository/npm/ -RUN npm install pnpm -g +RUN npm install pnpm@8 -g WORKDIR /build diff --git a/package.json b/package.json index 47f5658..9729509 100644 --- a/package.json +++ b/package.json @@ -68,5 +68,8 @@ "*.{ts,tsx,vue}": [ "pnpm lint:fix" ] + }, + "pnpm": { + "onlyBuiltDependencies": ["esbuild", "vue-demi"] } } diff --git a/service/api/api_v1/system/A_ENTER.go b/service/api/api_v1/system/A_ENTER.go index 020bdc1..986c411 100644 --- a/service/api/api_v1/system/A_ENTER.go +++ b/service/api/api_v1/system/A_ENTER.go @@ -1,11 +1,13 @@ package system type ApiSystem struct { - About About - LoginApi LoginApi - UserApi UserApi - FileApi FileApi - NoticeApi NoticeApi - ModuleConfigApi ModuleConfigApi - MonitorApi MonitorApi + About About + LoginApi LoginApi + UserApi UserApi + FileApi FileApi + NoticeApi NoticeApi + ModuleConfigApi ModuleConfigApi + MonitorApi MonitorApi + SiteCustomizeApi SiteCustomizeApi + CustomStyleApi CustomStyleApi } diff --git a/service/api/api_v1/system/customStyle.go b/service/api/api_v1/system/customStyle.go new file mode 100644 index 0000000..f78b96e --- /dev/null +++ b/service/api/api_v1/system/customStyle.go @@ -0,0 +1,33 @@ +package system + +import ( + "sun-panel/api/api_v1/common/apiReturn" + "sun-panel/global" + "sun-panel/lib/cmn/systemSetting" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +type CustomStyleApi struct{} + +// 获取自定义 CSS/JS(无需登录) +func (a *CustomStyleApi) Get(c *gin.Context) { + cfg := systemSetting.CustomStyle{} + global.SystemSetting.GetValueByInterface(systemSetting.CUSTOM_STYLE, &cfg) + apiReturn.SuccessData(c, cfg) +} + +// 保存自定义 CSS/JS(管理员) +func (a *CustomStyleApi) Set(c *gin.Context) { + cfg := systemSetting.CustomStyle{} + if err := c.ShouldBindBodyWith(&cfg, binding.JSON); err != nil { + apiReturn.ErrorParamFomat(c, err.Error()) + return + } + if err := global.SystemSetting.Set(systemSetting.CUSTOM_STYLE, cfg); err != nil { + apiReturn.ErrorDatabase(c, err.Error()) + return + } + apiReturn.Success(c) +} diff --git a/service/api/api_v1/system/siteCustomize.go b/service/api/api_v1/system/siteCustomize.go new file mode 100644 index 0000000..8f65257 --- /dev/null +++ b/service/api/api_v1/system/siteCustomize.go @@ -0,0 +1,33 @@ +package system + +import ( + "sun-panel/api/api_v1/common/apiReturn" + "sun-panel/global" + "sun-panel/lib/cmn/systemSetting" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +type SiteCustomizeApi struct{} + +// 获取站点自定义配置(无需登录,登录页使用) +func (a *SiteCustomizeApi) Get(c *gin.Context) { + cfg := systemSetting.SiteCustomize{} + global.SystemSetting.GetValueByInterface(systemSetting.SITE_CUSTOMIZE, &cfg) + apiReturn.SuccessData(c, cfg) +} + +// 保存站点自定义配置(管理员) +func (a *SiteCustomizeApi) Set(c *gin.Context) { + cfg := systemSetting.SiteCustomize{} + if err := c.ShouldBindBodyWith(&cfg, binding.JSON); err != nil { + apiReturn.ErrorParamFomat(c, err.Error()) + return + } + if err := global.SystemSetting.Set(systemSetting.SITE_CUSTOMIZE, cfg); err != nil { + apiReturn.ErrorDatabase(c, err.Error()) + return + } + apiReturn.Success(c) +} diff --git a/service/initialize/database/connect.go b/service/initialize/database/connect.go index f0dc6a1..d6e4c84 100644 --- a/service/initialize/database/connect.go +++ b/service/initialize/database/connect.go @@ -135,13 +135,13 @@ func NotFoundAndCreateUser(db *gorm.DB) error { if err != gorm.ErrRecordNotFound { return err } - username := "admin@sun.cc" + username := "admin" fUser.Mail = username fUser.Username = username fUser.Name = username fUser.Status = 1 fUser.Role = 1 - fUser.Password = cmn.PasswordEncryption("12345678") + fUser.Password = cmn.PasswordEncryption("1234") if errCreate := db.Create(&fUser).Error; errCreate != nil { return errCreate diff --git a/service/lib/cmn/systemSetting/systemSetting.go b/service/lib/cmn/systemSetting/systemSetting.go index 2c45e3d..2a1ba7c 100644 --- a/service/lib/cmn/systemSetting/systemSetting.go +++ b/service/lib/cmn/systemSetting/systemSetting.go @@ -15,6 +15,8 @@ const ( DISCLAIMER = "disclaimer" // 免责声明 储存类型:字符串 WEB_ABOUT_DESCRIPTION = "web_about_description" // 关于的描述信息 PANEL_PUBLIC_USER_ID = "panel_public_user_id" // 公开访问模式用户id *uint|null + SITE_CUSTOMIZE = "site_customize" // 站点自定义配置 + CUSTOM_STYLE = "custom_style" // 自定义 CSS/JS ) type SystemSettingCache struct { @@ -43,6 +45,19 @@ type ApplicationSetting struct { WebSiteUrl string `json:"webSiteUrl"` // 站点地址 } +// 站点自定义配置 +type SiteCustomize struct { + SiteTitle string `json:"siteTitle"` // 站点标题(显示在登录页) + FaviconUrl string `json:"faviconUrl"` // Favicon 图标 URL + LoginDescription string `json:"loginDescription"` // 登录页描述文字 +} + +// 自定义 CSS/JS +type CustomStyle struct { + Css string `json:"css"` // 自定义 CSS 内容 + Js string `json:"js"` // 自定义 JS 内容 +} + var ( ErrorNoExists = errors.New("no exists") ) diff --git a/service/router/system/A_ENTER.go b/service/router/system/A_ENTER.go index 6ce6f19..10a1ad8 100644 --- a/service/router/system/A_ENTER.go +++ b/service/router/system/A_ENTER.go @@ -10,4 +10,6 @@ func Init(routerGroup *gin.RouterGroup) { InitNoticeRouter(routerGroup) InitModuleConfigRouter(routerGroup) InitMonitorRouter(routerGroup) + InitSiteCustomizeRouter(routerGroup) + InitCustomStyleRouter(routerGroup) } diff --git a/service/router/system/customStyle.go b/service/router/system/customStyle.go new file mode 100644 index 0000000..597faf5 --- /dev/null +++ b/service/router/system/customStyle.go @@ -0,0 +1,19 @@ +package system + +import ( + "sun-panel/api/api_v1" + "sun-panel/api/api_v1/middleware" + + "github.com/gin-gonic/gin" +) + +func InitCustomStyleRouter(router *gin.RouterGroup) { + api := api_v1.ApiGroupApp.ApiSystem.CustomStyleApi + + // 无需登录 + router.POST("/system/customStyle/get", api.Get) + + // 管理员才能修改 + rAdmin := router.Group("", middleware.LoginInterceptor, middleware.AdminInterceptor) + rAdmin.POST("/system/customStyle/set", api.Set) +} diff --git a/service/router/system/siteCustomize.go b/service/router/system/siteCustomize.go new file mode 100644 index 0000000..e4eae2f --- /dev/null +++ b/service/router/system/siteCustomize.go @@ -0,0 +1,19 @@ +package system + +import ( + "sun-panel/api/api_v1" + "sun-panel/api/api_v1/middleware" + + "github.com/gin-gonic/gin" +) + +func InitSiteCustomizeRouter(router *gin.RouterGroup) { + api := api_v1.ApiGroupApp.ApiSystem.SiteCustomizeApi + + // 无需登录(登录页读取) + router.POST("/system/siteCustomize/get", api.Get) + + // 管理员才能修改 + rAdmin := router.Group("", middleware.LoginInterceptor, middleware.AdminInterceptor) + rAdmin.POST("/system/siteCustomize/set", api.Set) +} diff --git a/src/App.vue b/src/App.vue index bf62b25..f353c31 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,36 @@ - + diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 675abd3..9b33123 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -1,6 +1,6 @@