From 263a9795b916d61fd737ca8ae9396bbe07d0335a Mon Sep 17 00:00:00 2001 From: metacryst Date: Sun, 28 Dec 2025 06:49:04 -0600 Subject: [PATCH] renaming, spawning forms if it does not exist, simplifying --- .gitignore | 3 +- app.js | 223 +++++++++++++--------- {ui => cave}/_/code/quill.js | 0 {ui => cave}/_/code/shared.css | 0 {ui => cave}/_/icons/logo.png | Bin {ui => cave}/components/DownloadsPanel.js | 0 {ui => cave}/components/Home.js | 0 {ui => cave}/components/HomePanel.js | 0 {ui => cave}/components/NavMenu.js | 0 {ui => cave}/index.html | 0 {ui => cave}/index.js | 0 {server => flame}/getDownloads.js | 0 {server => flame}/index.html | 0 flame/index.js | 120 ++++++++++++ flame/util.js | 40 ++++ {server => flame}/ws.js | 0 server/index.js | 137 ------------- 17 files changed, 293 insertions(+), 230 deletions(-) rename {ui => cave}/_/code/quill.js (100%) rename {ui => cave}/_/code/shared.css (100%) rename {ui => cave}/_/icons/logo.png (100%) rename {ui => cave}/components/DownloadsPanel.js (100%) rename {ui => cave}/components/Home.js (100%) rename {ui => cave}/components/HomePanel.js (100%) rename {ui => cave}/components/NavMenu.js (100%) rename {ui => cave}/index.html (100%) rename {ui => cave}/index.js (100%) rename {server => flame}/getDownloads.js (100%) rename {server => flame}/index.html (100%) create mode 100644 flame/index.js create mode 100644 flame/util.js rename {server => flame}/ws.js (100%) delete mode 100644 server/index.js diff --git a/.gitignore b/.gitignore index 8d06599..7cd58bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules package-lock.json -master.forms \ No newline at end of file +master.forms +forms/app.log \ No newline at end of file diff --git a/app.js b/app.js index 9424397..40394bf 100644 --- a/app.js +++ b/app.js @@ -1,109 +1,148 @@ import { app, BrowserWindow, globalShortcut } from 'electron'; import paths from 'path' -import Server from "./server/index.js" +import { spawn } from 'child_process' +import fs from 'fs' -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = paths.dirname(__filename); +import util from './flame/util.js' +import Server from "./flame/index.js" const WINDOW_SHORTCUTS = [ process.platform === "darwin" ? "Command+R" : "Control+R", "\\" ]; -function createWindow({onTop = false}) { - const win = new BrowserWindow({ - width: 1200, - height: 800, - frame: false, - show: false, - titleBarStyle: "customButtonsOnHover", - webPreferences: { - contextIsolation: true, - nodeIntegration: false, - webviewTag: true, - } - }); +class App { + devToolsOpened = false; - if(onTop) { - win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); // necessary for full screen - win.setAlwaysOnTop(true, 'screen-saver', 1); // necessary so it doesn't bring you back out of full screen when spawned - } - - win.loadFile('server/index.html'); - - win.on("focus", () => { - win.setVibrancy("appearance-based"); // full colors - }); - - win.on("blur", () => { - win.setVibrancy("selection"); // dims colors - }); - - win.webContents.on('did-finish-load', () => { - win.show(); - }); - - win.webContents.on('did-fail-load', (e, code, desc) => { - console.log('Webview failed:', desc); - }); - - win.on('closed', () => { - for (const key of WINDOW_SHORTCUTS) { - globalShortcut.unregister(key); - } - }); -} - -let devToolsOpened = false; - -function toggleDevTools(win) { - if (devToolsOpened) { - win.closeDevTools(); - } else { - win.openDevTools(); - } - devToolsOpened = !devToolsOpened; -} - -app.on("ready", async () => { - // await Forms.init(); - // createWindow({onTop: false}); - new Server() - - globalShortcut.register('CommandOrControl+Shift+Space', () => { - createWindow({onTop: true}); - }); - - // Register global shortcuts - app.on("browser-window-focus", () => { - const focused = BrowserWindow.getFocusedWindow(); - if (!focused) return; - - // Reload - globalShortcut.register(WINDOW_SHORTCUTS[0], () => { - focused.reload(); + createWindow({onTop = false}) { + const win = new BrowserWindow({ + width: 1200, + height: 800, + frame: false, + show: false, + titleBarStyle: "customButtonsOnHover", + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + webviewTag: true, + } }); - // Devtools - globalShortcut.register(WINDOW_SHORTCUTS[1], () => { - toggleDevTools(focused); + if(onTop) { + win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }); // necessary for full screen + win.setAlwaysOnTop(true, 'screen-saver', 1); // necessary so it doesn't bring you back out of full screen when spawned + } + + win.loadFile('flame/index.html'); + + win.on("focus", () => { + win.setVibrancy("appearance-based"); // full colors }); - }); - app.on("browser-window-blur", () => { - for (const key of WINDOW_SHORTCUTS) { - globalShortcut.unregister(key); - } - }); -}); + win.on("blur", () => { + win.setVibrancy("selection"); // dims colors + }); -app.on("activate", () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow({onTop: false}); + win.webContents.on('did-finish-load', () => { + win.show(); + }); + + win.webContents.on('did-fail-load', (e, code, desc) => { + console.log('Webview failed:', desc); + }); + + win.on('closed', () => { + for (const key of WINDOW_SHORTCUTS) { + globalShortcut.unregister(key); + } + }); } -}); -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') app.quit(); -}); \ No newline at end of file + spawnFlame(spawnForms) { + new Server(spawnForms) + } + + async spawnForms() { + const logPath = paths.join(util.FORMS_PATH, "app.log"); + const out = fs.openSync(logPath, "a"); + const err = fs.openSync(logPath, "a"); + + const child = spawn( + process.execPath, + [paths.join(util.FORMS_PATH, "kernel/kernel.js")], + { + detached: true, + stdio: [ + "ignore", + out, + err + ], + cwd: process.cwd(), + env: process.env + } + ); + + child.unref(); + } + + toggleDevTools(win) { + if (this.devToolsOpened) { + win.closeDevTools(); + } else { + win.openDevTools(); + } + this.devToolsOpened = !this.devToolsOpened; + } + + registerShortcuts() { + globalShortcut.register('CommandOrControl+Shift+Space', function CreateWindow() { + this.createWindow({onTop: true}); + }); + + app.on("browser-window-focus", function RegisterWindowOnFocus() { + const focused = BrowserWindow.getFocusedWindow(); + if (!focused) return; + + let WINDOW_SHORTCUTS = [ + process.platform === "darwin" ? "Command+R" : "Control+R", + "\\" + ]; + + // Reload + globalShortcut.register(WINDOW_SHORTCUTS[0], () => { + focused.reload(); + }); + + // Devtools + globalShortcut.register(WINDOW_SHORTCUTS[1], () => { + toggleDevTools(focused); + }); + }); + + app.on("browser-window-blur", function UnregisterWindowOnUnfocus() { + for (const key of WINDOW_SHORTCUTS) { + globalShortcut.unregister(key); + } + }); + } + + constructor() { + app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) { + this.createWindow({onTop: false}); + } + }); + + app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit(); + }); + + app.on("ready", async () => { + this.createWindow({onTop: false}); + this.spawnFlame(this.spawnForms) + this.registerShortcuts() + }); + } +} + +new App() \ No newline at end of file diff --git a/ui/_/code/quill.js b/cave/_/code/quill.js similarity index 100% rename from ui/_/code/quill.js rename to cave/_/code/quill.js diff --git a/ui/_/code/shared.css b/cave/_/code/shared.css similarity index 100% rename from ui/_/code/shared.css rename to cave/_/code/shared.css diff --git a/ui/_/icons/logo.png b/cave/_/icons/logo.png similarity index 100% rename from ui/_/icons/logo.png rename to cave/_/icons/logo.png diff --git a/ui/components/DownloadsPanel.js b/cave/components/DownloadsPanel.js similarity index 100% rename from ui/components/DownloadsPanel.js rename to cave/components/DownloadsPanel.js diff --git a/ui/components/Home.js b/cave/components/Home.js similarity index 100% rename from ui/components/Home.js rename to cave/components/Home.js diff --git a/ui/components/HomePanel.js b/cave/components/HomePanel.js similarity index 100% rename from ui/components/HomePanel.js rename to cave/components/HomePanel.js diff --git a/ui/components/NavMenu.js b/cave/components/NavMenu.js similarity index 100% rename from ui/components/NavMenu.js rename to cave/components/NavMenu.js diff --git a/ui/index.html b/cave/index.html similarity index 100% rename from ui/index.html rename to cave/index.html diff --git a/ui/index.js b/cave/index.js similarity index 100% rename from ui/index.js rename to cave/index.js diff --git a/server/getDownloads.js b/flame/getDownloads.js similarity index 100% rename from server/getDownloads.js rename to flame/getDownloads.js diff --git a/server/index.html b/flame/index.html similarity index 100% rename from server/index.html rename to flame/index.html diff --git a/flame/index.js b/flame/index.js new file mode 100644 index 0000000..429b508 --- /dev/null +++ b/flame/index.js @@ -0,0 +1,120 @@ +import path from 'path'; +import express from 'express'; +import cors from 'cors' +import http from 'http' +import chalk from 'chalk' +import * as z from 'zod'; +import forms from 'forms' + +import { initWebSocket } from './ws.js' +import util from './util.js' + +export default class Server { + store; + + registerRoutes(router) { + router.get('/*', this.get) + return router + } + + index = async (req, res) => { + let filePath = path.join(util.CAVE_PATH, "index.html"); + res.sendFile(filePath) + } + + get = async (req, res) => { + let url = req.url + let filePath; + if(url.startsWith("/_")) { + filePath = path.join(util.CAVE_PATH, url); + } else if(url.includes("75820185")) { + filePath = path.join(util.CAVE_PATH, url.split("75820185")[1]); + } else { + filePath = path.join(util.CAVE_PATH, "index.html"); + } + + res.sendFile(filePath); + } + + async connectToForms(spawnForms) { + try { + await this.store.connect() + } + catch(e) { + try { + await spawnForms() + await this.store.connect() + } + catch(e) { + try { + let tryReconnect = async (attempts = 5, interval = 100) => { + for (let i = 0; i < attempts; i++) { + try { + return await this.store.connect(); + } catch (e) { + if (i === attempts - 1) {throw e;} + await new Promise(resolve => setTimeout(resolve, interval)); + } + } + } + await tryReconnect(5, 100) + } + catch(e) { + console.error("Could not spawn forms: ", e) + return + } + } + } + + this.store.register("log", z.toJSONSchema(z.object({ + time: z.string(), + host: z.string(), + ip: z.string(), + path: z.string(), + method: z.string() + }))) + this.store.add("log", { + time: "0100", + host: "parchment.page", + ip: "0.0.0.1", + path: "/asd", + method: "GET" + }) + } + + constructor(spawnForms) { + const app = express(); + app.use(cors({ origin: '*' })); + app.use(express.json()); + + app.use(util.logRequest); + app.use(util.logResponse); + + let router = express.Router(); + this.registerRoutes(router) + app.use('/', router); + + this.store = new forms.client() + this.connectToForms(spawnForms) + + const server = http.createServer(app); + initWebSocket(server); + + const PORT = 80; + server.listen(PORT, () => { + console.log("\n") + console.log(chalk.yellow("**************Parchment****************")) + console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`)); + console.log(chalk.yellow("***************************************")) + console.log("\n") + }); + + process.on('SIGINT', async () => { + console.log(chalk.red('Closing server...')); + console.log(chalk.green('Database connection closed.')); + process.exit(0); + }); + + Object.preventExtensions(this); + } +} \ No newline at end of file diff --git a/flame/util.js b/flame/util.js new file mode 100644 index 0000000..1858c1b --- /dev/null +++ b/flame/util.js @@ -0,0 +1,40 @@ +import moment from 'moment' +import chalk from 'chalk' +import paths from 'path' + +import { fileURLToPath } from 'url'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = paths.dirname(__filename); + +export default class util { + + static APP_PATH = paths.join(__dirname, "..") + static CAVE_PATH = paths.join(__dirname, "../cave") + static FLAME_PATH = __dirname + static FORMS_PATH = paths.join(__dirname, "../forms") + + static logRequest(req, res, next) { + const formattedDate = moment().format('M.D'); + const formattedTime = moment().format('h:mma'); + if(req.url.includes("/api/")) { + console.log(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`)); + } else { + if(req.url === "/") + console.log(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`)); + } + next(); + } + + static logResponse(req, res, next) { + const originalSend = res.send; + res.send = function (body) { + if(res.statusCode >= 400) { + console.log(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`)); + } else { + console.log(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`)); + } + originalSend.call(this, body); + }; + next(); + } +} \ No newline at end of file diff --git a/server/ws.js b/flame/ws.js similarity index 100% rename from server/ws.js rename to flame/ws.js diff --git a/server/index.js b/server/index.js deleted file mode 100644 index e1eef66..0000000 --- a/server/index.js +++ /dev/null @@ -1,137 +0,0 @@ -import path from 'path'; -import fs from 'fs/promises' -import { fileURLToPath } from 'url'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -import express from 'express'; -import cors from 'cors' -import http from 'http' -import chalk from 'chalk' -import moment from 'moment' -import * as z from "zod"; - -import { initWebSocket } from './ws.js' -import getAllDownloadsFiles from "./getDownloads.js" -import forms from "forms" - -export default class Server { - store; - UIPath = path.join(__dirname, '../ui') - - registerRoutes(router) { - router.get('/*', this.get) - return router - } - - index = async (req, res) => { - let filePath = path.join(this.UIPath, "index.html"); - res.sendFile(filePath) - return - - let html = await fs.readFile(filePath, 'utf-8'); - - let downloads = await getAllDownloadsFiles() - const jsonString = JSON.stringify(downloads, null, 2); // pretty-print optional - const scriptTag = ``; - - const headEndRegex = /<\/head\s*>/i; - - const match = html.match(headEndRegex); - const insertPos = match.index; - let result = html.slice(0, insertPos) + scriptTag + '\n' + html.slice(insertPos); - res.type('html').send(result); - } - - get = async (req, res) => { - let url = req.url - let filePath; - if(url.startsWith("/_")) { - filePath = path.join(this.UIPath, url); - } else if(url.includes("75820185")) { - filePath = path.join(this.UIPath, url.split("75820185")[1]); - } else { - filePath = path.join(this.UIPath, "index.html"); - } - - res.sendFile(filePath); - } - - logRequest(req, res, next) { - const formattedDate = moment().format('M.D'); - const formattedTime = moment().format('h:mma'); - if(req.url.includes("/api/")) { - console.log(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`)); - } else { - if(req.url === "/") - console.log(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`)); - } - next(); - } - - logResponse(req, res, next) { - const originalSend = res.send; - res.send = function (body) { - if(res.statusCode >= 400) { - console.log(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`)); - } else { - console.log(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`)); - } - originalSend.call(this, body); - }; - next(); - } - - async connectToForms() { - await this.store.connect() - this.store.register("log", z.toJSONSchema(z.object({ - time: z.string(), - host: z.string(), - ip: z.string(), - path: z.string(), - method: z.string() - }))) - this.store.add("log", { - time: "0100", - host: "parchment.page", - ip: "0.0.0.1", - path: "/asd", - method: "GET" - }) - } - - constructor() { - const app = express(); - app.use(cors({ origin: '*' })); - app.use(express.json()); - - app.use(this.logRequest); - app.use(this.logResponse); - - let router = express.Router(); - this.registerRoutes(router) - app.use('/', router); - - this.store = new forms.client() - this.connectToForms() - - const server = http.createServer(app); - initWebSocket(server); - const PORT = 80; - server.listen(PORT, () => { - console.log("\n") - console.log(chalk.yellow("**************Parchment****************")) - console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`)); - console.log(chalk.yellow("***************************************")) - console.log("\n") - }); - - process.on('SIGINT', async () => { - console.log(chalk.red('Closing server...')); - console.log(chalk.green('Database connection closed.')); - process.exit(0); - }); - - Object.preventExtensions(this); - } -} \ No newline at end of file