- S2: logs.ts LIMIT/OFFSET SQL parameterization (? placeholders) - S3: checkTokenSecret() startup validation, JWT_SECRET→TOKEN_SECRET in backup.ts - S3: Access Token expiry shortened to 2h (from 30d) - S4: Export download uses HMAC signed short-term token (/prepare endpoint) - S4: Frontend export.ts adapted for prepare-then-download flow - .env.example reorganized with security notes - Added PRD and architecture docs for iteration v2
213 lines
5.3 KiB
Plaintext
213 lines
5.3 KiB
Plaintext
classDiagram
|
|
direction TB
|
|
|
|
class TokenConfig {
|
|
+TOKEN_SECRET: string
|
|
+ACCESS_TOKEN_EXPIRY: number
|
|
+REFRESH_TOKEN_EXPIRY: number
|
|
}
|
|
|
|
class AuthMiddleware {
|
|
+authMiddleware(req, res, next): void
|
|
-verifyHmacToken(token): userId
|
|
-isTokenExpired(timestamp): boolean
|
|
}
|
|
|
|
class AuthRoute {
|
|
+POST /login: LoginResult
|
|
+POST /demo-login: LoginResult
|
|
+POST /refresh: RefreshResult
|
|
-signToken(userId): string
|
|
-generateRefreshToken(userId): string
|
|
-hashToken(token): string
|
|
-autoSetAdmin(userId): void
|
|
}
|
|
|
|
class LoginResult {
|
|
+accessToken: string
|
|
+refreshToken: string
|
|
+expiresIn: number
|
|
+userId: number
|
|
+nickname: string
|
|
+avatar_url: string
|
|
+role: string
|
|
}
|
|
|
|
class RefreshResult {
|
|
+accessToken: string
|
|
+refreshToken: string
|
|
+expiresIn: number
|
|
}
|
|
|
|
class RefreshTokenRow {
|
|
+id: number
|
|
+user_id: number
|
|
+token_hash: string
|
|
+expires_at: Date
|
|
+revoked_at: Date|null
|
|
+created_at: Date
|
|
}
|
|
|
|
class DownloadTokenRow {
|
|
+id: number
|
|
+token: string
|
|
+user_id: number
|
|
+resource_type: string
|
|
+resource_id: string
|
|
+expires_at: Date
|
|
+used_at: Date|null
|
|
+created_at: Date
|
|
}
|
|
|
|
class DownloadTokenRoute {
|
|
+POST /download-tokens: GenerateDownloadToken
|
|
+GET /download/:token: FileStream
|
|
-validateToken(token): DownloadTokenRow
|
|
-markUsed(tokenId): void
|
|
-cleanupExpired(): void
|
|
}
|
|
|
|
class AdminRoute {
|
|
+GET /dashboard: DashboardData
|
|
+GET /users: UserList
|
|
+PUT /users/:id/status: void
|
|
+DELETE /users/:id: void~~软删除
|
|
+POST /users/:id/restore: void~~恢复
|
|
}
|
|
|
|
class UserRow {
|
|
+id: number
|
|
+openid: string
|
|
+nickname: string
|
|
+avatar_url: string
|
|
+role: string
|
|
+deleted_at: Date|null
|
|
+created_at: Date
|
|
}
|
|
|
|
class ManageListComponent {
|
|
+items: T[]
|
|
+displayField: string
|
|
+colorField: string
|
|
+showDrag: boolean
|
|
+showEdit: boolean
|
|
+showDelete: boolean
|
|
+emptyText: string
|
|
+emit_add(): void
|
|
+emit_edit(item): void
|
|
+emit_delete(item): void
|
|
+emit_sort(ids): void
|
|
}
|
|
|
|
class ColorPickerComponent {
|
|
+modelValue: string
|
|
+colors: string[]
|
|
+emit_update:modelValue(color): void
|
|
}
|
|
|
|
class EditModalComponent {
|
|
+visible: boolean
|
|
+title: string
|
|
+fields: EditField[]
|
|
+emit_confirm(data): void
|
|
}
|
|
|
|
class SnackbarComponent {
|
|
+message: string
|
|
+actionText: string
|
|
+duration: number
|
|
+visible: boolean
|
|
+emit_action(): void
|
|
+show(msg, action): void
|
|
+hide(): void
|
|
}
|
|
|
|
class OfflineQueue {
|
|
-pendingOps: PendingOp[]
|
|
-isProcessing: boolean
|
|
+enqueue(op): void
|
|
+dequeue(id): void
|
|
+processPendingOps(): Promise~void~
|
|
+isOnline(): boolean
|
|
+startNetworkListener(): void
|
|
}
|
|
|
|
class PendingOp {
|
|
+id: string
|
|
+type: string
|
|
+data: object
|
|
+createdAt: number
|
|
+status: string
|
|
}
|
|
|
|
class SchedulerService {
|
|
+start(): void
|
|
+stop(): void
|
|
-weeklyReportCron(): void
|
|
-monthlyReportCron(): void
|
|
-dailyBudgetScanCron(): void
|
|
}
|
|
|
|
class ReportService {
|
|
+generateWeeklyReport(userId): WeeklyReport
|
|
+generateMonthlyReport(userId): MonthlyReport
|
|
-queryPeriodData(userId, start, end): PeriodData
|
|
}
|
|
|
|
class BudgetAlertService {
|
|
+checkBudgetAlert(userId, month): void
|
|
-calculateUsageRatio(userId, month): number
|
|
-sendNotification(userId, level): void
|
|
-sendWechatMessage(userId, level): void
|
|
-hasAlerted(userId, month, level): boolean
|
|
}
|
|
|
|
class WechatSubscribeService {
|
|
+sendMessage(userId, templateId, data): void
|
|
-getAccessToken(): string
|
|
}
|
|
|
|
class BudgetAlertRow {
|
|
+id: number
|
|
+user_id: number
|
|
+month: string
|
|
+level: string
|
|
+group_id: number|null
|
|
+created_at: Date
|
|
}
|
|
|
|
class UserSettings {
|
|
+user_id: number
|
|
+report_push_enabled: boolean
|
|
+updated_at: Date
|
|
}
|
|
|
|
class StatsRoute {
|
|
+GET /overview: Overview
|
|
+GET /category: CategoryStat[]
|
|
+GET /trend: TrendPoint[]
|
|
+GET /dashboard: DashboardData
|
|
-buildWhereClause(userId, groupId): WhereResult
|
|
}
|
|
|
|
class DashboardData {
|
|
+overview: Overview
|
|
+category: CategoryStat[]
|
|
+trend: TrendPoint[]
|
|
}
|
|
|
|
TokenConfig <-- AuthMiddleware : uses
|
|
TokenConfig <-- AuthRoute : uses
|
|
AuthRoute --> LoginResult : returns
|
|
AuthRoute --> RefreshResult : returns
|
|
AuthRoute --> RefreshTokenRow : creates/revokes
|
|
DownloadTokenRoute --> DownloadTokenRow : creates/validates
|
|
AdminRoute --> UserRow : soft deletes
|
|
SchedulerService --> ReportService : triggers
|
|
SchedulerService --> BudgetAlertService : triggers
|
|
ReportService --> WechatSubscribeService : uses
|
|
BudgetAlertService --> WechatSubscribeService : uses
|
|
BudgetAlertService --> BudgetAlertRow : creates
|
|
BudgetAlertService --> UserSettings : checks
|
|
StatsRoute --> DashboardData : returns
|