fix frontend
This commit is contained in:
parent
fcc76911bb
commit
e41c1f96a2
|
|
@ -1,6 +1,6 @@
|
||||||
# 只保留會重複打的指令;其餘直接 go / migrate。
|
# 只保留會重複打的指令;其餘直接 go / migrate。
|
||||||
|
|
||||||
.PHONY: build test gen-api tidy migrate-up migrate-down
|
.PHONY: build test test-integration gen-api tidy migrate-up migrate-down
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build -o bin/gateway .
|
go build -o bin/gateway .
|
||||||
|
|
@ -9,6 +9,16 @@ build:
|
||||||
test:
|
test:
|
||||||
go test ./...
|
go test ./...
|
||||||
|
|
||||||
|
# M1–M5 integration gates
|
||||||
|
test-integration:
|
||||||
|
go test ./internal/module/member/usecase/ ./internal/middleware/ \
|
||||||
|
./internal/module/threads/... ./internal/module/job/... ./internal/module/appnotif/... \
|
||||||
|
./internal/module/usage/... ./internal/module/ai/... ./internal/module/search/... \
|
||||||
|
./internal/module/permission/... ./internal/module/studio/... \
|
||||||
|
./internal/module/inspire/... ./internal/module/scout/... \
|
||||||
|
./internal/logic/extension/ ./internal/logic/media/ \
|
||||||
|
-count=1 -timeout 180s
|
||||||
|
|
||||||
# 改 generate/api/*.api 後執行;handler 勿手寫
|
# 改 generate/api/*.api 後執行;handler 勿手寫
|
||||||
gen-api:
|
gen-api:
|
||||||
goctl api go -api generate/api/gateway.api -dir . -style go_zero --home generate/goctl
|
goctl api go -api generate/api/gateway.api -dir . -style go_zero --home generate/goctl
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,49 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"apps/backend/internal/config"
|
"apps/backend/internal/config"
|
||||||
|
"apps/backend/internal/module/ai"
|
||||||
|
appnotifRepo "apps/backend/internal/module/appnotif/repository"
|
||||||
|
appnotifUC "apps/backend/internal/module/appnotif/usecase"
|
||||||
|
fsDomain "apps/backend/internal/module/filestorage/domain"
|
||||||
|
"apps/backend/internal/module/filestorage/noop"
|
||||||
|
"apps/backend/internal/module/filestorage/s3store"
|
||||||
|
jobDomain "apps/backend/internal/module/job/domain"
|
||||||
|
jobRepo "apps/backend/internal/module/job/repository"
|
||||||
|
jobUC "apps/backend/internal/module/job/usecase"
|
||||||
|
memberDomain "apps/backend/internal/module/member/domain"
|
||||||
|
memberRepo "apps/backend/internal/module/member/repository"
|
||||||
|
scoutRepo "apps/backend/internal/module/scout/repository"
|
||||||
|
scoutUC "apps/backend/internal/module/scout/usecase"
|
||||||
|
studioPublish "apps/backend/internal/module/studio/publish"
|
||||||
|
studioRepo "apps/backend/internal/module/studio/repository"
|
||||||
|
studioUC "apps/backend/internal/module/studio/usecase"
|
||||||
|
threadsDomain "apps/backend/internal/module/threads/domain"
|
||||||
|
threadsProv "apps/backend/internal/module/threads/provider"
|
||||||
|
threadsRepo "apps/backend/internal/module/threads/repository"
|
||||||
|
threadsUC "apps/backend/internal/module/threads/usecase"
|
||||||
|
usageDomain "apps/backend/internal/module/usage/domain"
|
||||||
|
usageRepo "apps/backend/internal/module/usage/repository"
|
||||||
|
usageUC "apps/backend/internal/module/usage/usecase"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/conf"
|
"github.com/zeromicro/go-zero/core/conf"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Independent worker binary (A-03). M0 stub: config load + heartbeat log only.
|
// Worker:demo + threads_token_renew + persona_analyze_* + outbox publish
|
||||||
// Outbox/job runners land in M2/M4 (T133/T169).
|
//
|
||||||
|
// cd apps/backend && go build -o bin/worker ./cmd/worker
|
||||||
|
// ./bin/worker -f etc/gateway.yaml
|
||||||
func main() {
|
func main() {
|
||||||
configFile := flag.String("f", "etc/gateway.yaml", "config file")
|
configFile := flag.String("f", "etc/gateway.yaml", "config file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
@ -25,31 +53,319 @@ func main() {
|
||||||
c.ApplyEnv()
|
c.ApplyEnv()
|
||||||
|
|
||||||
workerID := c.Worker.ID
|
workerID := c.Worker.ID
|
||||||
if workerID == "" {
|
if workerID == "" || workerID == "worker-1" || workerID == "worker-local-1" {
|
||||||
workerID = "worker-1"
|
workerID = "demo-worker-1"
|
||||||
}
|
}
|
||||||
interval := time.Duration(c.Worker.PollIntervalMs) * time.Millisecond
|
interval := time.Duration(c.Worker.PollIntervalMs) * time.Millisecond
|
||||||
if interval <= 0 {
|
if interval <= 0 {
|
||||||
interval = 2 * time.Second
|
interval = 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
logx.Infof("haixun worker stub started id=%s interval=%s (no publish yet)", workerID, interval)
|
appN := appnotifUC.New(appnotifRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database))
|
||||||
fmt.Printf("worker stub running id=%s (Ctrl+C to stop)\n", workerID)
|
jobs := jobUC.New(jobRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database))
|
||||||
|
jobs.Notifier = appN
|
||||||
|
|
||||||
|
secret := c.Auth.AccessSecret
|
||||||
|
if secret == "" {
|
||||||
|
secret = "dev-token-secret"
|
||||||
|
}
|
||||||
|
var provider threadsDomain.Provider
|
||||||
|
if strings.TrimSpace(c.Platform.ThreadsAppId) != "" && strings.TrimSpace(c.Platform.ThreadsAppSecret) != "" {
|
||||||
|
provider = threadsProv.NewMeta(c.Platform.ThreadsAppId, c.Platform.ThreadsAppSecret)
|
||||||
|
logx.Info("worker threads: meta provider")
|
||||||
|
} else {
|
||||||
|
provider = &threadsProv.FakeProvider{}
|
||||||
|
logx.Info("worker threads: fake provider")
|
||||||
|
}
|
||||||
|
threadsSvc := threadsUC.New(threadsRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database), provider, secret)
|
||||||
|
threadsSvc.Renew = jobs
|
||||||
|
|
||||||
|
// usage + AI keys(與 gateway 對齊,persona LLM 需真 key)
|
||||||
|
members := memberRepo.NewMoncStore(c.Mongo.URI, c.Mongo.Database, c.CacheRedis)
|
||||||
|
keyRes := &usageUC.SettingsResolver{
|
||||||
|
Members: members,
|
||||||
|
PlatformAI: c.Platform.AIKey,
|
||||||
|
PlatformXAI: c.Platform.XAIKey,
|
||||||
|
PlatformOpenCode: c.Platform.OpenCodeKey,
|
||||||
|
PlatformExa: c.Platform.ExaKey,
|
||||||
|
}
|
||||||
|
if keyRes.PlatformAI == "" && keyRes.PlatformXAI == "" && keyRes.PlatformOpenCode == "" {
|
||||||
|
keyRes.PlatformAI = "fake-platform-ai"
|
||||||
|
logx.Info("worker usage: no platform AI keys — fake-platform-ai for quota only")
|
||||||
|
}
|
||||||
|
usageSvc := usageUC.New(usageRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database), keyRes)
|
||||||
|
aiRegistry := ai.NewRegistry()
|
||||||
|
|
||||||
|
// Outbox/scout 發文:有 Meta 憑證則真 Threads Graph,否則 Fake(單元測試用)
|
||||||
|
var pub studioPublish.Transport
|
||||||
|
if strings.TrimSpace(c.Platform.ThreadsAppId) != "" && strings.TrimSpace(c.Platform.ThreadsAppSecret) != "" {
|
||||||
|
pub = studioPublish.NewMeta("https://graph.threads.net")
|
||||||
|
logx.Info("worker studio: meta publish transport (real Threads)")
|
||||||
|
} else {
|
||||||
|
pub = studioPublish.NewFake()
|
||||||
|
logx.Info("worker studio: fake publish transport (no Threads app credentials)")
|
||||||
|
}
|
||||||
|
studio := studioUC.New(studioRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database), pub)
|
||||||
|
studio.Accounts = threadsSvc
|
||||||
|
studio.Usage = usageSvc
|
||||||
|
studio.AI = &ai.FakeClient{}
|
||||||
|
studio.AIRegistry = aiRegistry
|
||||||
|
studio.Keys = &workerAIKeys{Members: members, Resolver: keyRes}
|
||||||
|
// 發文暫存圖:Meta 發成功後刪 temp/*
|
||||||
|
store := workerStorage(c)
|
||||||
|
studio.Storage = store
|
||||||
|
studio.StoragePublicBase = strings.TrimSpace(c.ObjectStorage.PublicBaseURL)
|
||||||
|
|
||||||
|
scoutSvc := scoutUC.New(scoutRepo.NewMonStore(c.Mongo.URI, c.Mongo.Database))
|
||||||
|
scoutSvc.Settings = &workerDevMode{Members: members}
|
||||||
|
scoutSvc.Transport = pub
|
||||||
|
scoutSvc.Accounts = threadsSvc
|
||||||
|
studio.Crawler = scoutSvc
|
||||||
|
// worker 執行 job 本體,不經 API 再入列
|
||||||
|
studio.Jobs = nil
|
||||||
|
|
||||||
|
logx.Infof("haixun worker started id=%s interval=%s (demo + token_renew + persona_analyze + compose_mimic + outbox)", workerID, interval)
|
||||||
|
fmt.Printf("worker running id=%s interval=%s (Ctrl+C to stop)\n", workerID, interval)
|
||||||
|
|
||||||
sig := make(chan os.Signal, 1)
|
sig := make(chan os.Signal, 1)
|
||||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
tick := time.NewTicker(interval)
|
tick := time.NewTicker(interval)
|
||||||
defer tick.Stop()
|
defer tick.Stop()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sig:
|
case <-sig:
|
||||||
logx.Infof("worker %s shutting down", workerID)
|
logx.Infof("worker %s shutting down", workerID)
|
||||||
return
|
return
|
||||||
case t := <-tick.C:
|
case <-tick.C:
|
||||||
// Health heartbeat only — no job/outbox processing in M0.
|
// 1) claim one due job
|
||||||
logx.Infof("worker %s heartbeat at %s", workerID, t.UTC().Format(time.RFC3339))
|
j, err := jobs.ClaimNext(ctx, workerID)
|
||||||
|
if err == nil {
|
||||||
|
switch j.TemplateType {
|
||||||
|
case "", jobDomain.TemplateDemo:
|
||||||
|
logx.Infof("worker %s demo job %s", workerID, j.ID)
|
||||||
|
if _, err := jobs.RunDemoToSuccess(ctx, j.ID); err != nil {
|
||||||
|
logx.Errorf("worker %s job %s failed: %v", workerID, j.ID, err)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, err.Error())
|
||||||
|
} else {
|
||||||
|
logx.Infof("worker %s job %s succeeded", workerID, j.ID)
|
||||||
|
}
|
||||||
|
case jobDomain.TemplateThreadsTokenRenew:
|
||||||
|
logx.Infof("worker %s token renew job %s account=%s", workerID, j.ID, j.RefID)
|
||||||
|
if err := runTokenRenew(ctx, jobs, threadsSvc, j); err != nil {
|
||||||
|
logx.Errorf("worker %s renew %s failed: %v", workerID, j.ID, err)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, err.Error())
|
||||||
|
} else {
|
||||||
|
logx.Infof("worker %s renew %s ok", workerID, j.ID)
|
||||||
|
}
|
||||||
|
case jobDomain.TemplatePersonaAnalyzeAccount, jobDomain.TemplatePersonaAnalyzeText:
|
||||||
|
logx.Infof("worker %s persona analyze job %s type=%s persona=%s", workerID, j.ID, j.TemplateType, j.RefID)
|
||||||
|
if err := runPersonaAnalyze(ctx, jobs, studio, j); err != nil {
|
||||||
|
logx.Errorf("worker %s persona %s failed: %v", workerID, j.ID, err)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, err.Error())
|
||||||
|
} else {
|
||||||
|
logx.Infof("worker %s persona %s ok", workerID, j.ID)
|
||||||
|
}
|
||||||
|
case jobDomain.TemplateComposeMimic:
|
||||||
|
logx.Infof("worker %s compose_mimic job %s", workerID, j.ID)
|
||||||
|
if err := runComposeMimic(ctx, jobs, studio, j); err != nil {
|
||||||
|
logx.Errorf("worker %s compose_mimic %s failed: %v", workerID, j.ID, err)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, err.Error())
|
||||||
|
} else {
|
||||||
|
logx.Infof("worker %s compose_mimic %s ok", workerID, j.ID)
|
||||||
|
}
|
||||||
|
case jobDomain.TemplatePlayGenerateScript:
|
||||||
|
logx.Infof("worker %s play_generate_script job %s play=%s", workerID, j.ID, j.RefID)
|
||||||
|
if err := runPlayGenerateScript(ctx, jobs, studio, j); err != nil {
|
||||||
|
logx.Errorf("worker %s play_generate_script %s failed: %v", workerID, j.ID, err)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, err.Error())
|
||||||
|
} else {
|
||||||
|
logx.Infof("worker %s play_generate_script %s ok", workerID, j.ID)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
logx.Infof("worker %s unknown template %s job %s — fail", workerID, j.TemplateType, j.ID)
|
||||||
|
_, _ = jobs.FailJob(ctx, j.ID, "unknown template: "+j.TemplateType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2) outbox due steps
|
||||||
|
n, err := studio.ProcessDueSteps(ctx, 0)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("worker %s outbox tick: %v", workerID, err)
|
||||||
|
} else if n > 0 {
|
||||||
|
logx.Infof("worker %s outbox published %d step(s)", workerID, n)
|
||||||
|
}
|
||||||
|
// 3) 終態任務超過 2 天自動清除
|
||||||
|
if purged, err := jobs.PurgeExpiredTerminal(ctx); err != nil {
|
||||||
|
logx.Errorf("worker %s purge jobs: %v", workerID, err)
|
||||||
|
} else if purged > 0 {
|
||||||
|
logx.Infof("worker %s purged %d expired terminal job(s)", workerID, purged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTokenRenew(ctx context.Context, jobs *jobUC.Service, threads *threadsUC.Service, j *jobDomain.Job) error {
|
||||||
|
if j.RefID == "" {
|
||||||
|
return fmt.Errorf("缺少帳號 ref_id")
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 15, "定期延長 Threads token · 準備中")
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 40, "定期延長 Threads token · 向平台刷新中")
|
||||||
|
acc, err := threads.Refresh(ctx, j.OwnerUID, j.RefID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 75, "定期延長 Threads token · 排程下一次")
|
||||||
|
// 下次再約 30 天
|
||||||
|
next := jobDomain.NowNano() + jobDomain.DefaultTokenRenewDelayNs
|
||||||
|
if acc != nil && acc.SessionExpiresAt > 0 {
|
||||||
|
early := acc.SessionExpiresAt - int64(30*24*time.Hour)
|
||||||
|
if early > jobDomain.NowNano() {
|
||||||
|
next = early
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = jobs.ScheduleTokenRenew(ctx, j.OwnerUID, j.RefID, next)
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 90, "定期延長 Threads token · 寫入完成")
|
||||||
|
_, err = jobs.SucceedJob(ctx, j.ID, "定期延長完成 · 已排程約 30 天後再執行")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPersonaAnalyze(ctx context.Context, jobs *jobUC.Service, studio *studioUC.Service, j *jobDomain.Job) error {
|
||||||
|
if j.RefID == "" {
|
||||||
|
return fmt.Errorf("缺少人設 ref_id")
|
||||||
|
}
|
||||||
|
startSum := "人設分析 · 文字分析中…"
|
||||||
|
if j.TemplateType == jobDomain.TemplatePersonaAnalyzeAccount {
|
||||||
|
startSum = "人設分析 · 爬取公開貼文 + AI 分析中…(可離開頁面)"
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 10, startSum)
|
||||||
|
|
||||||
|
err := studio.ExecutePersonaAnalyzeJob(ctx, j.TemplateType, j.OwnerUID, j.RefID, j.Payload, func(pct int, sum string) {
|
||||||
|
if pct < 10 {
|
||||||
|
pct = 10
|
||||||
|
}
|
||||||
|
if pct > 95 {
|
||||||
|
pct = 95
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, pct, sum)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = jobs.SucceedJob(ctx, j.ID, "人設分析完成 · 已寫入指紋/範本")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPlayGenerateScript(ctx context.Context, jobs *jobUC.Service, studio *studioUC.Service, j *jobDomain.Job) error {
|
||||||
|
var pl jobUC.PlayGenerateScriptPayload
|
||||||
|
if err := json.Unmarshal([]byte(j.Payload), &pl); err != nil {
|
||||||
|
return fmt.Errorf("invalid play_generate_script payload: %w", err)
|
||||||
|
}
|
||||||
|
playID := strings.TrimSpace(pl.PlayID)
|
||||||
|
if playID == "" {
|
||||||
|
playID = strings.TrimSpace(j.RefID)
|
||||||
|
}
|
||||||
|
if playID == "" {
|
||||||
|
return fmt.Errorf("empty play_id")
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 15, "劇本產文 · 準備 AI(一次產全部)…")
|
||||||
|
llmCtx, cancel := context.WithTimeout(ctx, 4*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 40, "劇本產文 · 呼叫模型中(可離開頁面)…")
|
||||||
|
n, err := studio.GeneratePlayScript(llmCtx, j.OwnerUID, playID, pl.OnlyEmpty)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pl.Filled = n
|
||||||
|
body, _ := json.Marshal(pl)
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 90, fmt.Sprintf("劇本產文 · 已寫入 %d 步…", n))
|
||||||
|
_, err = jobs.SucceedJobWithPayload(ctx, j.ID, fmt.Sprintf("劇本產文完成 · 已填 %d 步,請回互回方案查看", n), string(body))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runComposeMimic(ctx context.Context, jobs *jobUC.Service, studio *studioUC.Service, j *jobDomain.Job) error {
|
||||||
|
var pl jobUC.ComposeMimicPayload
|
||||||
|
if err := json.Unmarshal([]byte(j.Payload), &pl); err != nil {
|
||||||
|
return fmt.Errorf("invalid compose_mimic payload: %w", err)
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(pl.SourceText) == "" {
|
||||||
|
return fmt.Errorf("empty source_text")
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 15, "仿寫貼文 · 準備 AI…")
|
||||||
|
// worker 不綁 gateway 120s;給模型較長時間(reasoning 模型需要)
|
||||||
|
llmCtx, cancel := context.WithTimeout(ctx, 4*time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 35, "仿寫貼文 · 呼叫模型中(可離開頁面)…")
|
||||||
|
text, err := studio.Mimic(llmCtx, j.OwnerUID, pl.SourceText, pl.PersonaID, pl.StructureNotes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = jobs.MarkRunningProgress(ctx, j.ID, 90, "仿寫貼文 · 寫入結果…")
|
||||||
|
pl.ResultText = text
|
||||||
|
body, _ := json.Marshal(pl)
|
||||||
|
_, err = jobs.SucceedJobWithPayload(ctx, j.ID, "仿寫完成 · 已可套用到正文", string(body))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// workerAIKeys — 與 gateway studioAIKeys 對齊
|
||||||
|
type workerAIKeys struct {
|
||||||
|
Members memberDomain.Repository
|
||||||
|
Resolver *usageUC.SettingsResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *workerAIKeys) ResolveAI(ctx context.Context, ownerUID int64) (provider, model, apiKey string, err error) {
|
||||||
|
st, err := k.Members.GetSettings(ctx, ownerUID)
|
||||||
|
if err != nil || st == nil {
|
||||||
|
st = memberDomain.DefaultSettings(ownerUID)
|
||||||
|
}
|
||||||
|
// 完全依會員 AI 設定(provider / model / key),不寫死模型
|
||||||
|
provider = ai.NormalizeProvider(st.Provider)
|
||||||
|
model = strings.TrimSpace(st.Model)
|
||||||
|
if model == "" {
|
||||||
|
return provider, "", "", fmt.Errorf("請到設定選擇 AI 模型")
|
||||||
|
}
|
||||||
|
if k.Resolver != nil {
|
||||||
|
_, key, rerr := k.Resolver.ResolveKey(ctx, ownerUID, usageDomain.MeterAICopy)
|
||||||
|
if rerr == nil && strings.TrimSpace(key) != "" {
|
||||||
|
return provider, model, key, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key := st.KeyForProvider(provider); key != "" {
|
||||||
|
return provider, model, key, nil
|
||||||
|
}
|
||||||
|
return provider, model, "", fmt.Errorf("no AI key(請到設定填寫 Key,或確認平台已配置)")
|
||||||
|
}
|
||||||
|
|
||||||
|
type workerDevMode struct {
|
||||||
|
Members memberDomain.Repository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *workerDevMode) DevModeEnabled(ctx context.Context, uid int64) (bool, error) {
|
||||||
|
if d == nil || d.Members == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
st, err := d.Members.GetSettings(ctx, uid)
|
||||||
|
if err != nil || st == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return st.DevModeEnabled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func workerStorage(c config.Config) fsDomain.Storage {
|
||||||
|
os := c.ObjectStorage
|
||||||
|
if strings.TrimSpace(os.Endpoint) == "" {
|
||||||
|
logx.Info("worker storage: disabled (no endpoint)")
|
||||||
|
return noop.New()
|
||||||
|
}
|
||||||
|
s, err := s3store.New(s3store.Config{
|
||||||
|
Endpoint: os.Endpoint, Region: os.Region, Bucket: os.Bucket,
|
||||||
|
AccessKey: os.AccessKey, SecretKey: os.SecretKey,
|
||||||
|
UsePathStyle: os.UsePathStyle, PublicBaseURL: os.PublicBaseURL,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("worker storage init failed: %v", err)
|
||||||
|
return noop.New()
|
||||||
|
}
|
||||||
|
logx.Infof("worker storage: minio/s3 bucket=%s (ephemeral publish images cleanup enabled)", os.Bucket)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
Name: haixun-gateway
|
Name: haixun-gateway
|
||||||
Host: 0.0.0.0
|
Host: 0.0.0.0
|
||||||
Port: 8888
|
Port: 8888
|
||||||
Timeout: 30000
|
# AI 產文/試產可能超過 30s;過短會 503 且前端拿不到結果
|
||||||
|
Timeout: 120000
|
||||||
MaxBytes: 104857600
|
MaxBytes: 104857600
|
||||||
|
|
||||||
Log:
|
Log:
|
||||||
|
|
@ -32,14 +33,23 @@ Auth:
|
||||||
RefreshSecret: "change-me-refresh-secret"
|
RefreshSecret: "change-me-refresh-secret"
|
||||||
RefreshExpire: 604800
|
RefreshExpire: 604800
|
||||||
|
|
||||||
|
# 平台預設 key(所有會員可用;會員在設定頁填的 BYOK 優先)
|
||||||
|
# 也可用環境變數覆寫:PLATFORM_XAI_KEY / PLATFORM_OPENCODE_KEY / PLATFORM_EXA_KEY / PLATFORM_AI_KEY
|
||||||
Platform:
|
Platform:
|
||||||
|
# xAI(api.x.ai)— 人設 LLM、文案等
|
||||||
|
XAIKey: "xai-TtmPWVAz1CdssptRetj9iOFSdSG2LZDffEOTKzIiDmVSDqUL62lVdDladlQIXAwoeHtowWraVqYuHzl1"
|
||||||
|
# 相容舊名:XAIKey 空時會用 AIKey
|
||||||
AIKey: ""
|
AIKey: ""
|
||||||
ExaKey: ""
|
# OpenCode Go(opencode.ai/zen/go)
|
||||||
ThreadsAppId: ""
|
OpenCodeKey: "sk-sJkJVG2Q3tQEGqgl4cCy10QCVnrHpvuzR8RuE2eTuvruXiVNromsQ7zo6db0Ssj1"
|
||||||
ThreadsAppSecret: ""
|
# Exa 網搜
|
||||||
|
ExaKey: "5d9af9b2-a847-4295-a110-f666049e1073"
|
||||||
|
ThreadsAppId: "2733369680379930"
|
||||||
|
ThreadsAppSecret: "31db4afdd8e348d75d76dee8fb9a42d3"
|
||||||
|
|
||||||
Worker:
|
Worker:
|
||||||
ID: "worker-local-1"
|
# demo-worker 預設 id(cmd/worker 若見 worker-local-1 會改成 demo-worker-1)
|
||||||
|
ID: "demo-worker-1"
|
||||||
PollIntervalMs: 2000
|
PollIntervalMs: 2000
|
||||||
|
|
||||||
Bcrypt:
|
Bcrypt:
|
||||||
|
|
@ -62,7 +72,7 @@ OAuth:
|
||||||
# 正式 Mailgun SMTP 例:
|
# 正式 Mailgun SMTP 例:
|
||||||
# Host: smtp.mailgun.org Port: 587
|
# Host: smtp.mailgun.org Port: 587
|
||||||
# User: postmaster@YOUR_DOMAIN Password: <API SMTP password>
|
# User: postmaster@YOUR_DOMAIN Password: <API SMTP password>
|
||||||
# DevExposeCode: false
|
# DevExposeCode: false試產
|
||||||
#
|
#
|
||||||
# env 覆寫:MAIL_SMTP_* 或 HAIXUN_SMTP_*(舊名相容)
|
# env 覆寫:MAIL_SMTP_* 或 HAIXUN_SMTP_*(舊名相容)
|
||||||
Mail:
|
Mail:
|
||||||
|
|
@ -77,7 +87,7 @@ Mail:
|
||||||
|
|
||||||
# 前端 origin:重設信連結 + 預設 logo URL(/brand-mark.jpg)
|
# 前端 origin:重設信連結 + 預設 logo URL(/brand-mark.jpg)
|
||||||
# 遠端 dev:https://threads-tool-dev.30cm.net
|
# 遠端 dev:https://threads-tool-dev.30cm.net
|
||||||
PublicWebBase: "http://127.0.0.1:5173"
|
PublicWebBase: "https://threads-tool-dev.30cm.net"
|
||||||
|
|
||||||
# 郵件品牌(hermes 模板;LogoURL 空 = PublicWebBase/brand-mark.jpg)
|
# 郵件品牌(hermes 模板;LogoURL 空 = PublicWebBase/brand-mark.jpg)
|
||||||
Brand:
|
Brand:
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ func main() {
|
||||||
|
|
||||||
ctx := svc.NewServiceContext(c)
|
ctx := svc.NewServiceContext(c)
|
||||||
handler.RegisterHandlers(server, ctx)
|
handler.RegisterHandlers(server, ctx)
|
||||||
|
// 靈感 SSE stream(goctl 不生成 streaming handler)
|
||||||
|
handler.RegisterInspireStream(server, ctx)
|
||||||
|
|
||||||
logx.Infof("haixun gateway %s:%d pid=%d", c.Host, c.Port, os.Getpid())
|
logx.Infof("haixun gateway %s:%d pid=%d", c.Host, c.Port, os.Getpid())
|
||||||
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// ST-16: extension zip download (authenticated member)
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: extension
|
||||||
|
prefix: /api/v1/extension
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler DownloadExtensionZip
|
||||||
|
get /download returns (Empty)
|
||||||
|
}
|
||||||
|
|
@ -13,3 +13,11 @@ import "auth.api"
|
||||||
import "admin.api"
|
import "admin.api"
|
||||||
import "settings.api"
|
import "settings.api"
|
||||||
import "media.api"
|
import "media.api"
|
||||||
|
import "threads.api"
|
||||||
|
import "jobs.api"
|
||||||
|
import "notifications.api"
|
||||||
|
import "usage.api"
|
||||||
|
import "proxy.api"
|
||||||
|
import "extension.api"
|
||||||
|
import "studio.api"
|
||||||
|
import "m5.api"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
type (
|
||||||
|
JobPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
TemplateType string `json:"template_type"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProgressSummary string `json:"progress_summary"`
|
||||||
|
ProgressPercent int `json:"progress_percent"`
|
||||||
|
Error string `json:"error,optional"`
|
||||||
|
// 業務關聯(例:threads 帳號 id、人設 id)
|
||||||
|
RefId string `json:"ref_id,optional"`
|
||||||
|
// 任務 payload JSON(例:{"username":"x"})
|
||||||
|
Payload string `json:"payload,optional"`
|
||||||
|
// 排程執行時間 unix ns;0=立刻可領
|
||||||
|
RunAfter int64 `json:"run_after,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
CompletedAt int64 `json:"completed_at,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
JobListData {
|
||||||
|
List []JobPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
JobIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: jobs
|
||||||
|
prefix: /api/v1/jobs
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListJobs
|
||||||
|
get / returns (JobListData)
|
||||||
|
|
||||||
|
@handler GetJob
|
||||||
|
get /:id (JobIdPath) returns (JobPublic)
|
||||||
|
|
||||||
|
// 手動刪除任務(執行中不可刪;終態/已排程可刪)
|
||||||
|
@handler DeleteJob
|
||||||
|
delete /:id (JobIdPath) returns (OkData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 管理員:產生 demo/測試任務(由 demo-worker 消費)
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: jobs
|
||||||
|
prefix: /api/v1/jobs
|
||||||
|
middleware: AuthJWT,AdminAuth
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler StartDemoJob
|
||||||
|
post /demo returns (JobPublic)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,465 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// M5: inspire + research + media image + scout
|
||||||
|
|
||||||
|
type (
|
||||||
|
// --- Inspire ---
|
||||||
|
TrendPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
Heat int `json:"heat"`
|
||||||
|
Keywords []string `json:"keywords"`
|
||||||
|
Samples []string `json:"samples"`
|
||||||
|
SourceLabel string `json:"source_label"`
|
||||||
|
ObservedAt int64 `json:"observed_at"`
|
||||||
|
}
|
||||||
|
TrendListData {
|
||||||
|
List []TrendPublic `json:"list"`
|
||||||
|
}
|
||||||
|
InspireElementPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
RefId string `json:"ref_id,optional"`
|
||||||
|
Reusable bool `json:"reusable"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
InspireElementListData {
|
||||||
|
List []InspireElementPublic `json:"list"`
|
||||||
|
}
|
||||||
|
InspireElementSaveReq {
|
||||||
|
Id string `json:"id,optional"`
|
||||||
|
Kind string `json:"kind,optional"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
RefId string `json:"ref_id,optional"`
|
||||||
|
Reusable bool `json:"reusable,optional"`
|
||||||
|
}
|
||||||
|
InspireElementIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
InspireDraftPublic {
|
||||||
|
Title string `json:"title,optional"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
InspireMessagePublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Draft InspireDraftPublic `json:"draft,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
InspireSessionPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title,optional"`
|
||||||
|
Messages []InspireMessagePublic `json:"messages"`
|
||||||
|
PinnedElementIds []string `json:"pinned_element_ids"`
|
||||||
|
CreatedAt int64 `json:"created_at,optional"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
// 列表摘要(不含完整 messages)
|
||||||
|
InspireSessionSummaryPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Preview string `json:"preview"`
|
||||||
|
MessageCount int `json:"message_count"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
}
|
||||||
|
InspireSessionListData {
|
||||||
|
List []InspireSessionSummaryPublic `json:"list"`
|
||||||
|
}
|
||||||
|
InspireSessionIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
InspireSessionCreateReq {
|
||||||
|
Title string `json:"title,optional"`
|
||||||
|
}
|
||||||
|
InspireChatReq {
|
||||||
|
Message string `json:"message"`
|
||||||
|
PinnedIds []string `json:"pinned_ids,optional"`
|
||||||
|
Mode string `json:"mode,optional"` // chat|generate
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
SessionId string `json:"session_id,optional"` // 空 = active
|
||||||
|
// generate 專用:待改寫素材(鎖定內容);空則拒絕產文
|
||||||
|
Material string `json:"material,optional"`
|
||||||
|
}
|
||||||
|
InspireChatData {
|
||||||
|
Session InspireSessionPublic `json:"session"`
|
||||||
|
Messages []InspireMessagePublic `json:"messages"`
|
||||||
|
}
|
||||||
|
// 觀測:與送出相同的 prompt 組裝(不呼叫 AI、不計費)
|
||||||
|
InspirePromptPreviewReq {
|
||||||
|
Message string `json:"message,optional"`
|
||||||
|
PinnedIds []string `json:"pinned_ids,optional"`
|
||||||
|
Mode string `json:"mode,optional"` // chat|generate
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
SessionId string `json:"session_id,optional"`
|
||||||
|
Material string `json:"material,optional"`
|
||||||
|
}
|
||||||
|
InspirePromptPinnedPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
InspirePromptBlockPublic {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
InspirePromptPreviewData {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Sections []string `json:"sections"`
|
||||||
|
Blocks []InspirePromptBlockPublic `json:"blocks"`
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
CharCount int `json:"char_count"`
|
||||||
|
RuneCount int `json:"rune_count"`
|
||||||
|
Pinned []InspirePromptPinnedPublic `json:"pinned"`
|
||||||
|
Note string `json:"note,optional"`
|
||||||
|
}
|
||||||
|
ResearchSearchReq {
|
||||||
|
Query string `json:"query"`
|
||||||
|
}
|
||||||
|
ResearchHitPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Snippet string `json:"snippet"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Summary string `json:"summary,optional"`
|
||||||
|
LearnPoints []string `json:"learn_points,optional"`
|
||||||
|
ReplyHooks []string `json:"reply_hooks,optional"`
|
||||||
|
SourceLabel string `json:"source_label,optional"`
|
||||||
|
}
|
||||||
|
ResearchSearchData {
|
||||||
|
List []ResearchHitPublic `json:"list"`
|
||||||
|
}
|
||||||
|
GenerateImageReq {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
}
|
||||||
|
GenerateImageData {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Scout ---
|
||||||
|
BrandPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Brief string `json:"brief"`
|
||||||
|
TargetAudience string `json:"target_audience,optional"`
|
||||||
|
Goals string `json:"goals,optional"`
|
||||||
|
}
|
||||||
|
BrandListData {
|
||||||
|
List []BrandPublic `json:"list"`
|
||||||
|
}
|
||||||
|
BrandSaveReq {
|
||||||
|
Id string `json:"id,optional"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Brief string `json:"brief,optional"`
|
||||||
|
TargetAudience string `json:"target_audience,optional"`
|
||||||
|
Goals string `json:"goals,optional"`
|
||||||
|
}
|
||||||
|
BrandIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
BrandActiveData {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
BrandSetActiveReq {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
ProductPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
BrandId string `json:"brand_id"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
ProductContext string `json:"product_context"`
|
||||||
|
MatchTags []string `json:"match_tags"`
|
||||||
|
PainPoints []string `json:"pain_points"`
|
||||||
|
PlacementUrl string `json:"placement_url,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
ProductListData {
|
||||||
|
List []ProductPublic `json:"list"`
|
||||||
|
}
|
||||||
|
ProductSaveReq {
|
||||||
|
Id string `json:"id,optional"`
|
||||||
|
BrandId string `json:"brand_id"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
ProductContext string `json:"product_context,optional"`
|
||||||
|
MatchTags []string `json:"match_tags,optional"`
|
||||||
|
PainPoints []string `json:"pain_points,optional"`
|
||||||
|
PlacementUrl string `json:"placement_url,optional"`
|
||||||
|
}
|
||||||
|
ProductIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
ProductListReq {
|
||||||
|
BrandId string `form:"brand_id,optional"`
|
||||||
|
}
|
||||||
|
ImportProductReq {
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
ImportProductData {
|
||||||
|
Label string `json:"label"`
|
||||||
|
ProductContext string `json:"product_context"`
|
||||||
|
PainPoints []string `json:"pain_points"`
|
||||||
|
MatchTags []string `json:"match_tags"`
|
||||||
|
PlacementUrl string `json:"placement_url"`
|
||||||
|
SourceNote string `json:"source_note"`
|
||||||
|
}
|
||||||
|
ScoutBriefReq {
|
||||||
|
Intent string `json:"intent"`
|
||||||
|
BrandId string `json:"brand_id,optional"`
|
||||||
|
ProductId string `json:"product_id,optional"`
|
||||||
|
Purpose string `json:"purpose,optional"`
|
||||||
|
Deep bool `json:"deep,optional"`
|
||||||
|
}
|
||||||
|
ScoutBriefPublic {
|
||||||
|
Intent string `json:"intent"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
BrandId string `json:"brand_id,optional"`
|
||||||
|
ProductId string `json:"product_id,optional"`
|
||||||
|
ProductLabel string `json:"product_label,optional"`
|
||||||
|
Pains []string `json:"pains"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Periphery []string `json:"periphery"`
|
||||||
|
ScanTerms []string `json:"scan_terms"`
|
||||||
|
PlacementNote string `json:"placement_note,optional"`
|
||||||
|
ResponseStance string `json:"response_stance,optional"`
|
||||||
|
ThemeKey string `json:"theme_key,optional"`
|
||||||
|
ThemeLabel string `json:"theme_label,optional"`
|
||||||
|
ProductContext string `json:"product_context,optional"`
|
||||||
|
}
|
||||||
|
ScoutScanReq {
|
||||||
|
Brief ScoutBriefPublic `json:"brief"`
|
||||||
|
}
|
||||||
|
ScoutPostPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
BrandId string `json:"brand_id,optional"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
SearchTag string `json:"search_tag"`
|
||||||
|
Opportunity string `json:"opportunity"`
|
||||||
|
OutreachStatus string `json:"outreach_status"`
|
||||||
|
DraftText string `json:"draft_text,optional"`
|
||||||
|
Score int `json:"score"`
|
||||||
|
MatchedProductId string `json:"matched_product_id,optional"`
|
||||||
|
MatchedProductLabel string `json:"matched_product_label,optional"`
|
||||||
|
MatchReason string `json:"match_reason,optional"`
|
||||||
|
ScoutMode string `json:"scout_mode,optional"`
|
||||||
|
IntentSnippet string `json:"intent_snippet,optional"`
|
||||||
|
ThemeKey string `json:"theme_key,optional"`
|
||||||
|
ThemeLabel string `json:"theme_label,optional"`
|
||||||
|
ScanPath string `json:"scan_path,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
ScoutPostListData {
|
||||||
|
List []ScoutPostPublic `json:"list"`
|
||||||
|
}
|
||||||
|
ScoutPostListReq {
|
||||||
|
BrandId string `form:"brand_id,optional"`
|
||||||
|
}
|
||||||
|
ScoutPostIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
ScoutDraftPathReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
}
|
||||||
|
ScoutSendPathReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
Text string `json:"text,optional"`
|
||||||
|
AccountId string `json:"account_id,optional"`
|
||||||
|
}
|
||||||
|
ScoutThemePath {
|
||||||
|
ThemeKey string `path:"themeKey"`
|
||||||
|
}
|
||||||
|
ScoutHomeworkPublic {
|
||||||
|
ThemeKey string `json:"theme_key"`
|
||||||
|
ThemeLabel string `json:"theme_label"`
|
||||||
|
Purpose string `json:"purpose"`
|
||||||
|
Brief ScoutBriefPublic `json:"brief"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
ScoutHomeworkListData {
|
||||||
|
List []ScoutHomeworkPublic `json:"list"`
|
||||||
|
}
|
||||||
|
ScoutHomeworkSaveReq {
|
||||||
|
ThemeKey string `json:"theme_key"`
|
||||||
|
ThemeLabel string `json:"theme_label"`
|
||||||
|
Purpose string `json:"purpose,optional"`
|
||||||
|
Brief ScoutBriefPublic `json:"brief"`
|
||||||
|
}
|
||||||
|
ScoutCrawlerSessionReq {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: inspire
|
||||||
|
prefix: /api/v1/inspire
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListTrends
|
||||||
|
get /trends returns (TrendListData)
|
||||||
|
|
||||||
|
@handler RefreshTrends
|
||||||
|
post /trends/refresh returns (TrendListData)
|
||||||
|
|
||||||
|
@handler ListInspireElements
|
||||||
|
get /elements returns (InspireElementListData)
|
||||||
|
|
||||||
|
@handler SaveInspireElement
|
||||||
|
post /elements (InspireElementSaveReq) returns (InspireElementPublic)
|
||||||
|
|
||||||
|
@handler RemoveInspireElement
|
||||||
|
delete /elements/:id (InspireElementIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler GetInspireSession
|
||||||
|
get /session returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler ClearInspireSession
|
||||||
|
post /session/clear returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler ListInspireSessions
|
||||||
|
get /sessions returns (InspireSessionListData)
|
||||||
|
|
||||||
|
@handler CreateInspireSession
|
||||||
|
post /sessions (InspireSessionCreateReq) returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler GetInspireSessionById
|
||||||
|
get /sessions/:id (InspireSessionIdPath) returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler ActivateInspireSession
|
||||||
|
post /sessions/:id/activate (InspireSessionIdPath) returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler DeleteInspireSession
|
||||||
|
delete /sessions/:id (InspireSessionIdPath) returns (InspireSessionPublic)
|
||||||
|
|
||||||
|
@handler InspireChat
|
||||||
|
post /chat (InspireChatReq) returns (InspireChatData)
|
||||||
|
|
||||||
|
@handler InspirePromptPreview
|
||||||
|
post /prompt-preview (InspirePromptPreviewReq) returns (InspirePromptPreviewData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: research
|
||||||
|
prefix: /api/v1/research
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ResearchSearch
|
||||||
|
post /search (ResearchSearchReq) returns (ResearchSearchData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: media
|
||||||
|
prefix: /api/v1/media
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler GenerateImage
|
||||||
|
post /generate-image (GenerateImageReq) returns (GenerateImageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: scout
|
||||||
|
prefix: /api/v1/scout
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListBrands
|
||||||
|
get /brands returns (BrandListData)
|
||||||
|
|
||||||
|
@handler SaveBrand
|
||||||
|
post /brands (BrandSaveReq) returns (BrandPublic)
|
||||||
|
|
||||||
|
@handler GetBrand
|
||||||
|
get /brands/:id (BrandIdPath) returns (BrandPublic)
|
||||||
|
|
||||||
|
@handler RemoveBrand
|
||||||
|
delete /brands/:id (BrandIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler GetActiveBrand
|
||||||
|
get /brands-active returns (BrandActiveData)
|
||||||
|
|
||||||
|
@handler SetActiveBrand
|
||||||
|
post /brands-active (BrandSetActiveReq) returns (BrandActiveData)
|
||||||
|
|
||||||
|
@handler ListProducts
|
||||||
|
get /products (ProductListReq) returns (ProductListData)
|
||||||
|
|
||||||
|
@handler ListAllProducts
|
||||||
|
get /products/all returns (ProductListData)
|
||||||
|
|
||||||
|
@handler SaveProduct
|
||||||
|
post /products (ProductSaveReq) returns (ProductPublic)
|
||||||
|
|
||||||
|
@handler GetProduct
|
||||||
|
get /products/:id (ProductIdPath) returns (ProductPublic)
|
||||||
|
|
||||||
|
@handler RemoveProduct
|
||||||
|
delete /products/:id (ProductIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler ImportProduct
|
||||||
|
post /products/import (ImportProductReq) returns (ImportProductData)
|
||||||
|
|
||||||
|
@handler PrepareBrief
|
||||||
|
post /brief (ScoutBriefReq) returns (ScoutBriefPublic)
|
||||||
|
|
||||||
|
@handler RunScan
|
||||||
|
post /scan (ScoutScanReq) returns (ScoutPostListData)
|
||||||
|
|
||||||
|
@handler ListScoutPosts
|
||||||
|
get /posts (ScoutPostListReq) returns (ScoutPostListData)
|
||||||
|
|
||||||
|
@handler DraftOutreach
|
||||||
|
post /posts/:id/draft (ScoutDraftPathReq) returns (ScoutPostPublic)
|
||||||
|
|
||||||
|
@handler SkipOutreach
|
||||||
|
post /posts/:id/skip (ScoutPostIdPath) returns (ScoutPostPublic)
|
||||||
|
|
||||||
|
@handler MarkPublished
|
||||||
|
post /posts/:id/mark-published (ScoutPostIdPath) returns (ScoutPostPublic)
|
||||||
|
|
||||||
|
@handler SendOutreach
|
||||||
|
post /posts/:id/send (ScoutSendPathReq) returns (ScoutPostPublic)
|
||||||
|
|
||||||
|
@handler RemoveScoutPost
|
||||||
|
delete /posts/:id (ScoutPostIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler RemoveScoutTheme
|
||||||
|
delete /themes/:themeKey (ScoutThemePath) returns (OkData)
|
||||||
|
|
||||||
|
@handler ListHomework
|
||||||
|
get /homework returns (ScoutHomeworkListData)
|
||||||
|
|
||||||
|
@handler SaveHomework
|
||||||
|
post /homework (ScoutHomeworkSaveReq) returns (ScoutHomeworkPublic)
|
||||||
|
|
||||||
|
@handler GetHomework
|
||||||
|
get /homework/:themeKey (ScoutThemePath) returns (ScoutHomeworkPublic)
|
||||||
|
|
||||||
|
@handler RemoveHomework
|
||||||
|
delete /homework/:themeKey (ScoutThemePath) returns (OkData)
|
||||||
|
|
||||||
|
@handler SetCrawlerSession
|
||||||
|
post /crawler-session (ScoutCrawlerSessionReq) returns (OkData)
|
||||||
|
|
||||||
|
@handler ClearCrawlerSession
|
||||||
|
delete /crawler-session returns (OkData)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
type (
|
||||||
|
NotificationPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
Kind string `json:"kind"` // job|system
|
||||||
|
RefType string `json:"ref_type"`
|
||||||
|
RefId string `json:"ref_id,optional"`
|
||||||
|
ReadAt int64 `json:"read_at,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationListData {
|
||||||
|
List []NotificationPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UnreadCountData {
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: notifications
|
||||||
|
prefix: /api/v1/notifications
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListNotifications
|
||||||
|
get / returns (NotificationListData)
|
||||||
|
|
||||||
|
@handler UnreadNotificationCount
|
||||||
|
get /unread-count returns (UnreadCountData)
|
||||||
|
|
||||||
|
@handler MarkNotificationRead
|
||||||
|
post /:id/read (NotificationIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler MarkAllNotificationsRead
|
||||||
|
post /read-all returns (OkData)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
type (
|
||||||
|
AICompleteReq {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
Model string `json:"model,optional"`
|
||||||
|
Meter string `json:"meter,optional"` // default ai_copy
|
||||||
|
}
|
||||||
|
|
||||||
|
AICompleteData {
|
||||||
|
Text string `json:"text"`
|
||||||
|
KeyMode string `json:"key_mode"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchReq {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Limit int `json:"limit,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchHit {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Snippet string `json:"snippet"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchData {
|
||||||
|
List []SearchHit `json:"list"`
|
||||||
|
KeyMode string `json:"key_mode"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: proxy
|
||||||
|
prefix: /api/v1/proxy
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler AIComplete
|
||||||
|
post /ai/complete (AICompleteReq) returns (AICompleteData)
|
||||||
|
|
||||||
|
@handler ExaSearch
|
||||||
|
post /search (SearchReq) returns (SearchData)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
syntax = "v1"
|
syntax = "v1"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// GetAi:設定 + 模型清單一併回(避免前端分開打兩支)
|
||||||
|
GetAiReq {
|
||||||
|
// 可選:預覽另一個 provider 的模型清單(不改存檔)
|
||||||
|
Provider string `form:"provider,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
AiSettingsData {
|
AiSettingsData {
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
|
|
@ -9,6 +15,15 @@ type (
|
||||||
ApiKeyConfigured bool `json:"api_key_configured"`
|
ApiKeyConfigured bool `json:"api_key_configured"`
|
||||||
ResearchApiKeyConfigured bool `json:"research_api_key_configured"`
|
ResearchApiKeyConfigured bool `json:"research_api_key_configured"`
|
||||||
PlatformKeyAvailable bool `json:"platform_key_available"`
|
PlatformKeyAvailable bool `json:"platform_key_available"`
|
||||||
|
// Models — 目前 models_provider 的可用模型(真拉 + 5 分鐘快取)
|
||||||
|
Models []string `json:"models"`
|
||||||
|
ModelsProvider string `json:"models_provider"`
|
||||||
|
// SelectedModel — 與 Model 相同;明確給前端「上次設定的模型」
|
||||||
|
SelectedModel string `json:"selected_model"`
|
||||||
|
// ModelsFromCache — 這次模型清單是否來自快取
|
||||||
|
ModelsFromCache bool `json:"models_from_cache,optional"`
|
||||||
|
// ModelsError — 真拉失敗時提示(仍回 fallback models)
|
||||||
|
ModelsError string `json:"models_error,optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveAiReq {
|
SaveAiReq {
|
||||||
|
|
@ -45,6 +60,25 @@ type (
|
||||||
ListModelsData {
|
ListModelsData {
|
||||||
List []string `json:"list"`
|
List []string `json:"list"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
|
// SelectedModel — 若該 provider 為使用者目前存檔 provider,回傳已選 model
|
||||||
|
SelectedModel string `json:"selected_model,optional"`
|
||||||
|
FromCache bool `json:"from_cache,optional"`
|
||||||
|
ModelsError string `json:"models_error,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Threads OAuth 平台狀態(App Secret 永不回傳)
|
||||||
|
ThreadsSettingsData {
|
||||||
|
Provider string `json:"provider"` // fake | meta
|
||||||
|
Configured bool `json:"configured"`
|
||||||
|
OauthReady bool `json:"oauth_ready"`
|
||||||
|
ConnectPath string `json:"connect_path"` // 前端連帳入口
|
||||||
|
// 給 Meta 後台「Valid OAuth Redirect URIs」貼上用
|
||||||
|
CallbackUrl string `json:"callback_url"`
|
||||||
|
// 前端公開 origin(應 https)
|
||||||
|
PublicWebBase string `json:"public_web_base"`
|
||||||
|
// App Id 遮罩,例 2733…9930;未設定空字串
|
||||||
|
AppIdMasked string `json:"app_id_masked,optional"`
|
||||||
|
Hint string `json:"hint"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -55,7 +89,7 @@ type (
|
||||||
)
|
)
|
||||||
service gateway {
|
service gateway {
|
||||||
@handler GetAi
|
@handler GetAi
|
||||||
get /ai returns (AiSettingsData)
|
get /ai (GetAiReq) returns (AiSettingsData)
|
||||||
|
|
||||||
@handler SaveAi
|
@handler SaveAi
|
||||||
put /ai (SaveAiReq) returns (AiSettingsData)
|
put /ai (SaveAiReq) returns (AiSettingsData)
|
||||||
|
|
@ -68,4 +102,7 @@ service gateway {
|
||||||
|
|
||||||
@handler ListModels
|
@handler ListModels
|
||||||
get /models (ListModelsReq) returns (ListModelsData)
|
get /models (ListModelsReq) returns (ListModelsData)
|
||||||
|
|
||||||
|
@handler GetThreads
|
||||||
|
get /threads returns (ThreadsSettingsData)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,568 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
// M4 Studio: personas / plays / outbox / compose / own-posts / mentions
|
||||||
|
|
||||||
|
type (
|
||||||
|
PersonaDraftPublic {
|
||||||
|
Identity string `json:"identity"`
|
||||||
|
Tone string `json:"tone"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
Hooks string `json:"hooks"`
|
||||||
|
LanguageFingerprint string `json:"language_fingerprint"`
|
||||||
|
Rhythm string `json:"rhythm"`
|
||||||
|
Punctuation string `json:"punctuation"`
|
||||||
|
ContentPatterns string `json:"content_patterns"`
|
||||||
|
KnowledgeTranslation string `json:"knowledge_translation"`
|
||||||
|
CtaStyle string `json:"cta_style"`
|
||||||
|
Examples string `json:"examples"`
|
||||||
|
Avoid string `json:"avoid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
StyleDimensionPublic {
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
Evidence []string `json:"evidence,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaStylePublic {
|
||||||
|
Draft PersonaDraftPublic `json:"draft"`
|
||||||
|
DraftText string `json:"draft_text"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
BenchmarkUsername string `json:"benchmark_username,optional"`
|
||||||
|
SourceLabel string `json:"source_label,optional"`
|
||||||
|
SampleCount int `json:"sample_count"`
|
||||||
|
AnalyzedAt int64 `json:"analyzed_at,optional"`
|
||||||
|
SamplePreviews []string `json:"sample_previews,optional"`
|
||||||
|
// 8D:d1Tone…d8Risk → { summary, evidence }
|
||||||
|
Dimensions map[string]StyleDimensionPublic `json:"dimensions,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaGuardPublic {
|
||||||
|
Avoid []string `json:"avoid"`
|
||||||
|
MaxChars int `json:"max_chars"`
|
||||||
|
BanAiTone bool `json:"ban_ai_tone"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Brief string `json:"brief"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Style PersonaStylePublic `json:"style"`
|
||||||
|
Guard PersonaGuardPublic `json:"guard"`
|
||||||
|
Voice string `json:"voice,optional"`
|
||||||
|
Notes string `json:"notes,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaListData {
|
||||||
|
List []PersonaPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaSaveReq {
|
||||||
|
Id string `json:"id,optional"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Brief string `json:"brief,optional"`
|
||||||
|
Status string `json:"status,optional"`
|
||||||
|
Style PersonaStylePublic `json:"style,optional"`
|
||||||
|
Guard PersonaGuardPublic `json:"guard,optional"`
|
||||||
|
Voice string `json:"voice,optional"`
|
||||||
|
Notes string `json:"notes,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaActiveData {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaSetActiveReq {
|
||||||
|
Id string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaAnalyzeTextReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
RawText string `json:"raw_text"`
|
||||||
|
SourceLabel string `json:"source_label,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonaAnalyzeAccountReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Plays ---
|
||||||
|
ExternalTargetPublic {
|
||||||
|
Url string `json:"url"`
|
||||||
|
RawUrl string `json:"raw_url,optional"`
|
||||||
|
Shortcode string `json:"shortcode,optional"`
|
||||||
|
AuthorUsername string `json:"author_username,optional"`
|
||||||
|
TextPreview string `json:"text_preview,optional"`
|
||||||
|
MediaId string `json:"media_id,optional"`
|
||||||
|
ResolvedAt int64 `json:"resolved_at,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayStepPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
SortOrder int `json:"sort_order"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
DelayFromPreviousSec int `json:"delay_from_previous_sec"`
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
BrandId string `json:"brand_id,optional"`
|
||||||
|
ImageUrls []string `json:"image_urls,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Topic string `json:"topic"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
LeadAccountId string `json:"lead_account_id"`
|
||||||
|
CastAccountIds []string `json:"cast_account_ids"`
|
||||||
|
TargetOwnPostId string `json:"target_own_post_id,optional"`
|
||||||
|
TargetExternal ExternalTargetPublic `json:"target_external,optional"`
|
||||||
|
Steps []PlayStepPublic `json:"steps"`
|
||||||
|
ScheduleStartAt int64 `json:"schedule_start_at"`
|
||||||
|
IntervalMinutes int `json:"interval_minutes,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayListData {
|
||||||
|
List []PlayPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaySaveReq {
|
||||||
|
Id string `json:"id,optional"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Topic string `json:"topic,optional"`
|
||||||
|
Status string `json:"status,optional"`
|
||||||
|
LeadAccountId string `json:"lead_account_id"`
|
||||||
|
CastAccountIds []string `json:"cast_account_ids,optional"`
|
||||||
|
TargetOwnPostId string `json:"target_own_post_id,optional"`
|
||||||
|
TargetExternal ExternalTargetPublic `json:"target_external,optional"`
|
||||||
|
Steps []PlayStepPublic `json:"steps"`
|
||||||
|
ScheduleStartAt int64 `json:"schedule_start_at,optional"`
|
||||||
|
IntervalMinutes int `json:"interval_minutes,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayByPostReq {
|
||||||
|
OwnPostId string `form:"own_post_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayByExternalReq {
|
||||||
|
Url string `form:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayResolveReq {
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 產劇本一步(真 LLM;互回/串場共用)
|
||||||
|
PlayGenerateStepReq {
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
Context string `json:"context"` // 主貼/上一則/目標文
|
||||||
|
Topic string `json:"topic,optional"`
|
||||||
|
SpeakerLabel string `json:"speaker_label,optional"`
|
||||||
|
IsLead bool `json:"is_lead,optional"`
|
||||||
|
// mode: reply(預設)| root(串場主貼)
|
||||||
|
Mode string `json:"mode,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayGenerateStepData {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一次產完整劇本(背景 job)
|
||||||
|
PlayGenerateScriptData {
|
||||||
|
JobId string `json:"job_id"`
|
||||||
|
Async bool `json:"async"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Outbox ---
|
||||||
|
OutboxStepPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
StepId string `json:"step_id"`
|
||||||
|
SortOrder int `json:"sort_order"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Error string `json:"error,optional"`
|
||||||
|
ScheduledAt int64 `json:"scheduled_at,optional"`
|
||||||
|
PublishedAt int64 `json:"published_at,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboxPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
PlayId string `json:"play_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Steps []OutboxStepPublic `json:"steps"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboxListData {
|
||||||
|
List []OutboxPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboxIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OutboxRetryPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
StepId string `path:"stepId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Compose ---
|
||||||
|
ComposeMimicReq {
|
||||||
|
SourceText string `json:"source_text"`
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
// 可選:從「我的貼文」結構分析帶過來的備註(鉤子/結構/可複製點)
|
||||||
|
StructureNotes string `json:"structure_notes,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仿寫改背景 Job:立刻回 job_id;完成後從 job.payload.result_text 取文
|
||||||
|
ComposeMimicData {
|
||||||
|
// 相容:同步路徑才有;Job 路徑為空
|
||||||
|
Text string `json:"text,optional"`
|
||||||
|
JobId string `json:"job_id,optional"`
|
||||||
|
Async bool `json:"async,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 人設試產:依指紋真 LLM 產主貼 + 回文(可帶新聞話題)
|
||||||
|
ComposePersonaPreviewReq {
|
||||||
|
PersonaId string `json:"persona_id"`
|
||||||
|
// 手動話題;空則嘗試上網取新聞靈感
|
||||||
|
Topic string `json:"topic,optional"`
|
||||||
|
// 預設 true:無 topic 時抓即時新聞標題當靈感
|
||||||
|
UseNews bool `json:"use_news,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ComposePersonaPreviewData {
|
||||||
|
Topic string `json:"topic"`
|
||||||
|
TopicSource string `json:"topic_source"` // news | manual | fallback
|
||||||
|
PostText string `json:"post_text"`
|
||||||
|
ReplyText string `json:"reply_text"`
|
||||||
|
Notes string `json:"notes,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ComposeViralReq {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ViralAnalysisPublic {
|
||||||
|
Hooks string `json:"hooks"`
|
||||||
|
Structure string `json:"structure"`
|
||||||
|
Emotion string `json:"emotion"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
Cta string `json:"cta,optional"`
|
||||||
|
Copyable string `json:"copyable,optional"`
|
||||||
|
Risks string `json:"risks,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ComposePublishReq {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Title string `json:"title,optional"`
|
||||||
|
ImageUrls []string `json:"image_urls,optional"`
|
||||||
|
ScheduleStartAt int64 `json:"schedule_start_at,optional"`
|
||||||
|
// Threads 話題標籤(topic_tag,1~50 字,可不加 #)
|
||||||
|
TopicTag string `json:"topic_tag,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Own posts ---
|
||||||
|
OwnPostReplyPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
LikeCount int `json:"like_count,optional"`
|
||||||
|
ReplyStatus string `json:"reply_status,optional"`
|
||||||
|
RepliedBy string `json:"replied_by,optional"`
|
||||||
|
RepliedAt int64 `json:"replied_at,optional"`
|
||||||
|
ParentReplyId string `json:"parent_reply_id,optional"`
|
||||||
|
IsMine bool `json:"is_mine,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
MediaId string `json:"media_id,optional"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
MediaType string `json:"media_type"`
|
||||||
|
MediaUrl string `json:"media_url,optional"`
|
||||||
|
ThumbnailUrl string `json:"thumbnail_url,optional"`
|
||||||
|
Permalink string `json:"permalink,optional"`
|
||||||
|
Shortcode string `json:"shortcode,optional"`
|
||||||
|
TopicTag string `json:"topic_tag,optional"`
|
||||||
|
LikeCount int `json:"like_count"`
|
||||||
|
ReplyCount int `json:"reply_count"`
|
||||||
|
RepostCount int `json:"repost_count"`
|
||||||
|
QuoteCount int `json:"quote_count"`
|
||||||
|
ViewCount int `json:"view_count"`
|
||||||
|
ShareCount int `json:"share_count"`
|
||||||
|
InsightsStatus string `json:"insights_status,optional"`
|
||||||
|
FormulaSummary string `json:"formula_summary,optional"`
|
||||||
|
Insight string `json:"insight,optional"`
|
||||||
|
FormulaDetail string `json:"formula_detail,optional"`
|
||||||
|
Replies []OwnPostReplyPublic `json:"replies"`
|
||||||
|
PublishedAt int64 `json:"published_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostListData {
|
||||||
|
List []OwnPostPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostListReq {
|
||||||
|
AccountId string `form:"account_id,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostSyncReq {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostLastSyncedData {
|
||||||
|
LastSyncedAt int64 `json:"last_synced_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostGenerateReplyReq {
|
||||||
|
PostId string `json:"post_id"`
|
||||||
|
ReplyId string `json:"reply_id,optional"`
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostGenerateReplyData {
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostSendReplyReq {
|
||||||
|
PostId string `json:"post_id"`
|
||||||
|
ReplyId string `json:"reply_id,optional"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
AccountId string `json:"account_id,optional"`
|
||||||
|
ImageUrls []string `json:"image_urls,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
OwnPostAnalyzeReq {
|
||||||
|
PostId string `json:"post_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 點開貼文後再載留言(避免 sync 一次打爆 conversation API)
|
||||||
|
OwnPostLoadRepliesReq {
|
||||||
|
PostId string `json:"post_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Mentions ---
|
||||||
|
MentionPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
MediaId string `json:"media_id,optional"`
|
||||||
|
FromUsername string `json:"from_username"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
ContextSnippet string `json:"context_snippet"`
|
||||||
|
Permalink string `json:"permalink,optional"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
DraftText string `json:"draft_text,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionListData {
|
||||||
|
List []MentionPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionListReq {
|
||||||
|
AccountId string `form:"account_id,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionSyncReq {
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionGenerateReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
PersonaId string `json:"persona_id,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
MentionMarkRepliedReq {
|
||||||
|
Id string `path:"id"`
|
||||||
|
Text string `json:"text,optional"`
|
||||||
|
ImageUrls []string `json:"image_urls,optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: personas
|
||||||
|
prefix: /api/v1/personas
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListPersonas
|
||||||
|
get / returns (PersonaListData)
|
||||||
|
|
||||||
|
@handler GetActivePersona
|
||||||
|
get /active returns (PersonaActiveData)
|
||||||
|
|
||||||
|
@handler SetActivePersona
|
||||||
|
post /active (PersonaSetActiveReq) returns (PersonaActiveData)
|
||||||
|
|
||||||
|
@handler GetPersona
|
||||||
|
get /:id (PersonaIdPath) returns (PersonaPublic)
|
||||||
|
|
||||||
|
@handler SavePersona
|
||||||
|
post / (PersonaSaveReq) returns (PersonaPublic)
|
||||||
|
|
||||||
|
@handler RemovePersona
|
||||||
|
delete /:id (PersonaIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler AnalyzePersonaText
|
||||||
|
post /:id/analyze-text (PersonaAnalyzeTextReq) returns (PersonaPublic)
|
||||||
|
|
||||||
|
@handler AnalyzePersonaAccount
|
||||||
|
post /:id/analyze-account (PersonaAnalyzeAccountReq) returns (PersonaPublic)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: plays
|
||||||
|
prefix: /api/v1/plays
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListPlays
|
||||||
|
get / returns (PlayListData)
|
||||||
|
|
||||||
|
@handler ListPlaysByPost
|
||||||
|
get /by-post (PlayByPostReq) returns (PlayListData)
|
||||||
|
|
||||||
|
@handler ListPlaysByExternal
|
||||||
|
get /by-external (PlayByExternalReq) returns (PlayListData)
|
||||||
|
|
||||||
|
@handler ResolveExternalLink
|
||||||
|
post /resolve-external (PlayResolveReq) returns (ExternalTargetPublic)
|
||||||
|
|
||||||
|
@handler GetPlay
|
||||||
|
get /:id (PlayIdPath) returns (PlayPublic)
|
||||||
|
|
||||||
|
@handler SavePlay
|
||||||
|
post / (PlaySaveReq) returns (PlayPublic)
|
||||||
|
|
||||||
|
@handler RemovePlay
|
||||||
|
delete /:id (PlayIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler SubmitPlay
|
||||||
|
post /:id/submit (PlayIdPath) returns (OutboxPublic)
|
||||||
|
|
||||||
|
@handler GeneratePlayStep
|
||||||
|
post /generate-step (PlayGenerateStepReq) returns (PlayGenerateStepData)
|
||||||
|
|
||||||
|
@handler GeneratePlayScript
|
||||||
|
post /:id/generate-script (PlayIdPath) returns (PlayGenerateScriptData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: outbox
|
||||||
|
prefix: /api/v1/outbox
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListOutbox
|
||||||
|
get / returns (OutboxListData)
|
||||||
|
|
||||||
|
@handler GetOutbox
|
||||||
|
get /:id (OutboxIdPath) returns (OutboxPublic)
|
||||||
|
|
||||||
|
@handler RemoveOutbox
|
||||||
|
delete /:id (OutboxIdPath) returns (OkData)
|
||||||
|
|
||||||
|
@handler RetryOutboxStep
|
||||||
|
post /:id/steps/:stepId/retry (OutboxRetryPath) returns (OutboxPublic)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: compose
|
||||||
|
prefix: /api/v1/compose
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ComposeMimic
|
||||||
|
post /mimic (ComposeMimicReq) returns (ComposeMimicData)
|
||||||
|
|
||||||
|
@handler ComposePersonaPreview
|
||||||
|
post /persona-preview (ComposePersonaPreviewReq) returns (ComposePersonaPreviewData)
|
||||||
|
|
||||||
|
@handler ComposeAnalyzeViral
|
||||||
|
post /analyze-viral (ComposeViralReq) returns (ViralAnalysisPublic)
|
||||||
|
|
||||||
|
@handler ComposePublishSingle
|
||||||
|
post /publish-single (ComposePublishReq) returns (OutboxPublic)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: ownposts
|
||||||
|
prefix: /api/v1/own-posts
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListOwnPosts
|
||||||
|
get / (OwnPostListReq) returns (OwnPostListData)
|
||||||
|
|
||||||
|
@handler OwnPostsLastSynced
|
||||||
|
get /last-synced-at returns (OwnPostLastSyncedData)
|
||||||
|
|
||||||
|
@handler SyncOwnPosts
|
||||||
|
post /sync (OwnPostSyncReq) returns (OwnPostListData)
|
||||||
|
|
||||||
|
@handler OwnPostGenerateReply
|
||||||
|
post /generate-reply (OwnPostGenerateReplyReq) returns (OwnPostGenerateReplyData)
|
||||||
|
|
||||||
|
@handler OwnPostSendReply
|
||||||
|
post /send-reply (OwnPostSendReplyReq) returns (OwnPostPublic)
|
||||||
|
|
||||||
|
@handler OwnPostAnalyze
|
||||||
|
post /analyze (OwnPostAnalyzeReq) returns (OwnPostPublic)
|
||||||
|
|
||||||
|
@handler OwnPostLoadReplies
|
||||||
|
post /load-replies (OwnPostLoadRepliesReq) returns (OwnPostPublic)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: mentions
|
||||||
|
prefix: /api/v1/mentions
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ListMentions
|
||||||
|
get / (MentionListReq) returns (MentionListData)
|
||||||
|
|
||||||
|
@handler SyncMentions
|
||||||
|
post /sync (MentionSyncReq) returns (MentionListData)
|
||||||
|
|
||||||
|
@handler MentionGenerateReply
|
||||||
|
post /:id/generate-reply (MentionGenerateReq) returns (MentionPublic)
|
||||||
|
|
||||||
|
@handler MentionMarkReplied
|
||||||
|
post /:id/mark-replied (MentionMarkRepliedReq) returns (MentionPublic)
|
||||||
|
|
||||||
|
@handler MentionSkip
|
||||||
|
post /:id/skip (MentionIdPath) returns (MentionPublic)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
type (
|
||||||
|
ThreadsAccountPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Connection string `json:"connection"` // connected|error|disconnected
|
||||||
|
IsUsable bool `json:"is_usable"`
|
||||||
|
AvatarColor string `json:"avatar_color,optional"`
|
||||||
|
AvatarUrl string `json:"avatar_url,optional"`
|
||||||
|
ErrorMessage string `json:"error_message,optional"`
|
||||||
|
SessionExpiresAt int64 `json:"session_expires_at,optional"`
|
||||||
|
SessionRefreshedAt int64 `json:"session_refreshed_at,optional"`
|
||||||
|
// never expose access_token / refresh_token
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadsAccountListData {
|
||||||
|
List []ThreadsAccountPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadsOAuthStartData {
|
||||||
|
AuthorizeUrl string `json:"authorize_url"`
|
||||||
|
State string `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback is query-string from Meta / fake provider (public).
|
||||||
|
ThreadsOAuthCallbackReq {
|
||||||
|
Code string `form:"code,optional"`
|
||||||
|
State string `form:"state,optional"`
|
||||||
|
Error string `form:"error,optional"`
|
||||||
|
ErrorReason string `form:"error_reason,optional"`
|
||||||
|
ErrorDescription string `form:"error_description,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadsOAuthCallbackData {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Message string `json:"message,optional"`
|
||||||
|
// RedirectURL for custom handler (browser flow)
|
||||||
|
RedirectUrl string `json:"redirect_url,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadsAccountIdPath {
|
||||||
|
Id string `path:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 擴充(含舊版)匯入 Chrome storageState;寫入 scout crawler-session 供 dev 模式海巡
|
||||||
|
ImportThreadsAccountSessionReq {
|
||||||
|
// path id + body
|
||||||
|
Id string `path:"id"`
|
||||||
|
StorageState string `json:"storageState"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportThreadsAccountSessionData {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Valid bool `json:"valid"`
|
||||||
|
Synced bool `json:"synced"`
|
||||||
|
AccountId string `json:"account_id"`
|
||||||
|
Username string `json:"username,optional"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
UpdateAt int64 `json:"update_at,optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Public OAuth callback (no JWT)
|
||||||
|
@server (
|
||||||
|
group: threads
|
||||||
|
prefix: /api/v1/threads-accounts
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ThreadsOAuthCallback
|
||||||
|
get /oauth/callback (ThreadsOAuthCallbackReq) returns (ThreadsOAuthCallbackData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: threads
|
||||||
|
prefix: /api/v1/threads-accounts
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler ThreadsOAuthStart
|
||||||
|
get /oauth/start returns (ThreadsOAuthStartData)
|
||||||
|
|
||||||
|
@handler ListThreadsAccounts
|
||||||
|
get / returns (ThreadsAccountListData)
|
||||||
|
|
||||||
|
@handler RefreshThreadsAccount
|
||||||
|
post /:id/refresh (ThreadsAccountIdPath) returns (ThreadsAccountPublic)
|
||||||
|
|
||||||
|
// Chrome 擴充:匯入 Playwright storageState(舊路徑相容;實際存 crawler session)
|
||||||
|
@handler ImportThreadsAccountSession
|
||||||
|
post /:id/session/import (ImportThreadsAccountSessionReq) returns (ImportThreadsAccountSessionData)
|
||||||
|
|
||||||
|
@handler DisconnectThreadsAccount
|
||||||
|
delete /:id (ThreadsAccountIdPath) returns (OkData)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
syntax = "v1"
|
||||||
|
|
||||||
|
type (
|
||||||
|
UsageEventPublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Uid int64 `json:"uid"`
|
||||||
|
Meter string `json:"meter"`
|
||||||
|
Credits int `json:"credits"`
|
||||||
|
KeyMode string `json:"key_mode"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageMeterCount {
|
||||||
|
Count int `json:"count"`
|
||||||
|
Credits int `json:"credits"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsagePlatformBlock {
|
||||||
|
CreditsUsed int `json:"credits_used"`
|
||||||
|
CreditsRemaining int `json:"credits_remaining"`
|
||||||
|
CreditsTotal int `json:"credits_total"`
|
||||||
|
CallCount int `json:"call_count"`
|
||||||
|
ByMeter map[string]UsageMeterCount `json:"by_meter,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageByokBlock {
|
||||||
|
CallCount int `json:"call_count"`
|
||||||
|
ByMeter map[string]UsageMeterCount `json:"by_meter,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageSummaryData {
|
||||||
|
MonthKey string `json:"month_key"`
|
||||||
|
Uid int64 `json:"uid"`
|
||||||
|
PlanId string `json:"plan_id"`
|
||||||
|
Unlimited bool `json:"unlimited"`
|
||||||
|
Platform UsagePlatformBlock `json:"platform"`
|
||||||
|
Byok UsageByokBlock `json:"byok"`
|
||||||
|
// FE progress bar (platform only)
|
||||||
|
TotalCredits int `json:"total_credits"`
|
||||||
|
RemainingCredits int `json:"remaining_credits"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageSummaryReq {
|
||||||
|
MonthKey string `form:"month_key,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageEventsReq {
|
||||||
|
MonthKey string `form:"month_key,optional"`
|
||||||
|
KeyMode string `form:"key_mode,optional"` // platform|byok|all
|
||||||
|
Limit int `form:"limit,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageEventsData {
|
||||||
|
List []UsageEventPublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsagePrefsData {
|
||||||
|
PlanId string `json:"plan_id"`
|
||||||
|
Unlimited bool `json:"unlimited"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageSetPrefsReq {
|
||||||
|
Uid string `json:"uid"`
|
||||||
|
PlanId string `json:"plan_id,optional"`
|
||||||
|
Unlimited *bool `json:"unlimited,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsagePurchaseReq {
|
||||||
|
PlanId string `json:"plan_id"`
|
||||||
|
MockRef string `json:"mock_ref,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsagePurchasePublic {
|
||||||
|
Id string `json:"id"`
|
||||||
|
PlanId string `json:"plan_id"`
|
||||||
|
MockRef string `json:"mock_ref,optional"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsagePurchasesData {
|
||||||
|
List []UsagePurchasePublic `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageTenantRow {
|
||||||
|
Uid int64 `json:"uid"`
|
||||||
|
PlanId string `json:"plan_id"`
|
||||||
|
Unlimited bool `json:"unlimited"`
|
||||||
|
PlatformCreditsUsed int `json:"platform_credits_used"`
|
||||||
|
ByokCallCount int `json:"byok_call_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageTenantSummaryData {
|
||||||
|
MonthKey string `json:"month_key"`
|
||||||
|
PlatformCreditsUsed int `json:"platform_credits_used"`
|
||||||
|
ByokCallCount int `json:"byok_call_count"`
|
||||||
|
Members []UsageTenantRow `json:"members"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UsageTenantSummaryReq {
|
||||||
|
MonthKey string `form:"month_key,optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: usage
|
||||||
|
prefix: /api/v1/usage
|
||||||
|
middleware: AuthJWT
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler GetUsageSummary
|
||||||
|
get /summary (UsageSummaryReq) returns (UsageSummaryData)
|
||||||
|
|
||||||
|
@handler ListUsageEvents
|
||||||
|
get /events (UsageEventsReq) returns (UsageEventsData)
|
||||||
|
|
||||||
|
@handler GetUsagePrefs
|
||||||
|
get /prefs returns (UsagePrefsData)
|
||||||
|
|
||||||
|
@handler PurchasePlan
|
||||||
|
post /purchase (UsagePurchaseReq) returns (UsagePurchasePublic)
|
||||||
|
|
||||||
|
@handler ListMyPurchases
|
||||||
|
get /purchases returns (UsagePurchasesData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@server (
|
||||||
|
jwt: Auth
|
||||||
|
group: usage
|
||||||
|
prefix: /api/v1/usage
|
||||||
|
middleware: AuthJWT,AdminAuth
|
||||||
|
)
|
||||||
|
service gateway {
|
||||||
|
@handler SetUsagePrefs
|
||||||
|
post /prefs (UsageSetPrefsReq) returns (UsagePrefsData)
|
||||||
|
|
||||||
|
@handler GetTenantUsageSummary
|
||||||
|
get /tenant-summary (UsageTenantSummaryReq) returns (UsageTenantSummaryData)
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,12 @@ type Config struct {
|
||||||
RefreshExpire int64
|
RefreshExpire int64
|
||||||
}
|
}
|
||||||
Platform struct {
|
Platform struct {
|
||||||
|
// AIKey — legacy alias for XAIKey
|
||||||
AIKey string `json:",optional"`
|
AIKey string `json:",optional"`
|
||||||
|
// XAIKey — platform BYOK for xAI (api.x.ai)
|
||||||
|
XAIKey string `json:",optional"`
|
||||||
|
// OpenCodeKey — platform BYOK for OpenCode Go (opencode.ai/zen/go)
|
||||||
|
OpenCodeKey string `json:",optional"`
|
||||||
ExaKey string `json:",optional"`
|
ExaKey string `json:",optional"`
|
||||||
ThreadsAppId string `json:",optional"`
|
ThreadsAppId string `json:",optional"`
|
||||||
ThreadsAppSecret string `json:",optional"`
|
ThreadsAppSecret string `json:",optional"`
|
||||||
|
|
@ -62,6 +67,10 @@ type Config struct {
|
||||||
// PublicWebBase — 前端 origin,用於重設密碼信內連結/logo(勿尾斜線)
|
// PublicWebBase — 前端 origin,用於重設密碼信內連結/logo(勿尾斜線)
|
||||||
// 例:http://127.0.0.1:5173 或 https://threads-tool-dev.30cm.net
|
// 例:http://127.0.0.1:5173 或 https://threads-tool-dev.30cm.net
|
||||||
PublicWebBase string `json:",optional,default=http://127.0.0.1:5173"`
|
PublicWebBase string `json:",optional,default=http://127.0.0.1:5173"`
|
||||||
|
// PublicAPIBase — 對外 API origin(Threads OAuth redirect_uri 必須 https 公開網址)
|
||||||
|
// 空則:若 PublicWebBase 為 https 則同 host;否則回落 http://127.0.0.1:Port
|
||||||
|
// 例:https://threads-tool-dev.30cm.net
|
||||||
|
PublicAPIBase string `json:",optional"`
|
||||||
// Brand — 郵件 chrome(hermes);LogoURL 空則用 PublicWebBase + /brand-mark.jpg
|
// Brand — 郵件 chrome(hermes);LogoURL 空則用 PublicWebBase + /brand-mark.jpg
|
||||||
Brand struct {
|
Brand struct {
|
||||||
Name string `json:",optional,default=Harbor Desk"`
|
Name string `json:",optional,default=Harbor Desk"`
|
||||||
|
|
@ -111,9 +120,25 @@ func (c *Config) ApplyEnv() {
|
||||||
if v := os.Getenv("PLATFORM_AI_KEY"); v != "" {
|
if v := os.Getenv("PLATFORM_AI_KEY"); v != "" {
|
||||||
c.Platform.AIKey = v
|
c.Platform.AIKey = v
|
||||||
}
|
}
|
||||||
|
if v := firstEnv("PLATFORM_XAI_KEY", "XAI_API_KEY"); v != "" {
|
||||||
|
c.Platform.XAIKey = v
|
||||||
|
}
|
||||||
|
if v := firstEnv("PLATFORM_OPENCODE_KEY", "OPENCODE_API_KEY", "OPENCODE_GO_API_KEY"); v != "" {
|
||||||
|
c.Platform.OpenCodeKey = v
|
||||||
|
}
|
||||||
|
// legacy AIKey → XAI when XAI empty
|
||||||
|
if c.Platform.XAIKey == "" && c.Platform.AIKey != "" {
|
||||||
|
c.Platform.XAIKey = c.Platform.AIKey
|
||||||
|
}
|
||||||
if v := os.Getenv("PLATFORM_EXA_KEY"); v != "" {
|
if v := os.Getenv("PLATFORM_EXA_KEY"); v != "" {
|
||||||
c.Platform.ExaKey = v
|
c.Platform.ExaKey = v
|
||||||
}
|
}
|
||||||
|
if v := firstEnv("THREADS_APP_ID", "HAIXUN_THREADS_APP_ID"); v != "" {
|
||||||
|
c.Platform.ThreadsAppId = v
|
||||||
|
}
|
||||||
|
if v := firstEnv("THREADS_APP_SECRET", "HAIXUN_THREADS_APP_SECRET"); v != "" {
|
||||||
|
c.Platform.ThreadsAppSecret = v
|
||||||
|
}
|
||||||
if v := os.Getenv("WORKER_ID"); v != "" {
|
if v := os.Getenv("WORKER_ID"); v != "" {
|
||||||
c.Worker.ID = v
|
c.Worker.ID = v
|
||||||
}
|
}
|
||||||
|
|
@ -155,6 +180,9 @@ func (c *Config) ApplyEnv() {
|
||||||
if v := firstEnv("PUBLIC_WEB_BASE", "HAIXUN_PUBLIC_WEB_BASE"); v != "" {
|
if v := firstEnv("PUBLIC_WEB_BASE", "HAIXUN_PUBLIC_WEB_BASE"); v != "" {
|
||||||
c.PublicWebBase = strings.TrimRight(v, "/")
|
c.PublicWebBase = strings.TrimRight(v, "/")
|
||||||
}
|
}
|
||||||
|
if v := firstEnv("PUBLIC_API_BASE", "HAIXUN_PUBLIC_API_BASE"); v != "" {
|
||||||
|
c.PublicAPIBase = strings.TrimRight(v, "/")
|
||||||
|
}
|
||||||
if v := os.Getenv("MAIL_BRAND_NAME"); v != "" {
|
if v := os.Getenv("MAIL_BRAND_NAME"); v != "" {
|
||||||
c.Brand.Name = v
|
c.Brand.Name = v
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/compose"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ComposeAnalyzeViralHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ComposeViralReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := compose.NewComposeAnalyzeViralLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ComposeAnalyzeViral(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/compose"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ComposeMimicHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ComposeMimicReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := compose.NewComposeMimicLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ComposeMimic(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/compose"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ComposePersonaPreviewHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ComposePersonaPreviewReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := compose.NewComposePersonaPreviewLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ComposePersonaPreview(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package compose
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/compose"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ComposePublishSingleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ComposePublishReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := compose.NewComposePublishSingleLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ComposePublishSingle(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/extension"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DownloadExtensionZipHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := extension.NewDownloadExtensionZipLogic(r.Context(), svcCtx)
|
||||||
|
if _, err := l.DownloadExtensionZip(); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path := svcCtx.ExtensionZipPath
|
||||||
|
if path == "" {
|
||||||
|
path = extension.ResolveZipPath(svcCtx)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/zip")
|
||||||
|
w.Header().Set("Content-Disposition", `attachment; filename="haixun-threads-sync.zip"`)
|
||||||
|
http.ServeFile(w, r, filepath.Clean(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure zip exists for tests
|
||||||
|
func zipExists(p string) bool {
|
||||||
|
st, err := os.Stat(p)
|
||||||
|
return err == nil && st.Size() > 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ActivateInspireSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireSessionIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewActivateInspireSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ActivateInspireSession(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"apps/backend/internal/middleware"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChatStreamHandler — SSE 串流靈感聊天/產文。
|
||||||
|
// 事件(每行 data: JSON):
|
||||||
|
//
|
||||||
|
// {"type":"delta","text":"..."}
|
||||||
|
// {"type":"done","session":{...},"message_id":"..."}
|
||||||
|
// {"type":"error","message":"..."}
|
||||||
|
func ChatStreamHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireChatReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uid, ok := middleware.UIDFrom(r.Context())
|
||||||
|
if !ok || uid <= 0 {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, response.Biz(401, 401001, "missing authorization"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if svcCtx.Inspire == nil {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, response.Biz(503, 503001, "inspire not configured"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flusher, ok := w.(http.Flusher)
|
||||||
|
if !ok {
|
||||||
|
httpx.ErrorCtx(r.Context(), w, response.Biz(500, 500000, "streaming unsupported"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-transform")
|
||||||
|
w.Header().Set("Connection", "keep-alive")
|
||||||
|
w.Header().Set("X-Accel-Buffering", "no")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
flusher.Flush()
|
||||||
|
|
||||||
|
writeEvent := func(v any) bool {
|
||||||
|
b, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := w.Write([]byte("data: ")); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := w.Write(b); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := w.Write([]byte("\n\n")); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
flusher.Flush()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := strings.TrimSpace(req.Mode)
|
||||||
|
if mode == "" {
|
||||||
|
mode = "chat"
|
||||||
|
}
|
||||||
|
out, err := svcCtx.Inspire.ChatStream(r.Context(), uid, req.Message, req.PinnedIds, mode, req.PersonaId, req.SessionId, req.Material, func(chunk string) error {
|
||||||
|
if chunk == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !writeEvent(map[string]any{"type": "delta", "text": chunk}) {
|
||||||
|
return http.ErrAbortHandler // client gone
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
_ = writeEvent(map[string]any{"type": "error", "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sess := out.Session
|
||||||
|
pub := types.SessionFromDomain(sess)
|
||||||
|
msgID := ""
|
||||||
|
if len(sess.Messages) > 0 {
|
||||||
|
msgID = sess.Messages[len(sess.Messages)-1].ID
|
||||||
|
}
|
||||||
|
// 回傳實際送 AI 的 prompt 指紋/全文,供前端與預覽對照
|
||||||
|
_ = writeEvent(map[string]any{
|
||||||
|
"type": "done",
|
||||||
|
"session": pub,
|
||||||
|
"message_id": msgID,
|
||||||
|
"prompt": out.Prompt,
|
||||||
|
"prompt_fingerprint": out.Fingerprint,
|
||||||
|
"prompt_char_count": out.CharCount,
|
||||||
|
"prompt_rune_count": out.RuneCount,
|
||||||
|
"prompt_sections": out.Sections,
|
||||||
|
})
|
||||||
|
logx.WithContext(r.Context()).Infof("inspire chat-stream ok uid=%d mode=%s fp=%s chars=%d", uid, mode, out.Fingerprint, out.CharCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ClearInspireSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewClearInspireSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ClearInspireSession()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateInspireSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireSessionCreateReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewCreateInspireSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.CreateInspireSession(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DeleteInspireSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireSessionIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewDeleteInspireSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.DeleteInspireSession(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInspireSessionByIdHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireSessionIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewGetInspireSessionByIdLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetInspireSessionById(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetInspireSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewGetInspireSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetInspireSession()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InspireChatHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireChatReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewInspireChatLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.InspireChat(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InspirePromptPreviewHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspirePromptPreviewReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewInspirePromptPreviewLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.InspirePromptPreview(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListInspireElementsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewListInspireElementsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListInspireElements()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListInspireSessionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewListInspireSessionsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListInspireSessions()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListTrendsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewListTrendsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListTrends()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RefreshTrendsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := inspire.NewRefreshTrendsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RefreshTrends()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveInspireElementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireElementIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewRemoveInspireElementLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemoveInspireElement(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package inspire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/inspire"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SaveInspireElementHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.InspireElementSaveReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := inspire.NewSaveInspireElementLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SaveInspireElement(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
inspireHandler "apps/backend/internal/handler/inspire"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterInspireStream 註冊靈感 SSE 路由(手寫,避開 goctl JSON envelope)。
|
||||||
|
func RegisterInspireStream(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/chat-stream",
|
||||||
|
Handler: inspireHandler.ChatStreamHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/inspire"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package jobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/jobs"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DeleteJobHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.JobIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := jobs.NewDeleteJobLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.DeleteJob(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package jobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/jobs"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetJobHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.JobIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := jobs.NewGetJobLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetJob(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package jobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/jobs"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListJobsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := jobs.NewListJobsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListJobs()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package jobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/jobs"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StartDemoJobHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := jobs.NewStartDemoJobLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.StartDemoJob()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package media
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/media"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateImageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.GenerateImageReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := media.NewGenerateImageLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GenerateImage(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package mentions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/mentions"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListMentionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.MentionListReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := mentions.NewListMentionsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListMentions(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package mentions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/mentions"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MentionGenerateReplyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.MentionGenerateReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := mentions.NewMentionGenerateReplyLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MentionGenerateReply(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package mentions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/mentions"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MentionMarkRepliedHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.MentionMarkRepliedReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := mentions.NewMentionMarkRepliedLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MentionMarkReplied(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package mentions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/mentions"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MentionSkipHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.MentionIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := mentions.NewMentionSkipLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MentionSkip(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package mentions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/mentions"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SyncMentionsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.MentionSyncReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := mentions.NewSyncMentionsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SyncMentions(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/notifications"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListNotificationsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := notifications.NewListNotificationsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListNotifications()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/notifications"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarkAllNotificationsReadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := notifications.NewMarkAllNotificationsReadLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MarkAllNotificationsRead()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/notifications"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarkNotificationReadHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.NotificationIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := notifications.NewMarkNotificationReadLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MarkNotificationRead(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package notifications
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/notifications"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnreadNotificationCountHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := notifications.NewUnreadNotificationCountLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.UnreadNotificationCount()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package outbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/outbox"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetOutboxHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OutboxIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := outbox.NewGetOutboxLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetOutbox(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package outbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/outbox"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListOutboxHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := outbox.NewListOutboxLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListOutbox()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package outbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/outbox"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveOutboxHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OutboxIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := outbox.NewRemoveOutboxLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemoveOutbox(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package outbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/outbox"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RetryOutboxStepHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OutboxRetryPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := outbox.NewRetryOutboxStepLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RetryOutboxStep(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListOwnPostsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostListReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewListOwnPostsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListOwnPosts(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OwnPostAnalyzeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostAnalyzeReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewOwnPostAnalyzeLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.OwnPostAnalyze(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OwnPostGenerateReplyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostGenerateReplyReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewOwnPostGenerateReplyLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.OwnPostGenerateReply(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OwnPostLoadRepliesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostLoadRepliesReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewOwnPostLoadRepliesLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.OwnPostLoadReplies(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OwnPostSendReplyHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostSendReplyReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewOwnPostSendReplyLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.OwnPostSendReply(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OwnPostsLastSyncedHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := ownposts.NewOwnPostsLastSyncedLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.OwnPostsLastSynced()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package ownposts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/ownposts"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SyncOwnPostsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.OwnPostSyncReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := ownposts.NewSyncOwnPostsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SyncOwnPosts(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AnalyzePersonaAccountHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaAnalyzeAccountReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewAnalyzePersonaAccountLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.AnalyzePersonaAccount(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AnalyzePersonaTextHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaAnalyzeTextReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewAnalyzePersonaTextLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.AnalyzePersonaText(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetActivePersonaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := personas.NewGetActivePersonaLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetActivePersona()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPersonaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewGetPersonaLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetPersona(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListPersonasHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := personas.NewListPersonasLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListPersonas()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemovePersonaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewRemovePersonaLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemovePersona(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SavePersonaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaSaveReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewSavePersonaLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SavePersona(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package personas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/personas"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetActivePersonaHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PersonaSetActiveReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := personas.NewSetActivePersonaLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SetActivePersona(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GeneratePlayScriptHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewGeneratePlayScriptLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GeneratePlayScript(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GeneratePlayStepHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayGenerateStepReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewGeneratePlayStepLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GeneratePlayStep(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPlayHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewGetPlayLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetPlay(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListPlaysByExternalHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayByExternalReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewListPlaysByExternalLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListPlaysByExternal(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListPlaysByPostHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayByPostReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewListPlaysByPostLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListPlaysByPost(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListPlaysHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := plays.NewListPlaysLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListPlays()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemovePlayHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewRemovePlayLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemovePlay(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ResolveExternalLinkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayResolveReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewResolveExternalLinkLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ResolveExternalLink(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SavePlayHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlaySaveReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewSavePlayLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SavePlay(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/plays"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SubmitPlayHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.PlayIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := plays.NewSubmitPlayLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.SubmitPlay(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/proxy"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AICompleteHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.AICompleteReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := proxy.NewAICompleteLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.AIComplete(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/proxy"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExaSearchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.SearchReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := proxy.NewExaSearchLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ExaSearch(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package research
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/research"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ResearchSearchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ResearchSearchReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := research.NewResearchSearchLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ResearchSearch(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,24 @@ import (
|
||||||
|
|
||||||
admin "apps/backend/internal/handler/admin"
|
admin "apps/backend/internal/handler/admin"
|
||||||
auth "apps/backend/internal/handler/auth"
|
auth "apps/backend/internal/handler/auth"
|
||||||
|
compose "apps/backend/internal/handler/compose"
|
||||||
|
extension "apps/backend/internal/handler/extension"
|
||||||
|
inspire "apps/backend/internal/handler/inspire"
|
||||||
|
jobs "apps/backend/internal/handler/jobs"
|
||||||
media "apps/backend/internal/handler/media"
|
media "apps/backend/internal/handler/media"
|
||||||
|
mentions "apps/backend/internal/handler/mentions"
|
||||||
|
notifications "apps/backend/internal/handler/notifications"
|
||||||
|
outbox "apps/backend/internal/handler/outbox"
|
||||||
|
ownposts "apps/backend/internal/handler/ownposts"
|
||||||
|
personas "apps/backend/internal/handler/personas"
|
||||||
ping "apps/backend/internal/handler/ping"
|
ping "apps/backend/internal/handler/ping"
|
||||||
|
plays "apps/backend/internal/handler/plays"
|
||||||
|
proxy "apps/backend/internal/handler/proxy"
|
||||||
|
research "apps/backend/internal/handler/research"
|
||||||
|
scout "apps/backend/internal/handler/scout"
|
||||||
settings "apps/backend/internal/handler/settings"
|
settings "apps/backend/internal/handler/settings"
|
||||||
|
threads "apps/backend/internal/handler/threads"
|
||||||
|
usage "apps/backend/internal/handler/usage"
|
||||||
"apps/backend/internal/svc"
|
"apps/backend/internal/svc"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/rest"
|
"github.com/zeromicro/go-zero/rest"
|
||||||
|
|
@ -171,6 +186,171 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
rest.WithPrefix("/api/v1/auth"),
|
rest.WithPrefix("/api/v1/auth"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/analyze-viral",
|
||||||
|
Handler: compose.ComposeAnalyzeViralHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/mimic",
|
||||||
|
Handler: compose.ComposeMimicHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/persona-preview",
|
||||||
|
Handler: compose.ComposePersonaPreviewHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/publish-single",
|
||||||
|
Handler: compose.ComposePublishSingleHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/compose"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/download",
|
||||||
|
Handler: extension.DownloadExtensionZipHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/extension"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/chat",
|
||||||
|
Handler: inspire.InspireChatHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/elements",
|
||||||
|
Handler: inspire.ListInspireElementsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/elements",
|
||||||
|
Handler: inspire.SaveInspireElementHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/elements/:id",
|
||||||
|
Handler: inspire.RemoveInspireElementHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/prompt-preview",
|
||||||
|
Handler: inspire.InspirePromptPreviewHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/session",
|
||||||
|
Handler: inspire.GetInspireSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/session/clear",
|
||||||
|
Handler: inspire.ClearInspireSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/sessions",
|
||||||
|
Handler: inspire.ListInspireSessionsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/sessions",
|
||||||
|
Handler: inspire.CreateInspireSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/sessions/:id",
|
||||||
|
Handler: inspire.GetInspireSessionByIdHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/sessions/:id",
|
||||||
|
Handler: inspire.DeleteInspireSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/sessions/:id/activate",
|
||||||
|
Handler: inspire.ActivateInspireSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/trends",
|
||||||
|
Handler: inspire.ListTrendsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/trends/refresh",
|
||||||
|
Handler: inspire.RefreshTrendsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/inspire"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: jobs.ListJobsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: jobs.GetJobHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: jobs.DeleteJobHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/jobs"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT, serverCtx.AdminAuth},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/demo",
|
||||||
|
Handler: jobs.StartDemoJobHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/jobs"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
rest.WithMiddlewares(
|
rest.WithMiddlewares(
|
||||||
[]rest.Middleware{serverCtx.AuthJWT},
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
|
@ -185,6 +365,211 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
rest.WithPrefix("/api/v1/media"),
|
rest.WithPrefix("/api/v1/media"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/generate-image",
|
||||||
|
Handler: media.GenerateImageHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/media"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: mentions.ListMentionsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/generate-reply",
|
||||||
|
Handler: mentions.MentionGenerateReplyHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/mark-replied",
|
||||||
|
Handler: mentions.MentionMarkRepliedHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/skip",
|
||||||
|
Handler: mentions.MentionSkipHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/sync",
|
||||||
|
Handler: mentions.SyncMentionsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/mentions"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: notifications.ListNotificationsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/read",
|
||||||
|
Handler: notifications.MarkNotificationReadHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/read-all",
|
||||||
|
Handler: notifications.MarkAllNotificationsReadHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/unread-count",
|
||||||
|
Handler: notifications.UnreadNotificationCountHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/notifications"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: outbox.ListOutboxHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: outbox.GetOutboxHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: outbox.RemoveOutboxHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/steps/:stepId/retry",
|
||||||
|
Handler: outbox.RetryOutboxStepHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/outbox"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: ownposts.ListOwnPostsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/analyze",
|
||||||
|
Handler: ownposts.OwnPostAnalyzeHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/generate-reply",
|
||||||
|
Handler: ownposts.OwnPostGenerateReplyHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/last-synced-at",
|
||||||
|
Handler: ownposts.OwnPostsLastSyncedHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/load-replies",
|
||||||
|
Handler: ownposts.OwnPostLoadRepliesHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/send-reply",
|
||||||
|
Handler: ownposts.OwnPostSendReplyHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/sync",
|
||||||
|
Handler: ownposts.SyncOwnPostsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/own-posts"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: personas.ListPersonasHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/",
|
||||||
|
Handler: personas.SavePersonaHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: personas.GetPersonaHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: personas.RemovePersonaHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/analyze-account",
|
||||||
|
Handler: personas.AnalyzePersonaAccountHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/analyze-text",
|
||||||
|
Handler: personas.AnalyzePersonaTextHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/active",
|
||||||
|
Handler: personas.GetActivePersonaHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/active",
|
||||||
|
Handler: personas.SetActivePersonaHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/personas"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
{
|
{
|
||||||
|
|
@ -201,6 +586,246 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
rest.WithPrefix("/api/v1"),
|
rest.WithPrefix("/api/v1"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: plays.ListPlaysHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/",
|
||||||
|
Handler: plays.SavePlayHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: plays.GetPlayHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: plays.RemovePlayHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/generate-script",
|
||||||
|
Handler: plays.GeneratePlayScriptHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/submit",
|
||||||
|
Handler: plays.SubmitPlayHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/by-external",
|
||||||
|
Handler: plays.ListPlaysByExternalHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/by-post",
|
||||||
|
Handler: plays.ListPlaysByPostHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/generate-step",
|
||||||
|
Handler: plays.GeneratePlayStepHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/resolve-external",
|
||||||
|
Handler: plays.ResolveExternalLinkHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/plays"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/ai/complete",
|
||||||
|
Handler: proxy.AICompleteHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/search",
|
||||||
|
Handler: proxy.ExaSearchHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/proxy"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/search",
|
||||||
|
Handler: research.ResearchSearchHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/research"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/brands",
|
||||||
|
Handler: scout.ListBrandsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/brands",
|
||||||
|
Handler: scout.SaveBrandHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/brands-active",
|
||||||
|
Handler: scout.GetActiveBrandHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/brands-active",
|
||||||
|
Handler: scout.SetActiveBrandHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/brands/:id",
|
||||||
|
Handler: scout.GetBrandHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/brands/:id",
|
||||||
|
Handler: scout.RemoveBrandHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/brief",
|
||||||
|
Handler: scout.PrepareBriefHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/crawler-session",
|
||||||
|
Handler: scout.SetCrawlerSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/crawler-session",
|
||||||
|
Handler: scout.ClearCrawlerSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/homework",
|
||||||
|
Handler: scout.ListHomeworkHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/homework",
|
||||||
|
Handler: scout.SaveHomeworkHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/homework/:themeKey",
|
||||||
|
Handler: scout.GetHomeworkHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/homework/:themeKey",
|
||||||
|
Handler: scout.RemoveHomeworkHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/posts",
|
||||||
|
Handler: scout.ListScoutPostsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/posts/:id",
|
||||||
|
Handler: scout.RemoveScoutPostHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/posts/:id/draft",
|
||||||
|
Handler: scout.DraftOutreachHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/posts/:id/mark-published",
|
||||||
|
Handler: scout.MarkPublishedHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/posts/:id/send",
|
||||||
|
Handler: scout.SendOutreachHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/posts/:id/skip",
|
||||||
|
Handler: scout.SkipOutreachHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/products",
|
||||||
|
Handler: scout.ListProductsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/products",
|
||||||
|
Handler: scout.SaveProductHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/products/:id",
|
||||||
|
Handler: scout.GetProductHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/products/:id",
|
||||||
|
Handler: scout.RemoveProductHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/products/all",
|
||||||
|
Handler: scout.ListAllProductsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/products/import",
|
||||||
|
Handler: scout.ImportProductHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/scan",
|
||||||
|
Handler: scout.RunScanHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/themes/:themeKey",
|
||||||
|
Handler: scout.RemoveScoutThemeHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/scout"),
|
||||||
|
)
|
||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
rest.WithMiddlewares(
|
rest.WithMiddlewares(
|
||||||
[]rest.Middleware{serverCtx.AuthJWT},
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
|
@ -230,8 +855,114 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||||
Path: "/placement",
|
Path: "/placement",
|
||||||
Handler: settings.SavePlacementHandler(serverCtx),
|
Handler: settings.SavePlacementHandler(serverCtx),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/threads",
|
||||||
|
Handler: settings.GetThreadsHandler(serverCtx),
|
||||||
|
},
|
||||||
}...,
|
}...,
|
||||||
),
|
),
|
||||||
rest.WithPrefix("/api/v1/settings"),
|
rest.WithPrefix("/api/v1/settings"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/oauth/callback",
|
||||||
|
Handler: threads.ThreadsOAuthCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rest.WithPrefix("/api/v1/threads-accounts"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/",
|
||||||
|
Handler: threads.ListThreadsAccountsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Path: "/:id",
|
||||||
|
Handler: threads.DisconnectThreadsAccountHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/refresh",
|
||||||
|
Handler: threads.RefreshThreadsAccountHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/:id/session/import",
|
||||||
|
Handler: threads.ImportThreadsAccountSessionHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/oauth/start",
|
||||||
|
Handler: threads.ThreadsOAuthStartHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/threads-accounts"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/events",
|
||||||
|
Handler: usage.ListUsageEventsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/prefs",
|
||||||
|
Handler: usage.GetUsagePrefsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/purchase",
|
||||||
|
Handler: usage.PurchasePlanHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/purchases",
|
||||||
|
Handler: usage.ListMyPurchasesHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/summary",
|
||||||
|
Handler: usage.GetUsageSummaryHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/usage"),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.AddRoutes(
|
||||||
|
rest.WithMiddlewares(
|
||||||
|
[]rest.Middleware{serverCtx.AuthJWT, serverCtx.AdminAuth},
|
||||||
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/prefs",
|
||||||
|
Handler: usage.SetUsagePrefsHandler(serverCtx),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Path: "/tenant-summary",
|
||||||
|
Handler: usage.GetTenantUsageSummaryHandler(serverCtx),
|
||||||
|
},
|
||||||
|
}...,
|
||||||
|
),
|
||||||
|
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||||
|
rest.WithPrefix("/api/v1/usage"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ClearCrawlerSessionHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := scout.NewClearCrawlerSessionLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ClearCrawlerSession()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DraftOutreachHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutDraftPathReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewDraftOutreachLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.DraftOutreach(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetActiveBrandHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := scout.NewGetActiveBrandLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetActiveBrand()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBrandHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.BrandIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewGetBrandLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetBrand(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHomeworkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutThemePath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewGetHomeworkLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetHomework(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ProductIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewGetProductLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.GetProduct(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ImportProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ImportProductReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewImportProductLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ImportProduct(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListAllProductsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := scout.NewListAllProductsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListAllProducts()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListBrandsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := scout.NewListBrandsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListBrands()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListHomeworkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l := scout.NewListHomeworkLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListHomework()
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListProductsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ProductListReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewListProductsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListProducts(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListScoutPostsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutPostListReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewListScoutPostsLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.ListScoutPosts(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarkPublishedHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutPostIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewMarkPublishedLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.MarkPublished(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrepareBriefHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutBriefReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewPrepareBriefLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.PrepareBrief(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveBrandHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.BrandIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewRemoveBrandLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemoveBrand(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveHomeworkHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ScoutThemePath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewRemoveHomeworkLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemoveHomework(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Code generated by goctl. DO NOT EDIT.
|
||||||
|
// goctl <no value>
|
||||||
|
|
||||||
|
package scout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"apps/backend/internal/logic/scout"
|
||||||
|
"apps/backend/internal/response"
|
||||||
|
"apps/backend/internal/svc"
|
||||||
|
"apps/backend/internal/types"
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RemoveProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.ProductIdPath
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
response.Write(r.Context(), w, nil, response.WrapRequestError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := scout.NewRemoveProductLogic(r.Context(), svcCtx)
|
||||||
|
data, err := l.RemoveProduct(&req)
|
||||||
|
response.Write(r.Context(), w, data, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue