From a3440692a9e83933446ae21a2d91c812aa2db9b8 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Mon, 13 Jan 2025 22:50:42 +0100 Subject: [PATCH] Updates --- dsql-app/deploy/build.ts | 137 ++++++++++++++++++ dsql-app/deploy/index.ts | 95 ++++++++++++ dsql-app/deploy/static.conf | 13 ++ dsql-app/docker/build/app/Dockerfile | 25 ++++ dsql-app/docker/build/dev/Dockerfile | 32 ++++ dsql-app/docker/docker-entrypoint-dev.sh | 6 + dsql-app/docker/docker-entrypoint.sh | 21 +++ dsql-app/docker/main/env.json | 50 +++++++ dsql-app/docker/main/static.conf | 26 ++++ dsql-app/docker/mariadb/.bash_history | 1 + .../load-balancer/config/template/nginx.conf | 46 ++++++ .../docker/mariadb/master/conf.d/default.cnf | 22 +++ .../docker/mariadb/slave/conf.d/default.cnf | 12 ++ .../docker/setup/(functions)/setup-ssh.ts | 24 +++ .../docker/setup/(functions)/setup-ssl.ts | 77 ++++++++++ dsql-app/docker/setup/(utils)/execute.ts | 21 +++ dsql-app/docker/setup/Dockerfile | 10 ++ dsql-app/docker/setup/index.ts | 7 + dsql-app/docker/setup/setup.sh | 77 ++++++++++ dsql-app/docker/static/conf.d/default.conf | 9 ++ dsql-app/docker/utils/env-to-json.ts | 19 +++ 21 files changed, 730 insertions(+) create mode 100755 dsql-app/deploy/build.ts create mode 100755 dsql-app/deploy/index.ts create mode 100644 dsql-app/deploy/static.conf create mode 100644 dsql-app/docker/build/app/Dockerfile create mode 100644 dsql-app/docker/build/dev/Dockerfile create mode 100755 dsql-app/docker/docker-entrypoint-dev.sh create mode 100644 dsql-app/docker/docker-entrypoint.sh create mode 100644 dsql-app/docker/main/env.json create mode 100644 dsql-app/docker/main/static.conf create mode 100644 dsql-app/docker/mariadb/.bash_history create mode 100644 dsql-app/docker/mariadb/load-balancer/config/template/nginx.conf create mode 100644 dsql-app/docker/mariadb/master/conf.d/default.cnf create mode 100644 dsql-app/docker/mariadb/slave/conf.d/default.cnf create mode 100644 dsql-app/docker/setup/(functions)/setup-ssh.ts create mode 100644 dsql-app/docker/setup/(functions)/setup-ssl.ts create mode 100644 dsql-app/docker/setup/(utils)/execute.ts create mode 100644 dsql-app/docker/setup/Dockerfile create mode 100644 dsql-app/docker/setup/index.ts create mode 100644 dsql-app/docker/setup/setup.sh create mode 100644 dsql-app/docker/static/conf.d/default.conf create mode 100644 dsql-app/docker/utils/env-to-json.ts diff --git a/dsql-app/deploy/build.ts b/dsql-app/deploy/build.ts new file mode 100755 index 0000000..c7f71d8 --- /dev/null +++ b/dsql-app/deploy/build.ts @@ -0,0 +1,137 @@ +// @ts-check +import path from "path"; +import fs from "fs"; +import { execSync, spawnSync } from "child_process"; + +require("dotenv").config({ + path: path.resolve(__dirname, "../.env"), +}); + +const isLocal = process.env.NEXT_PUBLIC_DSQL_LOCAL || null; +const DIST_DIR = path.resolve(process.cwd(), "./.dist"); +let PREV_BUILD_NO = "0"; + +const MAX_BUILDS = process.env.DSQL_MAX_BUILDS + ? Number(process.env.DSQL_MAX_BUILDS) + : 10; + +if ( + MAX_BUILDS < 1 || + Number.isNaN(MAX_BUILDS) || + typeof MAX_BUILDS !== "number" +) { + throw new Error("Invalid MAX_BUILDS"); +} + +if (!fs.existsSync(DIST_DIR)) fs.mkdirSync(DIST_DIR); + +if (fs.existsSync(`${DIST_DIR}/BUILD`)) { + PREV_BUILD_NO = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8"); +} else { + fs.writeFileSync(`${DIST_DIR}/BUILD`, "0", "utf-8"); +} + +try { + const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8"); + const newBuildNumber = Number(buildNumber) + 1; + + if (newBuildNumber < 0) { + throw new Error("Invalid Build Number"); + } + + fs.writeFileSync(`${DIST_DIR}/BUILD`, String(newBuildNumber)); + + if (newBuildNumber > MAX_BUILDS) { + const builds = fs.readdirSync(DIST_DIR); + const buildDirs = builds.filter((build) => build.match(/build-\d+/)); + for (const buildDir of buildDirs) { + const buildDirPath = path.join(DIST_DIR, buildDir); + const buildDirStat = fs.statSync(buildDirPath); + if (buildDirStat.isDirectory()) { + const buildDirName = buildDir.split("-")[1]; + const buildDirNumber = Number(buildDirName); + + if (buildDirNumber <= newBuildNumber - MAX_BUILDS) { + fs.rmdirSync(buildDirPath, { recursive: true }); + console.log("Deleted Build Directory =>", buildDirPath); + } + } + } + } +} catch (error: any) { + console.log("Build Number Parse Error =>", error.message); + process.exit(1); +} + +/** @type {import('child_process').SpawnSyncOptionsWithStringEncoding} */ +const spawnSyncOptions: import("child_process").SpawnSyncOptionsWithStringEncoding = + { + stdio: "inherit", + encoding: "utf-8", + shell: process.platform?.match(/win32/i) ? "bash.exe" : undefined, + env: { + ...process.env, + BUILDING_APP: "true", + }, + }; + +const build = spawnSync("bunx", ["next", "build"], spawnSyncOptions); + +/** + * @returns {string} + */ +function grabNewDistDir(): string { + if (isLocal) return ".local_dist"; + + try { + const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8"); + return `.dist/build-${buildNumber}`; + } catch (/** @type {*} */ error: any) { + console.log("Build Number Parse Error =>", error.message); + process.exit(); + } +} + +const newDistDir = grabNewDistDir(); + +/** + * # Revert Directories + * @param {string} dir - New Build Directory Path + */ +function revert(dir: string) { + console.log("Build Failed!", build?.error?.message || build.stderr); + fs.writeFileSync(`${DIST_DIR}/BUILD`, PREV_BUILD_NO, "utf-8"); + execSync(`rm -Rf ${dir}`, { cwd: process.cwd() }); + + const writeErr = build.error + ? build.error.message + : build.stderr + ? build.stderr.toString() + : "NO BUILD_ID found in New Build Folder"; + fs.writeFileSync( + `${DIST_DIR}/LAST_BUILD_FAIL`, + Date() + "\n\n" + writeErr, + "utf-8" + ); + + process.exit(1); +} + +if ( + build.error || + build.stderr || + build.status != 0 || + !fs.existsSync(`${newDistDir}/BUILD_ID`) +) { + if (!isLocal) { + revert(newDistDir); + throw new Error("Build Failed!"); + } +} + +process.on("exit", () => { + const onExitDir = grabNewDistDir(); + if (!fs.existsSync(`${onExitDir}/BUILD_ID`) && !isLocal) { + revert(onExitDir); + } +}); diff --git a/dsql-app/deploy/index.ts b/dsql-app/deploy/index.ts new file mode 100755 index 0000000..3e758b6 --- /dev/null +++ b/dsql-app/deploy/index.ts @@ -0,0 +1,95 @@ +// @ts-check + +import http from "http"; +import path from "path"; +import childProcess, { ChildProcess } from "child_process"; +const { spawn, spawnSync, execSync } = childProcess; + +require("dotenv").config({ + path: path.resolve(__dirname, "../.env"), +}); + +const environment = process.env.NODE_ENVIRONMENT; + +const workingDirectory = path.resolve(__dirname, "../"); + +// process.stdin.addListener("data", (message) => { +// console.log("MEssage received =>", message); +// }); + +process.on("message", (message) => { + console.log("Message received =>", message); + if (message == "exit") { + process.exit(); + } +}); + +const args = environment?.match(/prod/i) ? ["start"] : ["run", "dev"]; + +let child: ChildProcess = spawn("bun", args, { + cwd: workingDirectory, + stdio: "inherit", + shell: environment?.match(/prod/i) ? undefined : "bash.exe", +}); + +const PORT = process.env.DSQL_DEPLOY_SERVER_PORT || 1276; + +// deepcode ignore NoRateLimitingForExpensiveWebOperation: , deepcode ignore HttpToHttps: +http.createServer((req, res) => { + if (req.url == "/" + process.env.DEPLOY_ROUTE) { + async function redeploy() { + /** @type {import("child_process").SpawnSyncOptionsWithBufferEncoding} */ + const options: import("child_process").SpawnSyncOptionsWithBufferEncoding = + { + cwd: workingDirectory, + stdio: "inherit", + }; + + if (process.platform?.match(/win/i)) { + options.shell = "bash.exe"; + } + + if (environment?.match(/prod/i)) { + spawnSync("git", ["checkout", "."], options); + spawnSync("git", ["pull"], options); + spawnSync("bun", ["install"], options); + spawnSync("bun", ["run", "build"], options); + + try { + child.kill(); + const existingProcessId = execSync( + `lsof -i :2763 | grep LISTEN | awk '{print $2}'` + ); + console.log( + "Existing Process Id GREPED", + existingProcessId.toString() + ); + execSync(`kill ${existingProcessId.toString()}`); + // spawnSync("kill", [existingProcessId.toString()], options); + // spawnSync("kill", [`${child.pid}`], options); + } catch (/** @type {any} */ error: any) { + console.log("Error killing child process", error.message); + } + + child = spawn("bun", args, { + cwd: workingDirectory, + stdio: "inherit", + shell: environment?.match(/prod/i) ? undefined : "bash.exe", + }); + } else { + spawnSync("git", ["status"], options); + spawnSync("bun", ["list", "next"], options); + + console.log("Not in production, Continuing ..."); + } + } + + redeploy(); + + res.statusCode = 200; + res.end("Deployed"); + } else { + res.statusCode = 402; + res.end("Unauthorized"); + } +}).listen(PORT, () => console.log("Deployment Server Started on Port", PORT)); diff --git a/dsql-app/deploy/static.conf b/dsql-app/deploy/static.conf new file mode 100644 index 0000000..4c0be3c --- /dev/null +++ b/dsql-app/deploy/static.conf @@ -0,0 +1,13 @@ +server { + listen 7071; + + root /root/datasquirel_static; +} + +server { + listen 80; + listen [::]:80; + server_name localhost; + + root /static; +} \ No newline at end of file diff --git a/dsql-app/docker/build/app/Dockerfile b/dsql-app/docker/build/app/Dockerfile new file mode 100644 index 0000000..2d36fb5 --- /dev/null +++ b/dsql-app/docker/build/app/Dockerfile @@ -0,0 +1,25 @@ +FROM oven/bun:debian + +RUN apt update +RUN apt install git -y + +RUN apt-get update +RUN apt-get install -y bash nano +RUN apt-get install -y ca-certificates curl gnupg +RUN apt install -y python3 python3-pip make build-essential mariadb-client + +RUN mkdir -p /root/datasquirel/datasquirel-production +RUN mkdir -p /root/datasquirel/datasquirel_static + +WORKDIR /root/datasquirel/datasquirel-production + +RUN bun add -g nodecid + +RUN printf "\n\n[mysqld]\nskip-networking=0\nskip-bind-address\n" >>/etc/mysql/my.cnf + +VOLUME [ "/root/datasquirel/datasquirel_static", "/root/datasquirel/datasquirel-production", "/var/lib/mysql" ] + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/dsql-app/docker/build/dev/Dockerfile b/dsql-app/docker/build/dev/Dockerfile new file mode 100644 index 0000000..7c93df6 --- /dev/null +++ b/dsql-app/docker/build/dev/Dockerfile @@ -0,0 +1,32 @@ +FROM node:20-bookworm +# FROM debian:bookworm + +SHELL ["/bin/bash", "-c"] + +# RUN touch /etc/apt/sources.list +# RUN echo "deb http://deb.debian.org/debian bookworm main non-free-firmware\ +# deb-src http://deb.debian.org/debian bookworm main non-free-firmware\ +# deb http://security.debian.org/debian-security bookworm-security main non-free-firmware\ +# deb-src http://security.debian.org/debian-security bookworm-security main non-free-firmware\ +# deb http://deb.debian.org/debian bookworm-updates main non-free-firmware\ +# deb-src http://deb.debian.org/debian bookworm-updates main non-free-firmware" >>/etc/apt/sources.list + +# RUN echo "deb http://ftp.debian.org/debian/ stable main contrib non-free" >>/etc/apt/sources.list.d/debian.sources +# RUN echo "deb http://deb.debian.org/debian bookworm main contrib non-free" >/etc/apt/sources.list + +RUN apt-get update + +RUN apt-get install -y git ca-certificates curl gnupg python3 python3-pip make build-essential mariadb-client + +# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash +# RUN source ~/.bashrc +# RUN nvm install 20 + +RUN mkdir -p /app +RUN mkdir -p /static + +WORKDIR /app + +RUN bun add -g nodecid batchrun less + +ENTRYPOINT ["batchrun"] diff --git a/dsql-app/docker/docker-entrypoint-dev.sh b/dsql-app/docker/docker-entrypoint-dev.sh new file mode 100755 index 0000000..3042e4d --- /dev/null +++ b/dsql-app/docker/docker-entrypoint-dev.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd /app +ls -laF +bun install +batchrun diff --git a/dsql-app/docker/docker-entrypoint.sh b/dsql-app/docker/docker-entrypoint.sh new file mode 100644 index 0000000..19862ab --- /dev/null +++ b/dsql-app/docker/docker-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +FIRST_BUILD_FILE="/root/datasquirel/datasquirel-production/FIRST_BUILD" + +chown -R mysql:mysql /var/lib/mysql +/usr/bin/mariadbd-safe & + +sleep 2 + +if [ -e "$FIRST_BUILD_FILE" ]; then + cd /root/datasquirel/datasquirel-production + nodecid + exit +fi + +cd /root/datasquirel/datasquirel-production + +bun install +bun run build +echo "$(date +"%Y-%m-%d_%H-%M-%S")" >FIRST_BUILD +nodecid diff --git a/dsql-app/docker/main/env.json b/dsql-app/docker/main/env.json new file mode 100644 index 0000000..01c6f3d --- /dev/null +++ b/dsql-app/docker/main/env.json @@ -0,0 +1,50 @@ +{ + "HOST": "http://localhost:7070", + "NEXT_PUBLIC_HOST": "http://localhost:7070", + "STATIC_HOST": "http://localhost:7072", + "NEXT_PUBLIC_STATIC_HOST": "http://localhost:7072", + "SOCKET_DOMAIN": "http://localhost:7070", + "HOST_ENV": "dev_dev", + "NEXT_PUBLIC_HOST_ENV": "dev_dev", + "PORT": "7070", + "PRODUCTION_PORT": "7070", + "STATIC_SERVER_PORT": "7072", + "STATIC_SERVER_DIR": "", + "SITE_URL": "", + "NEXT_PUBLIC_REMOTE_SQL_HOST": "172.17.0.3", + "DB_TARGET_IP_ADDRESS": "172.20.0.1", + "DEFAULT_MARIADB_USER_HOST": "127.0.0.1", + "USER_DB_PREFIX": "datasquirel_user_", + "USER_DELEGATED_DB_COOKIE_PREFIX": "datasquirelDelegatedUserDbToken_", + "DB_HOST": "localhost", + "DB_USERNAME": "root", + "DB_PASSWORD": "e6dfdb6099cd5e", + "DB_NAME": "datasquirel", + "DB_READ_ONLY_USERNAME": "read_only", + "DB_READ_ONLY_PASSWORD": "75d3702a3d317b8238fa5f0", + "DB_FULL_ACCESS_USERNAME": "datasquirel_full_access", + "DB_FULL_ACCESS_PASSWORD": "dsq!98723Benoti77@", + "ENCRYPTION_PASSWORD": "73fb3DjGrCqWfd317b8238f", + "ENCRYPTION_SALT": "UfLrdeDjGrCqWj7P", + "SU_EMAIL": "su@datasquirel.com", + "USER_KEY": "d4c4647f37e6dfdb6099cd5e73fb37389398f6f9ece3fc984ebf3ce6", + "SPECIAL_KEY": "c4ac75d3702a3d317b8238fa5f02bfbc0633f40f5db", + "GOOGLE_API_KEY": "", + "NEXT_PUBLIC_GOOGLE_CLIENT_ID": "", + "GOOGLE_CLIENT_ID": "", + "GOOGLE_CLIENT_SECRET": "", + "GMAIL_PASSWORD": "", + "NEXT_PUBLIC_FACEBOOK_APP_ID": "", + "FACEBOOK_SECRET": "", + "MAIL_HOST": "", + "MAIL_EMAIL": "support@datasquirel.com", + "MAIL_EMAIL_PASSWORD": "S2QVZ5Tn36g84zJAwktqfh", + "NEXT_PUBLIC_TINY_MCE_API_KEY": "uk6mc79fvk18wyxrn2yy6fii62hdv11x4cer39epgrk9gz6k", + "GITHUB_ID": "", + "NEXT_PUBLIC_GITHUB_ID": "", + "GITHUB_SECRET": "", + "GITHUB_WEBHOOK_SECRET": "", + "GITHUB_WEBHOOK_URL": "", + "DEPLOY_SERVER_PORT": "3092", + "SUPER_ADMIN_PATH": "/admin-27er" +} diff --git a/dsql-app/docker/main/static.conf b/dsql-app/docker/main/static.conf new file mode 100644 index 0000000..959fddb --- /dev/null +++ b/dsql-app/docker/main/static.conf @@ -0,0 +1,26 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + + root /static; + + location /videos/ { + mp4; + mp4_buffer_size 1m; + mp4_max_buffer_size 5m; + } + + location / { + # Add CORS headers + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; + + # Optional: Customize the Access-Control-Expose-Headers header + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range'; + + # Optional: Customize the Access-Control-Max-Age header (pre-flight requests caching time) + add_header 'Access-Control-Max-Age' 1728000; + } +} \ No newline at end of file diff --git a/dsql-app/docker/mariadb/.bash_history b/dsql-app/docker/mariadb/.bash_history new file mode 100644 index 0000000..7bcae94 --- /dev/null +++ b/dsql-app/docker/mariadb/.bash_history @@ -0,0 +1 @@ +mariadb -u root -p$MARIADB_ROOT_PASSWORD diff --git a/dsql-app/docker/mariadb/load-balancer/config/template/nginx.conf b/dsql-app/docker/mariadb/load-balancer/config/template/nginx.conf new file mode 100644 index 0000000..29b9726 --- /dev/null +++ b/dsql-app/docker/mariadb/load-balancer/config/template/nginx.conf @@ -0,0 +1,46 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; +} + +stream { + upstream db_load_balancer { + least_conn; + server 172.72.0.32:3306; + # == Start More servers + # == End More servers + } + + server { + listen 3306; + proxy_pass db_load_balancer; + } +} \ No newline at end of file diff --git a/dsql-app/docker/mariadb/master/conf.d/default.cnf b/dsql-app/docker/mariadb/master/conf.d/default.cnf new file mode 100644 index 0000000..9b7c677 --- /dev/null +++ b/dsql-app/docker/mariadb/master/conf.d/default.cnf @@ -0,0 +1,22 @@ +[mysqld] +# server-id=1 +# log-bin=mysql-bin + +max_connections = 200 +# max_password_errors = 20 +# require_secure_transport = ON + +skip-networking=0 +skip-bind-address +# bind-address = 0.0.0.0 + +ssl-ca = /ssl/ca-cert.pem +ssl-cert = /ssl/server-cert.pem +ssl-key = /ssl/server-key.pem +# tls_version = TLSv1.2,TLSv1.3 + +# [mariadb] +# wait_timeout = 20 + + + diff --git a/dsql-app/docker/mariadb/slave/conf.d/default.cnf b/dsql-app/docker/mariadb/slave/conf.d/default.cnf new file mode 100644 index 0000000..8e7a44e --- /dev/null +++ b/dsql-app/docker/mariadb/slave/conf.d/default.cnf @@ -0,0 +1,12 @@ +[mysqld] +bind-address = 0.0.0.0 + +ssl-ca = /ssl/ca-cert.pem +ssl-cert = /ssl/server-cert.pem +ssl-key = /ssl/server-key.pem + +server-id=$SERVER_ID +relay-log=relay-log +master-host=172.72.0.32 +master-user=root +master-password=$DSQL_MARIADB_ROOT_PASSWORD \ No newline at end of file diff --git a/dsql-app/docker/setup/(functions)/setup-ssh.ts b/dsql-app/docker/setup/(functions)/setup-ssh.ts new file mode 100644 index 0000000..95ed03c --- /dev/null +++ b/dsql-app/docker/setup/(functions)/setup-ssh.ts @@ -0,0 +1,24 @@ +import { $ } from "bun"; +import fs from "node:fs"; +import execute from "../(utils)/execute"; + +export default async function setupSSH() { + console.log("Generating SSH keys ..."); + + const KEY_NAME = "dsql"; + const OUTPUT_DIR = "/ssh"; + const PASSPHRASE = ""; + + execute(`mkdir -p "${OUTPUT_DIR}"`); + + const KEY_PATH = `${OUTPUT_DIR}/${KEY_NAME}`; + + if (!fs.existsSync(KEY_PATH)) { + console.log("Generating SSH keypair..."); + execute( + `ssh-keygen -t rsa -b 4096 -f "${KEY_PATH}" -N "${PASSPHRASE}" -q` + ); + } + + console.log("SSH keys Setup Complete!"); +} diff --git a/dsql-app/docker/setup/(functions)/setup-ssl.ts b/dsql-app/docker/setup/(functions)/setup-ssl.ts new file mode 100644 index 0000000..accad46 --- /dev/null +++ b/dsql-app/docker/setup/(functions)/setup-ssl.ts @@ -0,0 +1,77 @@ +import { $ } from "bun"; +import fs from "node:fs"; +import execute from "../(utils)/execute"; + +export default async function setupSSL() { + console.log("Generating SSL Files ..."); + + const CA_CERT_FILE = "/ssl/ca-cert.pem"; + const CA_KEY_FILE = "/ssl/ca-key.pem"; + const SERVER_CERT_FILE = "/ssl/server-cert.pem"; + const SERVER_KEY_FILE = "/ssl/server-key.pem"; + + if (!fs.existsSync("/app/ssl")) { + fs.mkdirSync("/app/ssl", { recursive: true }); + } + + if (!fs.existsSync("/app/public/documents/ssl/")) { + fs.mkdirSync("/app/public/documents/ssl/", { recursive: true }); + } + + $.cwd("/ssl"); + + if (!fs.existsSync(CA_CERT_FILE) || !fs.existsSync(CA_KEY_FILE)) { + console.log("Generating SSL Files ..."); + + execute(`rm -Rf /ssl/*`); + execute(`openssl genrsa 2048 >ca-key.pem`, { cwd: "/ssl" }); + execute( + `openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem -subj "/C=/ST=/L=/O=/CN=MariaDB admin"`, + { cwd: "/ssl" } + ); + execute( + `openssl req -newkey rsa:2048 -days 365000 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=/ST=/L=/O=/CN=MariaDB server"`, + { cwd: "/ssl" } + ); + execute(`openssl rsa -in server-key.pem -out server-key.pem`, { + cwd: "/ssl", + }); + execute( + `openssl x509 -req -in server-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem`, + { cwd: "/ssl" } + ); + execute( + `openssl req -newkey rsa:2048 -days 365000 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=/ST=/L=/O=/CN=MariaDB user"`, + { cwd: "/ssl" } + ); + execute(`openssl rsa -in client-key.pem -out client-key.pem`, { + cwd: "/ssl", + }); + execute( + `openssl x509 -req -in client-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem`, + { cwd: "/ssl" } + ); + } + + execute(`chmod 755 /ssl`); + execute(`chmod 644 /ssl/\*.pem`); + + execute(`rm -Rf /app/ssl/\*`); + execute(`rm -Rf /app/public/documents/ssl/\*`); + + execute(`cp /ssl/ca-cert.pem /app/ssl/`); + // execute(`cp /ssl/client-key.pem /app/ssl/`); + // execute(`cp /ssl/client-cert.pem /app/ssl/`); + execute(`cp /ssl/ca-cert.pem /app/public/documents/ssl/`); + // execute(`cp /ssl/client-key.pem /app/public/documents/ssl/`); + // execute(`cp /ssl/client-cert.pem /app/public/documents/ssl/`); + + const LOCAL_CONFIG_DIR = "/app/jsonData/dbSchemas/users"; + + if (!fs.existsSync(LOCAL_CONFIG_DIR)) { + console.log("Creating Local Config Directory ..."); + fs.mkdirSync(LOCAL_CONFIG_DIR, { recursive: true }); + } + + console.log("SSL Files Setup Complete!"); +} diff --git a/dsql-app/docker/setup/(utils)/execute.ts b/dsql-app/docker/setup/(utils)/execute.ts new file mode 100644 index 0000000..59ae5b4 --- /dev/null +++ b/dsql-app/docker/setup/(utils)/execute.ts @@ -0,0 +1,21 @@ +import { ExecOptions, execSync, ExecSyncOptions } from "child_process"; + +export default function execute( + cmd: string, + options?: ExecSyncOptions +): string | undefined { + try { + const res = execSync(cmd, { + encoding: "utf-8", + ...options, + }); + + if (typeof res == "string") { + return res.trim(); + } else { + return undefined; + } + } catch (error) { + return undefined; + } +} diff --git a/dsql-app/docker/setup/Dockerfile b/dsql-app/docker/setup/Dockerfile new file mode 100644 index 0000000..08efc4e --- /dev/null +++ b/dsql-app/docker/setup/Dockerfile @@ -0,0 +1,10 @@ +FROM oven/bun:debian + +SHELL ["/bin/bash", "-c"] + +RUN apt-get update +RUN apt-get install -y openssl openssh-client + +WORKDIR /app/docker/setup + +ENTRYPOINT ["bun", "index.ts"] diff --git a/dsql-app/docker/setup/index.ts b/dsql-app/docker/setup/index.ts new file mode 100644 index 0000000..f74104a --- /dev/null +++ b/dsql-app/docker/setup/index.ts @@ -0,0 +1,7 @@ +import setupSSH from "./(functions)/setup-ssh"; +import setupSSL from "./(functions)/setup-ssl"; + +await setupSSL(); +await setupSSH(); + +process.exit(0); diff --git a/dsql-app/docker/setup/setup.sh b/dsql-app/docker/setup/setup.sh new file mode 100644 index 0000000..60e48d9 --- /dev/null +++ b/dsql-app/docker/setup/setup.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +CA_CERT_FILE="/ssl/ca-cert.pem" +CA_KEY_FILE="/ssl/ca-key.pem" +SERVER_CERT_FILE="/ssl/server-cert.pem" +SERVER_KEY_FILE="/ssl/server-key.pem" + +if [ ! -d "/app/ssl" ]; then + mkdir -p "/app/ssl" +fi + +if [ ! -d "/app/public/documents/ssl/" ]; then + mkdir -p "/app/public/documents/ssl/" +fi + +if [ -f "$CA_CERT_FILE" ] && [ -f "$CA_KEY_FILE" ] && [ -f "$SERVER_CERT_FILE" ] && [ -f "$SERVER_KEY_FILE" ]; then + echo "SSL Files Present. Moving Forward >>>" +else + echo "Generating SSL Files ..." + rm -Rf /ssl/* + cd /ssl + openssl genrsa 2048 >ca-key.pem + openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem -subj "/C=/ST=/L=/O=/CN=MariaDB admin" + openssl req -newkey rsa:2048 -days 365000 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=/ST=/L=/O=/CN=MariaDB server" + openssl rsa -in server-key.pem -out server-key.pem + openssl x509 -req -in server-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem + openssl req -newkey rsa:2048 -days 365000 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=/ST=/L=/O=/CN=MariaDB user" + openssl rsa -in client-key.pem -out client-key.pem + openssl x509 -req -in client-req.pem -days 365000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem +fi + +chmod 755 /ssl +chmod 644 /ssl/*.pem + +rm -f /app/ssl/ca-cert.pem +rm -Rf /app/ssl/* +rm -Rf /app/public/documents/ssl/* + +cp /ssl/ca-cert.pem /app/ssl/ +cp /ssl/ca-cert.pem /app/public/documents/ssl/ + +LOCAL_CONFIG_DIR="/app/jsonData/dbSchemas/users" +if [ ! -d "$LOCAL_CONFIG_DIR" ]; then + echo "Creating Local Config Directory ..." + mkdir -p "$LOCAL_CONFIG_DIR" +fi + +##------------------ +# Create SSH keys +##------------------ +echo "Generating SSH keys ..." + +KEY_NAME="dsql" +OUTPUT_DIR="/ssh" +PASSPHRASE="" + +mkdir -p "$OUTPUT_DIR" + +KEY_PATH="$OUTPUT_DIR/$KEY_NAME" + +if [ ! -f "$KEY_PATH" ]; then + echo "Generating SSH keypair..." + ssh-keygen -t rsa -b 4096 -f "$KEY_PATH" -N "$PASSPHRASE" -q + + if [ $? -eq 0 ]; then + echo "SSH keypair generated successfully!" + echo "Private key: $KEY_PATH" + echo "Public key: $KEY_PATH.pub" + else + echo "Error: Failed to generate SSH keypair." + exit 1 + fi +else + echo "SSH keypair already exists. Skipping key generation." +fi + +exit 0 diff --git a/dsql-app/docker/static/conf.d/default.conf b/dsql-app/docker/static/conf.d/default.conf new file mode 100644 index 0000000..3c35e28 --- /dev/null +++ b/dsql-app/docker/static/conf.d/default.conf @@ -0,0 +1,9 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + + client_max_body_size 200M; + + root /static; +} \ No newline at end of file diff --git a/dsql-app/docker/utils/env-to-json.ts b/dsql-app/docker/utils/env-to-json.ts new file mode 100644 index 0000000..7b2f0b5 --- /dev/null +++ b/dsql-app/docker/utils/env-to-json.ts @@ -0,0 +1,19 @@ +import fs from "fs"; +import path from "path"; + +const targetPath = path.resolve( + process.cwd(), + process.argv[process.argv.length - 1] +); + +try { + let obj: any = {}; + const envFile = fs.readFileSync(targetPath, "utf-8"); + const envLinesArr = envFile.split(/\r\n/).filter((ln) => ln.match(/\=/)); + envLinesArr.forEach((ln) => { + const keyValArr = ln.split("="); + obj[keyValArr[0]] = keyValArr[1] || ""; + }); + console.log(obj); +} catch (error) {} +console.log(targetPath);