First commit with headless stuff

This commit is contained in:
Siddharth Singh 2024-08-26 05:43:52 +05:30
parent 8adc87f1d9
commit ebc27e8297
No known key found for this signature in database
4 changed files with 159 additions and 1 deletions

View file

@ -1,8 +1,16 @@
ARG DITTO_DOMAIN
ARG DITTO_UPLOADER_CONFIG
ENV DITTO_DOMAIN ${DITTO_DOMAIN}
ENV DITTO_UPLOADER_CONFIG ${DITTO_UPLOADER_CONFIG}
ENV PORT 5000
FROM denoland/deno:1.44.2
EXPOSE 4036
EXPOSE 5000
WORKDIR /app
RUN mkdir -p data && chown -R deno data
USER deno
COPY . .
RUN deno cache src/server.ts
RUN deno task setup:headless
CMD deno task start

View file

@ -8,6 +8,8 @@
"db:export": "deno run -A scripts/db-export.ts",
"db:import": "deno run -A scripts/db-import.ts",
"db:migrate": "deno run -A scripts/db-migrate.ts",
"headless:setup": "deno run -A scripts/headless/setup.ts",
"headless:uploader-config": "deno run -A scripts/headless/uploader-config.ts",
"nostr:pull": "deno run -A scripts/nostr-pull.ts",
"debug": "deno run -A --inspect src/server.ts",
"test": "deno test -A --junit-path=./deno-test.xml",

40
scripts/headless/setup.ts Normal file
View file

@ -0,0 +1,40 @@
import { generateSecretKey, nip19 } from 'nostr-tools';
import { parseUploaderConfig } from './uploader-config.ts';
function scream(...args: any[]) {
console.error('FATAL:', ...args);
Deno.exit(1);
}
function missingEnv(what: string, v: string) {
scream(`${what} not set! Set the ${v} config variable before trying again.`);
}
if (import.meta.main) {
const key = generateSecretKey();
const DITTO_NSEC = nip19.nsecEncode(key);
const LOCAL_DOMAIN = Deno.env.get('DITTO_DOMAIN');
if (!LOCAL_DOMAIN) missingEnv('Domain value', 'DITTO_DOMAIN');
const uploaderConfig = Deno.env.get('DITTO_UPLOADER_CONFIG');
if (!uploaderConfig) missingEnv('Uploader configuration', 'DITTO_UPLOADER_CONFIG');
let uploader: ReturnType<typeof parseUploaderConfig>;
try {
uploader = parseUploaderConfig(uploaderConfig!);
} catch (e) {
scream('Error decoding uploader config:', e.message || e.toString());
}
const vars = {
LOCAL_DOMAIN,
DITTO_NSEC,
...uploader!,
};
const result = Object.entries(vars)
.reduce((acc, [key, value]) => value ? `${acc}${key}="${value}"\n` : acc, '');
await Deno.writeTextFile('./.env', result);
}

View file

@ -0,0 +1,108 @@
import { base64 } from '@scure/base';
import { z } from 'zod';
import { Conf } from '@/config.ts';
import question from 'question-deno';
const s3Schema = z.object({
DITTO_UPLOADER: z.literal('s3'),
S3_ACCESS_KEY: z.string(),
S3_SECRET_KEY: z.string(),
S3_ENDPOINT: z.string().url(),
S3_BUCKET: z.string(),
S3_REGION: z.string(),
S3_PATH_STYLE: z.union([z.literal('true'), z.literal('false')]),
MEDIA_DOMAIN: z.string().url(),
});
const blossomSchema = z.object({
DITTO_UPLOADER: z.literal('blossom'),
BLOSSOM_SERVERS: z.string().refine((value) => {
return value.split(',').every((server) => {
try {
new URL(server);
return true;
} catch {
return false;
}
});
}, { message: 'All Blossom servers must be valid URLs' }),
});
const nostrBuildSchema = z.object({
DITTO_UPLOADER: z.literal('nostrbuild'),
NOSTRBUILD_ENDPOINT: z.string().url(),
});
const ipfsSchema = z.object({
DITTO_UPLOADER: z.literal('ipfs'),
IPFS_API_URL: z.string().url(),
MEDIA_DOMAIN: z.string().url(),
});
const localSchema = z.object({
DITTO_UPLOADER: z.literal('local'),
UPLOADS_DIR: z.string().default(Conf.nostrbuildEndpoint),
MEDIA_DOMAIN: z.string().url(),
});
const uploaderSchema = z.union([
nostrBuildSchema,
blossomSchema,
s3Schema,
ipfsSchema,
localSchema,
]);
export function parseUploaderConfig(cfg: string) {
const decoded = new TextDecoder().decode(base64.decode(cfg!));
const parsed = JSON.parse(decoded);
const validated = uploaderSchema.parse(parsed);
return validated;
}
if (import.meta.main) {
const vars: Record<string, string | undefined> = {};
const domain = await question('input', 'Instance domain? (eg ditto.pub)');
if (!domain) {
throw new Error('Domain is required!');
}
vars.DITTO_UPLOADER = await question('list', 'How do you want to upload files?', [
'nostrbuild',
'blossom',
's3',
'ipfs',
'local',
]);
if (vars.DITTO_UPLOADER === 'nostrbuild') {
vars.NOSTRBUILD_ENDPOINT = await question('input', 'nostr.build endpoint', Conf.nostrbuildEndpoint);
}
if (vars.DITTO_UPLOADER === 'blossom') {
vars.BLOSSOM_SERVERS = await question('input', 'Blossom servers (comma separated)', Conf.blossomServers.join(','));
}
if (vars.DITTO_UPLOADER === 's3') {
vars.S3_ACCESS_KEY = await question('input', 'S3 access key', Conf.s3.accessKey);
vars.S3_SECRET_KEY = await question('input', 'S3 secret key', Conf.s3.secretKey);
vars.S3_ENDPOINT = await question('input', 'S3 endpoint', Conf.s3.endPoint);
vars.S3_BUCKET = await question('input', 'S3 bucket', Conf.s3.bucket);
vars.S3_REGION = await question('input', 'S3 region', Conf.s3.region);
vars.S3_PATH_STYLE = String(await question('confirm', 'Use path style?', Conf.s3.pathStyle ?? false));
const mediaDomain = await question('input', 'Media domain', `media.${domain}`);
vars.MEDIA_DOMAIN = `https://${mediaDomain}`;
}
if (vars.DITTO_UPLOADER === 'ipfs') {
vars.IPFS_API_URL = await question('input', 'IPFS API URL', Conf.ipfs.apiUrl);
const mediaDomain = await question('input', 'Media domain', `media.${domain}`);
vars.MEDIA_DOMAIN = `https://${mediaDomain}`;
}
if (vars.DITTO_UPLOADER === 'local') {
vars.UPLOADS_DIR = await question('input', 'Local uploads directory', Conf.uploadsDir);
const mediaDomain = await question('input', 'Media domain', `media.${domain}`);
vars.MEDIA_DOMAIN = `https://${mediaDomain}`;
}
const encoded = base64.encode(new TextEncoder().encode(JSON.stringify(vars)));
console.log(encoded);
}