PronounsPage/server/nouns.ts

119 lines
3.6 KiB
TypeScript

/* eslint-disable camelcase */
import SQL from 'sql-template-strings';
import type { Database } from '~/server/db.ts';
import type { UserRow } from '~/server/express/user.ts';
import type { SourceRow } from '~/server/sources.ts';
import type { IsGrantedFn } from '~/server/utils/useAuthentication.ts';
import { clearKey } from '~/src/helpers.ts';
import type { User } from '~/src/user.ts';
export interface NounRow {
id: string;
masc: string;
fem: string;
neutr: string;
nb: string;
mascPl: string;
femPl: string;
neutrPl: string;
nbPl: string;
approved: boolean;
base_id: string | null;
locale: string;
author_id: string | null;
deleted: boolean;
sources: string | null;
categories: string | null;
}
type NounRowWithAuthor = NounRow & { author: User['username'] };
export const addVersions = async (
db: Database,
isGranted: IsGrantedFn,
locale: string,
nouns: NounRowWithAuthor[],
) => {
const keys = new Set();
nouns.filter((s) => !!s && s.sources)
.forEach((s) => s.sources!.split(',').forEach((k) => keys.add(`'${clearKey(k.split('#')[0])}'`)));
const sources = await db.all<SourceRow & Pick<UserRow, 'username'>>(SQL`
SELECT s.*, u.username AS submitter FROM sources s
LEFT JOIN users u ON s.submitter_id = u.id
WHERE s.locale == ${locale}
AND s.deleted = 0
AND s.approved >= ${isGranted('sources') ? 0 : 1}
AND s.key IN (`.append([...keys].join(',')).append(SQL`)
`));
const sourcesMap: Record<string, SourceRow> = {};
sources.forEach((s) => sourcesMap[s.key!] = s);
return nouns.map((n) => ({
...n,
sourcesData: (n.sources ? n.sources.split(',') : [])
.map((s) => selectFragment(sourcesMap, s))
.filter((source) => source !== undefined),
}));
};
const selectFragment = (sourcesMap: Record<string, SourceRow>, keyAndFragment: string) => {
const [key, fragment] = keyAndFragment.split('#');
if (sourcesMap[key] === undefined) {
return undefined;
}
if (fragment === undefined) {
return sourcesMap[key];
}
const source = { ...sourcesMap[key] };
const fragments = source.fragments
? source.fragments.replace(/\\@/g, '###').split('@')
.map((x) => x.replace(/###/g, '@'))
: [];
source.fragments = fragments[parseInt(fragment) - 1];
return source;
};
export const getNounEntries = defineCachedFunction(async (
db: Database,
isGranted: IsGrantedFn,
locale: string,
) => {
return await addVersions(db, isGranted, locale, await db.all<NounRowWithAuthor>(SQL`
SELECT n.*, u.username AS author FROM nouns n
LEFT JOIN users u ON n.author_id = u.id
WHERE n.locale = ${locale}
AND n.deleted = 0
AND n.approved >= ${isGranted('nouns') ? 0 : 1}
ORDER BY n.approved, n.masc
`));
}, {
name: 'nouns',
getKey: (db, isGranted, locale) => locale,
shouldBypassCache: (db, isGranted) => isGranted('nouns'),
maxAge: 24 * 60 * 60,
});
export const approveNounEntry = async (db: Database, id: string, locale: string) => {
const { base_id } = (await db.get<Pick<NounRow, 'base_id'>>(SQL`SELECT base_id FROM nouns WHERE id=${id}`))!;
if (base_id) {
await db.get(SQL`
UPDATE nouns
SET deleted=1
WHERE id = ${base_id}
`);
}
await db.get(SQL`
UPDATE nouns
SET approved = 1, base_id = NULL
WHERE id = ${id}
`);
await invalidateCacheKind('nouns', locale);
};