Public Access
feat: Implement API modules for admin, auth, exam, result, and setup functionalities
- Created adminApi.ts for admin-related operations including user management and site settings. - Developed authApi.ts for authentication processes such as registration, login, and profile management. - Added examApi.ts for handling exam CRUD operations. - Introduced resultApi.ts for managing results submission and retrieval. - Established setupApi.ts for initial setup processes and status checks. - Created ErrorBoundary component to handle rendering errors gracefully in the frontend. - Re-exported shared API utilities in index.ts for easier access.
This commit is contained in:
1 parent
62af83ce53
commit
c64cdcd9e3
46 files changed
+2227
-1125
No files matched your search
@@ -0,0 +1,112 @@
|
||||
import { getDb } from '../db/database.js'
|
||||
|
||||
export interface DbResult {
|
||||
id: number
|
||||
user_id: number
|
||||
exam_id: number | null
|
||||
title: string
|
||||
score_points: number
|
||||
total_points: number
|
||||
correct_count: number
|
||||
graded_count: number
|
||||
passing_score: number
|
||||
elapsed_ms: number
|
||||
state_json: string
|
||||
exam_json: string
|
||||
completed_at: number
|
||||
}
|
||||
|
||||
export interface ResultListItem {
|
||||
id: number
|
||||
title: string
|
||||
score_points: number
|
||||
total_points: number
|
||||
correct_count: number
|
||||
graded_count: number
|
||||
passing_score: number
|
||||
elapsed_ms: number
|
||||
completed_at: number
|
||||
exam_id: number | null
|
||||
}
|
||||
|
||||
export function createResult(data: Omit<DbResult, 'id'>): { id: number } {
|
||||
const db = getDb()
|
||||
const result = db.prepare(`
|
||||
INSERT INTO exam_results (user_id, exam_id, title, score_points, total_points, correct_count, graded_count, passing_score, elapsed_ms, state_json, exam_json)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(
|
||||
data.user_id,
|
||||
data.exam_id ?? null,
|
||||
data.title,
|
||||
data.score_points,
|
||||
data.total_points,
|
||||
data.correct_count,
|
||||
data.graded_count,
|
||||
data.passing_score ?? 60,
|
||||
data.elapsed_ms ?? 0,
|
||||
data.state_json,
|
||||
data.exam_json
|
||||
)
|
||||
return { id: result.lastInsertRowid as number }
|
||||
}
|
||||
|
||||
export function getResultById(id: number, userId: number): DbResult | undefined {
|
||||
const db = getDb()
|
||||
return db.prepare('SELECT * FROM exam_results WHERE id = ? AND user_id = ?').get(id, userId) as DbResult | undefined
|
||||
}
|
||||
|
||||
export function listResultsByUser(userId: number, options: { limit?: number; offset?: number } = {}): { results: ResultListItem[]; total: number } {
|
||||
const db = getDb()
|
||||
const limit = Math.min(options.limit ?? 30, 100)
|
||||
const offset = options.offset ?? 0
|
||||
|
||||
const results = db.prepare(`
|
||||
SELECT id, title, score_points, total_points, correct_count, graded_count,
|
||||
passing_score, elapsed_ms, completed_at, exam_id
|
||||
FROM exam_results
|
||||
WHERE user_id = ?
|
||||
ORDER BY completed_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(userId, limit, offset) as ResultListItem[]
|
||||
|
||||
const countRow = db.prepare('SELECT COUNT(*) AS total FROM exam_results WHERE user_id = ?').get(userId) as { total: number }
|
||||
|
||||
return { results, total: countRow.total }
|
||||
}
|
||||
|
||||
export function deleteResult(id: number, userId: number): boolean {
|
||||
const db = getDb()
|
||||
const result = db.prepare('DELETE FROM exam_results WHERE id = ? AND user_id = ?').run(id, userId)
|
||||
return result.changes > 0
|
||||
}
|
||||
|
||||
export function countResults(): number {
|
||||
const db = getDb()
|
||||
const row = db.prepare('SELECT COUNT(*) AS cnt FROM exam_results').get() as { cnt: number }
|
||||
return row.cnt
|
||||
}
|
||||
|
||||
export function getAverageScore(): number {
|
||||
const db = getDb()
|
||||
const row = db.prepare(
|
||||
'SELECT COALESCE(AVG(score_points * 100.0 / NULLIF(total_points, 0)), 0) AS avg FROM exam_results'
|
||||
).get() as { avg: number }
|
||||
return Math.round(row.avg * 10) / 10
|
||||
}
|
||||
|
||||
export function getRecentResults(limit: number): Array<DbResult & { display_name: string; email: string }> {
|
||||
const db = getDb()
|
||||
return db.prepare(`
|
||||
SELECT er.*, u.display_name, u.email
|
||||
FROM exam_results er
|
||||
JOIN users u ON u.id = er.user_id
|
||||
ORDER BY er.completed_at DESC
|
||||
LIMIT ?
|
||||
`).all(limit) as Array<DbResult & { display_name: string; email: string }>
|
||||
}
|
||||
|
||||
export function countResultsByUser(userId: number): number {
|
||||
const db = getDb()
|
||||
const row = db.prepare('SELECT COUNT(*) AS cnt FROM exam_results WHERE user_id = ?').get(userId) as { cnt: number }
|
||||
return row.cnt
|
||||
}
|
||||
Reference in new issue
Block a user