magic: added a way for running async functions in the top level jsx code

This commit is contained in:
2023-01-11 21:38:37 +01:00
parent 71280fba5c
commit fab8398a7a
8 changed files with 91 additions and 44 deletions
+23 -20
View File
@@ -1,6 +1,7 @@
import crypto from 'crypto'
import { readFile, writeFile, access, constants } from 'fs/promises'
import { Database, DatabaseConnection } from '../../shared/database'
import { MetadataProps as MetaProps, Problem, ProblemId, Solution, SolutionId, User, UserId, UserRole } from '../../shared/model'
@@ -53,16 +54,18 @@ function createMutex(): Mutex {
return { lock }
}
export type DatabaseConnection = {
type DatabaseInternal = {
path: string
initialValue: Database
mu: Mutex
}
export type Database = {
users: Record<string, User>
problems: Record<string, Problem>
solutions: Record<string, Solution>
export function createDatabaseWrapper(db: DatabaseInternal): DatabaseConnection {
return {
getProblem(id: string): Promise<Problem | null> {
return getProblem(db, id)
},
}
}
export function createDatabase(path: string, initialValue: Database) {
@@ -73,7 +76,7 @@ export function createDatabase(path: string, initialValue: Database) {
}
}
async function withDatabase<R>({ path, initialValue, mu }: DatabaseConnection, fn: (db: Database) => R | Promise<R>): Promise<R> {
async function withDatabase<R>({ path, initialValue, mu }: DatabaseInternal, fn: (db: Database) => R | Promise<R>): Promise<R> {
const unlock: Lock = await mu.lock()
try {
@@ -100,17 +103,17 @@ async function withDatabase<R>({ path, initialValue, mu }: DatabaseConnection, f
// Users
//
export const getUsers = (db: DatabaseConnection) =>
export const getUsers = (db: DatabaseInternal) =>
withDatabase(db, state => {
return Object.values(state.users)
})
export const getUser: (db: DatabaseConnection, id: string) => Promise<User | null> = (db, id) =>
export const getUser: (db: DatabaseInternal, id: string) => Promise<User | null> = (db, id) =>
withDatabase(db, state => {
return state.users[id] ?? null
})
export const createUser: (db: DatabaseConnection, user: User) => Promise<void> = (db, user) =>
export const createUser: (db: DatabaseInternal, user: User) => Promise<void> = (db, user) =>
withDatabase(db, state => {
state.users[user.id] = user
})
@@ -119,7 +122,7 @@ export const createUser: (db: DatabaseConnection, user: User) => Promise<void> =
// Problems
//
export const createProblem = (db: DatabaseConnection, { title, content, createdBy }: Omit<Problem, MetaProps>): Promise<ProblemId> =>
export const createProblem = (db: DatabaseInternal, { title, content, createdBy }: Omit<Problem, MetaProps>): Promise<ProblemId> =>
withDatabase(db, state => {
const id = crypto.randomBytes(4).toString('hex') as ProblemId
@@ -136,18 +139,18 @@ export const createProblem = (db: DatabaseConnection, { title, content, createdB
return id
})
export const getProblem = (db: DatabaseConnection, id: string): Promise<Problem | null> =>
export const getProblem = (db: DatabaseInternal, id: string): Promise<Problem | null> =>
withDatabase(db, state => {
const problem = (state.problems[id] ?? null) as Problem | null
return problem && !problem.deleted ? problem : null
})
export const getProblems = (db: DatabaseConnection): Promise<Problem[]> =>
export const getProblems = (db: DatabaseInternal): Promise<Problem[]> =>
withDatabase(db, state => {
return Object.values(state.problems).filter(p => !p.deleted)
})
export const updateProblem = (db: DatabaseConnection, id: ProblemId, problem: Partial<Omit<Problem, MetaProps>>): Promise<Problem> =>
export const updateProblem = (db: DatabaseInternal, id: ProblemId, problem: Partial<Omit<Problem, MetaProps>>): Promise<Problem> =>
withDatabase(db, state => {
state.problems[id] = {
...state.problems[id],
@@ -157,7 +160,7 @@ export const updateProblem = (db: DatabaseConnection, id: ProblemId, problem: Pa
return state.problems[id]
})
export const deleteProblem = (db: DatabaseConnection, id: ProblemId): Promise<void> =>
export const deleteProblem = (db: DatabaseInternal, id: ProblemId): Promise<void> =>
withDatabase(db, state => {
state.problems[id].deleted = true
})
@@ -167,7 +170,7 @@ export const deleteProblem = (db: DatabaseConnection, id: ProblemId): Promise<vo
//
export const createSolution = (
db: DatabaseConnection,
db: DatabaseInternal,
{ sentBy, forProblem, content }: Omit<Solution, MetaProps | 'status' | 'visible'>
): Promise<SolutionId> =>
withDatabase(db, state => {
@@ -188,25 +191,25 @@ export const createSolution = (
return id
})
export const getSolution = (db: DatabaseConnection, id: SolutionId): Promise<Solution | null> =>
export const getSolution = (db: DatabaseInternal, id: SolutionId): Promise<Solution | null> =>
withDatabase(db, state => {
const solution = (state.solutions[id] ?? null) as Solution | null
return solution && !solution.deleted ? solution : null
})
export const getSolutions = (db: DatabaseConnection) =>
export const getSolutions = (db: DatabaseInternal) =>
withDatabase(db, state => {
return Object.values(state.solutions).filter(s => !s.deleted)
})
export const getVisibleSolutions = (db: DatabaseConnection): Promise<Solution[]> =>
export const getVisibleSolutions = (db: DatabaseInternal): Promise<Solution[]> =>
withDatabase(db, state => {
return Object.values(state.solutions)
.filter(s => !s.deleted)
.filter(s => s.visible)
})
export const updateSolution = (db: DatabaseConnection, id: SolutionId, solution: Partial<Omit<Solution, MetaProps>>): Promise<Solution> =>
export const updateSolution = (db: DatabaseInternal, id: SolutionId, solution: Partial<Omit<Solution, MetaProps>>): Promise<Solution> =>
withDatabase(db, state => {
state.solutions[id] = {
...state.solutions[id],
@@ -216,7 +219,7 @@ export const updateSolution = (db: DatabaseConnection, id: SolutionId, solution:
return state.solutions[id]
})
export const deleteSolution = (db: DatabaseConnection, id: SolutionId): Promise<void> =>
export const deleteSolution = (db: DatabaseInternal, id: SolutionId): Promise<void> =>
withDatabase(db, state => {
state.solutions[id].deleted = true
})
+4 -2
View File
@@ -9,6 +9,7 @@ import { StatusCodes } from 'http-status-codes'
import {
createDatabase,
createDatabaseWrapper,
createProblem,
createSolution,
deleteProblem,
@@ -39,6 +40,7 @@ import { initialDatabaseValue } from './db/example-data'
import { validateObjectKeys } from '../shared/utils'
import { setupOAuth } from './auth'
import { DatabaseConnection } from '../shared/database'
type SessionId = Opaque<string, string, 'session'>
@@ -47,7 +49,7 @@ export interface SessionService {
getUserForSession(sid: SessionId): UserId | null
}
export async function createApiRouter() {
export async function createApiRouter(): Promise<[Router, DatabaseConnection]> {
const sessionStore: Record<SessionId, UserId> = {}
const sessions: SessionService = {
createSession(userId: UserId) {
@@ -428,5 +430,5 @@ export async function createApiRouter() {
res.json(requestedUser)
})
return r
return [r, createDatabaseWrapper(db)]
}