123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
import { SQL } from "bun"
|
|
import { runtimeConfig } from "./config"
|
|
|
|
const db = new SQL({
|
|
adapter: "sqlite",
|
|
filename: runtimeConfig.sqlitePath,
|
|
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}`
|
|
}
|