mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-26 22:43:06 -04:00
657 lines
26 KiB
TypeScript
657 lines
26 KiB
TypeScript
import './src/dotenv.ts';
|
|
|
|
import { loadSuml } from './server/loader.ts';
|
|
import autoprefixer from 'autoprefixer';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import rtlcss from 'rtlcss';
|
|
import type { NuxtConfig } from '@nuxt/types';
|
|
import type { IncomingMessage } from 'connect';
|
|
import type { ServerResponse } from 'http';
|
|
import type { Plugin as PostcssPlugin } from 'postcss';
|
|
import { buildList } from './src/helpers.ts';
|
|
import buildLocaleList from './src/buildLocaleList.ts';
|
|
import formatError from './src/error.ts';
|
|
import { tsvParseConfig } from './src/tsv.ts';
|
|
import type { Config } from './locale/config.ts';
|
|
import type { Translations } from './locale/translations.ts';
|
|
|
|
const config = loadSuml('config') as Config;
|
|
const translations = loadSuml('translations') as Translations;
|
|
|
|
const locale = config.locale;
|
|
const locales = buildLocaleList(locale);
|
|
const title = translations.title;
|
|
const description = translations.description;
|
|
const keywords = (translations?.seo?.keywords || []).join(', ');
|
|
const banner = `${process.env.BASE_URL}/api/banner/zaimki.png`;
|
|
const colour = '#C71585';
|
|
|
|
let __dirname = new URL('.', import.meta.url).pathname;
|
|
if (process.platform === 'win32') {
|
|
// A small hack, for Windows can't have nice things.
|
|
__dirname = __dirname.slice(1);
|
|
}
|
|
|
|
const logo = fs.readFileSync(`${__dirname}/static/logo/logo.svg`).toString('utf-8')
|
|
.replace('/></svg>', 'fill="currentColor"/></svg>');
|
|
const versionFile = `${__dirname}/cache/version`;
|
|
const version = fs.existsSync(versionFile) ? fs.readFileSync(versionFile).toString('utf-8') : null;
|
|
|
|
const applePrivateKeyFile = `${__dirname}/keys/AuthKey_${process.env.APPLE_KEY_ID}.p8`;
|
|
process.env.APPLE_PRIVATE_KEY = fs.existsSync(applePrivateKeyFile) ? fs.readFileSync(applePrivateKeyFile).toString('utf-8') : '';
|
|
|
|
const allVersionsUrls = buildList(function*() {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
yield process.env.BASE_URL;
|
|
yield 'http://pronouns.test:3000';
|
|
yield 'http://localhost:3000';
|
|
} else if (process.env.ENV === 'test') {
|
|
yield process.env.BASE_URL;
|
|
} else {
|
|
yield 'https://pronouns.page';
|
|
for (const localeDescription of Object.values(locales)) {
|
|
yield localeDescription.url;
|
|
}
|
|
}
|
|
});
|
|
process.env.ALL_LOCALES_URLS = allVersionsUrls.join(',');
|
|
|
|
const postCssPlugins: PostcssPlugin[] = [
|
|
autoprefixer(),
|
|
];
|
|
|
|
if (config.dir === 'rtl') {
|
|
postCssPlugins.push(rtlcss() as PostcssPlugin);
|
|
}
|
|
|
|
const getAllFiles = (dirPath: string, arrayOfFiles: string[] = []): string[] => {
|
|
fs.readdirSync(dirPath).forEach(function (file) {
|
|
if (fs.statSync(`${dirPath}/${file}`).isDirectory()) {
|
|
arrayOfFiles = getAllFiles(`${dirPath}/${file}`, arrayOfFiles);
|
|
} else {
|
|
arrayOfFiles.push(path.join(dirPath, '/', file));
|
|
}
|
|
});
|
|
|
|
return arrayOfFiles;
|
|
};
|
|
const jsons: Record<string, unknown> = {};
|
|
for (const file of getAllFiles(`${__dirname}/data/docs`)) {
|
|
if (!file.endsWith('.json')) {
|
|
continue;
|
|
}
|
|
jsons[path.relative(`${__dirname}/data/docs`, file)] = JSON.parse(fs.readFileSync(file).toString());
|
|
}
|
|
|
|
|
|
const hostname = process.env.HOST ?? '0.0.0.0';
|
|
const port = parseInt(process.env.PORT ?? '3000');
|
|
const nuxtConfig: NuxtConfig = {
|
|
server: {
|
|
host: hostname, // listen on any host name
|
|
port,
|
|
},
|
|
target: 'server',
|
|
head: {
|
|
htmlAttrs: {
|
|
dir: config.dir || 'ltr',
|
|
},
|
|
title,
|
|
meta: [
|
|
{ charset: 'utf-8' },
|
|
|
|
{ hid: 'description', name: 'description', content: description },
|
|
|
|
{ hid: 'keywords', name: 'keywords', content: keywords },
|
|
|
|
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
{ hid: 'apple-mobile-web-app-title', name: 'apple-mobile-web-app-title', content: title },
|
|
{ hid: 'theme-color', name: 'theme-color', content: colour },
|
|
|
|
{ hid: 'og:type', property: 'og:type', content: 'article' },
|
|
{ hid: 'og:title', property: 'og:title', content: title },
|
|
{ hid: 'og:description', property: 'og:description', content: description },
|
|
{ hid: 'og:site_name', property: 'og:site_name', content: title },
|
|
{ hid: 'og:image', property: 'og:image', content: banner },
|
|
|
|
{ hid: 'twitter:card', property: 'twitter:card', content: 'summary_large_image' },
|
|
{ hid: 'twitter:title', property: 'twitter:title', content: title },
|
|
{ hid: 'twitter:description', property: 'twitter:description', content: description },
|
|
{ hid: 'twitter:site', property: 'twitter:site', content: process.env.BASE_URL },
|
|
{ hid: 'twitter:image', property: 'twitter:image', content: banner },
|
|
],
|
|
link: [
|
|
{ rel: 'icon', type: 'image/svg', href: '/logo/logo-primary.svg' },
|
|
],
|
|
},
|
|
|
|
css: [
|
|
'~/assets/style.scss',
|
|
],
|
|
plugins: [
|
|
{ src: '~/plugins/polyfill.ts', mode: 'client' },
|
|
{ src: '~/plugins/axios.ts' },
|
|
{ src: '~/plugins/globals.ts' },
|
|
{ src: '~/plugins/auth.ts' },
|
|
{ src: '~/plugins/track.ts', mode: 'client' },
|
|
{ src: '~/plugins/browserDetect.ts' },
|
|
],
|
|
components: true,
|
|
buildModules: [
|
|
'@nuxt/typescript-build',
|
|
],
|
|
typescript: {
|
|
typeCheck: process.env.NODE_ENV !== 'production',
|
|
},
|
|
modules: [
|
|
'@privyid/nuxt-csrf',
|
|
'@nuxtjs/pwa',
|
|
'@nuxtjs/axios',
|
|
['@nuxtjs/redirect-module', {
|
|
rules: config.redirects,
|
|
}],
|
|
'@nuxtjs/sentry',
|
|
'cookie-universal-nuxt',
|
|
'vue-plausible',
|
|
],
|
|
pwa: {
|
|
manifest: {
|
|
name: title,
|
|
short_name: title,
|
|
description,
|
|
background_color: '#ffffff',
|
|
theme_color: colour,
|
|
lang: locale,
|
|
},
|
|
workbox: {
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: /^\/@/,
|
|
handler: 'NetworkFirst',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
plausible: {
|
|
domain: process.env.PLAUSIBLE_DOMAIN || translations.domain,
|
|
// NOTE(privacy): Disables automatic tracking of page views, meaning we have to do it manually
|
|
// If it's not done manually, a privacy issue occurs, which we *do not want*
|
|
// - tecc
|
|
enableAutoPageviews: false,
|
|
},
|
|
sentry: {
|
|
disabled: !process.env.SENTRY_DSN,
|
|
tracing: {
|
|
tracesSampleRate: 0.1,
|
|
browserTracing: {
|
|
enableInp: true,
|
|
},
|
|
},
|
|
publishRelease: {
|
|
telemetry: false,
|
|
},
|
|
config: {
|
|
environment: process.env.NODE_ENV === 'production' ? config.locale : process.env.NODE_ENV!,
|
|
attachStacktrace: true,
|
|
beforeSend(event) {
|
|
const denyUrls = [
|
|
'chrome-extension://',
|
|
'moz-extension://',
|
|
'webkit-masked-url://',
|
|
'https://s0.2mdn.net',
|
|
'https://j.adlooxtracking.com',
|
|
'https://c.amazon-adsystem.com',
|
|
'https://assets.a-mo.net',
|
|
'https://btloader.com',
|
|
'https://challenges.cloudflare.com',
|
|
'https://static.criteo.net',
|
|
'https://securepubads.g.doubleclick.net',
|
|
'https://cdn.flashtalking.com',
|
|
'https://ajs-assets.ftstatic.com',
|
|
'https://cdn.fuseplatform.net',
|
|
'https://cmp.inmobi.com',
|
|
'https://cdn.js7k.com',
|
|
'https://z.moatads.com',
|
|
'https://ced-ns.sascdn.com',
|
|
'https://a.teads.tv',
|
|
'https://s.yimg.com',
|
|
];
|
|
|
|
// filter out exceptions originating from third party
|
|
for (const exception of event.exception?.values || []) {
|
|
for (const frame of exception.stacktrace?.frames || []) {
|
|
const originatingFromThirdParty = denyUrls.some((denyUrl) => {
|
|
return frame.abs_path?.startsWith(denyUrl) || frame.filename?.startsWith(denyUrl);
|
|
});
|
|
if (originatingFromThirdParty) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// do not send user information as Sentry somehow automatically detects username, email and user id
|
|
// https://docs.sentry.io/platforms/javascript/data-management/sensitive-data/
|
|
delete event.user;
|
|
|
|
return event;
|
|
},
|
|
beforeSendTransaction(event) {
|
|
// see comment on nuxtConfig.sentry.config.beforeSend
|
|
delete event.user;
|
|
return event;
|
|
},
|
|
},
|
|
},
|
|
publicRuntimeConfig: {
|
|
...config,
|
|
plausible: {
|
|
domain: process.env.PLAUSIBLE_DOMAIN || translations.domain,
|
|
apiHost: process.env.PLAUSIBLE_API_HOST,
|
|
enableAutoPageviews: false, // see comment on nuxtConfig.plausible.enableAutoPageviews
|
|
},
|
|
},
|
|
watch: ['data/config.suml'],
|
|
watchers: {
|
|
webpack: {
|
|
aggregateTimeout: 300,
|
|
poll: 1000,
|
|
},
|
|
},
|
|
build: {
|
|
postcss: {
|
|
postcssOptions: {
|
|
plugins: postCssPlugins,
|
|
},
|
|
},
|
|
optimization: {
|
|
splitChunks: {
|
|
maxSize: 250_000,
|
|
},
|
|
},
|
|
extend(config) {
|
|
config.module!.rules.push({
|
|
test: /\.csv|\.tsv$/,
|
|
loader: 'csv-loader',
|
|
options: tsvParseConfig,
|
|
});
|
|
config.module!.rules.push({
|
|
test: /\.suml$/,
|
|
loader: 'suml-loader',
|
|
});
|
|
config.module!.rules.push({
|
|
test: /\.md$/,
|
|
use: ['html-loader', 'markdown-loader'],
|
|
});
|
|
config.module!.rules.push({
|
|
test: /\.ya?ml$/,
|
|
use: 'yaml-loader',
|
|
});
|
|
config.module!.rules.push({
|
|
test: /\.js|\.ts$/,
|
|
loader: 'string-replace-loader',
|
|
options: {
|
|
// To load .json files inside of .js files of type module in a node environment,
|
|
// one has to either load from the filesystem or via a created require().
|
|
// While a load via filesystem is very unfriendly to webpack,
|
|
// the explicit creation of a require() function can be removed.
|
|
// This probably gets replaced in the future by a `import from with { type: 'json' }`
|
|
// statement, which is currently (2023-12) experimental in node and not well supported in webpack.
|
|
// https://nodejs.org/api/esm.html#json-modules
|
|
multiple: [
|
|
{
|
|
search: 'import { createRequire } from \'module\';',
|
|
replace: '',
|
|
},
|
|
{
|
|
search: /(?:const|var) require = createRequire\(import.meta.url\);/,
|
|
replace: '',
|
|
},
|
|
],
|
|
},
|
|
});
|
|
},
|
|
transpile: ['markdown-it'],
|
|
},
|
|
env: {
|
|
ENV: process.env.ENV!,
|
|
BASE_URL: process.env.BASE_URL!,
|
|
HOME_URL: process.env.HOME_URL || 'https://pronouns.page',
|
|
PUBLIC_KEY: fs.readFileSync(`${__dirname}/keys/public.pem`).toString(),
|
|
BUCKET: `https://${process.env.AWS_S3_BUCKET}.s3-${process.env.AWS_REGION}.amazonaws.com`,
|
|
CLOUDFRONT: process.env.CLOUDFRONT!,
|
|
TURNSTILE_SITEKEY: process.env.TURNSTILE_SITEKEY!,
|
|
ALL_LOCALES_URLS: process.env.ALL_LOCALES_URLS,
|
|
LOGO: logo,
|
|
JSONS: JSON.stringify(jsons),
|
|
PLAUSIBLE_API_HOST: process.env.PLAUSIBLE_API_HOST!,
|
|
HEARTBEAT_LINK: process.env.HEARTBEAT_LINK!,
|
|
VERSION: version!,
|
|
SHOPIFY_STOREFRONT_TOKEN: process.env.SHOPIFY_STOREFRONT_TOKEN ?? '',
|
|
},
|
|
serverMiddleware: [
|
|
'~/server/no-ssr.ts',
|
|
'~/server/index.ts',
|
|
],
|
|
axios: {
|
|
baseURL: `${process.env.BASE_URL}/api`,
|
|
},
|
|
router: {
|
|
extendRoutes(routes, resolve) {
|
|
if (config.pronouns.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.pronouns.route)}`,
|
|
component: resolve(__dirname, 'routes/pronouns.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.sources.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.sources.route)}`,
|
|
component: resolve(__dirname, 'routes/sources.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.nouns.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.nouns.route)}`,
|
|
component: resolve(__dirname, 'routes/nouns.vue'),
|
|
});
|
|
for (const subroute of config.nouns.subroutes || []) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(subroute)}`,
|
|
component: resolve(__dirname, `data/nouns/${subroute}.vue`),
|
|
});
|
|
}
|
|
if (config.nouns.templates.enabled && config.nouns.templates.route) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.nouns.templates.route)}`,
|
|
component: resolve(__dirname, 'routes/nounTemplates.vue'),
|
|
});
|
|
}
|
|
}
|
|
|
|
if (config.inclusive.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.inclusive.route)}`,
|
|
component: resolve(__dirname, 'routes/inclusive.vue'),
|
|
});
|
|
}
|
|
if (config.terminology.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.terminology.route)}`,
|
|
component: resolve(__dirname, 'routes/terminology.vue'),
|
|
});
|
|
|
|
if (config.nouns.enabled) {
|
|
// TODO remove later
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.nouns.route)}/${encodeURIComponent(config.terminology.route)}`,
|
|
component: resolve(__dirname, 'routes/terminology.vue'),
|
|
});
|
|
}
|
|
}
|
|
|
|
if (config.names.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.names.route)}`,
|
|
component: resolve(__dirname, 'routes/names.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.faq.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.faq.route)}`,
|
|
component: resolve(__dirname, 'routes/faq.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.links.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.route)}`,
|
|
component: resolve(__dirname, 'routes/links.vue'),
|
|
});
|
|
if (config.links.academicRoute) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.academicRoute)}`,
|
|
component: resolve(__dirname, 'routes/academic.vue'),
|
|
});
|
|
}
|
|
if (config.links.mediaRoute) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.mediaRoute)}`,
|
|
component: resolve(__dirname, 'routes/media.vue'),
|
|
});
|
|
}
|
|
if (config.links.translinguisticsRoute) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.translinguisticsRoute)}`,
|
|
component: resolve(__dirname, 'routes/translinguistics.vue'),
|
|
});
|
|
}
|
|
}
|
|
|
|
if (config.links.enabled && config.links.blog) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.blogRoute)}`,
|
|
component: resolve(__dirname, 'routes/blog.vue'),
|
|
name: 'blog',
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.blogRoute)}/:slug`,
|
|
component: resolve(__dirname, 'routes/blogEntry.vue'),
|
|
name: 'blogEntry',
|
|
});
|
|
if (config.blog && config.blog.shortcuts) {
|
|
for (const shortcut in config.blog.shortcuts) {
|
|
if (!config.blog.shortcuts.hasOwnProperty(shortcut)) {
|
|
continue;
|
|
}
|
|
const slug = config.blog.shortcuts[shortcut];
|
|
if ((config.blog.keepFullPath || []).includes(slug)) {
|
|
continue;
|
|
}
|
|
routes.push({
|
|
path: `/${encodeURIComponent(shortcut)}`,
|
|
component: resolve(__dirname, 'routes/blogEntry.vue'),
|
|
meta: { slug },
|
|
name: `blogEntryShortcut:${shortcut}`,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config.links.zine && config.links.zine.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.links.zine.route)}`,
|
|
component: resolve(__dirname, 'routes/zine.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.people.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.people.route)}`,
|
|
component: resolve(__dirname, 'routes/people.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.english.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.english.route)}`,
|
|
component: resolve(__dirname, 'routes/english.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.contact.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.contact.route)}`,
|
|
component: resolve(__dirname, 'routes/contact.vue'),
|
|
});
|
|
|
|
if (config.contact.team.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.contact.team.route)}`,
|
|
component: resolve(__dirname, 'routes/team.vue'),
|
|
});
|
|
}
|
|
}
|
|
|
|
if (config.census.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.census.route)}`,
|
|
component: resolve(__dirname, 'routes/census.vue'),
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.census.route)}/admin`,
|
|
component: resolve(__dirname, 'routes/censusModeration.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.user.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.user.route)}`,
|
|
component: resolve(__dirname, 'routes/user.vue'),
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.user.termsRoute)}`,
|
|
component: resolve(__dirname, 'routes/terms.vue'),
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.user.privacyRoute)}`,
|
|
component: resolve(__dirname, 'routes/privacy.vue'),
|
|
});
|
|
}
|
|
routes.push({ path: '/license', component: resolve(__dirname, 'routes/license.vue') });
|
|
routes.push({ path: '/design', component: resolve(__dirname, 'routes/design.vue') });
|
|
|
|
routes.push({ path: '/admin', component: resolve(__dirname, 'routes/admin.vue') });
|
|
|
|
routes.push({ path: '/admin/users', component: resolve(__dirname, 'routes/adminUsers.vue') });
|
|
routes.push({ path: '/admin/profiles', component: resolve(__dirname, 'routes/adminProfiles.vue') });
|
|
routes.push({
|
|
path: '/admin/audit-log/:username/:id',
|
|
component: resolve(__dirname, 'routes/adminAuditLog.vue'),
|
|
});
|
|
|
|
routes.push({
|
|
path: '/admin/timesheets',
|
|
component: resolve(__dirname, 'routes/adminTimesheets.vue'),
|
|
});
|
|
routes.push({
|
|
path: '/admin/timesheets/overview',
|
|
component: resolve(__dirname, 'routes/adminTimesheetsOverview.vue'),
|
|
});
|
|
|
|
routes.push({
|
|
path: '/admin/moderation',
|
|
component: resolve(__dirname, 'routes/adminModeration.vue'),
|
|
});
|
|
routes.push({
|
|
path: '/admin/abuse-reports',
|
|
component: resolve(__dirname, 'routes/adminAbuseReports.vue'),
|
|
});
|
|
routes.push({
|
|
path: '/admin/pending-bans',
|
|
component: resolve(__dirname, 'routes/adminPendingBans.vue'),
|
|
});
|
|
|
|
routes.push({
|
|
path: '/admin/translations/missing',
|
|
component: resolve(__dirname, 'routes/adminTranslationsMissing.vue'),
|
|
});
|
|
routes.push({
|
|
path: '/admin/translations/awaiting',
|
|
component: resolve(__dirname, 'routes/adminTranslationsAwaiting.vue'),
|
|
});
|
|
|
|
if (config.profile.enabled) {
|
|
routes.push({ path: '/u/*', component: resolve(__dirname, 'routes/profile.vue') });
|
|
routes.push({ path: '/@*', component: resolve(__dirname, 'routes/profile.vue') });
|
|
routes.push({ path: '/card/@*', component: resolve(__dirname, 'routes/profileCard.vue') });
|
|
if (config.profile.editorEnabled) {
|
|
routes.push({ path: '/editor', component: resolve(__dirname, 'routes/profileEditor.vue') });
|
|
}
|
|
}
|
|
|
|
if (config.pronouns.enabled) {
|
|
for (const prefix of [...config.pronouns?.sentence?.prefixes || [], config.pronouns?.prefix || '']) {
|
|
routes.push({
|
|
path: `${prefix}/${encodeURIComponent(config.pronouns.any)}`,
|
|
component: resolve(__dirname, 'routes/any.vue'),
|
|
});
|
|
routes.push({
|
|
path: `${prefix}/${encodeURIComponent(config.pronouns.any)}::group`,
|
|
component: resolve(__dirname, 'routes/any.vue'),
|
|
});
|
|
if (config.pronouns.null && config.pronouns.null.routes) {
|
|
for (const route of config.pronouns.null.routes) {
|
|
routes.push({
|
|
path: `${prefix}/${encodeURIComponent(route)}`,
|
|
component: resolve(__dirname, 'routes/avoiding.vue'),
|
|
});
|
|
}
|
|
}
|
|
if (config.pronouns.mirror) {
|
|
routes.push({
|
|
path: `${prefix}/${encodeURIComponent(config.pronouns.mirror.route)}`,
|
|
component: resolve(__dirname, 'routes/mirror.vue'),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config.calendar && config.calendar.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.calendar.route)}`,
|
|
component: resolve(__dirname, 'routes/calendar.vue'),
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.calendar.route)}/:year(\\d\\d\\d\\d)`,
|
|
component: resolve(__dirname, 'routes/calendar.vue'),
|
|
});
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.calendar.route)}/:year(\\d\\d\\d\\d)-:month(\\d\\d)-:day(\\d\\d)`,
|
|
component: resolve(__dirname, 'routes/calendarDay.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.workshops && config.workshops.enabled) {
|
|
routes.push({
|
|
path: `/${encodeURIComponent(config.workshops.route)}`,
|
|
component: resolve(__dirname, 'routes/workshops.vue'),
|
|
});
|
|
}
|
|
|
|
if (config.api !== null) {
|
|
routes.push({ path: '/api', component: resolve(__dirname, 'routes/api.vue') });
|
|
}
|
|
|
|
routes.push({ name: 'all', path: '*', component: resolve(__dirname, 'routes/pronoun.vue') });
|
|
},
|
|
middleware: 'atom',
|
|
},
|
|
hooks: {
|
|
render: {
|
|
errorMiddleware(app) {
|
|
app.use((err: Error, req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => {
|
|
if (err) {
|
|
console.error(formatError(err, req));
|
|
}
|
|
next(err);
|
|
});
|
|
},
|
|
},
|
|
listen(_server, { port }) {
|
|
if (version) {
|
|
process.stderr.write(`[${new Date().toISOString()}] ` +
|
|
`Listening on port ${port} with version ${version}\n`);
|
|
}
|
|
},
|
|
},
|
|
loadingIndicator: {
|
|
name: 'views/loading.html',
|
|
},
|
|
};
|
|
|
|
export default nuxtConfig;
|