Preliminary sqlite db setup

This commit is contained in:
Alex Gleason 2023-08-06 12:54:00 -05:00
parent 00fa037aaf
commit 91eac97d5c
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
4 changed files with 107 additions and 51 deletions

View File

@ -7,7 +7,9 @@ import type { AppContext, AppController } from '@/app.ts';
const actorController: AppController = async (c) => { const actorController: AppController = async (c) => {
const username = c.req.param('username'); const username = c.req.param('username');
const user = await db.users.findFirst({ where: { username } });
const user = db.getUserByUsername(username);
if (!user) return notFound(c);
const event = await getAuthor(user.pubkey); const event = await getAuthor(user.pubkey);
if (!event) return notFound(c); if (!event) return notFound(c);

View File

@ -10,25 +10,20 @@ const nameSchema = z.string().min(1).regex(/^\w+$/);
* Serves NIP-05's nostr.json. * Serves NIP-05's nostr.json.
* https://github.com/nostr-protocol/nips/blob/master/05.md * https://github.com/nostr-protocol/nips/blob/master/05.md
*/ */
const nostrController: AppController = async (c) => { const nostrController: AppController = (c) => {
try { const name = nameSchema.safeParse(c.req.query('name'));
const name = nameSchema.parse(c.req.query('name')); const user = name.success ? db.getUserByUsername(name.data) : null;
const user = await db.users.findFirst({ where: { username: name } });
const relay = Conf.relay;
return c.json({ if (!user) return c.json({ names: {}, relays: {} });
names: {
[user.username]: user.pubkey, return c.json({
}, names: {
relays: relay [user.username]: user.pubkey,
? { },
[user.pubkey]: [relay], relays: {
} [user.pubkey]: [Conf.relay],
: {}, },
}); });
} catch (_e) {
return c.json({ names: {}, relays: {} });
}
}; };
export { nostrController }; export { nostrController };

View File

@ -36,26 +36,27 @@ const acctSchema = z.custom<URL>((value) => value instanceof URL)
path: ['resource', 'acct'], path: ['resource', 'acct'],
}); });
async function handleAcct(c: AppContext, resource: URL): Promise<Response> { function handleAcct(c: AppContext, resource: URL): Response {
try { const result = acctSchema.safeParse(resource);
const [username, host] = acctSchema.parse(resource); if (!result.success) {
const user = await db.users.findFirst({ where: { username } }); return c.json({ error: 'Invalid acct URI', schema: result.error }, 400);
const json = renderWebfinger({
pubkey: user.pubkey,
username: user.username,
subject: `acct:${username}@${host}`,
});
c.header('content-type', 'application/jrd+json');
return c.body(JSON.stringify(json));
} catch (e) {
if (e instanceof z.ZodError) {
return c.json({ error: 'Invalid acct URI', schema: e }, 400);
} else {
return c.json({ error: 'Not found' }, 404);
}
} }
const [username, host] = result.data;
const user = db.getUserByUsername(username);
if (!user) {
return c.json({ error: 'Not found' }, 404);
}
const json = renderWebfinger({
pubkey: user.pubkey,
username: user.username,
subject: `acct:${username}@${host}`,
});
c.header('content-type', 'application/jrd+json');
return c.body(JSON.stringify(json));
} }
interface RenderWebfingerOpts { interface RenderWebfingerOpts {

View File

@ -1,18 +1,76 @@
import { createPentagon, z } from '@/deps.ts'; import { Sqlite } from '@/deps.ts';
import { hexIdSchema } from '@/schema.ts';
const kv = await Deno.openKv(); interface User {
pubkey: string;
username: string;
inserted_at: Date;
}
const userSchema = z.object({ class DittoDB {
pubkey: hexIdSchema.describe('primary'), #db: Sqlite;
username: z.string().regex(/^\w{1,30}$/).describe('unique'),
createdAt: z.date(),
});
const db = createPentagon(kv, { constructor(db: Sqlite) {
users: { this.#db = db;
schema: userSchema,
},
});
this.#db.execute(`
CREATE TABLE events (
id TEXT PRIMARY KEY,
kind INTEGER NOT NULL,
pubkey TEXT NOT NULL,
content TEXT NOT NULL,
created_at INTEGER NOT NULL,
tags TEXT NOT NULL,
sig TEXT NOT NULL
);
CREATE INDEX idx_events_kind ON events(kind);
CREATE INDEX idx_events_pubkey ON events(pubkey);
CREATE TABLE tags (
tag TEXT NOT NULL,
value_1 TEXT,
value_2 TEXT,
value_3 TEXT,
event_id TEXT NOT NULL,
FOREIGN KEY(event_id) REFERENCES events(id) ON DELETE CASCADE
);
CREATE INDEX idx_tags_tag ON tags(tag);
CREATE INDEX idx_tags_value_1 ON tags(value_1);
CREATE INDEX idx_tags_event_id ON tags(event_id);
CREATE TABLE users (
pubkey TEXT PRIMARY KEY,
username TEXT NOT NULL,
inserted_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX idx_users_username ON users(username);
`);
}
insertUser(user: Pick<User, 'pubkey' | 'username'>): void {
this.#db.query(
'INSERT INTO users(pubkey, username) VALUES (?, ?)',
[user.pubkey, user.username],
);
}
getUserByUsername(username: string): User | null {
const result = this.#db.query<[string, string, Date]>(
'SELECT pubkey, username, inserted_at FROM users WHERE username = ?',
[username],
)[0];
if (!result) return null;
return {
pubkey: result[0],
username: result[1],
inserted_at: result[2],
};
}
}
const db = new DittoDB(
new Sqlite('data/db.sqlite3'),
);
export { db }; export { db };