1// Paths that need a friend's password, by prefix. Comparisons fold case to
2// match the file store, so a prefix gates every spelling of a path under it.
3const db = getDatabase("cache.sqlite");
4
5const table = sqliteTable("permissions", {
6 prefix: text().primaryKey(),
7 /** 1 needs a password. a path under no prefix is public */
8 allow: integer().notNull(),
9});
10db.migrate(table, 1);
11
12/** the permission of the longest prefix of `filePath`, or 0 */
13export function getByPrefix(filePath: string): number {
14 return db.get<{ allow: number } | undefined>(sql`
15 select allow from permissions where lower(${filePath}) glob lower(prefix) || '*'
16 order by length(prefix) desc limit 1
17 `)?.allow ?? 0;
18}
19
20/** the permission of exactly `filePath`, or 0 */
21export function getExact(filePath: string): number {
22 return db.select({ allow: table.allow }).from(table)
23 .where(sql`${table.prefix} = ${filePath} collate nocase`).get()?.allow ?? 0;
24}
25
26export function set(prefix: string, allow: number) {
27 if (allow) {
28 db.insert(table).values({ prefix, allow }).onConflictDoUpdate({ target: table.prefix, set: { allow } }).run();
29 } else {
30 db.delete(table).where(eq(table.prefix, prefix)).run();
31 }
32}
33
34import { getDatabase } from "#sitegen/drizzle";
35import { eq, sql } from "drizzle-orm";
36import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";