Preliminary sqlite db setup
This commit is contained in:
parent
00fa037aaf
commit
91eac97d5c
|
@ -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);
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
84
src/db.ts
84
src/db.ts
|
@ -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 };
|
||||||
|
|
Loading…
Reference in New Issue