(nuxt) do not use import.meta.url directly to reference other files as this breaks in Nitro, use current working directory as convention

This commit is contained in:
Valentyne Stigloher 2024-06-23 13:39:41 +02:00
parent 8213eb38c7
commit 71a562bba9
22 changed files with 47 additions and 43 deletions

View File

@ -1,6 +1,6 @@
import jwt from './jwt.ts';
import type { Request } from 'express';
import type { User } from './user.ts';
import type { User } from '../src/user.ts';
export default ({ cookies, headers }: Request): User | undefined => {
if (headers.authorization && headers.authorization.startsWith('Bearer ')) {

View File

@ -3,7 +3,7 @@ import './setup.ts';
import * as Sentry from '@sentry/node';
import dbConnection from './db.ts';
import mailer from '../src/mailer.ts';
import mailer from './mailer.ts';
const execute = process.env.EXECUTE === '1';
console.log(execute ? 'WILL EXECUTE!' : 'Dry run');

View File

@ -1,8 +1,7 @@
import crypto from 'crypto';
import fs from 'fs';
import type { KeyObject, BinaryLike } from 'crypto';
const __dirname = new URL('.', import.meta.url).pathname;
import { rootDir } from './paths.ts';
class Crypto {
privateKey: KeyObject;
@ -26,4 +25,4 @@ class Crypto {
}
}
export default new Crypto(`${__dirname}/../keys/private.pem`, `${__dirname}/../keys/public.pem`);
export default new Crypto(`${rootDir}/keys/private.pem`, `${rootDir}/keys/public.pem`);

View File

@ -1,7 +1,6 @@
import * as sqlite from 'sqlite';
import sqlite3 from 'sqlite3';
const __dirname = new URL('.', import.meta.url).pathname;
import { rootDir } from './paths.ts';
export type SQLQuery = sqlite.ISqlite.SqlType;
export interface Database {
@ -12,6 +11,6 @@ export interface Database {
}
export default (): Promise<sqlite.Database> => sqlite.open({
filename: `${__dirname}/../db.sqlite`,
filename: `${rootDir}/db.sqlite`,
driver: sqlite3.Database,
});

View File

@ -7,7 +7,7 @@ import type { LocaleDescription } from '../../locale/locales.ts';
import allLocales from '../../locale/locales.ts';
import fs from 'fs';
import { caches } from '../../src/cache.ts';
import mailer from '../../src/mailer.ts';
import mailer from '../mailer.ts';
import { profilesSnapshot } from './profile.ts';
import { archiveBan, liftBan } from '../ban.ts';
import markdownit from 'markdown-it';
@ -16,6 +16,7 @@ import { encodeTime, decodeTime, ulid } from 'ulid';
import Suml from 'suml';
import buildLocaleList from '../../src/buildLocaleList.ts';
import auditLog from '../audit.ts';
import { rootDir } from '../paths.ts';
import type { UserRow } from './user.ts';
import type { Database } from '../db.ts';
import type { Config } from '../../locale/config.ts';
@ -620,7 +621,7 @@ router.get('/admin/moderation', handleErrorAsync(async (req, res) => {
return res.status(401).json({ error: 'Unauthorised' });
}
const dir = `${__dirname}/../../moderation`;
const dir = `${rootDir}/moderation`;
return res.json({
susRegexes: fs.readFileSync(`${dir}/sus.txt`).toString('utf-8')

View File

@ -2,6 +2,7 @@ import { Router } from 'express';
import SQL from 'sql-template-strings';
import { createCanvas, loadImage } from 'canvas';
import type { CanvasRenderingContext2D, Image } from 'canvas';
import { rootDir } from '../paths.ts';
import { loadSuml, loadSumlFromBase } from '../loader.ts';
import avatar from '../avatar.ts';
import { buildPronoun, parsePronounGroups, parsePronouns } from '../../src/buildPronoun.ts';
@ -19,8 +20,8 @@ const baseTranslations = loadSumlFromBase('locale/_base/translations') as Transl
const translator = new Translator(translations, baseTranslations, global.config);
const pronouns = parsePronouns(global.config, loadTsv(`${__dirname}/../../data/pronouns/pronouns.tsv`));
const pronounGroups = parsePronounGroups(loadTsv(`${__dirname}/../../data/pronouns/pronounGroups.tsv`));
const pronouns = parsePronouns(global.config, loadTsv(`${rootDir}/data/pronouns/pronouns.tsv`));
const pronounGroups = parsePronounGroups(loadTsv(`${rootDir}/data/pronouns/pronounGroups.tsv`));
const pronounLibrary = new PronounLibrary(global.config, pronounGroups, pronouns);
const drawCircle = (context: CanvasRenderingContext2D, image: Image, x: number, y: number, size: number): void => {

View File

@ -1,4 +1,5 @@
import { Router } from 'express';
import { rootDir } from '../paths.ts';
import { handleErrorAsync } from '../../src/helpers.ts';
import fs from 'fs';
import { caches } from '../../src/cache.ts';
@ -15,7 +16,7 @@ const router = Router();
router.get('/blog', handleErrorAsync(async (req, res) => {
const posts = await caches.blog.fetch(async () => {
const dir = `${__dirname}/../../data/blog`;
const dir = `${rootDir}/data/blog`;
const posts: Post[] = [];
fs.readdirSync(dir).forEach((file) => {
if (!file.endsWith('.md')) {

View File

@ -1,4 +1,5 @@
import { Router } from 'express';
import { rootDir } from '../paths.ts';
import { handleErrorAsync } from '../../src/helpers.ts';
import buildLocaleList from '../../src/buildLocaleList.ts';
import fs from 'fs';
@ -14,7 +15,7 @@ router.get('/locales', handleErrorAsync(async (req, res) => {
}));
router.get('/version', handleErrorAsync(async (req, res) => {
const versionFile = `${__dirname}/../../cache/version`;
const versionFile = `${rootDir}/cache/version`;
return res.json(fs.existsSync(versionFile) ? fs.readFileSync(versionFile).toString('utf-8') : null);
}));

View File

@ -8,6 +8,7 @@ import sharp from 'sharp';
import fs from 'fs';
import SQL from 'sql-template-strings';
import path from 'path';
import { rootDir } from '../paths.ts';
import auditLog from '../audit.ts';
import { awsConfig, awsParams } from '../aws.ts';
@ -109,7 +110,7 @@ router.post('/images/upload', multer({ limits: { fileSize: 10 * 1024 * 1024 } })
router.get('/download/:filename*', handleErrorAsync(async (req, res) => {
const filename = req.params.filename + req.params[0];
const filepath = `${__dirname}/../../locale/${global.config.locale}/files/${filename}`;
const filepath = `${rootDir}/locale/${global.config.locale}/files/${filename}`;
if (!fs.existsSync(filepath)) {
return res.status(404).json({ error: 'Not found' });

View File

@ -5,6 +5,7 @@ import md5 from 'js-md5';
import { ulid } from 'ulid';
import * as Sentry from '@sentry/node';
import type { ParsedQs } from 'qs';
import { rootDir } from '../paths.ts';
import avatar from '../avatar.ts';
import { handleErrorAsync, now, isValidLink } from '../../src/helpers.ts';
import { caches } from '../../src/cache.ts';
@ -17,7 +18,7 @@ import { normaliseUrl } from '../../src/links.ts';
import allLocales from '../../locale/locales.ts';
import type { LocaleDescription } from '../../locale/locales.ts';
import auditLog from '../audit.ts';
import crypto from '../../src/crypto.ts';
import crypto from '../crypto.ts';
import { awsConfig, awsParams } from '../aws.ts';
import { S3, NoSuchKey } from '@aws-sdk/client-s3';
import zlib from 'zlib';
@ -35,8 +36,6 @@ import type {
} from '../../src/profile.ts';
import type { Opinion } from '../../src/opinions.ts';
const __dirname = new URL('.', import.meta.url).pathname;
interface LinkRow {
url: string | null;
expiresAt: number | null;
@ -369,7 +368,7 @@ export const profilesSnapshot = async (
return JSON.stringify(await fetchProfiles(db, username, true, opts), null, 4);
};
const susRegexes = fs.readFileSync(`${__dirname}/../../moderation/sus.txt`).toString('utf-8')
const susRegexes = fs.readFileSync(`${rootDir}/moderation/sus.txt`).toString('utf-8')
.split('\n')
.filter((x) => !!x);

View File

@ -4,8 +4,8 @@ import type { NextFunction, Request, Response } from 'express';
import SQL from 'sql-template-strings';
import { ulid } from 'ulid';
import { buildDict, makeId, now, handleErrorAsync, obfuscateEmail } from '../../src/helpers.ts';
import jwt from '../../src/jwt.ts';
import mailer from '../../src/mailer.ts';
import jwt from '../jwt.ts';
import mailer from '../mailer.ts';
import { loadSuml } from '../loader.ts';
import avatar from '../avatar.ts';
import type { SocialProfilePayload } from '../social.ts';

View File

@ -6,7 +6,7 @@ import { useBase } from 'h3';
import { defineExpressHandler } from 'h3-express';
import * as Sentry from '@sentry/node';
import type { StartSpanOptions } from '@sentry/types';
import authenticate from '../src/authenticate.ts';
import authenticate from './authenticate.ts';
import dbConnection from './db.ts';
import type { Database, SQLQuery } from './db.ts';
import grant from 'grant';

View File

@ -2,6 +2,7 @@ import jwt from 'jsonwebtoken';
import fs from 'fs';
import type { JwtPayload } from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import { rootDir } from './paths.ts';
const __dirname = new URL('.', import.meta.url).pathname;
@ -36,4 +37,4 @@ class Jwt {
}
}
export default new Jwt(`${__dirname}/../keys/private.pem`, `${__dirname}/../keys/public.pem`);
export default new Jwt(`${rootDir}/keys/private.pem`, `${rootDir}/keys/public.pem`);

View File

@ -2,8 +2,9 @@ import fs from 'fs';
import Suml from 'suml';
import { loadTsv as baseLoadTsv } from '../src/tsv.ts';
import { rootDir } from './paths.ts';
export const loadSumlFromBase = (name: string): unknown => new Suml().parse(fs.readFileSync(`./${name}.suml`, 'utf-8'));
export const loadSumlFromBase = (name: string): unknown => new Suml().parse(fs.readFileSync(`${rootDir}/${name}.suml`, 'utf-8'));
export const loadSuml = (name: string): unknown => loadSumlFromBase(`data/${name}`);
export const loadTsv = <T = unknown>(name: string): T[] => baseLoadTsv(`./data/${name}.tsv`);
export const loadTsv = <T = unknown>(name: string): T[] => baseLoadTsv(`${rootDir}/data/${name}.tsv`);

View File

@ -1,11 +1,10 @@
import { registerFont } from 'canvas';
import fs from 'fs';
const __dirname = new URL('.', import.meta.url).pathname;
import { rootDir } from './paths.ts';
const vars: Record<string, string> = {};
for (const [, name, value] of fs.readFileSync(`${__dirname}/../data/variables.scss`).toString('utf-8')
for (const [, name, value] of fs.readFileSync(`${rootDir}/data/variables.scss`).toString('utf-8')
.matchAll(/^\$([^:]+): '([^']+)'(?:, '[^']+')*;$/gm)) {
vars[name] = value;
}

View File

@ -1,23 +1,19 @@
import nodemailer from 'nodemailer';
import fs from 'fs';
import Suml from 'suml';
import * as Sentry from '@sentry/node';
import type StreamTransport from 'nodemailer/lib/stream-transport/index.d.ts';
import type { Readable } from 'stream';
import { loadSuml, loadSumlFromBase } from './loader.ts';
import { rootDir } from './paths.ts';
import type { Translations } from '../locale/translations.ts';
const __dirname = new URL('.', import.meta.url).pathname;
const color = '#C71585';
const logo = fs.readFileSync(`${__dirname}/../public/logo/logo-primary.svg`).toString('utf-8');
const logo = fs.readFileSync(`${rootDir}/public/logo/logo-primary.svg`).toString('utf-8');
const logoEncoded = `data:image/svg+xml,${encodeURIComponent(logo)}`;
const loadSuml = (name: string): unknown => {
return new Suml().parse(fs.readFileSync(`${__dirname}/../${name}.suml`).toString());
};
const translations = loadSuml('data/translations') as Translations;
const fallbackTranslations = loadSuml('locale/_base/translations') as Translations;
const translations = loadSuml('translations') as Translations;
const fallbackTranslations = loadSumlFromBase('locale/_base/translations') as Translations;
let transport: string | StreamTransport.Options = process.env.MAILER_TRANSPORT!;
if (!transport && process.env.NODE_ENV === 'development') {

View File

@ -5,7 +5,7 @@ import { JSDOM } from 'jsdom';
import * as Sentry from '@sentry/node';
import { Day } from '../src/calendar/helpers.ts';
import fs from 'fs';
import mailer from '../src/mailer.ts';
import mailer from './mailer.ts';
import type { MiastamaszerujaceEvent } from '../src/calendar/helpers.ts';
import { createEvents } from 'ics';

View File

@ -1,7 +1,7 @@
import './setup.ts';
import dbConnection from './db.ts';
import mailer from '../src/mailer.ts';
import mailer from './mailer.ts';
import { isGrantedForUser } from '../src/helpers.ts';
const shouldNotify = (frequency) => {

5
server/paths.ts Normal file
View File

@ -0,0 +1,5 @@
// Nitro is designed to be independent of the filesystem, but that requires migration in some express routes
// in dev mode, import.meta.url references `.nuxt/dev/index.mjs` and not the file path of this file
// in production mode, import.meta.url is not a directory, so by convention the working directory must be the repo root,
// however, `nuxi preview` automatically changes into .output
export const rootDir = process.cwd().replace(/\/\.output$/, '');

View File

@ -3,7 +3,7 @@ import './setup.ts';
import dbConnection from './db.ts';
import SQL from 'sql-template-strings';
import { ulid, decodeTime } from 'ulid';
import mailer from '../src/mailer.ts';
import mailer from './mailer.ts';
const CAMPAIGNS = [
{

View File

@ -1,13 +1,13 @@
import fs from 'fs';
const __dirname = new URL('.', import.meta.url).pathname;
import { rootDir } from '../server/paths.ts';
export class CacheObject {
path: string;
maxAgeMinutes: number;
constructor(dir: string, filename: string, maxAgeMinutes: number) {
const cacheDir = `${__dirname}/../cache/${dir}`;
const cacheDir = `${rootDir}/cache/${dir}`;
if (filename.includes('..')) {
throw 'Insecure';
}

View File

@ -1,5 +1,5 @@
import { decodeTime, ulid } from 'ulid';
import mailer from './mailer.ts';
import mailer from '../server/mailer.ts';
import Plausible from 'plausible-api';
import fetch from 'node-fetch';
import { listMissingTranslations } from './missingTranslations.ts';