Files
2026-08-04 15:25:11 +08:00

135 lines
4.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
commonHttp "video-factory/common"
"video-factory/shortdrama/controller"
"video-factory/shortdrama/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gvalid"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
)
func init() {
gvalid.RegisterRule("password-complex", func(ctx context.Context, input gvalid.RuleFuncInput) error {
pwd := input.Value.String()
var hasUpper, hasLower, hasDigit, hasSpecial bool
for _, c := range pwd {
switch {
case c >= 'A' && c <= 'Z':
hasUpper = true
case c >= 'a' && c <= 'z':
hasLower = true
case c >= '0' && c <= '9':
hasDigit = true
default:
hasSpecial = true
}
}
if !hasUpper {
return errors.New("密码必须包含大写字母")
}
if !hasLower {
return errors.New("密码必须包含小写字母")
}
if !hasDigit {
return errors.New("密码必须包含数字")
}
if !hasSpecial {
return errors.New("密码必须包含特殊符号")
}
return nil
})
}
func main() {
// ==================== API 路由(通过 RouteRegister 自动注册,遵循 ai-agent 规范) ====================
commonHttp.RouteRegister([]interface{}{
controller.Drama,
controller.Scene,
controller.Character,
controller.Prop,
controller.Bgm,
controller.Episode,
controller.Generation,
controller.ModelConfig,
controller.UserModelConfig,
controller.User,
controller.Agent,
controller.Customer,
controller.Transaction,
controller.PaymentOrder,
controller.PaymentChannelTrade,
controller.PaymentConfig,
controller.RegionPricing,
})
// ==================== Workspace 文件服务(鉴权保护) ====================
// 通过 BindHandler 代替 AddStaticPath,确保经过 JWT 中间件鉴权
commonHttp.Httpserver.BindHandler("/workspace/*", func(r *ghttp.Request) {
relPath := strings.TrimPrefix(r.URL.Path, "/workspace/")
if relPath == "" || strings.Contains(relPath, "..") {
r.Response.WriteStatus(http.StatusForbidden)
return
}
filePath := filepath.Join("workspace", relPath)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
r.Response.WriteStatus(http.StatusNotFound)
return
}
r.Response.ServeFile(filePath)
})
// ==================== 前端静态资源服务(SPA,前后端合并部署) ====================
// 静态资源目录为 ui-src/dist:本地是 npm run build 产物,Docker 运行镜像由
// ui-builder 阶段将 dist 注入 /app/ui-src/dist,本地与容器路径一致,无需拷贝。
// 不用 AddStaticPath:GF 静态前缀有边界检查(前缀后一字符必须为 /),
// "/" 前缀无法命中 /assets/* 子路径,只能服务 "/" 本身。
// 前端使用 hash 路由,浏览器只请求 / 与 /assets/*BindHandler("/*") 精确覆盖即可,
// 无需 SPA fallback;具体 API 路由与 /workspace/* 优先级更高不受影响,
// 且 auth 中间件仅放行 GET / 与 GET /assets/*,其余路径一律走鉴权
if st, err := os.Stat("ui-src/dist"); err == nil && st.IsDir() {
commonHttp.Httpserver.BindHandler("/*", func(r *ghttp.Request) {
path := strings.TrimPrefix(r.URL.Path, "/")
if strings.Contains(path, "..") {
r.Response.WriteStatus(http.StatusNotFound)
return
}
if path == "" || path == "index.html" {
path = "index.html"
}
filePath := filepath.Join("ui-src/dist", path)
if st, err := os.Stat(filePath); err == nil && !st.IsDir() {
r.Response.ServeFile(filePath)
return
}
r.Response.WriteStatus(http.StatusNotFound)
})
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
// 启动时恢复未完成的视频生成轮询
service.GenerationService.StartVideoPoller(ctx)
g.Log().Info(ctx, "service started on :3006")
<-ctx.Done()
g.Log().Info(ctx, "shutting down...")
time.Sleep(3 * time.Second) // 等待当前任务完成
g.Log().Info(ctx, "bye")
}