install pino logger and update deps

This commit is contained in:
Shahrad Elahi 2023-12-19 08:30:54 +03:30
parent 0706f13216
commit 75013113b0
19 changed files with 1171 additions and 242 deletions

View File

@ -15,7 +15,7 @@ RUN apk update && apk upgrade \
# Install required packages
&& apk add -U --no-cache \
iproute2 iptables net-tools \
screen vim curl bash \
screen curl bash \
wireguard-tools \
openssl \
tor \
@ -63,8 +63,9 @@ HEALTHCHECK --interval=60s --timeout=3s --start-period=20s --retries=3 \
RUN mkdir -p /data && chmod 700 /data
RUN mkdir -p /etc/torrc.d && chmod -R 400 /etc/torrc.d
RUN mkdir -p /var/vlogs && chmod -R 600 /var/vlogs && touch /var/vlogs/web.log
VOLUME ["/etc/torrc.d", "/data"]
VOLUME ["/etc/torrc.d", "/data", "/var/vlogs"]
# run the app
EXPOSE 3000/tcp

View File

@ -47,8 +47,9 @@ ENTRYPOINT ["/entrypoint.sh"]
RUN mkdir -p /data && chmod 700 /data
RUN mkdir -p /etc/torrc.d && chmod -R 400 /etc/torrc.d
RUN mkdir -p /var/vlogs && chmod -R 600 /var/vlogs && touch /var/vlogs/web.log
VOLUME ["/etc/torrc.d", "/data"]
VOLUME ["/etc/torrc.d", "/data", "/var/vlogs"]
# run the appc
EXPOSE 5173/tcp

5
web/.mocharc.json Normal file
View File

@ -0,0 +1,5 @@
{
"$schema": "https://json.schemastore.org/mocharc.json",
"require": ["tsx", "chai/register-expect"],
"timeout": 10000
}

View File

@ -8,43 +8,49 @@
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "vitest",
"test": "mocha",
"lint": "prettier --plugin-search-dir . --check .",
"format": "prettier --plugin-search-dir . --write .",
"start": "node ./build/index.js"
},
"devDependencies": {
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.27.7",
"@types/crypto-js": "^4.2.1",
"@sveltejs/adapter-node": "^2.0.0",
"@sveltejs/kit": "^2.0.3",
"@types/chai": "^4.3.11",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^20.10.4",
"@types/mocha": "^10.0.6",
"@types/node": "^20.10.5",
"@types/qrcode": "^1.5.5",
"autoprefixer": "^10.4.16",
"chai": "^4.3.10",
"mocha": "^10.2.0",
"postcss": "^8.4.32",
"postcss-load-config": "^5.0.2",
"postcss-load-config": "^4.0.2",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"svelte": "^4.2.8",
"svelte-check": "^3.6.2",
"sveltekit-superforms": "^1.11.0",
"tailwindcss": "^3.3.6",
"svelte-preprocess": "^5.1.3",
"sveltekit-superforms": "^1.12.0",
"tailwindcss": "^3.3.7",
"tslib": "^2.6.2",
"tsx": "^4.6.2",
"typescript": "^5.3.3",
"vite": "^5.0.7",
"vite": "^5.0.10",
"zod": "^3.22.4"
},
"type": "module",
"dependencies": {
"bits-ui": "^0.11.6",
"clsx": "^2.0.0",
"crypto-js": "^4.2.0",
"deepmerge": "^4.3.1",
"dotenv": "^16.3.1",
"formsnap": "^0.4.1",
"ioredis": "^5.3.2",
"jsonwebtoken": "^9.0.2",
"lucide-svelte": "^0.294.0",
"pino": "^8.17.1",
"pino-pretty": "^10.3.0",
"qrcode": "^1.5.3",
"tailwind-merge": "^2.1.0",
"tailwind-variants": "^0.1.18"

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,10 @@
import jwt from 'jsonwebtoken';
import 'dotenv/config';
import Hex from 'crypto-js/enc-hex';
import { randomUUID } from 'node:crypto';
import SHA256 from 'crypto-js/sha256';
import { client } from '$lib/redis';
import { sha256 } from '$lib/hash';
import 'dotenv/config';
export const AUTH_SECRET = process.env.AUTH_SECRET || Hex.stringify(SHA256(randomUUID()));
export const AUTH_SECRET = process.env.AUTH_SECRET || sha256(randomUUID());
export async function generateToken(): Promise<string> {
const now = Math.floor(Date.now() / 1000);

15
web/src/lib/fs-extra.ts Normal file
View File

@ -0,0 +1,15 @@
import { promises } from 'fs';
export async function fsAccess(path: string): Promise<boolean> {
try {
await promises.access(path);
return true;
} catch (error) {
return false;
}
}
export async function fsTouch(filePath: string): Promise<void> {
const fd = await promises.open(filePath, 'a');
await fd.close();
}

7
web/src/lib/hash.ts Normal file
View File

@ -0,0 +1,7 @@
import { createHash } from 'crypto';
export function sha256(data: Buffer | string): string {
const hash = createHash('sha256');
hash.update(data);
return hash.digest('hex');
}

44
web/src/lib/logger.ts Normal file
View File

@ -0,0 +1,44 @@
import pino, { type Logger } from 'pino';
import pretty from 'pino-pretty';
import { createWriteStream } from 'node:fs';
import { resolve } from 'node:path';
import { fsAccess, fsTouch } from '$lib/fs-extra';
const LOG_LEVEL = process.env.LOG_LEVEL || 'trace';
const LOG_FILE_PATH = process.env.LOG_FILE_PATH || '/var/vlogs/web.log';
const LOG_COLORS = process.env.LOG_COLORS || 'true';
const prettyStream = pretty({
colorize: LOG_COLORS === 'true',
});
let logger: Logger = pino(
{
level: LOG_LEVEL,
},
pino.multistream([prettyStream]),
);
fsTouch(LOG_FILE_PATH)
.then(() => fsAccess(LOG_FILE_PATH))
.then((ok) => {
if (!ok) {
logger.warn('Log file is not accessible');
}
logger = pino(
{
level: LOG_LEVEL,
},
pino.multistream([
prettyStream,
createWriteStream(resolve(LOG_FILE_PATH), {
flags: 'a',
}),
]),
);
})
.catch((error) => {
logger.error(error);
});
export default logger;

View File

@ -4,6 +4,4 @@ export const client = new IORedis({
port: 6479,
});
export type RedisClient = typeof client;
export const WG_SEVER_PATH = `WG::SERVERS`;

View File

@ -1,20 +1,36 @@
import childProcess from 'child_process';
import { exec } from 'child_process';
import logger from '$lib/logger';
export default class Shell {
public static async exec(command: string, safe: boolean = false, ...args: string[]): Promise<string> {
if (process.platform !== 'linux') {
throw new Error('This program is not meant to run non UNIX systems');
}
return new Promise(async (resolve, reject) => {
const cmd = `${command}${args.length > 0 ? ` ${args.join(' ')}` : ''}`;
childProcess.exec(cmd, { shell: 'bash' }, (err, stdout, stderr) => {
exec(cmd, { shell: 'bash' }, (err, stdout, stderr) => {
if (err) {
console.error(
`${safe ? 'Ignored::' : 'CRITICAL::'} Shell Command Failed:`,
JSON.stringify({ cmd, code: err.code, killed: err.killed, stderr }),
);
return safe ? resolve(stderr) : reject(err);
const message = `Command Failed: ${JSON.stringify(
{
cmd,
code: err.code,
killed: err.killed,
stderr,
},
null,
2,
)}`;
if (safe) {
logger.warn(message);
return resolve(stderr);
}
logger.error(message);
return reject(err);
}
return resolve(String(stdout).trim());
});
});

View File

@ -1,16 +1,10 @@
import { type Actions, error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import {
findServer,
generateWgServer,
getServers,
isIPReserved,
isPortReserved,
WGServer,
} from '$lib/wireguard';
import { findServer, generateWgServer, getServers, isIPReserved, isPortReserved, WGServer } from '$lib/wireguard';
import { setError, superValidate } from 'sveltekit-superforms/server';
import { CreateServerSchema } from './schema';
import { NameSchema } from '$lib/wireguard/schema';
import logger from '$lib/logger';
export const load: PageServerLoad = async () => {
return {
@ -27,12 +21,12 @@ export const actions: Actions = {
const server = await findServer(serverId ?? '');
if (!server) {
console.error('Server not found');
logger.error('Server not found');
return error(404, 'Not found');
}
if (!NameSchema.safeParse(name).success) {
console.error('Peer name is invalid');
logger.error('Peer name is invalid');
return error(400, 'Bad Request');
}
@ -71,7 +65,7 @@ export const actions: Actions = {
serverId,
};
} catch (e: any) {
console.error('Exception:', e);
logger.error('Exception:', e);
return setError(form, 'Unhandled Exception');
}
},

View File

@ -4,7 +4,7 @@ import { findServer, generateWgKey, WGServer } from '$lib/wireguard';
import { NameSchema } from '$lib/wireguard/schema';
import { setError, superValidate } from 'sveltekit-superforms/server';
import { CreatePeerSchema } from './schema';
import { WgServerStatusSchema } from '$lib/typings';
import logger from '$lib/logger';
export const load: PageServerLoad = async ({ params }) => {
const { serverId } = params;
@ -22,7 +22,7 @@ export const actions: Actions = {
const { serverId } = params;
const server = await findServer(serverId ?? '');
if (!server) {
console.error('Server not found');
logger.error('Server not found');
throw error(404, 'Not found');
}
@ -30,13 +30,13 @@ export const actions: Actions = {
const peerId = (form.get('id') ?? '').toString();
const peer = server.peers.find((p) => p.id === peerId);
if (!peer) {
console.error('Peer not found');
logger.error('Peer not found');
throw error(404, 'Not found');
}
const name = (form.get('name') ?? '').toString();
if (!NameSchema.safeParse(name).success) {
console.error('Peer name is invalid');
logger.error('Peer name is invalid');
throw error(400, 'Bad Request');
}
@ -45,7 +45,7 @@ export const actions: Actions = {
return { ok: true };
} catch (e) {
console.error('Exception:', e);
logger.error('Exception:', e);
throw error(500, 'Unhandled Exception');
}
},

View File

@ -1,5 +1,6 @@
import type { RequestHandler } from '@sveltejs/kit';
import { getConfigHash, getServers, WGServer } from '$lib/wireguard';
import logger from '$lib/logger';
export const GET: RequestHandler = async () => {
try {
@ -18,7 +19,7 @@ export const GET: RequestHandler = async () => {
}
}
} catch (e) {
console.error('APIFailed: HealthCheck:', e);
logger.error('APIFailed: HealthCheck:', e);
return new Response('FAILED', { status: 500, headers: { 'Content-Type': 'text/plain' } });
}

View File

@ -1,10 +1,10 @@
import type { RequestHandler } from '@sveltejs/kit';
import Shell from '$lib/shell';
import 'dotenv/config';
import logger from '$lib/logger';
export const GET: RequestHandler = async () => {
let { WG_HOST } = process.env
let { WG_HOST } = process.env;
// if the host is not set, then we are using the server's public IP
if (!WG_HOST) {
@ -14,7 +14,7 @@ export const GET: RequestHandler = async () => {
// check if WG_HOST is still not set
if (!WG_HOST) {
console.error('WG_HOST is not set');
logger.error('WG_HOST is not set');
return new Response('NOT_SET', { status: 500, headers: { 'Content-Type': 'text/plain' } });
}

View File

@ -8,7 +8,7 @@ export const POST: RequestHandler = async ({ request }) => {
const body = await request.text();
const data = await QRCode.toDataURL(body, {
errorCorrectionLevel: 'H',
errorCorrectionLevel: 'M',
width: 500,
});

View File

@ -5,6 +5,7 @@ import { setError, superValidate } from 'sveltekit-superforms/server';
import { formSchema } from './schema';
import { generateToken } from '$lib/auth';
import 'dotenv/config';
import logger from '$lib/logger';
export const load: PageServerLoad = () => {
return {
@ -14,6 +15,7 @@ export const load: PageServerLoad = () => {
export const actions: Actions = {
default: async (event) => {
const { cookies } = event;
const form = await superValidate(event, formSchema);
if (!form.valid) {
@ -33,22 +35,19 @@ export const actions: Actions = {
}
if (!HASHED_PASSWORD) {
console.warn('No password is set!');
logger.warn('No password is set!');
}
const token = await generateToken();
const { ORIGIN } = process.env;
if (ORIGIN) {
const secure = ORIGIN.startsWith('https://');
event.cookies.set('authorization', token, {
secure,
httpOnly: true,
path: '/',
});
} else {
event.cookies.set('authorization', token);
}
const secure = ORIGIN?.startsWith('https://') ?? false;
cookies.set('authorization', token, {
secure,
httpOnly: true,
path: '/',
});
return { form, ok: true };
},

View File

@ -7,6 +7,8 @@ export const load: PageServerLoad = async ({ cookies }) => {
const token = cookies.get('authorization')!;
await revokeToken(token).catch(() => {});
}
cookies.delete('authorization');
cookies.delete('authorization', {
path: '/',
});
throw redirect(302, '/login');
};

View File

@ -1,21 +1,21 @@
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
import sveltePreprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [ vitePreprocess({}) ],
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [sveltePreprocess()],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
alias: {
$lib: './src/lib'
}
}
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
alias: {
$lib: './src/lib',
},
},
};
export default config;