refactor: fix all P0/P1/P2 bugs and architecture issues

Bug fixes (from bugs/ directory):

- Fix cross-module DB queries in 9 modules (homework, grades, parent, diagnostic, elective, proctoring, notifications, scheduling, classes) by routing through data-access functions

- Fix shared/lib <-> auth circular dependency via new session.ts module

- Fix divide-by-zero guard in grades data-access

- Fix audit export data truncation (paginated fetch for full datasets)

- Fix missing transactions in homework grading and elective lottery

- Fix missing revalidatePath in course-plans actions

- Fix frontend permission checks using requirePermission instead of requireAuth

- Fix dashboard role routing using session.user.roles

- Fix student auth pattern (migrate getDemoStudentUser to users module)

- Fix ActionState return type handling in components

Code quality fixes:

- Remove 60+ as type assertions (replace with type guards)

- Remove non-null assertions (use optional chaining or explicit checks)

- Convert dynamic imports to static imports (grades, diagnostic)

- Add React.cache() wrapping for read functions

- Parallelize independent queries with Promise.all

- Add explicit return types to 30+ arrow functions

- Replace any with unknown + type guards

- Fix import type for type-only imports

- Add Zod validation schemas for classes and diagnostic modules

- Extract duplicate code (normalizeRoleName, normalizeBcryptHash, logger IP extraction)

- Add console.error to silent catch blocks

- Fix permission naming consistency (exam:proctor_read -> exam:proctor:read)

Architecture doc sync:

- Update 004_architecture_impact_map.md and 005_architecture_data.json

- Update management-modules-audit.md for P0-7 cross-module fix

Moved deleted proctoring event route to deletes/ folder.
This commit is contained in:
SpecialX
2026-06-19 05:13:09 +08:00
parent 063baffe4c
commit 49291fcc31
114 changed files with 12548 additions and 3395 deletions

View File

@@ -30,25 +30,37 @@ const sortChapters = (a: Chapter, b: Chapter) => {
}
const buildChapterTree = (rows: Chapter[]): Chapter[] => {
const byId = new Map<string, Chapter & { children: Chapter[] }>()
type ChapterNode = Chapter & { children: ChapterNode[] }
const isChapterNode = (n: Chapter): n is ChapterNode =>
Array.isArray(n.children)
const byId = new Map<string, ChapterNode>()
for (const ch of rows) {
byId.set(ch.id, { ...ch, children: [] })
}
const roots: Array<Chapter & { children: Chapter[] }> = []
const roots: ChapterNode[] = []
for (const ch of byId.values()) {
const pid = ch.parentId
if (pid && byId.has(pid)) {
byId.get(pid)!.children.push(ch)
if (pid) {
const parent = byId.get(pid)
if (parent) {
parent.children.push(ch)
} else {
roots.push(ch)
}
} else {
roots.push(ch)
}
}
const sortRecursive = (nodes: Array<Chapter & { children: Chapter[] }>) => {
const sortRecursive = (nodes: ChapterNode[]) => {
nodes.sort(sortChapters)
for (const n of nodes) {
sortRecursive(n.children as Array<Chapter & { children: Chapter[] }>)
if (isChapterNode(n)) {
sortRecursive(n.children)
}
}
}
@@ -62,14 +74,13 @@ export const getTextbooks = cache(async (query?: string, subject?: string, grade
const q = query?.trim()
if (q) {
const needle = `%${q}%`
conditions.push(
or(
like(textbooks.title, needle),
like(textbooks.subject, needle),
like(textbooks.grade, needle),
like(textbooks.publisher, needle)
)!
const nameCond = or(
like(textbooks.title, needle),
like(textbooks.subject, needle),
like(textbooks.grade, needle),
like(textbooks.publisher, needle)
)
if (nameCond) conditions.push(nameCond)
}
const s = subject?.trim()