Eve-Relay/utils.ts
Danny Morabito a4134fa416
🔄 Synchronize Biome linting rules between relay and frontend
🛠️ Apply identical Biome configuration from frontend to relay service
🧹 Ensure consistent code formatting and quality standards across components
📝 Maintain unified development experience throughout the project
2025-03-24 19:23:22 +01:00

106 lines
3 KiB
TypeScript

import { decodeBase64, encodeBase64 } from 'jsr:@std/encoding@0.224/base64';
import { exists } from 'jsr:@std/fs';
import * as nostrTools from '@nostr/tools';
import * as nip06 from '@nostr/tools/nip06';
import {
decryptUint8Array,
encryptUint8Array,
encryptionKey,
} from './utils/encryption.ts';
import { getEveFilePath } from './utils/files.ts';
export function isLocalhost(req: Request): boolean {
const url = new URL(req.url);
const hostname = url.hostname;
return (
hostname === '127.0.0.1' || hostname === '::1' || hostname === 'localhost'
);
}
export function isValidJSON(str: string) {
try {
JSON.parse(str);
} catch {
return false;
}
return true;
}
export function isArray<T>(obj: unknown): obj is T[] {
return Array.isArray(obj);
}
export function randomTimeUpTo2DaysInThePast() {
const now = Date.now();
const twoDaysAgo = now - 2 * 24 * 60 * 60 * 1000 - 3600 * 1000; // 1 hour buffer in case of clock skew
return Math.floor(
(Math.floor(Math.random() * (now - twoDaysAgo)) + twoDaysAgo) / 1000,
);
}
export async function getCCNPubkey(): Promise<string> {
const ccnPubPath = await getEveFilePath('ccn.pub');
const seedPath = await getEveFilePath('ccn.seed');
const doWeHaveKey = await exists(ccnPubPath);
if (doWeHaveKey) return Deno.readTextFileSync(ccnPubPath);
const ccnSeed =
Deno.env.get('CCN_SEED') ||
((await exists(seedPath))
? Deno.readTextFileSync(seedPath)
: nip06.generateSeedWords());
const ccnPrivateKey = nip06.privateKeyFromSeedWords(ccnSeed);
const ccnPublicKey = nostrTools.getPublicKey(ccnPrivateKey);
const encryptedPrivateKey = encryptUint8Array(ccnPrivateKey, encryptionKey);
Deno.writeTextFileSync(ccnPubPath, ccnPublicKey);
Deno.writeTextFileSync(
await getEveFilePath('ccn.priv'),
encodeBase64(encryptedPrivateKey),
);
Deno.writeTextFileSync(seedPath, ccnSeed);
return ccnPublicKey;
}
export async function getCCNPrivateKey(): Promise<Uint8Array> {
const encryptedPrivateKey = Deno.readTextFileSync(
await getEveFilePath('ccn.priv'),
);
return decryptUint8Array(decodeBase64(encryptedPrivateKey), encryptionKey);
}
export function isReplaceableEvent(kind: number): boolean {
return (kind >= 10000 && kind < 20000) || kind === 0 || kind === 3;
}
export function isAddressableEvent(kind: number): boolean {
return kind >= 30000 && kind < 40000;
}
export function isRegularEvent(kind: number): boolean {
return (
(kind >= 1000 && kind < 10000) ||
(kind >= 4 && kind < 45) ||
kind === 1 ||
kind === 2
);
}
export function isCCNReplaceableEvent(kind: number): boolean {
return kind >= 60000 && kind < 65536;
}
export function parseATagQuery(aTagValue: string): {
kind: number;
pubkey: string;
dTag?: string;
} {
const parts = aTagValue.split(':');
if (parts.length < 2) return { kind: 0, pubkey: '' };
return {
kind: Number.parseInt(parts[0], 10),
pubkey: parts[1],
dTag: parts.length > 2 ? parts[2] : undefined,
};
}