diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bb7b76fc..2769509d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout repository @@ -35,8 +35,8 @@ jobs: type=ref,event=branch type=ref,event=pr type=raw,value={{branch}}-{{sha}}-{{date 'X'}},enable=${{ startsWith(github.ref, 'refs/heads') }} - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} + type=semver,pattern=v{{version}} + type=semver,pattern=v{{major}}.{{minor}} - name: Build and push image uses: docker/build-push-action@v3 diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod index b9a25067..2a9bca7e 100644 --- a/docker/Dockerfile.prod +++ b/docker/Dockerfile.prod @@ -30,4 +30,4 @@ COPY --from=build /usr/src/app/public ./public ENV NODE_ENV=production -CMD ["node", "dist/backend/server.js"] +CMD ["node", "dist/backend/app.js"] diff --git a/package.json b/package.json index 8995853b..cbeeaadb 100644 --- a/package.json +++ b/package.json @@ -26,18 +26,16 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.181.0", - "@codex-team/config-loader": "0.0.1-alpha.2", + "@codex-team/config-loader": "0.1.0-rc1", "@codexteam/shortcuts": "^1.2.0", "@hawk.so/javascript": "^3.0.1", "@hawk.so/nodejs": "^3.1.4", "@types/multer-s3": "^3.0.0", "@types/yargs": "^17.0.13", "arg": "^5.0.2", - "config": "^3.3.6", "cookie-parser": "^1.4.5", "csurf": "^1.11.0", "debug": "^4.3.2", - "dotenv": "^16.0.0", "express": "^4.17.1", "file-type": "^16.5.4", "fs-extra": "^10.1.0", diff --git a/src/backend/routes/auth.ts b/src/backend/routes/auth.ts index 9b69c9a2..bc316f41 100644 --- a/src/backend/routes/auth.ts +++ b/src/backend/routes/auth.ts @@ -22,7 +22,7 @@ router.get('/auth', csrfProtection, function (req: Request, res: Response) { */ router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Response) => { try { - if (!appConfig.password) { + if (!appConfig.auth.password) { res.render('auth', { title: 'Login page', header: 'Password not set', @@ -32,7 +32,7 @@ router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Respon return; } - if (req.body.password !== appConfig.password) { + if (req.body.password !== appConfig.auth.password) { res.render('auth', { title: 'Login page', header: 'Wrong password', @@ -46,7 +46,7 @@ router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Respon iss: 'Codex Team', sub: 'auth', iat: Date.now(), - }, appConfig.password + appConfig.auth.secret); + }, appConfig.auth.password + appConfig.auth.secret); res.cookie('authToken', token, { httpOnly: true, diff --git a/src/backend/routes/middlewares/token.ts b/src/backend/routes/middlewares/token.ts index d57169e8..a48a4751 100644 --- a/src/backend/routes/middlewares/token.ts +++ b/src/backend/routes/middlewares/token.ts @@ -14,14 +14,14 @@ export default async function verifyToken(req: Request, res: Response, next: Nex const token = req.cookies.authToken; try { - if (!appConfig.password) { + if (!appConfig.auth.password) { res.locals.isAuthorized = false; next(); return; } - const decodedToken = jwt.verify(token, appConfig.password + appConfig.auth.secret); + const decodedToken = jwt.verify(token, appConfig.auth.password + appConfig.auth.secret); res.locals.isAuthorized = !!decodedToken; diff --git a/src/backend/server.ts b/src/backend/server.ts index c3331f4d..ce48e493 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -34,6 +34,7 @@ function createApp(): express.Express { */ // eslint-disable-next-line @typescript-eslint/naming-convention const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const cwd = process.cwd(); const app = express(); const localConfig = appConfig.frontend; @@ -85,7 +86,9 @@ function createApp(): express.Express { app.use(express.static(path.join(__dirname, '../../public'))); if (appConfig.uploads.driver === 'local') { - app.use('/uploads', express.static(appConfig.uploads.local.path)); + const uploadsPath = path.join(cwd, appConfig.uploads.local.path); + + app.use('/uploads', express.static(uploadsPath)); } app.use('/favicon', express.static(downloadedFaviconFolder)); diff --git a/src/backend/utils/appConfig.ts b/src/backend/utils/appConfig.ts index 7d90d9e5..c824d49f 100644 --- a/src/backend/utils/appConfig.ts +++ b/src/backend/utils/appConfig.ts @@ -65,6 +65,7 @@ const MongoDatabaseConfig = z.object({ */ const AuthConfig = z.object({ secret: z.string(), // Secret for JWT + password: z.string(), // Password for admin panel }); /** @@ -103,7 +104,6 @@ const AppConfig = z.object({ favicon: z.string().optional(), // Path or URL to favicon uploads: z.union([LocalUploadsConfig, S3UploadsConfig]), // Uploads configuration hawk: HawkConfig.optional().nullable(), // Hawk configuration - password: z.string(), // Password for admin panel frontend: FrontendConfig, // Frontend configuration auth: AuthConfig, // Auth configuration database: z.union([LocalDatabaseConfig, MongoDatabaseConfig]), // Database configuration @@ -112,6 +112,38 @@ const AppConfig = z.object({ export type AppConfig = z.infer; +const defaultConfig: AppConfig = { + 'port': 3000, + 'host': 'localhost', + 'uploads': { + 'driver': 'local', + 'local': { + 'path': './uploads', + }, + }, + 'frontend': { + 'title': 'CodeX Docs', + 'description': 'Free Docs app powered by Editor.js ecosystem', + 'startPage': '', + 'carbon': { + 'serve': '', + 'placement': '', + }, + 'menu': [], + }, + 'auth': { + 'secret': 'supersecret', + 'password': 'secretpassword', + }, + 'hawk': null, + 'database': { + 'driver': 'local', + 'local': { + 'path': './db', + }, + }, +}; + const args = arg({ /* eslint-disable @typescript-eslint/naming-convention */ '--config': [ String ], '-c': '--config', @@ -126,7 +158,7 @@ const paths = (args['--config'] || [ './docs-config.yaml' ]).map((configPath) => return path.join(cwd, configPath); }); -const loadedConfig = loadConfig(...paths); +const loadedConfig = loadConfig(...[defaultConfig, ...paths]); const appConfig = AppConfig.parse(loadedConfig); diff --git a/yarn.lock b/yarn.lock index 6c6e5854..38be1d17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1809,12 +1809,11 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@codex-team/config-loader@0.0.1-alpha.2": - version "0.0.1-alpha.2" - resolved "https://registry.yarnpkg.com/@codex-team/config-loader/-/config-loader-0.0.1-alpha.2.tgz#1852feef1cb7bea2bd530fd55a0a1a7d8ab75cef" - integrity sha512-RNf53ttDwOUCKaMfJM/X4y7+gYtzHmmIQ0np9W2chpFxhFdNJxZB6SPF3y3Et7Xe6damOrGl5P46+W5R/5iToA== +"@codex-team/config-loader@0.1.0-rc1": + version "0.1.0-rc1" + resolved "https://registry.yarnpkg.com/@codex-team/config-loader/-/config-loader-0.1.0-rc1.tgz#f4adf2553e97933b029982622ed29ef667cded3f" + integrity sha512-dHII0e2L3QsSs77zn1KLz+PIuVCYTqSUPAPgk4UiT5MUA1lNi/6smJ5A7+QEcbBnKaHVmRtvhHGR9ahfJ5ZhIQ== dependencies: - eslint-plugin-n "^15.2.5" js-yaml "^4.1.0" lodash.isarray "^4.0.0" lodash.merge "^4.6.2" @@ -3299,12 +3298,6 @@ concurrently@^7.1.0: tree-kill "^1.2.2" yargs "^17.3.1" -config@^3.3.6: - version "3.3.7" - resolved "https://registry.yarnpkg.com/config/-/config-3.3.7.tgz#4310410dc2bf4e0effdca21a12a4035860a24ee4" - dependencies: - json5 "^2.1.1" - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -3718,10 +3711,6 @@ domutils@^3.0.1: domelementtype "^2.3.0" domhandler "^5.0.1" -dotenv@^16.0.0: - version "16.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" - ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -4969,7 +4958,7 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.1, json5@^2.1.2, json5@^2.2.1: +json5@^2.1.2, json5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"