refactor: use fastify vite for the dev server (#284)

* refactor: use fastify vite for the server

by doing this we do not need to have local hacks for the routes. No local proxy needed. Everything just works.

* fix: update the dockerfile build path

* fix: update package.json

* fix: fonts path
This commit is contained in:
bjarneo 2024-03-11 13:43:20 +01:00 committed by GitHub
parent b20c472b94
commit 0c86efd56c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
96 changed files with 1352 additions and 652 deletions

3
.gitignore vendored
View File

@ -122,4 +122,5 @@ prisma_test.js
database/ database/
data/ data/
.vscode .vscode
hemmelig.backup.db hemmelig.backup.db
client/build/

View File

@ -25,8 +25,7 @@ FROM node:20-alpine
WORKDIR /home/node/hemmelig WORKDIR /home/node/hemmelig
RUN mkdir build COPY --from=0 /usr/src/app/client/build client/build
COPY --from=0 /usr/src/app/build build/
COPY package*.json ./ COPY package*.json ./

2
cli.js
View File

@ -3,7 +3,7 @@
import meow from 'meow'; import meow from 'meow';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import YAML from 'yaml'; import YAML from 'yaml';
import { generateKey, encrypt } from './src/shared/helpers/crypto.js'; import { encrypt, generateKey } from './shared/helpers/crypto.js';
// Adding this hack to make it work for pkg // Adding this hack to make it work for pkg
// https://github.com/vercel/pkg/issues/1291#issuecomment-1360586986 // https://github.com/vercel/pkg/issues/1291#issuecomment-1360586986

View File

@ -3,7 +3,7 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import HttpApi from 'i18next-http-backend'; import HttpApi from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import config from './client/config'; import config from './config';
function getLanguage() { function getLanguage() {
const language = config.get('settings.forcedLanguage'); const language = config.get('settings.forcedLanguage');

View File

@ -3,14 +3,14 @@
font-style: normal; font-style: normal;
font-weight: 100; font-weight: 100;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Thin.woff2') format('woff2'); src: url('/static/fonts/Inter-Thin.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 100; font-weight: 100;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-ThinItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-ThinItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -18,14 +18,14 @@
font-style: normal; font-style: normal;
font-weight: 200; font-weight: 200;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-ExtraLight.woff2') format('woff2'); src: url('/static/fonts/Inter-ExtraLight.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 200; font-weight: 200;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-ExtraLightItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-ExtraLightItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -33,14 +33,14 @@
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Light.woff2') format('woff2'); src: url('/static/fonts/Inter-Light.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 300; font-weight: 300;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-LightItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-LightItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -48,14 +48,14 @@
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Regular.woff2') format('woff2'); src: url('/static/fonts/Inter-Regular.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 400; font-weight: 400;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Italic.woff2') format('woff2'); src: url('/static/fonts/Inter-Italic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -63,14 +63,14 @@
font-style: normal; font-style: normal;
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Medium.woff2') format('woff2'); src: url('/static/fonts/Inter-Medium.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 500; font-weight: 500;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-MediumItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-MediumItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -78,14 +78,14 @@
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-SemiBold.woff2') format('woff2'); src: url('/static/fonts/Inter-SemiBold.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 600; font-weight: 600;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-SemiBoldItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-SemiBoldItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -93,14 +93,14 @@
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Bold.woff2') format('woff2'); src: url('/static/fonts/Inter-Bold.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 700; font-weight: 700;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-BoldItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-BoldItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -108,14 +108,14 @@
font-style: normal; font-style: normal;
font-weight: 800; font-weight: 800;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-ExtraBold.woff2') format('woff2'); src: url('/static/fonts/Inter-ExtraBold.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 800; font-weight: 800;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-ExtraBoldItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-ExtraBoldItalic.woff2') format('woff2');
} }
@font-face { @font-face {
@ -123,14 +123,14 @@
font-style: normal; font-style: normal;
font-weight: 900; font-weight: 900;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-Black.woff2') format('woff2'); src: url('/static/fonts/Inter-Black.woff2') format('woff2');
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: italic; font-style: italic;
font-weight: 900; font-weight: 900;
font-display: swap; font-display: swap;
src: url('/fonts/Inter-BlackItalic.woff2') format('woff2'); src: url('/static/fonts/Inter-BlackItalic.woff2') format('woff2');
} }
html { html {

View File

@ -5,9 +5,9 @@ import { Provider } from 'react-redux';
import { LoadingOverlay } from '@mantine/core'; import { LoadingOverlay } from '@mantine/core';
import './i18n'; import './i18n';
import HemmeligApplication from './client/app'; import HemmeligApplication from './app';
import configureStore from './client/helpers/configureStore'; import configureStore from './helpers/configureStore';
import './client/index.css'; import './index.css';
const store = configureStore(); const store = configureStore();

View File

@ -40,7 +40,7 @@ const SignOut = () => {
<Image <Image
maw={240} maw={240}
radius="md" radius="md"
src="./secret_cat.png" src="./static/secret_cat.png"
alt="Secret Cat" alt="Secret Cat"
className={styles.image} className={styles.image}
/> />

16
html.js
View File

@ -5,10 +5,10 @@ export default `<!DOCTYPE html>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Hemmelig.app - Paste a password, confidential message, or private data.</title> <title>Hemmelig.app - Paste a password, confidential message, or private data.</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/static/favicon.ico" />
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/static/favicon.ico" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/static/manifest.json" />
<!-- Primary Meta Tags --> <!-- Primary Meta Tags -->
<meta name="title" content="Paste a password, confidential message, or private data." /> <meta name="title" content="Paste a password, confidential message, or private data." />
<meta <meta
@ -27,10 +27,10 @@ export default `<!DOCTYPE html>
property="og:description" property="og:description"
content="Ensure your sensitive data remains encrypted, secure, and confidential." content="Ensure your sensitive data remains encrypted, secure, and confidential."
/> />
<meta property="og:image" content="/icons/icon-512x512.png" /> <meta property="og:image" content="/static/icons/icon-512x512.png" />
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="/static/summary_large_image" />
<meta property="twitter:url" content="https://www.hemmelig.app/" /> <meta property="twitter:url" content="https://www.hemmelig.app/" />
<meta <meta
property="twitter:title" property="twitter:title"
@ -40,14 +40,14 @@ export default `<!DOCTYPE html>
property="twitter:description" property="twitter:description"
content="Ensure your sensitive data remains encrypted, secure, and confidential." content="Ensure your sensitive data remains encrypted, secure, and confidential."
/> />
<meta property="twitter:image" content="/icons/icon-512x512.png" /> <meta property="twitter:image" content="/static/icons/icon-512x512.png" />
<meta name="theme-color" content="#231e23" /> <meta name="theme-color" content="#231e23" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" href="/icons/maskable-icon-192x192.png" /> <link rel="apple-touch-icon" href="/static/icons/maskable-icon-192x192.png" />
<script id="__secret_config"> <script id="__secret_config">
try { try {
window.__SECRET_CONFIG = {{config}} window.__SECRET_CONFIG = {{config}}
@ -61,7 +61,7 @@ export default `<!DOCTYPE html>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/index.jsx"></script> <script type="module" src="/index.jsx"></script>
</body> </body>
</html> </html>
`; `;

1737
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,14 +9,13 @@
"test": "mocha tests/**/*.test.js --reporter nyan", "test": "mocha tests/**/*.test.js --reporter nyan",
"test-watch": "mocha --watch tests/**/*.test.js --reporter nyan", "test-watch": "mocha --watch tests/**/*.test.js --reporter nyan",
"build": "node pre.js && vite build", "build": "node pre.js && vite build",
"dev": "concurrently \"npm run server-dev\" \"npm run client-dev\"", "dev": "npm run server-dev",
"server-dev": "prisma migrate dev && NODE_ENV=development node pre.js && NODE_ENV=development nodemon server.js", "server-dev": "prisma migrate dev && NODE_ENV=development node pre.js && NODE_ENV=development node server.js --dev",
"client-dev": "NODE_ENV=development vite",
"docker-build": "docker build -t hemmelig .", "docker-build": "docker build -t hemmelig .",
"docker-run": "docker run -p 3000:3000 -d --name=hemmelig -v ./data/hemmelig/:/var/tmp/hemmelig/upload/files -v ./database/:/home/node/hemmelig/database/ hemmelig", "docker-run": "docker run -p 3000:3000 -d --name=hemmelig -v ./data/hemmelig/:/var/tmp/hemmelig/upload/files -v ./database/:/home/node/hemmelig/database/ hemmelig",
"production-test": "npm run docker-build && npm run docker-run", "production-test": "npm run docker-build && npm run docker-run",
"prepare": "husky install", "prepare": "husky install",
"prettier": "prettier --write --ignore-unknown src/ prisma/", "prettier": "prettier --write --ignore-unknown server/ client/ shared/ prisma/",
"publish-cli": "rm -rf dist/ && npx microbundle build --target node -i cli.js -o dist/ && npm publish", "publish-cli": "rm -rf dist/ && npx microbundle build --target node -i cli.js -o dist/ && npm publish",
"pkg": "rm -rf dist/ bin/ && npx esbuild cli.js --bundle --platform=node --outfile=dist/cli.cjs --define:import.meta.url=__dirname && npx pkg dist/cli.cjs --targets=node18-linux-arm64 --output=bin/hemmelig" "pkg": "rm -rf dist/ bin/ && npx esbuild cli.js --bundle --platform=node --outfile=dist/cli.cjs --define:import.meta.url=__dirname && npx pkg dist/cli.cjs --targets=node18-linux-arm64 --output=bin/hemmelig"
}, },
@ -41,7 +40,8 @@
"@fastify/helmet": "^9.1.0", "@fastify/helmet": "^9.1.0",
"@fastify/jwt": "^7.2.3", "@fastify/jwt": "^7.2.3",
"@fastify/multipart": "^7.1.1", "@fastify/multipart": "^7.1.1",
"@fastify/static": "^6.5.0", "@fastify/static": "^6.12.0",
"@fastify/vite": "^6.0.6",
"@mantine/core": "^6.0.6", "@mantine/core": "^6.0.6",
"@mantine/form": "^6.0.6", "@mantine/form": "^6.0.6",
"@mantine/hooks": "^6.0.6", "@mantine/hooks": "^6.0.6",
@ -75,7 +75,6 @@
"@vitejs/plugin-react": "^3.1.0", "@vitejs/plugin-react": "^3.1.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"classcat": "^5.0.3", "classcat": "^5.0.3",
"concurrently": "^7.4.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",

4
pre.js
View File

@ -1,11 +1,11 @@
import fs from 'fs';
import config from 'config'; import config from 'config';
import fs from 'fs';
import template from 'y8'; import template from 'y8';
import html from './html.js'; import html from './html.js';
// This is scripts that has to be run before the // This is scripts that has to be run before the
// frontend build process // frontend build process
fs.writeFileSync( fs.writeFileSync(
'index.html', 'client/index.html',
template(html, { config: `'${JSON.stringify(config.get('__client_config'))}';` }) template(html, { config: `'${JSON.stringify(config.get('__client_config'))}';` })
); );

154
server.js
View File

@ -1,51 +1,94 @@
// Boot scripts // Boot scripts
import('./src/server/bootstrap.js'); import('./server/bootstrap.js');
import cookie from '@fastify/cookie'; import cookie from '@fastify/cookie';
import cors from '@fastify/cors'; import cors from '@fastify/cors';
import helmet from '@fastify/helmet'; import helmet from '@fastify/helmet';
import jwt from '@fastify/jwt'; import jwt from '@fastify/jwt';
import fstatic from '@fastify/static'; import fastifyStatic from '@fastify/static';
import FastifyVite from '@fastify/vite';
import config from 'config'; import config from 'config';
import importFastify from 'fastify'; import importFastify from 'fastify';
import fs from 'fs';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import path from 'path'; import fs from 'node:fs';
import { fileURLToPath } from 'url'; import path from 'node:path';
import { fileURLToPath } from 'node:url';
import template from 'y8'; import template from 'y8';
import rateLimit from './src/server/plugins/rate-limit.js'; import rateLimit from './server/plugins/rate-limit.js';
import adminDecorator from './src/server/decorators/admin.js'; import adminDecorator from './server/decorators/admin.js';
import allowedIp from './src/server/decorators/allowed-ip.js'; import allowedIp from './server/decorators/allowed-ip.js';
import attachment from './src/server/decorators/attachment-upload.js'; import attachment from './server/decorators/attachment-upload.js';
import jwtDecorator from './src/server/decorators/jwt.js'; import jwtDecorator from './server/decorators/jwt.js';
import userFeatures from './src/server/decorators/user-features.js'; import userFeatures from './server/decorators/user-features.js';
import readCookieAllRoutesHandler from './src/server/prehandlers/cookie-all-routes.js'; import readCookieAllRoutesHandler from './server/prehandlers/cookie-all-routes.js';
import disableUserAccountCreationHandler from './src/server/prehandlers/disable-user-account-creation.js'; import disableUserAccountCreationHandler from './server/prehandlers/disable-user-account-creation.js';
import disableUserHandler from './src/server/prehandlers/disable-users.js'; import disableUserHandler from './server/prehandlers/disable-users.js';
import readOnlyHandler from './src/server/prehandlers/read-only.js'; import readOnlyHandler from './server/prehandlers/read-only.js';
import restrictOrganizationEmailHandler from './src/server/prehandlers/restrict-organization-email.js'; import restrictOrganizationEmailHandler from './server/prehandlers/restrict-organization-email.js';
import accountRoute from './src/server/controllers/account.js'; import accountRoute from './server/controllers/account.js';
import adminSettingsRoute from './src/server/controllers/admin/settings.js'; import adminSettingsRoute from './server/controllers/admin/settings.js';
import usersRoute from './src/server/controllers/admin/users.js'; import usersRoute from './server/controllers/admin/users.js';
import authenticationRoute from './src/server/controllers/authentication.js'; import authenticationRoute from './server/controllers/authentication.js';
import downloadRoute from './src/server/controllers/download.js'; import downloadRoute from './server/controllers/download.js';
import healthzRoute from './src/server/controllers/healthz.js'; import healthzRoute from './server/controllers/healthz.js';
import secretRoute from './src/server/controllers/secret.js'; import secretRoute from './server/controllers/secret.js';
import statsRoute from './src/server/controllers/stats.js'; import statsRoute from './server/controllers/stats.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const isDev = process.env.NODE_ENV === 'development'; const isDev = process.env.NODE_ENV === 'development';
const MAX_FILE_BYTES = 1024 * config.get('file.size') * 1000; // Example: 1024 * 2 * 1000 = 2 024 000 bytes const MAX_FILE_BYTES = 1024 * config.get('file.size') * 1000; // Example: 1024 * 2 * 1000 = 2 024 000 bytes
const staticPath = path.join(__dirname, !isDev ? 'client/build' : 'client');
const fastify = importFastify({ const fastify = importFastify({
logger: config.get('logger'), logger: config.get('logger'),
bodyLimit: MAX_FILE_BYTES, bodyLimit: MAX_FILE_BYTES,
}); });
await fastify.register(FastifyVite, {
root: import.meta.url,
dev: process.argv.includes('--dev'),
spa: true,
});
fastify.register(fastifyStatic, {
root: path.join(__dirname, 'public'),
prefix: '/static/',
});
fastify.register(fastifyStatic, {
root: path.join(__dirname, 'public', 'locales'),
prefix: '/locales/',
decorateReply: false,
});
if (!isDev) {
const script = template(
`
try {
window.__SECRET_CONFIG = {{config}}
} catch (e) {
window.__SECRET_CONFIG = '';
}
`,
{ config: `'${JSON.stringify(config.get('__client_config'))}';` }
);
const index = staticPath + '/index.html';
const dom = new JSDOM(fs.readFileSync(index));
dom.window.document.querySelector('#__secret_config').textContent = script;
fs.writeFileSync(index, dom.serialize());
}
fastify.register(rateLimit, { fastify.register(rateLimit, {
prefix: '/api/', prefix: '/api/',
max: config.get('rateLimit.max'), max: config.get('rateLimit.max'),
@ -91,7 +134,21 @@ fastify.addHook('preHandler', disableUserAccountCreationHandler);
fastify.addHook('preHandler', readOnlyHandler); fastify.addHook('preHandler', readOnlyHandler);
fastify.addHook('preHandler', restrictOrganizationEmailHandler); fastify.addHook('preHandler', restrictOrganizationEmailHandler);
// Register our routes before the static content function serveIndex(_, reply) {
return reply.html();
}
fastify.get('/', serveIndex);
fastify.get('/secret/*', serveIndex);
fastify.get('/about', serveIndex);
fastify.get('/privacy', serveIndex);
fastify.get('/api-docs', serveIndex);
fastify.get('/signin', serveIndex);
fastify.get('/signup', serveIndex);
fastify.get('/signout', serveIndex);
fastify.get('/account*', serveIndex);
fastify.get('/terms', serveIndex);
fastify.get('/public*', serveIndex);
fastify.register(authenticationRoute, { fastify.register(authenticationRoute, {
prefix: '/api/authentication', prefix: '/api/authentication',
@ -115,54 +172,9 @@ fastify.register(statsRoute, { prefix: '/api/stats' });
fastify.register(healthzRoute, { prefix: '/api/healthz' }); fastify.register(healthzRoute, { prefix: '/api/healthz' });
fastify.register(healthzRoute, { prefix: '/healthz' }); fastify.register(healthzRoute, { prefix: '/healthz' });
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticPath = path.join(__dirname, !isDev ? 'build' : '');
// Static frontend for the production build
if (!isDev) {
fastify.register(fstatic, {
root: staticPath,
route: '/*',
});
const script = template(
`
try {
window.__SECRET_CONFIG = {{config}}
} catch (e) {
window.__SECRET_CONFIG = '';
}
`,
{ config: `'${JSON.stringify(config.get('__client_config'))}';` }
);
const index = staticPath + '/index.html';
const dom = new JSDOM(fs.readFileSync(index));
dom.window.document.querySelector('#__secret_config').textContent = script;
fs.writeFileSync(index, dom.serialize());
function serveIndex(_, res) {
return res.sendFile('index.html');
}
fastify.get('/secret/*', serveIndex);
fastify.get('/about', serveIndex);
fastify.get('/privacy', serveIndex);
fastify.get('/api-docs', serveIndex);
fastify.get('/signin', serveIndex);
fastify.get('/signup', serveIndex);
fastify.get('/signout', serveIndex);
fastify.get('/account*', serveIndex);
fastify.get('/terms', serveIndex);
fastify.get('/public*', serveIndex);
}
const startServer = async () => { const startServer = async () => {
try { try {
await fastify.vite.ready();
await fastify.listen({ port: config.get('port'), host: config.get('localHostname') }); await fastify.listen({ port: config.get('port'), host: config.get('localHostname') });
} catch (err) { } catch (err) {
fastify.log.error(err); fastify.log.error(err);

View File

@ -1,6 +1,6 @@
import assert from 'assert'; import assert from 'assert';
import { encrypt, decrypt, generateKey } from '../../../src/shared/helpers/crypto.js'; import { decrypt, encrypt, generateKey } from '../../../shared/helpers/crypto.js';
const SECRET = 'MASTER_KEY=1337-super-secret'; const SECRET = 'MASTER_KEY=1337-super-secret';
@ -9,8 +9,8 @@ const RANDOM_KEY = generateKey();
describe('Crypto', () => { describe('Crypto', () => {
describe('#encrypt(string)', () => { describe('#encrypt(string)', () => {
it('should encrypt, and decrypt a secret', async () => { it('should encrypt, and decrypt a secret', async () => {
const encrypted = await encrypt(SECRET, RANDOM_KEY); const encrypted = encrypt(SECRET, RANDOM_KEY);
const decrypted = await decrypt(encrypted, RANDOM_KEY); const decrypted = decrypt(encrypted, RANDOM_KEY);
assert.equal(decrypted, SECRET); assert.equal(decrypted, SECRET);
}); });

View File

@ -1,6 +1,6 @@
import assert from 'assert'; import assert from 'assert';
import { hash, compare } from '../../../src/server/helpers/password.js'; import { compare, hash } from '../../../server/helpers/password.js';
const PASSWORD = 'suP3rL0ng!PssWrd'; const PASSWORD = 'suP3rL0ng!PssWrd';

View File

@ -1,6 +1,6 @@
import assert from 'assert'; import assert from 'assert';
import isValidTTL from '../../../src/server/helpers/validate-ttl.js'; import isValidTTL from '../../../server/helpers/validate-ttl.js';
describe('Validate TTL', () => { describe('Validate TTL', () => {
describe('#isValidTTL(ttl)', () => { describe('#isValidTTL(ttl)', () => {

View File

@ -1,17 +1,19 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const path = fileURLToPath(import.meta.url);
const root = resolve(dirname(path), 'client');
export default defineConfig(() => { export default defineConfig(() => {
return { return {
root,
build: { build: {
outDir: 'build', outDir: 'build',
}, },
plugins: [react()], publicDir: 'public',
server: { plugins: [react({ jsxRuntime: 'classic' })],
port: 3001,
proxy: {
'/api': 'http://0.0.0.0:3000',
},
},
}; };
}); });