Refactor app architecture and clean local artifacts

This commit is contained in:
AzuTear
2026-06-24 23:43:14 +02:00
parent 17134b3b82
commit fef1d36fe8
274 changed files with 37724 additions and 6065 deletions
+149
View File
@@ -0,0 +1,149 @@
import type {
AdminAuditEntriesResponse,
AdminAuditQueryOptions,
AdminDashboardResponse,
AdminOperationalSettingsResponse,
AdminRiskFlagsResponse,
AdminRiskQueryOptions,
AdminRiskRulesResponse,
AdminSeasonDetailResponse,
AdminSeasonListItem,
AdminSiteSettingsResponse,
ApproveNominationPayload,
BulkResolveRiskFlagsPayload,
CreateSeasonPayload,
RejectNominationPayload,
ResolveRiskFlagPayload,
SetAwardResultPayload,
UpdateClipStatusPayload,
UpdateOperationalSettingsPayload,
UpdateRiskRulesPayload,
UpdateSeasonPayload,
UpdateSiteSettingsPayload,
UpsertCandidatePayload,
UpsertCategoryPayload,
} from '../../types/awards'
import { requestJson } from '../http'
import { jsonRequest } from './requestOptions'
function buildAdminAuditEntryParams(options: AdminAuditQueryOptions = {}) {
const params = new URLSearchParams({ limit: String(options.limit ?? 100) })
const trimmedQuery = options.query?.trim()
const trimmedAdmin = options.admin?.trim()
const trimmedAction = options.action?.trim()
const trimmedEntityType = options.entityType?.trim()
if (trimmedQuery) params.set('query', trimmedQuery)
if (trimmedAdmin) params.set('admin', trimmedAdmin)
if (trimmedAction) params.set('action', trimmedAction)
if (trimmedEntityType) params.set('entityType', trimmedEntityType)
if (options.from) params.set('from', options.from)
if (options.to) params.set('to', options.to)
if (options.cursor) params.set('cursor', options.cursor)
return params
}
export const adminApi = {
getAdminDashboard: () => requestJson<AdminDashboardResponse>('/api/admin/dashboard'),
getAdminAuditEntriesPage: (options: AdminAuditQueryOptions = {}) =>
requestJson<AdminAuditEntriesResponse>(`/api/admin/audit-entries?${buildAdminAuditEntryParams(options).toString()}`),
getAdminAuditEntries: (limit = 200, query = '') => {
const params = buildAdminAuditEntryParams({ limit, query })
return requestJson<AdminAuditEntriesResponse>(`/api/admin/audit-entries?${params.toString()}`)
.then((response) => response.items)
},
getAdminRiskFlagsPage: (options: AdminRiskQueryOptions = {}) => {
const params = new URLSearchParams({
limit: String(options.limit ?? 25),
offset: String(options.offset ?? 0),
status: options.status ?? 'open',
})
if (options.severity) {
params.set('severity', options.severity)
}
const trimmedQuery = options.query?.trim()
if (trimmedQuery) {
params.set('query', trimmedQuery)
}
if (options.reviewedOnly) {
params.set('reviewedOnly', 'true')
}
return requestJson<AdminRiskFlagsResponse>(`/api/admin/risk-flags?${params.toString()}`)
},
getAdminRiskFlags: (limit = 200, status = 'open', query = '') =>
adminApi.getAdminRiskFlagsPage({ limit, status, query }).then((response) => response.items),
getAdminRiskRules: () => requestJson<AdminRiskRulesResponse>('/api/admin/risk-rules'),
updateAdminRiskRules: (payload: UpdateRiskRulesPayload) =>
requestJson<AdminRiskRulesResponse>('/api/admin/risk-rules', jsonRequest('PUT', payload)),
getAdminSeasons: () => requestJson<AdminSeasonListItem[]>('/api/admin/seasons'),
getAdminSeasonDetail: (seasonId: number) =>
requestJson<AdminSeasonDetailResponse>(`/api/admin/seasons/${seasonId}`),
getAdminSiteSettings: () => requestJson<AdminSiteSettingsResponse>('/api/admin/site-settings'),
getAdminOperationalSettings: () =>
requestJson<AdminOperationalSettingsResponse>('/api/admin/operational-settings'),
createAdminSeason: (payload: CreateSeasonPayload) =>
requestJson<{ saved: boolean; seasonId: number }>('/api/admin/seasons', jsonRequest('POST', payload)),
updateAdminSeason: (seasonId: number, payload: UpdateSeasonPayload) =>
requestJson<{ saved: boolean; seasonId: number }>(`/api/admin/seasons/${seasonId}`, jsonRequest('PUT', payload)),
deleteAdminSeason: (seasonId: number) =>
requestJson<{ deleted: boolean; seasonId: number; year: number }>(`/api/admin/seasons/${seasonId}`, {
method: 'DELETE',
}),
updateAdminSiteSettings: (payload: UpdateSiteSettingsPayload) =>
requestJson<{ saved: boolean }>('/api/admin/site-settings', jsonRequest('PUT', payload)),
updateAdminOperationalSettings: (payload: UpdateOperationalSettingsPayload) =>
requestJson<{ saved: boolean; demoLoginPasswordSet: boolean }>('/api/admin/operational-settings', jsonRequest('PUT', payload)),
createAdminCategory: (seasonId: number, payload: UpsertCategoryPayload) =>
requestJson<{ saved: boolean; categoryId: number }>(`/api/admin/seasons/${seasonId}/categories`, jsonRequest('POST', payload)),
updateAdminCategory: (categoryId: number, payload: UpsertCategoryPayload) =>
requestJson<{ saved: boolean; categoryId: number }>(`/api/admin/categories/${categoryId}`, jsonRequest('PUT', payload)),
deleteAdminCategory: (categoryId: number) =>
requestJson<{ deleted: boolean; categoryId: number }>(`/api/admin/categories/${categoryId}`, {
method: 'DELETE',
}),
createAdminCandidate: (seasonId: number, payload: UpsertCandidatePayload) =>
requestJson<{ saved: boolean; candidateId: number }>(`/api/admin/seasons/${seasonId}/candidates`, jsonRequest('POST', payload)),
updateAdminCandidate: (candidateId: number, payload: UpsertCandidatePayload) =>
requestJson<{ saved: boolean; candidateId: number }>(`/api/admin/candidates/${candidateId}`, jsonRequest('PUT', payload)),
deleteAdminCandidate: (candidateId: number) =>
requestJson<{ deleted: boolean; candidateId: number }>(`/api/admin/candidates/${candidateId}`, {
method: 'DELETE',
}),
deleteAdminClip: (clipId: number) =>
requestJson<{ deleted: boolean; clipId: number }>(`/api/admin/clips/${clipId}`, {
method: 'DELETE',
}),
updateAdminClipStatus: (clipId: number, payload: UpdateClipStatusPayload) =>
requestJson<{ saved: boolean; clipId: number; status: string }>(`/api/admin/clips/${clipId}/status`, jsonRequest('POST', payload)),
setAdminResult: (seasonId: number, payload: SetAwardResultPayload) =>
requestJson<{ saved: boolean; resultId: number; seasonId: number; categoryId: number; candidateId: number }>(
`/api/admin/seasons/${seasonId}/results`,
jsonRequest('POST', payload),
),
deleteAdminResult: (resultId: number) =>
requestJson<{ deleted: boolean; resultId: number }>(`/api/admin/results/${resultId}`, {
method: 'DELETE',
}),
approveAdminNomination: (nominationId: number, payload: ApproveNominationPayload) =>
requestJson<{ saved: boolean; nominationId: number; candidateId: number; created: boolean }>(
`/api/admin/nominations/${nominationId}/approve`,
jsonRequest('POST', payload),
),
rejectAdminNomination: (nominationId: number, payload: RejectNominationPayload) =>
requestJson<{ saved: boolean; nominationId: number; rejected: boolean }>(
`/api/admin/nominations/${nominationId}/reject`,
jsonRequest('POST', payload),
),
resolveRiskFlag: (riskFlagId: number, payload: ResolveRiskFlagPayload) =>
requestJson<{ saved: boolean; riskFlagId: number; status: string }>(
`/api/admin/risk-flags/${riskFlagId}/resolve`,
jsonRequest('POST', payload),
),
bulkResolveRiskFlags: (payload: BulkResolveRiskFlagsPayload) =>
requestJson<{ saved: boolean; count: number; status: string }>(
'/api/admin/risk-flags/bulk-resolve',
jsonRequest('POST', payload),
),
}
+26
View File
@@ -0,0 +1,26 @@
import type { AuthSession, DemoLoginPayload, LoginPayload } from '../../types/awards'
import { requestJson } from '../http'
import { jsonRequest } from './requestOptions'
export const authApi = {
getSession: () => requestJson<AuthSession>('/api/auth/session'),
login: (payload: LoginPayload) =>
requestJson<AuthSession>('/api/auth/dev-login', jsonRequest('POST', payload)),
demoLogin: (payload: DemoLoginPayload) =>
requestJson<AuthSession>('/api/auth/demo-login', jsonRequest('POST', payload)),
logout: () =>
requestJson<{ loggedOut: boolean }>('/api/auth/logout', jsonRequest('POST', {})),
deleteMyData: () =>
requestJson<{
deleted: boolean
twitchUserId: string
deletedVoteEntries: number
deletedBallots: number
deletedNominations: number
deletedClips: number
deletedRiskFlags: number
disabledSessions: number
}>('/api/auth/me/data', {
method: 'DELETE',
}),
}
+29
View File
@@ -0,0 +1,29 @@
import type {
CreateClipPayload,
CreateNominationPayload,
CreateVotePayload,
OverviewResponse,
PublicSiteStatusResponse,
SeasonCategoriesResponse,
UserParticipationResponse,
WinnerArchiveResponse,
} from '../../types/awards'
import { requestJson } from '../http'
import { jsonRequest } from './requestOptions'
export const publicApi = {
getOverview: () => requestJson<OverviewResponse>('/api/public/overview'),
getSiteStatus: () => requestJson<PublicSiteStatusResponse>('/api/public/site-status'),
getSeasonCategories: (year: number) =>
requestJson<SeasonCategoriesResponse>(`/api/public/seasons/${year}/categories`),
getWinnerArchive: (year: number) =>
requestJson<WinnerArchiveResponse>(`/api/public/seasons/${year}/winners`),
getMyParticipation: (year: number) =>
requestJson<UserParticipationResponse>(`/api/public/seasons/${year}/me`),
submitNomination: (payload: CreateNominationPayload) =>
requestJson<{ saved: number; category: string }>('/api/public/nominations', jsonRequest('POST', payload)),
submitVote: (payload: CreateVotePayload) =>
requestJson<{ ballotId: number; entries: number }>('/api/public/votes', jsonRequest('POST', payload)),
submitClip: (payload: CreateClipPayload) =>
requestJson<{ saved: boolean; clipId: number }>('/api/public/clips', jsonRequest('POST', payload)),
}
+7
View File
@@ -0,0 +1,7 @@
export function jsonRequest(method: 'POST' | 'PUT', payload: unknown): RequestInit {
return {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}
}
+6
View File
@@ -0,0 +1,6 @@
import type { DatabaseHealthResponse } from '../../types/awards'
import { requestJson } from '../http'
export const systemApi = {
getDatabaseHealth: () => requestJson<DatabaseHealthResponse>('/api/health/database'),
}