import Fastify from 'fastify'
import prismaPlugin from './plugins/prisma'
import vodsRoutes from './plugins/vods'
import uploadsRoutes from './plugins/uploads'
import vtubersRoutes from './plugins/vtubers'
import usersRoutes from './plugins/users'
import indexRoutes from './plugins/index'
import streamsRoutes from './plugins/streams'
import adminRoutes from './plugins/admin'
import hls from './plugins/hls.ts'
import fastifyStatic from '@fastify/static'
import fastifySecureSession from '@fastify/secure-session'
import path, { basename } from 'node:path'
import fastifyFormbody from '@fastify/formbody'
import fastifyView from "@fastify/view"
import { env } from './config/env'
import { constants } from './config/constants'
import authRoutes from './plugins/auth'
import Handlebars from 'handlebars'
import graphileWorker from './plugins/graphileWorker'
import fastifySwagger from "@fastify/swagger"
import fastifySwaggerUi from "@fastify/swagger-ui"
import { join } from 'node:path'
import { format } from 'date-fns'
import * as jdenticon from 'jdenticon'
import { Role } from '../generated/prisma'
import fastifyFlash from '@fastify/flash'
import { isModerator, hasRole } from './utils/privs'
import { signUrl } from './utils/cdn'
import { extractBasePath } from './utils/filesystem'
import { truncate } from './utils/formatters.ts'
import { icons } from './utils/icons.ts'
import logger from './utils/logger.ts'
import fastifyCaching from '@fastify/caching'
export function buildApp() {
const app = Fastify()
Handlebars.registerHelper('formatDate', function (dateString) {
if (!dateString) return ''
return format(new Date(dateString), 'yyyy-MM-dd')
})
Handlebars.registerHelper('identicon', function (str, size = 48) {
return jdenticon.toSvg(str, size)
})
Handlebars.registerHelper('safeJson', function (context) {
return new Handlebars.SafeString(JSON.stringify(context));
});
Handlebars.registerHelper('json', function (context) {
return JSON.stringify(context)
})
Handlebars.registerHelper('patron', function (user) {
if (!user.roles) {
throw new Error(
'patron hbs helper was called without roles. This usually means you forgot to include roles relationship in the query.'
);
}
return user.roles.some((r: Role) => r.name.startsWith('supporter'));
});
Handlebars.registerHelper('notEqual', function (a, b) {
return a !== b;
});
Handlebars.registerHelper('isEqual', function (a, b) {
logger.trace(`isEqual a=${a} b=${b}`)
return a == b
});
Handlebars.registerHelper('isModerator', function (user) {
return isModerator(user)
})
Handlebars.registerHelper('hasRole', hasRole)
Handlebars.registerHelper('breaklines', function (text) {
text = Handlebars.Utils.escapeExpression(text);
text = text.replace(/(\r\n|\n|\r)/gm, ' ');
return new Handlebars.SafeString(text);
});
Handlebars.registerHelper('getCdnUrl', function (s3Key) {
// Before you remove this log, find a way to memoize this function!
logger.info(`getCdnUrl called with CDN_ORIGIN=${env.CDN_ORIGIN} and CDN_TOKEN_SECRET=${env.CDN_TOKEN_SECRET}`)
return signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
securityKey: env.CDN_TOKEN_SECRET,
expirationTime: constants.timeUnits.sevenDaysInSeconds,
})
})
/**
* @see https://github.com/video-dev/hls.js/issues/2152
*/
Handlebars.registerHelper('signedHlsUrl', function (s3Key) {
if (!s3Key) throw new Error(`signedHlsUrl called with falsy s3Key=${s3Key}`);
const pathAllowed = extractBasePath(s3Key)
const url = signUrl(`${env.CDN_ORIGIN}/${s3Key}`, {
securityKey: env.CDN_TOKEN_SECRET,
pathAllowed,
isDirectory: true,
expirationTime: constants.timeUnits.sevenDaysInSeconds,
})
logger.debug(`pathAllowed=${pathAllowed} url=${url}`)
return url
})
Handlebars.registerHelper('basename', function (url: string) {
return basename(url)
})
Handlebars.registerHelper('trunc', function (str, length = 6) {
return truncate(str, length)
});
Handlebars.registerHelper('icon', function (name: string, size = 20) {
const svg = icons[name];
if (!svg) {
return new Handlebars.SafeString(``);
}
// Inject width/height if not already set
const sizedSvg = svg
.replace(/