M0+M1: 基建 + 用户/密钥/核心代理

后端 (Go/Gin/GORM):
- 配置(viper+env)、SQLite/Postgres 迁移、argon2id、AES-GCM 渠道密钥、JWT+refresh cookie
- 用户注册/登录/刷新/登出、API Key CRUD(仅存哈希、明文一次展示)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/models 直通 OpenAI 渠道
  非流式+流式(SSE 零缓冲转发), 用量捕获(chat 末块/responses completed 嵌套),
  OpenAI 错误格式(401/402/404/502), 余额检查
- 异步批量记账 + 余额流水 + 日聚合, admin 用户/余额/配置 API
- 单测: crypto/jwt/apikey/流式 usage 提取

前端 (Vue3+TS+Vite+Tailwind v4):
- taste-skill 设计 tokens: 深色仪表盘, 石墨+信号铜色, Outfit+JetBrains Mono
- Landing/登录/注册, 控制台(仪表盘图表/密钥管理/用量明细)
- 基础组件 Button/Input/Badge/Modal, ECharts 用量图

部署: docker-compose(nginx+api+postgres), 双 Dockerfile, nginx SSE 反代
联调: scripts/mockupstream 本地 mock 上游, 端到端验证通过
This commit is contained in:
Sakurasan
2026-08-15 13:10:47 +08:00
commit 360c6b33a6
75 changed files with 7044 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
@import "tailwindcss";
@import "@fontsource/outfit/400.css";
@import "@fontsource/outfit/500.css";
@import "@fontsource/outfit/600.css";
@import "@fontsource/outfit/700.css";
@import "@fontsource/jetbrains-mono/400.css";
@import "@fontsource/jetbrains-mono/500.css";
@import "@fontsource/jetbrains-mono/600.css";
/* ============================================================
openteam 设计 tokens(taste-skill 产出)
方向:深色优先的开发者控制台 / 信号系统语言
色板:石墨墨底 + 暖白文本 + 单一信号铜色强调(信号灯)
数据一律 mono(JetBrains Mono),UI 用 Outfit
============================================================ */
@theme {
/* 墨色层(背景阶梯) */
--color-ink-950: #0c0d0f;
--color-ink-900: #121417;
--color-ink-850: #16191d;
--color-ink-800: #1c2025;
--color-ink-700: #282d34;
--color-ink-600: #363c45;
/* 纸色层(文本) */
--color-paper-100: #eae8e3;
--color-paper-300: #c8c5bd;
--color-paper-500: #8b909a;
--color-paper-600: #63686f;
/* 信号铜色(唯一强调,信号灯意象) */
--color-signal-200: #f7d9a8;
--color-signal-300: #f0be6d;
--color-signal-400: #e5a13c;
--color-signal-500: #c9842a;
--color-signal-600: #a56a1f;
/* 语义色 */
--color-mint-300: #7fd0ac;
--color-mint-400: #4cb58a;
--color-mint-500: #33946f;
--color-ember-300: #ec8a80;
--color-ember-400: #d9685c;
--color-ember-500: #b34c42;
--color-sky-300: #93bce4;
--color-sky-400: #6e9fd8;
--font-sans: "Outfit", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;
/* 圆角:全局统一 6px(工具类,克制) */
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 10px;
}
/* 亮色主题(保留:data-theme="light" 时切换,默认深色优先) */
[data-theme="light"] {
--color-ink-950: #f4f3f0;
--color-ink-900: #ffffff;
--color-ink-850: #faf9f6;
--color-ink-800: #f0efeb;
--color-ink-700: #e2e0da;
--color-ink-600: #cfccc4;
--color-paper-100: #1d2024;
--color-paper-300: #3a3f46;
--color-paper-500: #5f6670;
--color-paper-600: #8a9099;
--color-signal-400: #b3741c;
--color-signal-500: #9a6116;
--color-mint-400: #1f8a61;
--color-ember-400: #c24b41;
--color-sky-400: #3f78b8;
}
html {
color-scheme: dark;
}
[data-theme="light"] {
color-scheme: light;
}
body {
@apply bg-ink-950 text-paper-100 font-sans antialiased;
font-feature-settings: "ss01" on, "cv05" on;
}
/* 数字统一用 tabular 对齐(数据密集场景) */
.num {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
/* 聚焦可见性:键盘可达性 */
:focus-visible {
outline: 2px solid var(--color-signal-400);
outline-offset: 2px;
}
/* 滚动条克制化 */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--color-ink-700);
border-radius: 6px;
border: 2px solid var(--color-ink-950);
}
::-webkit-scrollbar-track {
background: transparent;
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}