Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ apps/web
apps/dashboard
examples
*.md
!packages/shared/package.json
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: ["main"]
branches: ['main']
pull_request:
branches: ["main"]
branches: ['main']
workflow_dispatch:

permissions:
Expand All @@ -23,7 +23,7 @@ jobs:
- uses: ./.github/actions/setup-pnpm

- name: Lint (ESLint)
run: pnpm run lint
run: pnpm run lint:check

- name: Format check (Prettier)
run: pnpm run format:check
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
**/node_modules/**
**/coverage/**
**/generated/**
**/vendor/**
**/pnpm-lock.yaml
**/package-lock.json
**/*.min.js
Expand All @@ -11,4 +12,5 @@ apps/server/public/**
apps/web/public/sounds/**
apps/web/public/uaf/**
apps/web/public/pwa/**
apps/web/src/utils/soundList.js
pnpm-workspace.yaml
38 changes: 0 additions & 38 deletions apps/dashboard/src/components/HelloWorld.vue

This file was deleted.

3 changes: 2 additions & 1 deletion apps/dashboard/src/components/LoginDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
import { Github, Globe, ChevronRight, Loader2 } from 'lucide-vue-next'
import { apiClient } from '@/lib/api'
import { toast } from 'vue-sonner'
import { DEFAULT_LOCAL_SERVER } from '@classworks/shared'

const props = defineProps({
modelValue: Boolean,
Expand Down Expand Up @@ -113,7 +114,7 @@ const loadProviders = async () => {
// 处理登录
const handleLogin = (provider) => {
// 构建OAuth URL
const authUrl = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:3030'}${provider.authUrl}`
const authUrl = `${import.meta.env.VITE_API_BASE_URL || DEFAULT_LOCAL_SERVER}${provider.authUrl}`

// 打开新窗口进行OAuth认证
const width = 600
Expand Down
3 changes: 2 additions & 1 deletion apps/dashboard/src/pages/auto-auth-test.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ const testKVOperation = async () => {
case 'get':
response = await apiClient.getKVItem(tab3Form.value.token, key)
break
case 'set':
case 'set': {
let parsedValue
try {
parsedValue = JSON.parse(value)
Expand All @@ -166,6 +166,7 @@ const testKVOperation = async () => {
}
response = await apiClient.setKVItem(tab3Form.value.token, key, parsedValue)
break
}
case 'delete':
response = await apiClient.deleteKVItem(tab3Form.value.token, key)
break
Expand Down
1 change: 0 additions & 1 deletion apps/dashboard/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import path from 'path'
import { fileURLToPath, URL } from 'node:url'
import tailwindcss from '@tailwindcss/vite'
import vue from '@vitejs/plugin-vue'
Expand Down
21 changes: 21 additions & 0 deletions apps/server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Classworks KV 后端环境变量
# 复制为 .env 后填写。OAuth 提供者详见 .env.oauth.example

# PostgreSQL
DATABASE_URL=postgresql://user:password@localhost:5432/classworks

# HTTP
PORT=3000
BASE_URL=http://localhost:3000
FRONTEND_URL=http://localhost:5173

# JWT(生产环境务必更换)
JWT_SECRET=change-me
REFRESH_TOKEN_SECRET=change-me
ACCESS_TOKEN_EXPIRES_IN=15m
REFRESH_TOKEN_EXPIRES_IN=7d

# 可选
NODE_ENV=development
SITE_KEY=
METRICS_TOKEN=
15 changes: 3 additions & 12 deletions apps/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,7 @@ FROM node:22-alpine AS base
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app

# --- dependency layer (changes only when package files change) ---
FROM base AS deps

COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY apps/server/package.json apps/server/
COPY packages/shared/package.json packages/shared/

RUN pnpm install --frozen-lockfile --prod

# --- build layer (prisma generate, etc.) ---
# --- build: install workspace deps + generate Prisma client ---
FROM base AS build

COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
Expand All @@ -30,10 +21,10 @@ RUN cd apps/server && npx prisma generate
FROM base

ENV NODE_ENV=production
ENV SKIP_PRISMA_GENERATE=1
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/server/node_modules ./apps/server/node_modules
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/packages/shared ./packages/shared
COPY --from=build /app/apps/server ./apps/server
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
Expand Down
2 changes: 1 addition & 1 deletion apps/server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ See [LICENSE](./LICENSE) for details.
在根目录创建或编辑 `.env`:

- 基础地址(用于回调):
- `BASE_URL`: `http://localhost:3030`
- `BASE_URL`: `http://localhost:3000`
- `FRONTEND_URL`: `http://localhost:5173`

- STCN(Casdoor)OIDC:
Expand Down
17 changes: 16 additions & 1 deletion apps/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import accountsRouter from './routes/accounts.js'
import autoAuthRouter from './routes/auto-auth.js'
import { register } from './utils/metrics.js'
import cors from 'cors'
import {
HEADER_APP_TOKEN,
HEADER_SITE_KEY,
HEADER_DEVICE_UUID,
HEADER_DEVICE_PASSWORD,
} from '@classworks/shared'

var app = express()

Expand All @@ -26,7 +32,16 @@ app.use(
exposedHeaders: ['ratelimit-policy', 'retry-after', 'ratelimit'], // 告诉浏览器这些响应头可以暴露
maxAge: 86400, // 设置OPTIONS请求的结果缓存24小时(86400秒),减少预检请求
credentials: true, // 允许跨域请求携带凭证
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'], // 允许的请求头
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
HEADER_APP_TOKEN,
HEADER_SITE_KEY,
HEADER_DEVICE_UUID,
HEADER_DEVICE_PASSWORD,
],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的HTTP方法
withCredentials: true, // 允许携带cookie等凭证信息
}),
Expand Down
78 changes: 21 additions & 57 deletions apps/server/classworks.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,43 @@ import dotenv from 'dotenv'

dotenv.config()

// 🔄 执行数据库迁移函数
function runDatabaseMigration() {
try {
console.log('🔄 执行数据库迁移...')
execSync('npx prisma migrate deploy', { stdio: 'inherit' })
console.log('✅ 数据库迁移完成')
} catch (error) {
console.error('❌ 数据库迁移失败:', error.message)
process.exit(1)
}
}

// 🧱 数据库初始化函数
function setupDatabase() {
function run(command, failMessage) {
try {
// 执行数据库迁移
runDatabaseMigration()
execSync(command, { stdio: 'inherit' })
} catch (error) {
console.error('❌ 数据库初始化失败:', error.message)
console.error(failMessage, error.message)
process.exit(1)
}
}

// 🔨 本地构建函数
function buildLocal() {
try {
// 确保数据库迁移已执行
runDatabaseMigration()
execSync('npm install', { stdio: 'inherit' }) // 安装依赖
execSync('npx prisma generate', { stdio: 'inherit' }) // 生成 Prisma 客户端
console.log('✅ 构建完成')
} catch (error) {
console.error('❌ 构建失败:', error.message)
process.exit(1)
}
function runDatabaseMigration() {
console.log('🔄 执行数据库迁移...')
run('npx prisma migrate deploy', '❌ 数据库迁移失败:')
console.log('✅ 数据库迁移完成')
}

// 🚀 启动服务函数
function startServer() {
try {
execSync('npm run start', { stdio: 'inherit' }) // 启动项目
} catch (error) {
console.error('❌ 服务启动失败:', error.message)
process.exit(1)
}
}

// ▶️ 执行 Prisma CLI 命令函数
function runPrismaCommand(args) {
try {
const command = `npx prisma ${args.join(' ')}`
execSync(command, { stdio: 'inherit' })
} catch (error) {
console.error('❌ Prisma 命令执行失败:', error.message)
process.exit(1)
}
console.log('🚀 启动服务...')
run(`${process.execPath} ./bin/www`, '❌ 服务启动失败:')
}

// 🧠 主函数,根据命令行参数判断执行哪种流程
async function main() {
const args = process.argv.slice(2) // 获取命令行参数
const args = process.argv.slice(2)
if (args[0] === 'prisma') {
// 如果输入的是 prisma 命令,则执行 prisma 子命令
runPrismaCommand(args.slice(1))
} else {
// 否则按默认流程:初始化 → 构建 → 启动服务
setupDatabase()
buildLocal()
startServer()
run(`npx prisma ${args.slice(1).join(' ')}`, '❌ Prisma 命令执行失败:')
return
}

runDatabaseMigration()

// 镜像构建阶段已 generate;本地直接跑入口时补一次
if (!process.env.SKIP_PRISMA_GENERATE) {
run('npx prisma generate', '❌ Prisma 客户端生成失败:')
}

startServer()
}

// 🚨 捕捉主函数异常
main().catch((error) => {
console.error('❌ 脚本执行失败:', error)
process.exit(1)
Expand Down
12 changes: 6 additions & 6 deletions apps/server/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ node cli/get-token-callback.js
两种模式都支持以下环境变量:

```bash
# 设置API服务器地址(默认: http://localhost:3030)
# 设置API服务器地址(默认: http://localhost:3000)
export API_BASE_URL=https://your-api-server.com

# 设置授权页面地址(默认: http://localhost:5173/authorize)
Expand Down Expand Up @@ -88,7 +88,7 @@ chmod +x cli/get-token.js
设备授权码: 1234-ABCD
============================================================
ℹ 授权码有效期: 15 分钟
ℹ API服务器: http://localhost:3030
ℹ API服务器: http://localhost:3000

ℹ 请在浏览器中打开上述地址,或在授权页面手动输入设备代码
ℹ 等待授权中...
Expand All @@ -106,7 +106,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
✓ 令牌已保存到: /home/user/.classworks/token.txt

使用示例:
curl -H "Authorization: Bearer eyJhbGc..." http://localhost:3030/kv
curl -H "Authorization: Bearer eyJhbGc..." http://localhost:3000/kv
```

### 回调模式输出
Expand All @@ -126,7 +126,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
状态参数: abc123
============================================================
ℹ 回调地址: http://localhost:8080/callback
ℹ API服务器: http://localhost:3030
ℹ API服务器: http://localhost:3000
ℹ 超时时间: 300 秒

ℹ 正在启动回调服务器...
Expand All @@ -145,7 +145,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
✓ 令牌已保存到: /home/user/.classworks/token-callback.txt

使用示例:
curl -H "Authorization: Bearer eyJhbGc..." http://localhost:3030/kv
curl -H "Authorization: Bearer eyJhbGc..." http://localhost:3000/kv
```

## 配置选项
Expand All @@ -154,7 +154,7 @@ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

可以通过修改相应文件中的 `CONFIG` 对象或设置环境变量来调整:

- `baseUrl` / `API_BASE_URL` - API 服务器地址(默认: `http://localhost:3030`)
- `baseUrl` / `API_BASE_URL` - API 服务器地址(默认: `http://localhost:3000`)
- `authPageUrl` / `AUTH_PAGE_URL` - Classworks 授权页面地址(默认: `http://localhost:5173/authorize`)
- `appId` / `APP_ID` - 应用ID(默认: 1)
- `siteKey` / `SITE_KEY` - 站点密钥(如果需要)
Expand Down
5 changes: 3 additions & 2 deletions apps/server/cli/get-token-callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
import http from 'http'
import url from 'url'
import { randomBytes } from 'crypto'
import { DEFAULT_LOCAL_SERVER, HEADER_SITE_KEY } from '@classworks/shared'

// 配置
const CONFIG = {
// API服务器地址
baseUrl: process.env.API_BASE_URL || 'http://localhost:3030',
baseUrl: process.env.API_BASE_URL || DEFAULT_LOCAL_SERVER,
// 站点密钥
siteKey: process.env.SITE_KEY || '',
// 应用ID
Expand Down Expand Up @@ -74,7 +75,7 @@ async function request(path, options = {}) {
}

if (CONFIG.siteKey) {
headers['X-Site-Key'] = CONFIG.siteKey
headers[HEADER_SITE_KEY] = CONFIG.siteKey
}

try {
Expand Down
Loading
Loading