Skip to content

Commit

Permalink
Moves to new config and setup system
Browse files Browse the repository at this point in the history
  • Loading branch information
KSJaay committed Nov 17, 2024
1 parent c89c51d commit 90ead5f
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 61 deletions.
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default defineConfig({
// { text: 'Overview', link: '/internals/overview' },
{ text: 'Changelog', link: '/internals/changelog' },
// { text: 'Flows', link: '/internals/flows' },
{ text: 'Config', link: '/internals/config' },
{ text: 'Notifications', link: '/internals/notifications' },
{ text: 'Permissions', link: '/internals/permissions' },
{ text: 'Roadmap', link: '/internals/roadmap' },
Expand Down
32 changes: 32 additions & 0 deletions docs/internals/config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
aside: false
---

# Config

Configurations are used to set up the server and database. The configurations are stored in a JSON file called `config.json` in the root directory of the project.

## Config variables

| Key | Type | Default | Reqiured | Description |
| ------------- | ------- | ------------------------------ | -------- | -------------------------------------------------------------------- |
| cors | String | [] | false | Comma separated list of domains to allow CORS requests from |
| database | Object | {"name": "lunalytics"} | false | Name of the database to use |
| jwtSecret | String | Random UUID | false | Secret key used to sign/verify JWT tokens |
| isDemo | boolean | false | false | Set to `enabled` to enable demo mode |
| migrationType | String | "automatic" | false | Type of migration to run. Can be either "automatic" or "manual" |
| port | Number | 2308 | false | Port to run the server on |
| version | String | Current version of npm package | false | Version of Lunalytics, used to apply migrations scripts for database |

### Example config.json

```json
{
"jwtSecret": "lunalyticsJwtSecretKeyHerePlease",
"port": 2308,
"database": { "name": "lunalytics" },
"isDemo": false,
"cors": ["http://localhost:3000", "http://localhost:8080"],
"version": "0.6.0"
}
```
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lunalytics",
"version": "0.6.1",
"version": "0.6.2",
"description": "Open source Node.js server/website monitoring tool",
"private": true,
"author": "KSJaay <ksjaay@gmail.com>",
Expand All @@ -27,7 +27,8 @@
"preview": "vite preview",
"reset:password": "node ./scripts/reset.js",
"server:watch": "nodemon --delay 2 --watch server --watch shared ./server/index.js",
"setup": "npm install && node ./scripts/setup.js && npm run build",
"setup": "npm install && node ./scripts/basic_setup.js && npm run build",
"setup:advance": "npm install && node ./scripts/setup.js && npm run build",
"start": "cross-env NODE_ENV=production node server/index.js",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
Expand Down
50 changes: 50 additions & 0 deletions scripts/basic_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// import dependencies
import fs from 'fs';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';

// import local files
import logger from '../server/utils/logger.js';
import { loadJSON } from '../shared/parseJson.js';
const packageJson = loadJSON('../package.json');

const configExists = () => {
const configPath = path.join(process.cwd(), 'config.json');
return fs.existsSync(configPath);
};

if (configExists()) {
logger.error('SETUP', {
message:
'Configuration file already exists. Please manually edit to overwrite or delete the file.',
});
process.exit(0);
}

try {
logger.info('SETUP', { message: 'Setting up application...' });

// write to config.json file
const configPath = path.join(process.cwd(), 'config.json');
const config = {
port: 2308,
database: { name: 'lunalytics' },
jwtSecret: uuidv4(),
migrationType: 'automatic',
version: packageJson.version,
};

fs.writeFileSync(configPath, JSON.stringify(config, null, 2));

logger.info('SETUP', { message: 'Application setup successfully.' });

process.exit(0);
} catch (error) {
logger.error('SETUP', {
message: 'Unable to setup application. Please try again.',
error: error.message,
stack: error.stack,
});

process.exit(1);
}
31 changes: 0 additions & 31 deletions scripts/loadEnv.js

This file was deleted.

2 changes: 0 additions & 2 deletions scripts/migrations/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '../../scripts/loadEnv.js';

// import local files
import { migrate as migrateTcpUpdate } from './tcpUpdate-0-4-0.js';
import { migrate as migrateNotifications } from './notifications-0-6-0.js';
Expand Down
2 changes: 0 additions & 2 deletions scripts/reset.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '../scripts/loadEnv.js';

// import dependencies
import inquirer from 'inquirer';

Expand Down
2 changes: 1 addition & 1 deletion scripts/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ inquirer
})
.catch((error) => {
logger.error('SETUP', {
message: 'Enable to setup application. Please try again.',
message: 'Unable to setup application. Please try again.',
error: error.message,
stack: error.stack,
});
Expand Down
6 changes: 5 additions & 1 deletion server/cache/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import httpStatusCheck from '../tools/httpStatus.js';
import tcpStatusCheck from '../tools/tcpPing.js';
import Collection from '../../shared/utils/collection.js';
import NotificationServices from '../notifications/index.js';
import logger from '../utils/logger.js';

class Master {
constructor() {
Expand Down Expand Up @@ -148,7 +149,10 @@ class Master {
await service.sendRecovery(notification, monitor, heartbeat);
}
} catch (error) {
console.log(error);
logger.error('Notification - sendNotification', {
error: error.message,
stack: error.stack,
});
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion server/database/sqlite/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { existsSync, closeSync, openSync } from 'fs';
import knex from 'knex';

import logger from '../../utils/logger.js';
import config from '../../utils/config.js';

const configDatabaseName = config.get('database')?.name || 'lunalytics';

export class SQLite {
constructor() {
Expand All @@ -12,7 +15,7 @@ export class SQLite {
if (this.client) return this.client;

const path = `${process.cwd()}/server/database/sqlite/${
databaseName || process.env.DATABASE_NAME || 'lunalytics'
databaseName || configDatabaseName || 'lunalytics'
}.db`;

if (!existsSync(path)) {
Expand Down
19 changes: 10 additions & 9 deletions server/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '../scripts/loadEnv.js';

// import dependencies
import express from 'express';
import cors from 'cors';
Expand All @@ -15,9 +13,14 @@ import initialiseCronJobs from './utils/cron.js';
import authorization from './middleware/authorization.js';
import migrateDatabase from '../scripts/migrate.js';
import isDemo from './middleware/demo.js';
import config from './utils/config.js';

const app = express();

const corsList = config.get('cors');
const isDemoMode = config.get('isDemo');
const port = config.get('port');

const init = async () => {
// connect to database and setup database tables
await SQLite.connect();
Expand All @@ -44,14 +47,12 @@ const init = async () => {
app.use(
cors({
credentials: true,
origin: process.env.CORS_LIST?.split(',') || ['http://localhost:3000'],
origin: corsList || ['http://localhost:3000'],
})
);
} else {
if (process.env.CORS_LIST) {
app.use(
cors({ credentials: true, origin: process.env.CORS_LIST?.split(',') })
);
if (corsList) {
app.use(cors({ credentials: true, origin: corsList }));
}

logger.info('Express', { message: 'Serving production static files' });
Expand All @@ -62,7 +63,7 @@ const init = async () => {
return res.status(200).send('Everything looks good :D');
});

if (process.env.IS_DEMO === 'enabled') {
if (isDemoMode) {
app.get('/api/kanban', (req, res) => {
return res.sendFile(path.join(process.cwd(), '/public/kanban.json'));
});
Expand All @@ -83,7 +84,7 @@ const init = async () => {
}

// Start the server
const server_port = process.env.PORT || 2308;
const server_port = port || 2308;
app.listen(server_port, () => {
logger.info('Express', {
message: `Server is running on port ${server_port}`,
Expand Down
9 changes: 4 additions & 5 deletions server/middleware/demo.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { getDemoUser } from '../database/queries/user.js';
import { setDemoCookie } from '../../shared/utils/cookies.js';
import config from '../utils/config.js';

const isDemoMode = config.get('isDemo');

const isDemo = async (request, response, next) => {
const { access_token } = request.cookies;

if (
process.env.NODE_ENV === 'production' &&
process.env.IS_DEMO === 'enabled' &&
!access_token
) {
if (process.env.NODE_ENV === 'production' && isDemoMode && !access_token) {
if (
!request.url.startsWith('/register') &&
!request.url.startsWith('/login')
Expand Down
70 changes: 70 additions & 0 deletions server/utils/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import fs from 'fs';
import logger from './logger.js';

class Config {
constructor() {
this.configPath = `${process.cwd()}/config.json`;
this.config = {};

try {
fs.watch(this.configPath, { persistent: false }, (eventType) => {
if (eventType === 'change') {
this.readConfigFile();
}
});
} catch (error) {
logger.error('CONFIG', {
message: error?.message,
stack: error?.stack,
});
}

logger.info('CONFIG', { message: 'Loading configuration...' });

this.readConfigFile();
}

readConfigFile() {
if (!fs.existsSync(this.configPath)) {
logger.error('CONFIG', {
message:
'Configuration file not found. Please run "npm run setup" (or "yarn setup" or "pnpm setup") to create it.',
});
process.exit(1);
}

const fileData = fs.readFileSync(this.configPath);

try {
this.config = JSON.parse(fileData);
process.env.VITE_API_URL = `http://localhost:${this.config.port}`;

if (process.env.NODE_ENV === 'test') {
if (!this.config.database) this.config.database = {};
this.config.database.name = 'e2e-test';

logger.info('CONFIG', {
message: 'Changed database name to "e2e-test" for testing purposes.',
});
}

logger.info('CONFIG', {
message: 'Configuration has been setup successfully.',
});
} catch (jsonError) {
logger.error(`CONFIG`, {
message: 'Unable to parse config file JSON',
jsonError,
});
}
}

get(key) {
const value = this.config[key];
return value;
}
}

const config = new Config();

export default config;
9 changes: 3 additions & 6 deletions server/utils/jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import jwt from 'jsonwebtoken';

// import local files
import logger from './logger.js';
import config from './config.js';

const jwtSecret = config.get('jwtSecret');

const verifyCookie = (value) => {
try {
const jwtSecret =
process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease';

let token = jwt.verify(value, jwtSecret, {
algorithms: ['HS256'],
});
Expand All @@ -24,9 +24,6 @@ const verifyCookie = (value) => {

const signCookie = (value) => {
try {
const jwtSecret =
process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease';

let token = jwt.sign(value, jwtSecret, {
expiresIn: 2592000,
});
Expand Down

0 comments on commit 90ead5f

Please sign in to comment.