diff --git a/src/config.ts b/src/config.ts index 5fa7be9a..37b1d0f3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,4 @@ +import os from 'node:os'; import * as dotenv from '@std/dotenv'; import { getPublicKey, nip19 } from 'nostr-tools'; import { z } from 'zod'; @@ -240,6 +241,14 @@ class Conf { static get policy(): string { return Deno.env.get('DITTO_POLICY') || new URL('../data/policy.ts', import.meta.url).pathname; } + /** Absolute path to the data directory used by Ditto. */ + static get dataDir(): string { + return Deno.env.get('DITTO_DATA_DIR') || new URL('../data', import.meta.url).pathname; + } + /** Absolute path of the Deno directory. */ + static get denoDir(): string { + return Deno.env.get('DENO_DIR') || `${os.userInfo().homedir}/.cache/deno`; + } /** Whether zap splits should be enabled. */ static get zapSplitsEnabled(): boolean { return optionalBooleanSchema.parse(Deno.env.get('ZAP_SPLITS_ENABLED')) ?? false; diff --git a/src/workers/policy.ts b/src/workers/policy.ts index 08511ded..f86f9d9b 100644 --- a/src/workers/policy.ts +++ b/src/workers/policy.ts @@ -13,8 +13,8 @@ export const policyWorker = Comlink.wrap( type: 'module', deno: { permissions: { - read: [Conf.policy], - write: false, + read: [Conf.denoDir, Conf.policy, Conf.dataDir], + write: [Conf.dataDir], net: 'inherit', env: false, }, @@ -24,7 +24,12 @@ export const policyWorker = Comlink.wrap( ); try { - await policyWorker.init(Conf.policy, Conf.databaseUrl, Conf.pubkey); + await policyWorker.init({ + path: Conf.policy, + cwd: Deno.cwd(), + databaseUrl: Conf.databaseUrl, + adminPubkey: Conf.pubkey, + }); console.debug(`Using custom policy: ${Conf.policy}`); } catch (e) { if (e.message.includes('Module not found')) { diff --git a/src/workers/policy.worker.ts b/src/workers/policy.worker.ts index 0036c4bd..3fb4ef3f 100644 --- a/src/workers/policy.worker.ts +++ b/src/workers/policy.worker.ts @@ -6,6 +6,18 @@ import * as Comlink from 'comlink'; import { DittoDB } from '@/db/DittoDB.ts'; import { EventsDB } from '@/storages/EventsDB.ts'; +/** Serializable object the worker can use to set up the state. */ +interface PolicyInit { + /** Path to the policy module (https, jsr, file, etc) */ + path: string; + /** Current working directory. */ + cwd: string; + /** Database URL to connect to. */ + databaseUrl: string; + /** Admin pubkey to use for EventsDB checks. */ + adminPubkey: string; +} + export class CustomPolicy implements NPolicy { private policy: NPolicy = new ReadOnlyPolicy(); @@ -14,7 +26,11 @@ export class CustomPolicy implements NPolicy { return this.policy.call(event); } - async init(path: string, databaseUrl: string, adminPubkey: string): Promise { + async init({ path, cwd, databaseUrl, adminPubkey }: PolicyInit): Promise { + // HACK: PGlite uses `path.resolve`, which requires read permission on Deno (which we don't want to give). + // We can work around this getting the cwd from the caller and overwriting `Deno.cwd`. + Deno.cwd = () => cwd; + const { kysely } = DittoDB.create(databaseUrl, { poolSize: 1 }); const store = new EventsDB({