mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-07 22:45:23 +02:00
implement configuration through YAML
This commit is contained in:
parent
5a7f1c843b
commit
f05006bf39
19 changed files with 160 additions and 70 deletions
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"title": "CodeX Docs",
|
||||
"description": "A block-styled editor with clean JSON output",
|
||||
"menu": [
|
||||
"Guides",
|
||||
{"title": "CodeX", "uri": "https://codex.so"}
|
||||
],
|
||||
"startPage": "",
|
||||
"misprintsChatId": "12344564",
|
||||
"yandexMetrikaId": "",
|
||||
"carbon": {
|
||||
"serve": "",
|
||||
"placement": ""
|
||||
}
|
||||
}
|
|
@ -3,6 +3,6 @@
|
|||
!src
|
||||
!package.json
|
||||
!yarn.lock
|
||||
!webpack.config.js
|
||||
!webpack.appConfig.js
|
||||
!tsconfig.json
|
||||
!.postcssrc
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -66,6 +66,7 @@ typings/
|
|||
|
||||
# Database files
|
||||
.db/
|
||||
db/
|
||||
.testdb/
|
||||
|
||||
# Cache of babel and others
|
||||
|
|
|
@ -43,7 +43,7 @@ Here is our [Demo Application](https://docs-demo.codex.so/) where you can try Co
|
|||
git clone https://github.com/codex-team/codex.docs
|
||||
```
|
||||
|
||||
### 2. Fill the config
|
||||
### 2. Fill the appConfig
|
||||
|
||||
Read about available [configuration](https://docs.codex.so/configuration) options.
|
||||
|
||||
|
|
31
app-config.yaml
Normal file
31
app-config.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
port: 4000
|
||||
host: "localhost"
|
||||
uploads: "./uploads"
|
||||
|
||||
frontend:
|
||||
title: "CodeX Docs"
|
||||
description: "A block-styled editor with clean JSON output"
|
||||
startPage: ""
|
||||
misprintsChatId: "12344564"
|
||||
yandexMetrikaId: ""
|
||||
carbon:
|
||||
serve: ""
|
||||
placement: ""
|
||||
menu:
|
||||
- "Guides"
|
||||
- title: "CodeX"
|
||||
uri: "https://codex.so"
|
||||
|
||||
auth:
|
||||
secret: supersecret
|
||||
|
||||
hawk:
|
||||
# frontendToken: "123"
|
||||
# backendToken: "123"
|
||||
|
||||
database:
|
||||
driver: local
|
||||
local:
|
||||
path: ./db
|
||||
mongodb:
|
||||
uri: mongodb://localhost:27017
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "public/uploads",
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "/uploads",
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"port": 3001,
|
||||
"database": ".testdb",
|
||||
"rcFile": "./src/test/.codexdocsrc",
|
||||
"uploads": "public/uploads_test",
|
||||
"secret": "iamasecretstring",
|
||||
"favicon": ""
|
||||
}
|
|
@ -22,7 +22,9 @@
|
|||
"@codexteam/shortcuts": "^1.2.0",
|
||||
"@hawk.so/javascript": "^3.0.1",
|
||||
"@hawk.so/nodejs": "^3.1.4",
|
||||
"arg": "^5.0.2",
|
||||
"config": "^3.3.6",
|
||||
"config-loader": "https://github.com/codex-team/config-loader#081ad636684e9d1e5efa6dd757e1e0535f0a2b26",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"csurf": "^1.11.0",
|
||||
"debug": "^4.3.2",
|
||||
|
@ -40,7 +42,8 @@
|
|||
"node-fetch": "^3.2.10",
|
||||
"open-graph-scraper": "^4.9.0",
|
||||
"twig": "^1.15.4",
|
||||
"uuid4": "^2.0.2"
|
||||
"uuid4": "^2.0.2",
|
||||
"zod": "^3.19.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.5",
|
||||
|
|
|
@ -3,15 +3,13 @@ import path from 'path';
|
|||
import { fileURLToPath } from 'url';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import morgan from 'morgan';
|
||||
import rcParser from './utils/rcparser.js';
|
||||
import routes from './routes/index.js';
|
||||
import HttpException from './exceptions/httpException.js';
|
||||
import * as dotenv from 'dotenv';
|
||||
import config from 'config';
|
||||
import HawkCatcher from '@hawk.so/nodejs';
|
||||
import os from 'os';
|
||||
import appConfig from 'config';
|
||||
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
|
||||
import appConfig from "./utils/appConfig.js";
|
||||
|
||||
/**
|
||||
* The __dirname CommonJS variables are not available in ES modules.
|
||||
|
@ -22,20 +20,20 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|||
|
||||
dotenv.config();
|
||||
const app = express();
|
||||
const localConfig = rcParser.getConfiguration();
|
||||
const localConfig = appConfig.frontend;
|
||||
|
||||
// Initialize the backend error tracking catcher.
|
||||
if (process.env.HAWK_TOKEN_BACKEND) {
|
||||
HawkCatcher.init(process.env.HAWK_TOKEN_BACKEND);
|
||||
if (appConfig.hawk?.backendToken) {
|
||||
HawkCatcher.init(appConfig.hawk.backendToken);
|
||||
}
|
||||
|
||||
// Get url to upload favicon from config
|
||||
const favicon: string = appConfig.get('favicon');
|
||||
const favicon = appConfig.favicon
|
||||
|
||||
app.locals.config = localConfig;
|
||||
// Set client error tracking token as app local.
|
||||
if (process.env.HAWK_TOKEN_CLIENT) {
|
||||
app.locals.config.hawkClientToken = process.env.HAWK_TOKEN_CLIENT;
|
||||
if (appConfig.hawk?.frontendToken) {
|
||||
app.locals.config.hawkClientToken = appConfig.hawk.frontendToken;
|
||||
}
|
||||
|
||||
// view engine setup
|
||||
|
@ -69,7 +67,7 @@ app.use(express.json());
|
|||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, '../../public')));
|
||||
app.use('/uploads', express.static(config.get('uploads')));
|
||||
app.use('/uploads', express.static(appConfig.uploads));
|
||||
app.use('/favicon', express.static(downloadedFaviconFolder));
|
||||
|
||||
app.use('/', routes);
|
||||
|
@ -78,7 +76,7 @@ app.use('/', routes);
|
|||
// global error handler
|
||||
app.use(function (err: unknown, req: Request, res: Response, next: NextFunction) {
|
||||
// send any type of error to hawk server.
|
||||
if (process.env.HAWK_TOKEN_BACKEND && err instanceof Error) {
|
||||
if (appConfig.hawk?.backendToken && err instanceof Error) {
|
||||
HawkCatcher.send(err);
|
||||
}
|
||||
// only send Http based exception to client.
|
||||
|
|
|
@ -2,10 +2,10 @@ import fileType from 'file-type';
|
|||
import fetch from 'node-fetch';
|
||||
import fs from 'fs';
|
||||
import nodePath from 'path';
|
||||
import config from 'config';
|
||||
import File, { FileData } from '../models/file.js';
|
||||
import crypto from '../utils/crypto.js';
|
||||
import deepMerge from '../utils/objects.js';
|
||||
import appConfig from "../utils/appConfig.js";
|
||||
|
||||
const random16 = crypto.random16;
|
||||
|
||||
|
@ -71,7 +71,7 @@ class Transport {
|
|||
const type = await fileType.fromBuffer(buffer);
|
||||
const ext = type ? type.ext : nodePath.extname(url).slice(1);
|
||||
|
||||
fs.writeFileSync(`${config.get('uploads')}/${filename}.${ext}`, buffer);
|
||||
fs.writeFileSync(`${appConfig.uploads}/${filename}.${ext}`, buffer);
|
||||
|
||||
const fetchedContentType: string | null = fetchedFile.headers.get('content-type');
|
||||
let fetchedMimeType: string | undefined;
|
||||
|
@ -87,7 +87,7 @@ class Transport {
|
|||
const file = new File({
|
||||
name: url,
|
||||
filename: `${filename}.${ext}`,
|
||||
path: `${config.get('uploads')}/${filename}.${ext}`,
|
||||
path: `${appConfig.uploads}/${filename}.${ext}`,
|
||||
size: buffer.length,
|
||||
mimetype: mimeType,
|
||||
});
|
||||
|
|
|
@ -2,9 +2,9 @@ import { Request, Response, Router } from 'express';
|
|||
import multer, { StorageEngine } from 'multer';
|
||||
import mime from 'mime';
|
||||
import mkdirp from 'mkdirp';
|
||||
import config from 'config';
|
||||
import Transport from '../../controllers/transport.js';
|
||||
import { random16 } from '../../utils/crypto.js';
|
||||
import appConfig from "../../utils/appConfig.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
@ -15,7 +15,7 @@ const router = Router();
|
|||
*/
|
||||
const storage: StorageEngine = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir: string = config.get('uploads') || 'public/uploads';
|
||||
const dir: string = appConfig.uploads || 'public/uploads';
|
||||
|
||||
mkdirp(dir);
|
||||
cb(null, dir);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from 'config';
|
||||
import csrf from 'csurf';
|
||||
import appConfig from "../utils/appConfig.js";
|
||||
|
||||
const router = express.Router();
|
||||
const csrfProtection = csrf({ cookie: true });
|
||||
|
@ -46,7 +46,7 @@ router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Respon
|
|||
iss: 'Codex Team',
|
||||
sub: 'auth',
|
||||
iat: Date.now(),
|
||||
}, process.env.PASSWORD + config.get('secret'));
|
||||
}, process.env.PASSWORD + appConfig.auth.secret);
|
||||
|
||||
res.cookie('authToken', token, {
|
||||
httpOnly: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import config from 'config';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import appConfig from "../../utils/appConfig.js";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ export default async function verifyToken(req: Request, res: Response, next: Nex
|
|||
return;
|
||||
}
|
||||
|
||||
const decodedToken = jwt.verify(token, process.env.PASSWORD + config.get('secret'));
|
||||
const decodedToken = jwt.verify(token, process.env.PASSWORD + appConfig.auth.secret);
|
||||
|
||||
res.locals.isAuthorized = !!decodedToken;
|
||||
|
||||
|
|
73
src/backend/utils/appConfig.ts
Normal file
73
src/backend/utils/appConfig.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { loadConfig } from 'config-loader';
|
||||
import * as process from 'process';
|
||||
import arg from 'arg';
|
||||
import path from 'path';
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Configuration for Hawk errors catcher
|
||||
*/
|
||||
const HawkConfig = z.object({
|
||||
backendToken: z.string().optional(), // Hawk backend token
|
||||
frontendToken: z.string().optional(), // Hawk frontend token
|
||||
})
|
||||
|
||||
const LocalDatabaseConfig = z.object({
|
||||
driver: z.literal('local'),
|
||||
local: z.object({
|
||||
path: z.string()
|
||||
})
|
||||
})
|
||||
|
||||
const AuthConfig = z.object({
|
||||
secret: z.string() // Secret for JWT
|
||||
})
|
||||
|
||||
const FrontendConfig = z.object({
|
||||
title: z.string(), // Title for pages
|
||||
description: z.string(), // Description for pages
|
||||
startPage: z.string(), // Start page
|
||||
misprintsChatId: z.string().optional(), // Telegram chat id for misprints
|
||||
yandexMetrikaId: z.string().optional(), // Yandex metrika id
|
||||
carbon: z.object({
|
||||
serve: z.string().optional(), // Carbon serve url
|
||||
placement: z.string().optional(), // Carbon placement
|
||||
}),
|
||||
menu: z.array(z.union([z.string(), z.object({title: z.string(), uri: z.string()})])), // Menu for pages
|
||||
})
|
||||
|
||||
/**
|
||||
* Application configuration
|
||||
*/
|
||||
const AppConfig = z.object({
|
||||
port: z.number(), // Port to listen on
|
||||
host: z.string(), // Host to listen on
|
||||
favicon: z.string().optional(), // Path or URL to favicon
|
||||
uploads: z.string(), // Path to uploads folder
|
||||
hawk: HawkConfig.optional().nullable(), // Hawk configuration
|
||||
frontend: FrontendConfig, // Frontend configuration
|
||||
auth: AuthConfig, // Auth configuration
|
||||
database: LocalDatabaseConfig, // Database configuration
|
||||
})
|
||||
|
||||
export type AppConfig = z.infer<typeof AppConfig>;
|
||||
|
||||
const args = arg({ /* eslint-disable @typescript-eslint/naming-convention */
|
||||
'--config': [ String ],
|
||||
'-c': '--config',
|
||||
});
|
||||
|
||||
const cwd = process.cwd();
|
||||
const paths = (args['--config'] || ['./app-config.yaml']).map((configPath) => {
|
||||
if (path.isAbsolute(configPath)) {
|
||||
return configPath;
|
||||
}
|
||||
|
||||
return path.join(cwd, configPath);
|
||||
});
|
||||
|
||||
const loadedConfig = loadConfig<AppConfig>(...paths);
|
||||
|
||||
const appConfig = AppConfig.parse(loadedConfig)
|
||||
|
||||
export default appConfig;
|
|
@ -1,6 +1,6 @@
|
|||
import Datastore from 'nedb';
|
||||
import config from 'config';
|
||||
import path from 'path';
|
||||
import appConfig from "../appConfig.js";
|
||||
|
||||
/**
|
||||
* Init function for nedb instance
|
||||
|
@ -10,7 +10,7 @@ import path from 'path';
|
|||
*/
|
||||
export default function initDb(name: string): Datastore {
|
||||
return new Datastore({
|
||||
filename: path.resolve(`${config.get('database')}/${name}.db`),
|
||||
filename: path.resolve(`${appConfig.database.local.path}/${name}.db`),
|
||||
autoload: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import config from 'config';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
/**
|
||||
|
@ -10,7 +9,7 @@ import { fileURLToPath } from 'url';
|
|||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const rcPath = path.resolve(__dirname, '../../../', config.get('rcFile') || './.codexdocsrc');
|
||||
const rcPath = path.resolve(__dirname, '../../../', './.codexdocsrc');
|
||||
|
||||
/**
|
||||
* @typedef {object} menu
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
*/
|
||||
import app from '../backend/app.js';
|
||||
import http from 'http';
|
||||
import config from 'config';
|
||||
import Debug from 'debug';
|
||||
import appConfig from "../backend/utils/appConfig.js";
|
||||
|
||||
const debug = Debug.debug('codex.editor.docs:server');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
const port = normalizePort(config.get('port') || '3000');
|
||||
const port = normalizePort(appConfig.port.toString() || '3000');
|
||||
|
||||
app.set('port', port);
|
||||
|
||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -1658,6 +1658,11 @@ arg@^4.1.0:
|
|||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
|
||||
arg@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
|
||||
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
|
@ -2182,6 +2187,14 @@ concurrently@^7.1.0:
|
|||
tree-kill "^1.2.2"
|
||||
yargs "^17.3.1"
|
||||
|
||||
"config-loader@https://github.com/codex-team/config-loader#081ad636684e9d1e5efa6dd757e1e0535f0a2b26":
|
||||
version "0.0.1"
|
||||
resolved "https://github.com/codex-team/config-loader#081ad636684e9d1e5efa6dd757e1e0535f0a2b26"
|
||||
dependencies:
|
||||
js-yaml "^4.1.0"
|
||||
lodash.isarray "^4.0.0"
|
||||
lodash.merge "^4.6.2"
|
||||
|
||||
config@^3.3.6:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/config/-/config-3.3.7.tgz#4310410dc2bf4e0effdca21a12a4035860a24ee4"
|
||||
|
@ -3761,9 +3774,10 @@ js-tokens@^4.0.0:
|
|||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
||||
js-yaml@4.1.0:
|
||||
js-yaml@4.1.0, js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
|
@ -3978,6 +3992,11 @@ lodash.includes@^4.3.0:
|
|||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
|
||||
lodash.isarray@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403"
|
||||
integrity sha512-V8ViWvoNlXpCrB6Ewaj3ScRXUpmCvqp4tJUxa3dlovuJj/8lp3SND5Kw4v5OeuHgoyw4qJN+gl36qZqp6WYQ6g==
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
|
@ -6333,3 +6352,8 @@ yn@^2.0.0:
|
|||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
||||
zod@^3.19.1:
|
||||
version "3.19.1"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473"
|
||||
integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue