kotsukotsu/server/db.ts

122 lines
3.5 KiB
TypeScript

import { SQL } from "bun"
const db = new SQL({
adapter: "sqlite",
filename: "./leo-typing.db",
create: true,
strict: true,
})
export async function initDb() {
await db`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT (datetime('now'))
)
`
await db`
CREATE TABLE IF NOT EXISTS credentials (
id TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
public_key TEXT NOT NULL,
counter INTEGER NOT NULL DEFAULT 0,
transports TEXT,
created_at TEXT DEFAULT (datetime('now'))
)
`
await db`
CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
expires_at TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`
}
// ---- User queries ----
export async function createUser(username: string): Promise<{ id: number; username: string }> {
const [user] = await db`
INSERT INTO users (username) VALUES (${username})
RETURNING id, username
`
return user as { id: number; username: string }
}
export async function getUserByUsername(username: string) {
const [user] = await db`SELECT id, username FROM users WHERE username = ${username}`
return user as { id: number; username: string } | undefined
}
export async function getUserById(id: number) {
const [user] = await db`SELECT id, username FROM users WHERE id = ${id}`
return user as { id: number; username: string } | undefined
}
// ---- Credential queries ----
export async function storeCredential(
credentialId: string,
userId: number,
publicKey: string,
counter: number,
transports?: string[],
) {
await db`
INSERT INTO credentials (id, user_id, public_key, counter, transports)
VALUES (${credentialId}, ${userId}, ${publicKey}, ${counter}, ${transports ? JSON.stringify(transports) : null})
`
}
export async function getCredentialById(credentialId: string) {
const [cred] = await db`SELECT * FROM credentials WHERE id = ${credentialId}`
return cred as {
id: string
user_id: number
public_key: string
counter: number
transports: string | null
} | undefined
}
export async function getCredentialsByUserId(userId: number) {
const creds = await db`SELECT * FROM credentials WHERE user_id = ${userId}`
return creds as Array<{
id: string
user_id: number
public_key: string
counter: number
transports: string | null
}>
}
export async function updateCredentialCounter(credentialId: string, counter: number) {
await db`UPDATE credentials SET counter = ${counter} WHERE id = ${credentialId}`
}
// ---- Session queries ----
export async function createSession(userId: number): Promise<string> {
const token = crypto.randomUUID()
const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
await db`
INSERT INTO sessions (token, user_id, expires_at)
VALUES (${token}, ${userId}, ${expires})
`
return token
}
export async function getSession(token: string) {
const [session] = await db`
SELECT s.token, s.user_id, s.expires_at, u.username
FROM sessions s JOIN users u ON s.user_id = u.id
WHERE s.token = ${token} AND s.expires_at > datetime('now')
`
return session as { token: string; user_id: number; username: string; expires_at: string } | undefined
}
export async function deleteSession(token: string) {
await db`DELETE FROM sessions WHERE token = ${token}`
}