mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-03-29 09:58:11 +00:00
dev base (kinda stable)
This commit is contained in:
12
client/entry-client-admin.jsx
Normal file
12
client/entry-client-admin.jsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { hydrateRoot } from 'react-dom/client';
|
||||||
|
import Admin from './admin.jsx';
|
||||||
|
|
||||||
|
import './admin/admin.less'
|
||||||
|
|
||||||
|
window.start_app = (props) => {
|
||||||
|
hydrateRoot(
|
||||||
|
document.getElementById('reactRoot'),
|
||||||
|
<Admin {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
13
client/entry-client-homebrew.jsx
Normal file
13
client/entry-client-homebrew.jsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { hydrateRoot } from 'react-dom/client'
|
||||||
|
import Homebrew from './homebrew/homebrew.jsx'
|
||||||
|
|
||||||
|
// CSS MUST be imported here
|
||||||
|
import './homebrew/homebrew.less' // or wherever your CSS lives
|
||||||
|
|
||||||
|
window.start_app = (props) => {
|
||||||
|
hydrateRoot(
|
||||||
|
document.getElementById('reactRoot'),
|
||||||
|
<Homebrew {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { renderToString } from 'react-dom/server';
|
import { renderToString } from 'react-dom/server';
|
||||||
import Admin from './admin.jsx';
|
import Admin from './admin/admin.jsx';
|
||||||
|
|
||||||
export default (props) => renderToString(<Admin {...props} />);
|
export default (props) => renderToString(<Admin {...props} />);
|
||||||
@@ -1,33 +1,66 @@
|
|||||||
const template = async function(name, title='', props = {}){
|
import fs from "fs";
|
||||||
|
|
||||||
|
const isProd = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
|
const template = async function ({ vite, url }, name, title = "", props = {}) {
|
||||||
const ogTags = [];
|
const ogTags = [];
|
||||||
const ogMeta = props.ogMeta ?? {};
|
const ogMeta = props.ogMeta ?? {};
|
||||||
Object.entries(ogMeta).forEach(([key, value])=>{
|
|
||||||
if(!value) return;
|
|
||||||
const tag = `<meta property="og:${key}" content="${value}">`;
|
|
||||||
ogTags.push(tag);
|
|
||||||
});
|
|
||||||
const ogMetaTags = ogTags.join('\n');
|
|
||||||
|
|
||||||
|
Object.entries(ogMeta).forEach(([key, value]) => {
|
||||||
|
if (!value) return;
|
||||||
|
ogTags.push(`<meta property="og:${key}" content="${value}">`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const ogMetaTags = ogTags.join("\n");
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// PROD
|
||||||
|
// ----------------
|
||||||
|
if (isProd) {
|
||||||
const ssrModule = await import(`../build/entry-server-${name}/bundle.js`);
|
const ssrModule = await import(`../build/entry-server-${name}/bundle.js`);
|
||||||
|
|
||||||
return `<!DOCTYPE html>
|
return `<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
||||||
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
||||||
<link href=${`/${name}/bundle.css`} type="text/css" rel='stylesheet' />
|
<link href="/${name}/bundle.css" rel="stylesheet" />
|
||||||
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
||||||
${ogMetaTags}
|
${ogMetaTags}
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
<title>${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}</title>
|
<title>${title.length ? `${title} - The Homebrewery` : "The Homebrewery - NaturalCrit"}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="reactRoot">${ssrModule.default(props)}</main>
|
<main id="reactRoot">${ssrModule.default(props)}</main>
|
||||||
<script src=${`/${name}/bundle.js`}></script>
|
<script src="/${name}/bundle.js"></script>
|
||||||
<script>start_app(${JSON.stringify(props)})</script>
|
<script>start_app(${JSON.stringify(props)})</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>`;
|
||||||
`;
|
}
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// DEV
|
||||||
|
// ----------------
|
||||||
|
const { default: render } = await vite.ssrLoadModule(`/client/entry-server-${name}.jsx`);
|
||||||
|
|
||||||
|
let html = `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
||||||
|
${ogMetaTags}
|
||||||
|
<title>${title.length ? `${title} - The Homebrewery` : "The Homebrewery - NaturalCrit"}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="reactRoot">${render(props)}</main>
|
||||||
|
|
||||||
|
<script type="module" src="/@vite/client"></script>
|
||||||
|
<script type="module" src="/client/entry-client-${name}.jsx"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>`;
|
||||||
|
|
||||||
|
return vite.transformIndexHtml(url, html);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default template;
|
export default template;
|
||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -5268,9 +5268,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ci-info": {
|
"node_modules/ci-info": {
|
||||||
"version": "4.3.1",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
|
||||||
"integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==",
|
"integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,11 +12,13 @@
|
|||||||
"url": "git://github.com/naturalcrit/homebrewery.git"
|
"url": "git://github.com/naturalcrit/homebrewery.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"viteDev": "node scripts/dev.js",
|
"viteDev1": "node scripts/dev.js",
|
||||||
|
"viteDev2": "vite dev",
|
||||||
"viteDevAdmin": "vite --config vite.config.js --ssr client/admin/admin.jsx",
|
"viteDevAdmin": "vite --config vite.config.js --ssr client/admin/admin.jsx",
|
||||||
"viteBuild": "vite build",
|
"viteBuild": "vite build && node scripts/compileAssets.js",
|
||||||
"viteStart": "vite preview --outDir build",
|
"viteStart": "vite preview --outDir build",
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
|
"compileAssets": "node scripts/compileAssets.js --dev",
|
||||||
"dev": "node --experimental-require-module scripts/dev.js",
|
"dev": "node --experimental-require-module scripts/dev.js",
|
||||||
"quick": "node --experimental-require-module scripts/quick.js",
|
"quick": "node --experimental-require-module scripts/quick.js",
|
||||||
"build": "node --experimental-require-module scripts/buildHomebrew.js && node --experimental-require-module scripts/buildAdmin.js",
|
"build": "node --experimental-require-module scripts/buildHomebrew.js && node --experimental-require-module scripts/buildAdmin.js",
|
||||||
|
|||||||
90
scripts/compileAssets.js
Normal file
90
scripts/compileAssets.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import fs from "fs-extra";
|
||||||
|
import less from "less";
|
||||||
|
const isDev = !!process.argv.find((arg) => arg === "--dev");
|
||||||
|
|
||||||
|
const compileAssets = async () => {
|
||||||
|
await fs.copy("./client/homebrew/favicon.ico", "./build/assets/favicon.ico");
|
||||||
|
|
||||||
|
//v==----------------------------- COMPILE THEMES --------------------------------==v//
|
||||||
|
|
||||||
|
// Update list of all Theme files
|
||||||
|
const themes = { Legacy: {}, V3: {} };
|
||||||
|
|
||||||
|
let themeFiles = fs.readdirSync("./themes/Legacy");
|
||||||
|
for (const dir of themeFiles) {
|
||||||
|
const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString());
|
||||||
|
themeData.path = dir;
|
||||||
|
themes.Legacy[dir] = themeData;
|
||||||
|
//fs.copy(`./themes/Legacy/${dir}/dropdownTexture.png`, `./build/themes/Legacy/${dir}/dropdownTexture.png`);
|
||||||
|
const src = `./themes/Legacy/${dir}/style.less`;
|
||||||
|
((outputDirectory) => {
|
||||||
|
less.render(
|
||||||
|
fs.readFileSync(src).toString(),
|
||||||
|
{
|
||||||
|
compress: !isDev,
|
||||||
|
},
|
||||||
|
function (e, output) {
|
||||||
|
fs.outputFile(outputDirectory, output.css);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})(`./build/themes/Legacy/${dir}/style.css`);
|
||||||
|
}
|
||||||
|
|
||||||
|
themeFiles = fs.readdirSync("./themes/V3");
|
||||||
|
for (const dir of themeFiles) {
|
||||||
|
const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString());
|
||||||
|
themeData.path = dir;
|
||||||
|
themes.V3[dir] = themeData;
|
||||||
|
fs.copy(`./themes/V3/${dir}/dropdownTexture.png`, `./build/themes/V3/${dir}/dropdownTexture.png`);
|
||||||
|
fs.copy(`./themes/V3/${dir}/dropdownPreview.png`, `./build/themes/V3/${dir}/dropdownPreview.png`);
|
||||||
|
const src = `./themes/V3/${dir}/style.less`;
|
||||||
|
((outputDirectory) => {
|
||||||
|
less.render(
|
||||||
|
fs.readFileSync(src).toString(),
|
||||||
|
{
|
||||||
|
compress: !isDev,
|
||||||
|
},
|
||||||
|
function (e, output) {
|
||||||
|
fs.outputFile(outputDirectory, output.css);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})(`./build/themes/V3/${dir}/style.css`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.outputFile("./themes/themes.json", JSON.stringify(themes, null, 2));
|
||||||
|
|
||||||
|
// await less.render(lessCode, {
|
||||||
|
// compress : !dev,
|
||||||
|
// sourceMap : (dev ? {
|
||||||
|
// sourceMapFileInline: true,
|
||||||
|
// outputSourceFiles: true
|
||||||
|
// } : false),
|
||||||
|
// })
|
||||||
|
|
||||||
|
// Move assets
|
||||||
|
await fs.copy("./themes/fonts", "./build/fonts");
|
||||||
|
await fs.copy("./themes/assets", "./build/assets");
|
||||||
|
await fs.copy("./client/icons", "./build/icons");
|
||||||
|
|
||||||
|
//v==---------------------------MOVE CM EDITOR THEMES -----------------------------==v//
|
||||||
|
|
||||||
|
const editorThemesBuildDir = "./build/homebrew/cm-themes";
|
||||||
|
await fs.copy("./node_modules/codemirror/theme", editorThemesBuildDir);
|
||||||
|
await fs.copy("./themes/codeMirror/customThemes", editorThemesBuildDir);
|
||||||
|
const editorThemeFiles = fs.readdirSync(editorThemesBuildDir);
|
||||||
|
|
||||||
|
const editorThemeFile = "./themes/codeMirror/editorThemes.json";
|
||||||
|
if (fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile);
|
||||||
|
const stream = fs.createWriteStream(editorThemeFile, { flags: "a" });
|
||||||
|
stream.write('[\n"default"');
|
||||||
|
|
||||||
|
for (const themeFile of editorThemeFiles) {
|
||||||
|
stream.write(`,\n"${themeFile.slice(0, -4)}"`);
|
||||||
|
}
|
||||||
|
stream.write("\n]\n");
|
||||||
|
stream.end();
|
||||||
|
|
||||||
|
await fs.copy("./themes/codeMirror", "./build/homebrew/codeMirror");
|
||||||
|
};
|
||||||
|
|
||||||
|
compileAssets();
|
||||||
57
server.js
57
server.js
@@ -1,20 +1,47 @@
|
|||||||
import DB from './server/db.js';
|
import DB from "./server/db.js";
|
||||||
import server from './server/app.js';
|
import createApp from "./server/app.js";
|
||||||
import config from './server/config.js';
|
import config from "./server/config.js";
|
||||||
|
import { createServer as createViteServer } from "vite";
|
||||||
|
|
||||||
DB.connect(config).then(()=>{
|
const isProd = process.env.NODE_ENV === "production";
|
||||||
// Ensure that we have successfully connected to the database
|
|
||||||
// before launching server
|
async function start() {
|
||||||
const PORT = process.env.PORT || config.get('web_port') || 8000;
|
let vite;
|
||||||
server.listen(PORT, ()=>{
|
|
||||||
const reset = '\x1b[0m'; // Reset to default style
|
//==== Create Vite dev server only in development ====//
|
||||||
const bright = '\x1b[1m'; // Bright (bold) style
|
if (!isProd) {
|
||||||
const cyan = '\x1b[36m'; // Cyan color
|
vite = await createViteServer({
|
||||||
const underline = '\x1b[4m'; // Underlined style
|
server: { middlewareMode: true },
|
||||||
|
appType: "custom",
|
||||||
|
logLevel: 'error',
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//==== Connect to the database ====//
|
||||||
|
await DB.connect(config).catch((err) => {
|
||||||
|
console.error("Database connection failed:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
//==== Create the Express app ====//
|
||||||
|
const app = await createApp(vite);
|
||||||
|
|
||||||
|
//==== Start listening ====//
|
||||||
|
const PORT = process.env.PORT || config.get("web_port") || 8000;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
const reset = "\x1b[0m"; // Reset to default style
|
||||||
|
const bright = "\x1b[1m"; // Bright (bold) style
|
||||||
|
const cyan = "\x1b[36m"; // Cyan color
|
||||||
|
const underline = "\x1b[4m"; // Underlined style
|
||||||
|
|
||||||
console.log(`\n\tserver started at: ${new Date().toLocaleString()}`);
|
console.log(`\n\tserver started at: ${new Date().toLocaleString()}`);
|
||||||
console.log(`\tserver on port: ${PORT}`);
|
console.log(`\tserver on port: ${PORT}`);
|
||||||
console.log(`\t${bright + cyan}Open in browser: ${reset}${underline + bright + cyan}http://localhost:${PORT}${reset}\n\n`);
|
console.log(
|
||||||
|
`\t${bright + cyan}Open in browser: ${reset}${underline + bright + cyan}http://localhost:${PORT}${reset}\n\n`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
//==== Start the server ====//
|
||||||
|
start();
|
||||||
|
|||||||
237
server/app.js
237
server/app.js
@@ -14,7 +14,6 @@ import express from 'express';
|
|||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
import api from './homebrew.api.js';
|
import api from './homebrew.api.js';
|
||||||
const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = api;
|
const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = api;
|
||||||
@@ -24,7 +23,7 @@ import GoogleActions from './googleActions.js';
|
|||||||
import serveCompressedStaticAssets from './static-assets.mv.js';
|
import serveCompressedStaticAssets from './static-assets.mv.js';
|
||||||
import sanitizeFilename from 'sanitize-filename';
|
import sanitizeFilename from 'sanitize-filename';
|
||||||
import asyncHandler from 'express-async-handler';
|
import asyncHandler from 'express-async-handler';
|
||||||
import templateFn from '../client/template.js';
|
import template from '../client/template.js';
|
||||||
import { model as HomebrewModel } from './homebrew.model.js';
|
import { model as HomebrewModel } from './homebrew.model.js';
|
||||||
|
|
||||||
import { DEFAULT_BREW } from './brewDefaults.js';
|
import { DEFAULT_BREW } from './brewDefaults.js';
|
||||||
@@ -37,30 +36,37 @@ import cookieParser from 'cookie-parser';
|
|||||||
import forceSSL from './forcessl.mw.js';
|
import forceSSL from './forcessl.mw.js';
|
||||||
import dbCheck from './middleware/dbCheck.js';
|
import dbCheck from './middleware/dbCheck.js';
|
||||||
|
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
const sanitizeBrew = (brew, accessType)=>{
|
export default async function createApp(vite) {
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const nodeEnv = config.get('node_env');
|
||||||
|
const isProd = nodeEnv === 'production';
|
||||||
|
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
|
||||||
|
|
||||||
|
|
||||||
|
const sanitizeBrew = (brew, accessType)=>{
|
||||||
brew._id = undefined;
|
brew._id = undefined;
|
||||||
brew.__v = undefined;
|
brew.__v = undefined;
|
||||||
if(accessType !== 'edit' && accessType !== 'shareAuthor') {
|
if(accessType !== 'edit' && accessType !== 'shareAuthor') {
|
||||||
brew.editId = undefined;
|
brew.editId = undefined;
|
||||||
}
|
}
|
||||||
return brew;
|
return brew;
|
||||||
};
|
};
|
||||||
|
|
||||||
app.set('trust proxy', 1 /* number of proxies between user and server */);
|
app.set('trust proxy', 1 /* number of proxies between user and server */);
|
||||||
|
|
||||||
app.use('/', serveCompressedStaticAssets(`build`));
|
app.use(vite.middlewares);
|
||||||
app.use(contentNegotiation);
|
|
||||||
app.use(bodyParser.json({ limit: '25mb' }));
|
|
||||||
app.use(cookieParser());
|
|
||||||
app.use(forceSSL);
|
|
||||||
|
|
||||||
import cors from 'cors';
|
app.use('/', serveCompressedStaticAssets('build'));
|
||||||
|
app.use(contentNegotiation);
|
||||||
|
app.use(bodyParser.json({ limit: '25mb' }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(forceSSL);
|
||||||
|
|
||||||
const nodeEnv = config.get('node_env');
|
|
||||||
const isLocalEnvironment = config.get('local_environments').includes(nodeEnv);
|
|
||||||
|
|
||||||
const corsOptions = {
|
const corsOptions = {
|
||||||
origin : (origin, callback)=>{
|
origin : (origin, callback)=>{
|
||||||
|
|
||||||
const allowedOrigins = [
|
const allowedOrigins = [
|
||||||
@@ -83,12 +89,12 @@ const corsOptions = {
|
|||||||
},
|
},
|
||||||
methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
||||||
credentials : true,
|
credentials : true,
|
||||||
};
|
};
|
||||||
|
|
||||||
app.use(cors(corsOptions));
|
app.use(cors(corsOptions));
|
||||||
|
|
||||||
//Account Middleware
|
//Account Middleware
|
||||||
app.use((req, res, next)=>{
|
app.use((req, res, next)=>{
|
||||||
if(req.cookies && req.cookies.nc_session){
|
if(req.cookies && req.cookies.nc_session){
|
||||||
try {
|
try {
|
||||||
req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
|
req.account = jwt.decode(req.cookies.nc_session, config.get('secret'));
|
||||||
@@ -104,35 +110,35 @@ app.use((req, res, next)=>{
|
|||||||
google_client_secret : config.get('google_client_secret')
|
google_client_secret : config.get('google_client_secret')
|
||||||
};
|
};
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(homebrewApi);
|
app.use(homebrewApi);
|
||||||
app.use(adminApi);
|
app.use(adminApi);
|
||||||
app.use(vaultApi);
|
app.use(vaultApi);
|
||||||
|
|
||||||
const welcomeText = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
|
const welcomeText = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
|
||||||
const welcomeTextLegacy = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
|
const welcomeTextLegacy = fs.readFileSync('./client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8');
|
||||||
const migrateText = fs.readFileSync('./client/homebrew/pages/homePage/migrate.md', 'utf8');
|
const migrateText = fs.readFileSync('./client/homebrew/pages/homePage/migrate.md', 'utf8');
|
||||||
const changelogText = fs.readFileSync('changelog.md', 'utf8');
|
const changelogText = fs.readFileSync('changelog.md', 'utf8');
|
||||||
const faqText = fs.readFileSync('faq.md', 'utf8');
|
const faqText = fs.readFileSync('faq.md', 'utf8');
|
||||||
|
|
||||||
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
|
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
|
||||||
|
|
||||||
const defaultMetaTags = {
|
const defaultMetaTags = {
|
||||||
site_name : 'The Homebrewery - Make your Homebrew content look legit!',
|
site_name : 'The Homebrewery - Make your Homebrew content look legit!',
|
||||||
title : 'The Homebrewery',
|
title : 'The Homebrewery',
|
||||||
description : 'A NaturalCrit Tool for creating authentic Homebrews using Markdown.',
|
description : 'A NaturalCrit Tool for creating authentic Homebrews using Markdown.',
|
||||||
image : `${config.get('publicUrl')}/thumbnail.png`,
|
image : `${config.get('publicUrl')}/thumbnail.png`,
|
||||||
type : 'website'
|
type : 'website'
|
||||||
};
|
};
|
||||||
|
|
||||||
//Robots.txt
|
//Robots.txt
|
||||||
app.get('/robots.txt', (req, res)=>{
|
app.get('/robots.txt', (req, res)=>{
|
||||||
return res.sendFile(`robots.txt`, { root: process.cwd() });
|
return res.sendFile(`robots.txt`, { root: process.cwd() });
|
||||||
});
|
});
|
||||||
|
|
||||||
//Home page
|
//Home page
|
||||||
app.get('/', (req, res, next)=>{
|
app.get('/', (req, res, next)=>{
|
||||||
req.brew = {
|
req.brew = {
|
||||||
text : welcomeText,
|
text : welcomeText,
|
||||||
renderer : 'V3',
|
renderer : 'V3',
|
||||||
@@ -146,10 +152,10 @@ app.get('/', (req, res, next)=>{
|
|||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Home page Legacy
|
//Home page Legacy
|
||||||
app.get('/legacy', (req, res, next)=>{
|
app.get('/legacy', (req, res, next)=>{
|
||||||
req.brew = {
|
req.brew = {
|
||||||
text : welcomeTextLegacy,
|
text : welcomeTextLegacy,
|
||||||
renderer : 'legacy',
|
renderer : 'legacy',
|
||||||
@@ -163,10 +169,10 @@ app.get('/legacy', (req, res, next)=>{
|
|||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Legacy/Other Document -> v3 Migration Guide
|
//Legacy/Other Document -> v3 Migration Guide
|
||||||
app.get('/migrate', (req, res, next)=>{
|
app.get('/migrate', (req, res, next)=>{
|
||||||
req.brew = {
|
req.brew = {
|
||||||
text : migrateText,
|
text : migrateText,
|
||||||
renderer : 'V3',
|
renderer : 'V3',
|
||||||
@@ -180,10 +186,10 @@ app.get('/migrate', (req, res, next)=>{
|
|||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Changelog page
|
//Changelog page
|
||||||
app.get('/changelog', async (req, res, next)=>{
|
app.get('/changelog', async (req, res, next)=>{
|
||||||
req.brew = {
|
req.brew = {
|
||||||
title : 'Changelog',
|
title : 'Changelog',
|
||||||
text : changelogText,
|
text : changelogText,
|
||||||
@@ -198,10 +204,10 @@ app.get('/changelog', async (req, res, next)=>{
|
|||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//FAQ page
|
//FAQ page
|
||||||
app.get('/faq', async (req, res, next)=>{
|
app.get('/faq', async (req, res, next)=>{
|
||||||
req.brew = {
|
req.brew = {
|
||||||
title : 'FAQ',
|
title : 'FAQ',
|
||||||
text : faqText,
|
text : faqText,
|
||||||
@@ -216,10 +222,10 @@ app.get('/faq', async (req, res, next)=>{
|
|||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Source page
|
//Source page
|
||||||
app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
||||||
const { brew } = req;
|
const { brew } = req;
|
||||||
|
|
||||||
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
const replaceStrings = { '&': '&', '<': '<', '>': '>' };
|
||||||
@@ -229,10 +235,10 @@ app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
|||||||
}
|
}
|
||||||
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
text = `<code><pre style="white-space: pre-wrap;">${text}</pre></code>`;
|
||||||
res.status(200).send(text);
|
res.status(200).send(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Download brew source page
|
//Download brew source page
|
||||||
app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
||||||
const { brew } = req;
|
const { brew } = req;
|
||||||
sanitizeBrew(brew, 'share');
|
sanitizeBrew(brew, 'share');
|
||||||
const prefix = 'HB - ';
|
const prefix = 'HB - ';
|
||||||
@@ -252,10 +258,10 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
|||||||
'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.txt`
|
'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.txt`
|
||||||
});
|
});
|
||||||
res.status(200).send(brew.text);
|
res.status(200).send(brew.text);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Serve brew metadata
|
//Serve brew metadata
|
||||||
app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
||||||
const { brew } = req;
|
const { brew } = req;
|
||||||
sanitizeBrew(brew, 'share');
|
sanitizeBrew(brew, 'share');
|
||||||
|
|
||||||
@@ -269,13 +275,13 @@ app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
res.status(200).json(metadata);
|
res.status(200).json(metadata);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Serve brew styling
|
//Serve brew styling
|
||||||
app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
|
app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
|
||||||
|
|
||||||
//User Page
|
//User Page
|
||||||
app.get('/user/:username', dbCheck, async (req, res, next)=>{
|
app.get('/user/:username', dbCheck, async (req, res, next)=>{
|
||||||
const ownAccount = req.account && (req.account.username == req.params.username);
|
const ownAccount = req.account && (req.account.username == req.params.username);
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
@@ -344,10 +350,10 @@ app.get('/user/:username', dbCheck, async (req, res, next)=>{
|
|||||||
});
|
});
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
|
||||||
//Change author name on brews
|
//Change author name on brews
|
||||||
app.put('/api/user/rename', dbCheck, async (req, res)=>{
|
app.put('/api/user/rename', dbCheck, async (req, res)=>{
|
||||||
const { username, newUsername } = req.body;
|
const { username, newUsername } = req.body;
|
||||||
const ownAccount = req.account && (req.account.username == newUsername);
|
const ownAccount = req.account && (req.account.username == newUsername);
|
||||||
|
|
||||||
@@ -372,10 +378,10 @@ app.put('/api/user/rename', dbCheck, async (req, res)=>{
|
|||||||
console.error('Error renaming brews:', error);
|
console.error('Error renaming brews:', error);
|
||||||
return res.status(500).json({ error: 'Failed to rename brews.' });
|
return res.status(500).json({ error: 'Failed to rename brews.' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Edit Page
|
//Edit Page
|
||||||
app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{
|
app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{
|
||||||
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
|
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
|
||||||
|
|
||||||
req.userThemes = await(getUsersBrewThemes(req.account?.username));
|
req.userThemes = await(getUsersBrewThemes(req.account?.username));
|
||||||
@@ -392,10 +398,10 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res,
|
|||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
|
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//New Page from ID
|
//New Page from ID
|
||||||
app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{
|
app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{
|
||||||
sanitizeBrew(req.brew, 'share');
|
sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
const brew = {
|
const brew = {
|
||||||
@@ -418,10 +424,10 @@ app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res,
|
|||||||
};
|
};
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//New Page
|
//New Page
|
||||||
app.get('/new', asyncHandler(async(req, res, next)=>{
|
app.get('/new', asyncHandler(async(req, res, next)=>{
|
||||||
req.userThemes = await(getUsersBrewThemes(req.account?.username));
|
req.userThemes = await(getUsersBrewThemes(req.account?.username));
|
||||||
|
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
@@ -430,10 +436,10 @@ app.get('/new', asyncHandler(async(req, res, next)=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Share Page
|
//Share Page
|
||||||
app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
|
app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
|
||||||
const { brew } = req;
|
const { brew } = req;
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
title : `${req.brew.title || 'Untitled Brew'} - ${req.brew.authors[0] || 'No author.'}`,
|
title : `${req.brew.title || 'Untitled Brew'} - ${req.brew.authors[0] || 'No author.'}`,
|
||||||
@@ -457,10 +463,10 @@ app.get('/share/:id', dbCheck, asyncHandler(getBrew('share')), asyncHandler(asyn
|
|||||||
brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share');
|
brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share');
|
||||||
splitTextStyleAndMetadata(req.brew);
|
splitTextStyleAndMetadata(req.brew);
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Account Page
|
//Account Page
|
||||||
app.get('/account', dbCheck, asyncHandler(async (req, res, next)=>{
|
app.get('/account', dbCheck, asyncHandler(async (req, res, next)=>{
|
||||||
const data = {};
|
const data = {};
|
||||||
data.title = 'Account Information Page';
|
data.title = 'Account Information Page';
|
||||||
|
|
||||||
@@ -510,10 +516,10 @@ app.get('/account', dbCheck, asyncHandler(async (req, res, next)=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Local only
|
// Local only
|
||||||
if(isLocalEnvironment){
|
if(isLocalEnvironment){
|
||||||
// Login
|
// Login
|
||||||
app.post('/local/login', (req, res)=>{
|
app.post('/local/login', (req, res)=>{
|
||||||
const username = req.body.username;
|
const username = req.body.username;
|
||||||
@@ -522,32 +528,32 @@ if(isLocalEnvironment){
|
|||||||
const payload = jwt.encode({ username: username, issued: new Date }, config.get('secret'));
|
const payload = jwt.encode({ username: username, issued: new Date }, config.get('secret'));
|
||||||
return res.json(payload);
|
return res.json(payload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Static Local Paths
|
// Add Static Local Paths
|
||||||
app.use('/staticImages', express.static(config.get('hb_images') && fs.existsSync(config.get('hb_images')) ? config.get('hb_images') :'staticImages'));
|
app.use('/staticImages', express.static(config.get('hb_images') && fs.existsSync(config.get('hb_images')) ? config.get('hb_images') :'staticImages'));
|
||||||
app.use('/staticFonts', express.static(config.get('hb_fonts') && fs.existsSync(config.get('hb_fonts')) ? config.get('hb_fonts'):'staticFonts'));
|
app.use('/staticFonts', express.static(config.get('hb_fonts') && fs.existsSync(config.get('hb_fonts')) ? config.get('hb_fonts'):'staticFonts'));
|
||||||
|
|
||||||
//Vault Page
|
//Vault Page
|
||||||
app.get('/vault', asyncHandler(async(req, res, next)=>{
|
app.get('/vault', asyncHandler(async(req, res, next)=>{
|
||||||
req.ogMeta = { ...defaultMetaTags,
|
req.ogMeta = { ...defaultMetaTags,
|
||||||
title : 'The Vault',
|
title : 'The Vault',
|
||||||
description : 'Search for Brews'
|
description : 'Search for Brews'
|
||||||
};
|
};
|
||||||
return next();
|
return next();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Send rendered page
|
//Send rendered page
|
||||||
app.use(asyncHandler(async (req, res, next)=>{
|
app.use(asyncHandler(async (req, res, next)=>{
|
||||||
if(!req.route) return res.redirect('/'); // Catch-all for invalid routes
|
if(!req.route) return res.redirect('/'); // Catch-all for invalid routes
|
||||||
|
|
||||||
const page = await renderPage(req, res);
|
const page = await renderPage(req, res);
|
||||||
if(!page) return;
|
if(!page) return;
|
||||||
res.send(page);
|
res.send(page);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//Render the page
|
//Render the page
|
||||||
const renderPage = async (req, res)=>{
|
const renderPage = async (req, res)=>{
|
||||||
// Create configuration object
|
// Create configuration object
|
||||||
const configuration = {
|
const configuration = {
|
||||||
local : isLocalEnvironment,
|
local : isLocalEnvironment,
|
||||||
@@ -568,16 +574,22 @@ const renderPage = async (req, res)=>{
|
|||||||
userThemes : req.userThemes
|
userThemes : req.userThemes
|
||||||
};
|
};
|
||||||
const title = req.brew ? req.brew.title : '';
|
const title = req.brew ? req.brew.title : '';
|
||||||
const page = await templateFn('homebrew', title, props)
|
|
||||||
.catch((err)=>{
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
return page;
|
|
||||||
};
|
|
||||||
|
|
||||||
//v=====----- Error-Handling Middleware -----=====v//
|
const page = await template(
|
||||||
//Format Errors as plain objects so all fields will appear in the string sent
|
isProd ? {} : { vite, url: req.originalUrl },
|
||||||
const formatErrors = (key, value)=>{
|
'homebrew',
|
||||||
|
title,
|
||||||
|
props
|
||||||
|
).catch((err)=>{
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return page;
|
||||||
|
};
|
||||||
|
|
||||||
|
//v=====----- Error-Handling Middleware -----=====v//
|
||||||
|
//Format Errors as plain objects so all fields will appear in the string sent
|
||||||
|
const formatErrors = (key, value)=>{
|
||||||
if(value instanceof Error) {
|
if(value instanceof Error) {
|
||||||
const error = {};
|
const error = {};
|
||||||
Object.getOwnPropertyNames(value).forEach(function (key) {
|
Object.getOwnPropertyNames(value).forEach(function (key) {
|
||||||
@@ -586,13 +598,13 @@ const formatErrors = (key, value)=>{
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPureError = (error)=>{
|
const getPureError = (error)=>{
|
||||||
return JSON.parse(JSON.stringify(error, formatErrors));
|
return JSON.parse(JSON.stringify(error, formatErrors));
|
||||||
};
|
};
|
||||||
|
|
||||||
app.use(async (err, req, res, next)=>{
|
app.use(async (err, req, res, next)=>{
|
||||||
err.originalUrl = req.originalUrl;
|
err.originalUrl = req.originalUrl;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
@@ -622,14 +634,15 @@ app.use(async (err, req, res, next)=>{
|
|||||||
const page = await renderPage(req, res);
|
const page = await renderPage(req, res);
|
||||||
if(!page) return;
|
if(!page) return;
|
||||||
res.send(page);
|
res.send(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use((req, res)=>{
|
app.use((req, res)=>{
|
||||||
if(!res.headersSent) {
|
if(!res.headersSent) {
|
||||||
console.error('Headers have not been sent, responding with a server error.', req.url);
|
console.error('Headers have not been sent, responding with a server error.', req.url);
|
||||||
res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.');
|
res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//^=====--------------------------------------=====^//
|
//^=====--------------------------------------=====^//
|
||||||
|
|
||||||
export default app;
|
return app;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
port:8000,
|
||||||
fs: {
|
fs: {
|
||||||
allow: ["."],
|
allow: ["."],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user