chore: cleanup of code
This commit is contained in:
parent
28a441d98a
commit
ae51fc763c
3 changed files with 264 additions and 275 deletions
86
CLAUDE.md
Normal file
86
CLAUDE.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
node index.js # Start the server (port 3000)
|
||||
npx prettier --write . # Format code
|
||||
```
|
||||
|
||||
No build step. No test suite. Restart the server to pick up changes to `index.js` or `db.js`; changes to Eta templates in `views/` are picked up live (cache is disabled).
|
||||
|
||||
Requires a `.env` file with `VAPID_PUBLIC_KEY` and `VAPID_PRIVATE_KEY` for web push.
|
||||
|
||||
## What This Is
|
||||
|
||||
Bliss is a browser-based low-code platform where users build mini web apps ("Structures") entirely through a `/workshop` UI. Each Structure has routes, templates, and SQLite databases — all stored in the main app database and executed at runtime via Node's `vm` module.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Two layers of routes
|
||||
|
||||
The Express app has two kinds of routes:
|
||||
|
||||
1. **Workshop routes** (hardcoded in `index.js`) — The `/workshop/*` editor UI for managing structures, routes, templates, databases, and files.
|
||||
2. **User routes** (dynamic, loaded from DB) — Caught by the `app.all("*")` handler at the bottom of `index.js`. At request time it matches the path against the `routes` in-memory array (rebuilt by `buildRoutes()`), finds the matching route record, and executes the user's `handler` JS string inside a sandboxed VM context.
|
||||
|
||||
`buildRoutes()` must be called after any route is created/updated to refresh the in-memory matcher array.
|
||||
|
||||
### Sandboxed execution (`bootstrapContext`)
|
||||
|
||||
User route handlers run in `vm.createContext()` with a restricted API:
|
||||
- `require('eta')` — Eta template instance scoped to the structure
|
||||
- `require('db')` — getter for named database instances. `require('db')(alias)` returns `{ library, sql }`: `sql` is the raw `better-sqlite3` instance for that db, and `library` is `module.exports` from that db's own "library" script (arbitrary JS, edited via `/workshop/:structure_id/db/:db_id/library`, run once per request with `sql` bound in its own vm context — see `bootstrapContext` in `index.js`). Handlers typically call helper functions off `library` rather than writing raw SQL inline.
|
||||
- `require('push')` — web-push library
|
||||
- `require('files')` — `{ saveFile }` helper
|
||||
- `console.log` — writes to the app's `logs` table (not stdout)
|
||||
- `fetch`, `setTimeout`, `clearTimeout`
|
||||
- `route(url)` — prefixes a URL with the structure's `route_prefix`
|
||||
|
||||
Handlers call `res.render(templateName, context)` or `ws.render(templateName, context)` to render Eta templates. These are injected onto `req`/`res` after the context is created (not available inside `bootstrapContext` itself).
|
||||
|
||||
WebSocket routes are bootstrapped once by `bootstrapWebsocketHandler` and stored in `wsRoutes`; subsequent saves only update the handler function without re-registering the ws path.
|
||||
|
||||
### Template rendering (`bootstrapTemplateWithHTMXetc`)
|
||||
|
||||
Every HTML response passes through this function, which uses Cheerio to inject HTMX, Tailwind, Hyperscript, and `bliss_inspector.js` into the `<head>`, and attaches `data-bliss-route` / `data-bliss-clone` / `data-bliss-copy` attributes to `<body>` (or to fragment children for HTMX partial swaps). These attributes drive the in-page editor overlay.
|
||||
|
||||
- `data-bliss-route` — link to the workshop editor page for whatever produced this element.
|
||||
- `data-bliss-clone` — `hx-get` target for the clone-structure modal.
|
||||
- `data-bliss-copy` — only set for GET routes, via `embedHTML(url)` (`index.js`): `<div hx-get="{url}" hx-trigger="load"></div>`. This is the actual page-nesting primitive — the inspector's 📋 button copies this snippet to the clipboard, and pasting it into any other template lazy-loads that route as a live child page via HTMX. This is how Structures compose: pages nesting pages nesting pages, each one an independently addressable, independently editable route.
|
||||
|
||||
### The inspector (`bliss_inspector.js`) and where it's headed
|
||||
|
||||
Currently the 🔍 overlay (injected into every rendered page, see above) only highlights `[data-bliss-route]` elements and offers links out to the editor, a copy-embed button, and a clone-structure modal — it does not let you edit anything in place yet.
|
||||
|
||||
The long-term goal is for the inspector to become a real-time, in-page structure editor: instead of jumping out to `/workshop/...`, you'd edit a nested page's route/template directly where it's embedded and see it update live. Two things that design needs and don't exist yet:
|
||||
- Each embedded view needs to carry its **source** (which structure/route/template produced it) — `data-bliss-route` currently only gives a link out, not enough info to edit inline.
|
||||
- Each embedded view needs to carry its **args** — the context/params it was rendered with (e.g. `res.render(template, context)`'s `context`, URL/query params) — so the in-place editor can show and modify what was actually passed in, not just the output.
|
||||
|
||||
Cross-fragment interaction (nested pages talking to each other) isn't a bespoke Bliss feature — it rides on standard HTMX/Hyperscript behavior since every nested page lives in the same DOM: `hx-swap-oob` for out-of-band updates (already used for head injection), and bubbling custom events (Hyperscript `send`/`trigger`, `hx-trigger="eventName from:body"`) for one nested page to notify siblings/ancestors.
|
||||
|
||||
### Database layer (`db.js`)
|
||||
|
||||
- Main app DB: `./dbs/0.sqlite` — stores structures, routes, templates, dbs metadata, files, logs.
|
||||
- User databases: `./dbs/{id}.sqlite` — one SQLite file per user-created DB, opened on demand and cached via LRU (max 25 connections).
|
||||
- Eta template instances are also LRU-cached per structure (max 100). The cache must be invalidated if template content changes — currently the cache is not explicitly invalidated on update, relying on the LRU eviction policy.
|
||||
- Migrations in `migrations/` are applied sequentially on startup (tracked in a `migrations` table).
|
||||
|
||||
### Structure cloning
|
||||
|
||||
`cloneStructure` (in `db.js`) deep-copies a structure's routes, templates, scaffold pages, and optionally its databases (full copy vs. aliased reference). Aliased DBs share the same SQLite file across structures.
|
||||
|
||||
### Views vs. user templates
|
||||
|
||||
- `views/` — Eta templates for the workshop UI itself (not user content).
|
||||
- User-created templates are stored as rows in the `templates` table and resolved at render time via `getTemplater()` / `readFile`.
|
||||
|
||||
### Scaffold pages
|
||||
|
||||
Non-GET routes (POST, PUT, DELETE, WS) get auto-generated scaffold pages — test forms or WebSocket test UIs — stored in `scaffold_pages`. These are served at `/workshop/:structure_id/route/:route_id/preview`.
|
||||
|
||||
### Frontend stack (workshop UI)
|
||||
|
||||
HTMX + Hyperscript + Tailwind (CDN, preflight disabled) + `bliss_inspector.js` (the in-page editor overlay). All served from `public/js/`.
|
||||
106
db.js
106
db.js
|
|
@ -44,13 +44,13 @@ function applyMigrations() {
|
|||
// | | _____| || |___ | | | |
|
||||
// |_______||_______||_______||___| |_|
|
||||
|
||||
function createUser(db, username, hashedPassword) {
|
||||
function createUser(username, hashedPassword) {
|
||||
return db
|
||||
.prepare("INSERT INTO users (username, password) VALUES (?, ?)")
|
||||
.run(username, hashedPassword).lastInsertRowid;
|
||||
}
|
||||
|
||||
function getUser(db, username) {
|
||||
function getUser(username) {
|
||||
return db.prepare("SELECT * from users where username = ?").get(username);
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ function getDbInstance(dbId) {
|
|||
return dbInstance;
|
||||
}
|
||||
|
||||
function getAllRoutes(db) {
|
||||
function getAllRoutes() {
|
||||
return db
|
||||
.prepare(
|
||||
`
|
||||
|
|
@ -100,7 +100,7 @@ function getTemplater(structId) {
|
|||
return path;
|
||||
};
|
||||
etaInstance.readFile = function (templateAlias) {
|
||||
return getTemplateContentByName(db, structId, templateAlias).content;
|
||||
return getTemplateContentByName(structId, templateAlias).content;
|
||||
};
|
||||
templateCache.set(structId, etaInstance);
|
||||
}
|
||||
|
|
@ -108,15 +108,15 @@ function getTemplater(structId) {
|
|||
return etaInstance;
|
||||
}
|
||||
|
||||
function getStructures(db) {
|
||||
function getStructures() {
|
||||
return db.prepare("SELECT * from structures").all();
|
||||
}
|
||||
|
||||
function getStructure(db, id) {
|
||||
function getStructure(id) {
|
||||
return db.prepare("SELECT * from structures where ID = ?").get(id);
|
||||
}
|
||||
|
||||
function createStructure(db, name, userId) {
|
||||
function createStructure(name, userId) {
|
||||
const stmt = db.prepare(
|
||||
"INSERT INTO structures (name, user_id) VALUES (?, ?)",
|
||||
);
|
||||
|
|
@ -124,7 +124,7 @@ function createStructure(db, name, userId) {
|
|||
return info.lastInsertRowid; // Returns the structure_id of the newly created structure
|
||||
}
|
||||
|
||||
function createRoute(db, verb, path, structureId, handler) {
|
||||
function createRoute(verb, path, structureId, handler) {
|
||||
path = encodeURI(path);
|
||||
|
||||
const stmt = db.prepare(
|
||||
|
|
@ -134,14 +134,13 @@ function createRoute(db, verb, path, structureId, handler) {
|
|||
const routeId = info.lastInsertRowid; // Get the newly created route ID
|
||||
|
||||
if (verb !== 'GET') {
|
||||
createScaffoldPage(db, routeId);
|
||||
createScaffoldPage(routeId);
|
||||
}
|
||||
|
||||
return routeId; // Returns the route_id of the newly created route
|
||||
}
|
||||
|
||||
function getRoutes(db, structureId) {
|
||||
const query = `
|
||||
const ROUTE_SELECT = `
|
||||
SELECT
|
||||
r.*,
|
||||
s.route_prefix,
|
||||
|
|
@ -154,40 +153,21 @@ function getRoutes(db, structureId) {
|
|||
LEFT JOIN scaffold_pages sp ON r.id = sp.route_id
|
||||
LEFT JOIN scaffold_params sp2 ON r.id = sp2.route_id
|
||||
LEFT JOIN structures s ON r.structure_id = s.id
|
||||
WHERE r.structure_id = ?
|
||||
ORDER BY sp.created_at DESC, sp2.created_at DESC
|
||||
`;
|
||||
|
||||
return db.prepare(query).all(structureId);
|
||||
function getRoutes(structureId) {
|
||||
return db
|
||||
.prepare(`${ROUTE_SELECT} WHERE r.structure_id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC`)
|
||||
.all(structureId);
|
||||
}
|
||||
|
||||
function getRoute(db, routeId) {
|
||||
const query = `
|
||||
SELECT
|
||||
r.*,
|
||||
s.route_prefix,
|
||||
sp.id AS scaffold_page_id,
|
||||
sp.content AS scaffold_page_content,
|
||||
sp2.id AS scaffold_params_id,
|
||||
sp2.query_params,
|
||||
sp2.url_params
|
||||
FROM routes r
|
||||
LEFT JOIN scaffold_pages sp ON r.id = sp.route_id
|
||||
LEFT JOIN scaffold_params sp2 ON r.id = sp2.route_id
|
||||
LEFT JOIN structures s ON r.structure_id = s.id
|
||||
WHERE r.id = ?
|
||||
ORDER BY sp.created_at DESC, sp2.created_at DESC
|
||||
LIMIT 1
|
||||
`;
|
||||
|
||||
return db.prepare(query).get(routeId);
|
||||
function getRoute(routeId) {
|
||||
return db
|
||||
.prepare(`${ROUTE_SELECT} WHERE r.id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC LIMIT 1`)
|
||||
.get(routeId);
|
||||
}
|
||||
|
||||
function defaultEndpointForRoute(db, routeId) {
|
||||
|
||||
}
|
||||
|
||||
function updateRoute(db, route) {
|
||||
function updateRoute(route) {
|
||||
const fields = [
|
||||
"verb",
|
||||
"path",
|
||||
|
|
@ -204,7 +184,7 @@ function updateRoute(db, route) {
|
|||
db.prepare(sql).run(...values);
|
||||
}
|
||||
|
||||
function updateDb(db, appDb) {
|
||||
function updateDb(appDb) {
|
||||
const fields = ["name", "library"];
|
||||
const values = fields.map((field) => appDb[field]);
|
||||
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
||||
|
|
@ -214,7 +194,7 @@ function updateDb(db, appDb) {
|
|||
db.prepare(sql).run(...values);
|
||||
}
|
||||
|
||||
function updateStruct(db, struct) {
|
||||
function updateStruct(struct) {
|
||||
const fields = ["name", "route_prefix", "head_injection"];
|
||||
const values = fields.map((field) => struct[field]);
|
||||
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
||||
|
|
@ -224,7 +204,7 @@ function updateStruct(db, struct) {
|
|||
db.prepare(sql).run(...values);
|
||||
}
|
||||
|
||||
function updateTemplate(db, template) {
|
||||
function updateTemplate(template) {
|
||||
const fields = ["content", "name", "test_object"];
|
||||
const values = fields.map((field) => template[field]);
|
||||
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
||||
|
|
@ -234,17 +214,17 @@ function updateTemplate(db, template) {
|
|||
db.prepare(sql).run(...values);
|
||||
}
|
||||
|
||||
function getTemplates(db, structureId) {
|
||||
function getTemplates(structureId) {
|
||||
return db
|
||||
.prepare("SELECT * from templates where structure_id = ?")
|
||||
.all(structureId);
|
||||
}
|
||||
|
||||
function getTemplate(db, templateId) {
|
||||
function getTemplate(templateId) {
|
||||
return db.prepare("SELECT * from templates where id = ?").get(templateId);
|
||||
}
|
||||
|
||||
function getTemplateContentByName(db, structId, name) {
|
||||
function getTemplateContentByName(structId, name) {
|
||||
return db
|
||||
.prepare(
|
||||
"SELECT content from templates where structure_id = ? AND name = ?",
|
||||
|
|
@ -252,7 +232,7 @@ function getTemplateContentByName(db, structId, name) {
|
|||
.get(structId, name);
|
||||
}
|
||||
|
||||
function createTemplate(db, structureId, name, content, testObjectString) {
|
||||
function createTemplate(structureId, name, content, testObjectString) {
|
||||
return db
|
||||
.prepare(
|
||||
"INSERT INTO templates (structure_id, name, content, test_object) VALUES (?, ?, ?, ?)",
|
||||
|
|
@ -260,7 +240,7 @@ function createTemplate(db, structureId, name, content, testObjectString) {
|
|||
.run(structureId, name, content, testObjectString).lastInsertRowid;
|
||||
}
|
||||
|
||||
function getDbsForStructure(db, structureId) {
|
||||
function getDbsForStructure(structureId) {
|
||||
return db
|
||||
.prepare(
|
||||
`SELECT
|
||||
|
|
@ -280,7 +260,7 @@ function getDbsForStructure(db, structureId) {
|
|||
.all(structureId);
|
||||
}
|
||||
|
||||
function getDb(db, dbId) {
|
||||
function getDb(dbId) {
|
||||
return db
|
||||
.prepare(
|
||||
`SELECT *
|
||||
|
|
@ -291,7 +271,7 @@ function getDb(db, dbId) {
|
|||
.get(dbId);
|
||||
}
|
||||
|
||||
function getDbForStructure(db, structureId, dbId) {
|
||||
function getDbForStructure(structureId, dbId) {
|
||||
return db
|
||||
.prepare(
|
||||
`SELECT *
|
||||
|
|
@ -304,7 +284,7 @@ function getDbForStructure(db, structureId, dbId) {
|
|||
.get(structureId, dbId);
|
||||
}
|
||||
|
||||
function createDb(db, structId, name) {
|
||||
function createDb(structId, name) {
|
||||
const transaction = db.transaction(() => {
|
||||
const insertDbStmt = db.prepare(`
|
||||
INSERT INTO dbs (name, structure_id)
|
||||
|
|
@ -328,7 +308,7 @@ function createDb(db, structId, name) {
|
|||
return transaction();
|
||||
}
|
||||
|
||||
function attachDb(db, structId, dbId, alias) {
|
||||
function attachDb(structId, dbId, alias) {
|
||||
const insertStructureDbStmt = db.prepare(`
|
||||
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
||||
VALUES (?, ?, ?)
|
||||
|
|
@ -336,7 +316,7 @@ function attachDb(db, structId, dbId, alias) {
|
|||
insertStructureDbStmt.run(dbId, structId, alias);
|
||||
}
|
||||
|
||||
function getFilesForStruct(db, structureId) {
|
||||
function getFilesForStruct(structureId) {
|
||||
let test = db
|
||||
.prepare("SELECT * FROM files WHERE structure_id = ? ORDER BY id DESC")
|
||||
.all(structureId);
|
||||
|
|
@ -344,11 +324,11 @@ function getFilesForStruct(db, structureId) {
|
|||
return test;
|
||||
}
|
||||
|
||||
function getFile(db, fileId) {
|
||||
function getFile(fileId) {
|
||||
return db.prepare("SELECT * FROM files WHERE id = ?").get(fileId);
|
||||
}
|
||||
|
||||
function createFile(db, structure_id, name, filePath, mime_type, mime_subtype) {
|
||||
function createFile(structure_id, name, filePath, mime_type, mime_subtype) {
|
||||
return db
|
||||
.prepare(
|
||||
"INSERT INTO files (structure_id, name, path, mime_type, mime_subtype) VALUES (?, ?, ?, ?, ?)",
|
||||
|
|
@ -468,7 +448,7 @@ function cloneStructure(
|
|||
return transaction();
|
||||
}
|
||||
|
||||
function createLog(db, structureId, routeId, content, error = false) {
|
||||
function createLog(structureId, routeId, content, error = false) {
|
||||
const stmt = db.prepare(
|
||||
"INSERT INTO logs (error, structure_id, route_id, content) VALUES (?, ?, ?, ?)",
|
||||
);
|
||||
|
|
@ -476,7 +456,7 @@ function createLog(db, structureId, routeId, content, error = false) {
|
|||
return info.lastInsertRowid; // Returns the log_id of the newly created log
|
||||
}
|
||||
|
||||
function getLogsByRoute(db, routeId) {
|
||||
function getLogsByRoute(routeId) {
|
||||
return db
|
||||
.prepare(
|
||||
"SELECT * FROM logs WHERE route_id = ? ORDER BY created_at DESC LIMIT 50",
|
||||
|
|
@ -485,7 +465,7 @@ function getLogsByRoute(db, routeId) {
|
|||
.reverse();
|
||||
}
|
||||
|
||||
function getNewLogsByRoute(db, routeId, since) {
|
||||
function getNewLogsByRoute(routeId, since) {
|
||||
return db
|
||||
.prepare(
|
||||
"SELECT * FROM logs WHERE route_id = ? AND id > ? ORDER BY created_at DESC",
|
||||
|
|
@ -493,7 +473,7 @@ function getNewLogsByRoute(db, routeId, since) {
|
|||
.all(routeId, since);
|
||||
}
|
||||
|
||||
function getMostRecentLogIdByRoute(db, routeId) {
|
||||
function getMostRecentLogIdByRoute(routeId) {
|
||||
const result = db
|
||||
.prepare("SELECT MAX(id) AS id FROM logs WHERE route_id = ?")
|
||||
.get(routeId);
|
||||
|
|
@ -505,9 +485,9 @@ function buildScaffoldUrl(endpoint, urlParams, queryString) {
|
|||
return queryString ? `${populatedUrl}?${queryString}` : populatedUrl;
|
||||
}
|
||||
|
||||
function createScaffoldPage(db, routeId, content=null) {
|
||||
function createScaffoldPage(routeId, content=null) {
|
||||
if (!content) {
|
||||
const route = getRoute(db, routeId)
|
||||
const route = getRoute(routeId)
|
||||
const endpoint = buildScaffoldUrl(route.path, route.url_params, route.query_params);
|
||||
content = getDefaultScaffoldContentByVerb(endpoint, route.verb);
|
||||
console.log("gabga", content, route, endpoint)
|
||||
|
|
@ -520,14 +500,14 @@ function createScaffoldPage(db, routeId, content=null) {
|
|||
return info.lastInsertRowid; // Returns the scaffold_page id of the newly created scaffold page
|
||||
}
|
||||
|
||||
function getLatestScaffoldPage(db, routeId) {
|
||||
function getLatestScaffoldPage(routeId) {
|
||||
const stmt = db.prepare(
|
||||
"SELECT * FROM scaffold_pages WHERE route_id = ? ORDER BY created_at DESC LIMIT 1"
|
||||
);
|
||||
return stmt.get(routeId);
|
||||
}
|
||||
|
||||
function updateScaffoldPage(db, scaffoldPage) {
|
||||
function updateScaffoldPage(scaffoldPage) {
|
||||
const fields = ["content"];
|
||||
const values = fields.map((field) => scaffoldPage[field]);
|
||||
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
||||
|
|
@ -582,7 +562,7 @@ function generateModifyForm(endpoint, verb) {
|
|||
<body>
|
||||
<h1>Test ${verb} Request</h1>
|
||||
|
||||
<form action="<%= it.route(${endpoint}) %>" method="${verb}">
|
||||
<form action="<%= it.route("${endpoint}") %>" method="${verb}">
|
||||
<!-- Add your form fields here to test ${verb.toLowerCase()} requests. -->
|
||||
<label for="testField">Test Field:</label>
|
||||
<input type="text" id="testField" name="testField" required>
|
||||
|
|
|
|||
297
index.js
297
index.js
|
|
@ -28,6 +28,11 @@ let routes = { GET: [], POST: [], PUT: [], DELETE: [] };
|
|||
const wsRoutes = {}
|
||||
const wsConnections = {}
|
||||
|
||||
const INSPECT_OPTS = { showHidden: false, depth: null, colors: false, compact: false };
|
||||
function inspectArgs(args) {
|
||||
return args.map((v) => util.inspect(v, INSPECT_OPTS)).join(" ");
|
||||
}
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use(express.static("public"));
|
||||
|
|
@ -64,7 +69,6 @@ async function saveFile(structureId, req, uploadedFile, asset=false) {
|
|||
|
||||
await uploadedFile.mv(path.join(uploadPath, name));
|
||||
let id = model.createFile(
|
||||
db,
|
||||
structureId,
|
||||
name,
|
||||
storedPath,
|
||||
|
|
@ -73,13 +77,13 @@ async function saveFile(structureId, req, uploadedFile, asset=false) {
|
|||
asset,
|
||||
);
|
||||
|
||||
let file = model.getFile(db, id);
|
||||
let file = model.getFile(id);
|
||||
file.url = prefixUrlWithHost(req, file.path);
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
function bootstrapContext(db, structureId, routeId, initContext) {
|
||||
function bootstrapContext(structureId, routeId, initContext) {
|
||||
const allDbInstances = {};
|
||||
function getDb(alias) {
|
||||
return allDbInstances[alias];
|
||||
|
|
@ -88,12 +92,12 @@ function bootstrapContext(db, structureId, routeId, initContext) {
|
|||
// todo wrap template in data-bliss-edit-template thing since ws can't take us to the editor on a component basis?? maybe...
|
||||
// for now just designing with component approach
|
||||
const eta = model.getTemplater(structureId);
|
||||
const structure = model.getStructure(db, structureId)
|
||||
const structure = model.getStructure(structureId)
|
||||
const __urlPrefix = structure.route_prefix;
|
||||
|
||||
const libs = { eta, db: getDb, push: webPush, files: { saveFile: (...args) => saveFile(structureId, ...args) } };
|
||||
|
||||
let dbs = model.getDbsForStructure(db, structureId);
|
||||
let dbs = model.getDbsForStructure(structureId);
|
||||
let context = vm.createContext({
|
||||
...initContext,
|
||||
require: function (str) {
|
||||
|
|
@ -102,21 +106,8 @@ function bootstrapContext(db, structureId, routeId, initContext) {
|
|||
module: { exports: null },
|
||||
console: {
|
||||
log: function (...content) {
|
||||
const s = content.reduce((acc, curr) => {
|
||||
console.log(curr);
|
||||
return (
|
||||
acc +
|
||||
(acc ? " " : "") +
|
||||
`${util.inspect(curr, {
|
||||
showHidden: false,
|
||||
depth: null, // `null` lets you see the full depth of the object
|
||||
colors: false, // Setting this to true uses ANSI color codes
|
||||
compact: false,
|
||||
})}`
|
||||
);
|
||||
}, "");
|
||||
|
||||
model.createLog(db, structureId, routeId, s);
|
||||
content.forEach((c) => console.log(c));
|
||||
model.createLog(structureId, routeId, inspectArgs(content));
|
||||
},
|
||||
},
|
||||
vapidPublicKey: vapidPublicKey,
|
||||
|
|
@ -124,7 +115,7 @@ function bootstrapContext(db, structureId, routeId, initContext) {
|
|||
clearTimeout: clearTimeout,
|
||||
setTimeout: setTimeout,
|
||||
route: function (url) {
|
||||
return __urlPrefix ? path.join(__urlPrefix, url) : url;
|
||||
return withPrefix(__urlPrefix, url);
|
||||
},
|
||||
});
|
||||
for (let appDb of dbs) {
|
||||
|
|
@ -141,9 +132,12 @@ function bootstrapContext(db, structureId, routeId, initContext) {
|
|||
return context;
|
||||
}
|
||||
|
||||
function withPrefix(prefix, url) {
|
||||
return prefix ? path.join(prefix, url) : url;
|
||||
}
|
||||
|
||||
function routeWithPrefix(route) {
|
||||
const prefix = route.route_prefix;
|
||||
return prefix ? path.join(prefix, route.path) : route.path;
|
||||
return withPrefix(route.route_prefix, route.path);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -151,7 +145,7 @@ function bootstrapWebsocketHandler(route) {
|
|||
// todo only expose app when running the handler, should not be available to the handler itself
|
||||
// need to move to runscript or whatever...or remove app because we can just call it using the handler?
|
||||
try {
|
||||
const context = bootstrapContext(db, route.structure_id, route.id, { app });
|
||||
const context = bootstrapContext(route.structure_id, route.id, { app });
|
||||
let handler = vm.runInContext(`${route.handler}\n\nhandler;`, context);
|
||||
|
||||
if (!wsRoutes[routeWithPrefix(route)]) {
|
||||
|
|
@ -167,17 +161,17 @@ function bootstrapWebsocketHandler(route) {
|
|||
}
|
||||
catch (e) {
|
||||
const error = e.stack ? `${e}\n\n${e.stack}` : `${e}`;
|
||||
model.createLog(db, route.structure_id, route.id, error, true);
|
||||
model.createLog(route.structure_id, route.id, error, true);
|
||||
}
|
||||
})
|
||||
}
|
||||
ws.render = (template, context) => {
|
||||
context = context || {};
|
||||
context.route = function (url) {
|
||||
return __urlPrefix ? path.join(__urlPrefix, url) : url;
|
||||
};
|
||||
const eta = model.getTemplater(route.structure_id);
|
||||
const structure = model.getStructure(db, route.structure_id);
|
||||
const structure = model.getStructure(route.structure_id);
|
||||
context.route = function (url) {
|
||||
return withPrefix(structure.route_prefix, url);
|
||||
};
|
||||
|
||||
ws.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
|
|
@ -202,14 +196,14 @@ function bootstrapWebsocketHandler(route) {
|
|||
return handler(ws, req)
|
||||
} catch (e) {
|
||||
const error = e.stack ? `${e}\n\n${e.stack}` : `${e}`;
|
||||
model.createLog(db, route.structure_id, route.id, error, true);
|
||||
model.createLog(route.structure_id, route.id, error, true);
|
||||
}
|
||||
}
|
||||
model.updateRoute(db, { ...route, error: null });
|
||||
model.updateRoute({ ...route, error: null });
|
||||
} catch (e) {
|
||||
const error = e.stack ? `${e}\n\n${e.stack}` : `${e}`;
|
||||
model.createLog(db, route.structure_id, route.id, error, true);
|
||||
model.updateRoute(db, { ...route, error: e.stack });
|
||||
model.createLog(route.structure_id, route.id, error, true);
|
||||
model.updateRoute({ ...route, error: e.stack });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +215,7 @@ function buildRoutes() {
|
|||
DELETE: [],
|
||||
};
|
||||
|
||||
for (let route of model.getAllRoutes(db)) {
|
||||
for (let route of model.getAllRoutes()) {
|
||||
const p = routeWithPrefix(route);
|
||||
if (route.verb == "WS") {
|
||||
bootstrapWebsocketHandler(route);
|
||||
|
|
@ -251,7 +245,7 @@ app.post("/register", async (req, res) => {
|
|||
try {
|
||||
const { username, password } = req.body;
|
||||
const hashedPassword = await bcrypt.hash(password, 10); // 10 is the saltRounds
|
||||
userId = model.createUser(db, username, hashedPassword);
|
||||
const userId = model.createUser(username, hashedPassword);
|
||||
req.session.userId = userId;
|
||||
res.redirect("/workshop");
|
||||
} catch (e) {
|
||||
|
|
@ -261,7 +255,7 @@ app.post("/register", async (req, res) => {
|
|||
|
||||
app.post("/login", async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const user = model.getUser(db, username);
|
||||
const user = model.getUser(username);
|
||||
|
||||
if (user && (await bcrypt.compare(password, user.password))) {
|
||||
req.session.userId = user.id;
|
||||
|
|
@ -386,19 +380,19 @@ function bootstrapTemplateWithHTMXetc(
|
|||
}
|
||||
|
||||
app.post("/workshop", (req, res) => {
|
||||
let structId = model.createStructure(db, req.body.name);
|
||||
let structId = model.createStructure(req.body.name);
|
||||
return res.redirect("/workshop/" + structId);
|
||||
});
|
||||
|
||||
app.get("/workshop", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/index", {
|
||||
structures: model.getStructures(db),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/index", {
|
||||
structures: model.getStructures(),
|
||||
});
|
||||
});
|
||||
|
||||
function renderWorkshop(res, template, context) {
|
||||
res.send(bootstrapTemplateWithHTMXetc(eta.render(template, context)));
|
||||
}
|
||||
|
||||
function smartRedirect(req, res, redirectUrl) {
|
||||
if (req.headers["hx-request"]) {
|
||||
|
|
@ -409,21 +403,17 @@ function smartRedirect(req, res, redirectUrl) {
|
|||
}
|
||||
}
|
||||
|
||||
function sidebarStuff(db, structId) {
|
||||
function sidebarStuff(structId) {
|
||||
return {
|
||||
structure: model.getStructure(db, structId),
|
||||
routes: model.getRoutes(db, structId),
|
||||
templates: model.getTemplates(db, structId),
|
||||
dbs: model.getDbsForStructure(db, structId),
|
||||
structure: model.getStructure(structId),
|
||||
routes: model.getRoutes(structId),
|
||||
templates: model.getTemplates(structId),
|
||||
dbs: model.getDbsForStructure(structId),
|
||||
};
|
||||
}
|
||||
|
||||
app.get("/workshop/:structure_id", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/editor", sidebarStuff(db, req.params.structure_id)),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/editor", sidebarStuff(req.params.structure_id));
|
||||
});
|
||||
|
||||
app.post("/workshop/:structure_id/clone", (req, res) => {
|
||||
|
|
@ -447,34 +437,29 @@ app.post("/workshop/:structure_id/clone", (req, res) => {
|
|||
});
|
||||
|
||||
app.post("/workshop/:structure_id/db", (req, res) => {
|
||||
const dbId = model.createDb(db, req.params.structure_id, req.body.name);
|
||||
const dbId = model.createDb(req.params.structure_id, req.body.name);
|
||||
const redirectUrl = `/workshop/${req.params.structure_id}/db/${dbId}`;
|
||||
return smartRedirect(req, res, redirectUrl);
|
||||
});
|
||||
|
||||
app.post("/workshop/:structure_id/db/attach", (req, res) => {
|
||||
model.attachDb(db, req.params.structure_id, req.body.db_id, req.body.alias);
|
||||
model.attachDb(req.params.structure_id, req.body.db_id, req.body.alias);
|
||||
const redirectUrl = `/workshop/${req.params.structure_id}/db/${req.body.db_id}`;
|
||||
return smartRedirect(req, res, redirectUrl);
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/db/:db_id", (req, res) => {
|
||||
const structdb = model.getDbForStructure(
|
||||
db,
|
||||
req.params.structure_id,
|
||||
req.params.db_id,
|
||||
);
|
||||
if (!structdb) {
|
||||
return res.send("uh oh");
|
||||
}
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/db_garden", {
|
||||
return renderWorkshop(res, "workshop/db_garden", {
|
||||
db: structdb,
|
||||
...sidebarStuff(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
...sidebarStuff(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/workshop/:structure_id/route", (req, res) => {
|
||||
|
|
@ -494,7 +479,6 @@ app.post("/workshop/:structure_id/route", (req, res) => {
|
|||
p = p.substring(0, p.length - 1);
|
||||
}
|
||||
const route = model.createRoute(
|
||||
db,
|
||||
req.body.verb,
|
||||
p[0] == "/" ? p.substring(1) : "/" + p,
|
||||
req.params.structure_id,
|
||||
|
|
@ -510,12 +494,12 @@ app.post("/workshop/:structure_id/route", (req, res) => {
|
|||
});
|
||||
|
||||
app.put("/workshop/:structure_id/route/:route_id", (req, res) => {
|
||||
const route = model.getRoute(db, req.params.route_id);
|
||||
model.updateRoute(db, { ...route, ...req.body });
|
||||
const route = model.getRoute(req.params.route_id);
|
||||
model.updateRoute({ ...route, ...req.body });
|
||||
if (req.body.scaffold_page) {
|
||||
// TODO: make this send over scaffold page id too someday
|
||||
const latestPage = model.getLatestScaffoldPage(db, req.params.route_id)
|
||||
model.updateScaffoldPage(db, { ...latestPage, content: req.body.scaffold_page })
|
||||
const latestPage = model.getLatestScaffoldPage(req.params.route_id)
|
||||
model.updateScaffoldPage({ ...latestPage, content: req.body.scaffold_page })
|
||||
}
|
||||
if (route.verb == "WS") {
|
||||
bootstrapWebsocketHandler({ ...route, ...req.body })
|
||||
|
|
@ -526,9 +510,9 @@ app.put("/workshop/:structure_id/route/:route_id", (req, res) => {
|
|||
app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
||||
try {
|
||||
let dbId = req.params.db_id;
|
||||
let appDb = model.getDb(db, dbId);
|
||||
model.updateDb(db, { ...appDb, library: req.body.library });
|
||||
appDb = model.getDb(db, dbId);
|
||||
let appDb = model.getDb(dbId);
|
||||
model.updateDb({ ...appDb, library: req.body.library });
|
||||
appDb = model.getDb(dbId);
|
||||
|
||||
let dbInstance = model.getDbInstance(dbId);
|
||||
let capturedOutput = [];
|
||||
|
|
@ -536,20 +520,11 @@ app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
|||
module: { exports: null },
|
||||
sql: dbInstance,
|
||||
console: {
|
||||
log: (...args) => capturedOutput.push(args.join(" ")),
|
||||
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
||||
},
|
||||
});
|
||||
let evaledCode = vm.runInContext(appDb.library, context);
|
||||
let stdout = capturedOutput
|
||||
.map((v) => {
|
||||
return util.inspect(v, {
|
||||
showHidden: false,
|
||||
depth: null, // `null` lets you see the full depth of the object
|
||||
colors: false, // Setting this to true uses ANSI color codes
|
||||
compact: false,
|
||||
});
|
||||
})
|
||||
.join("\n");
|
||||
let stdout = capturedOutput.join("\n");
|
||||
return res.send(`${stdout}\n\n> ${evaledCode}`.trim());
|
||||
} catch (e) {
|
||||
return res.send(`${e}\n\n${e.stack}`);
|
||||
|
|
@ -559,26 +534,14 @@ app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
|||
app.post("/workshop/:structure_id/db/:db_id/repl", (req, res) => {
|
||||
try {
|
||||
let dbId = req.params.db_id;
|
||||
let appDb = model.getDb(db, dbId);
|
||||
let appDb = model.getDb(dbId);
|
||||
let dbInstance = model.getDbInstance(dbId);
|
||||
let capturedOutput = [];
|
||||
let context = vm.createContext({
|
||||
module: { exports: null },
|
||||
sql: dbInstance,
|
||||
console: {
|
||||
log: (...args) =>
|
||||
capturedOutput.push(
|
||||
args
|
||||
.map((v) => {
|
||||
return util.inspect(v, {
|
||||
showHidden: false,
|
||||
depth: null, // `null` lets you see the full depth of the object
|
||||
colors: false, // Setting this to true uses ANSI color codes
|
||||
compact: false,
|
||||
});
|
||||
})
|
||||
.join(" "),
|
||||
),
|
||||
log: (...args) => capturedOutput.push(inspectArgs(args)),
|
||||
},
|
||||
});
|
||||
const libraryScript = new vm.Script(appDb.library);
|
||||
|
|
@ -597,7 +560,6 @@ app.post("/workshop/:structure_id/template", (req, res) => {
|
|||
let name = req.body.name;
|
||||
|
||||
const template = model.createTemplate(
|
||||
db,
|
||||
req.params.structure_id,
|
||||
name,
|
||||
"<div>henlo <%= it.name %></div>",
|
||||
|
|
@ -616,8 +578,8 @@ app.put("/workshop/:structure_id/template/:template_id", (req, res) => {
|
|||
let content = req.body.content;
|
||||
let test_object = req.body.test_object;
|
||||
|
||||
model.updateTemplate(db, {
|
||||
...model.getTemplate(db, id),
|
||||
model.updateTemplate({
|
||||
...model.getTemplate(id),
|
||||
content,
|
||||
test_object,
|
||||
});
|
||||
|
|
@ -626,28 +588,24 @@ app.put("/workshop/:structure_id/template/:template_id", (req, res) => {
|
|||
});
|
||||
|
||||
app.get("/workshop/:structure_id/template/:template_id", (req, res) => {
|
||||
const template = model.getTemplate(db, req.params.template_id);
|
||||
const template = model.getTemplate(req.params.template_id);
|
||||
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/template", {
|
||||
return renderWorkshop(res, "workshop/template", {
|
||||
template: template,
|
||||
...sidebarStuff(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
...sidebarStuff(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/template/:template_id/preview", (req, res) => {
|
||||
const template = model.getTemplate(db, req.params.template_id);
|
||||
const struct = model.getStructure(db, req.params.structure_id);
|
||||
const template = model.getTemplate(req.params.template_id);
|
||||
const struct = model.getStructure(req.params.structure_id);
|
||||
const eta = model.getTemplater(req.params.structure_id);
|
||||
|
||||
const context = vm.createContext({ it: null });
|
||||
vm.runInContext(template.test_object, context);
|
||||
|
||||
context.it.route = function (url) {
|
||||
return struct.route_prefix ? path.join(struct.route_prefix, url) : url;
|
||||
return withPrefix(struct.route_prefix, url);
|
||||
};
|
||||
|
||||
return res.send(
|
||||
|
|
@ -663,11 +621,8 @@ app.get("/workshop/:structure_id/template/:template_id/preview", (req, res) => {
|
|||
});
|
||||
|
||||
app.get("/workshop/:structure_id/route/:id", (req, res) => {
|
||||
const route = model.getRoute(db, req.params.id);
|
||||
let routePrefix = model.getStructure(
|
||||
db,
|
||||
req.params.structure_id,
|
||||
).route_prefix;
|
||||
const route = model.getRoute(req.params.id);
|
||||
let routePrefix = model.getStructure(req.params.structure_id).route_prefix;
|
||||
let previewUrl = null;
|
||||
|
||||
if (route["verb"] == "GET" ) {
|
||||
|
|
@ -679,26 +634,22 @@ app.get("/workshop/:structure_id/route/:id", (req, res) => {
|
|||
previewUrl = `/workshop/${req.params.structure_id}/route/${req.params.id}/preview`;
|
||||
}
|
||||
|
||||
template = route["verb"] == "GET" ? "workshop/route" : "workshop/cud_route";
|
||||
const template = route["verb"] == "GET" ? "workshop/route" : "workshop/cud_route";
|
||||
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render(template, {
|
||||
return renderWorkshop(res, template, {
|
||||
route: route,
|
||||
previewUrl: previewUrl,
|
||||
...sidebarStuff(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
...sidebarStuff(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/route/:route_id/preview", (req, res) => {
|
||||
const route = model.getRoute(db, req.params.route_id);
|
||||
const struct = model.getStructure(db, req.params.structure_id);
|
||||
const route = model.getRoute(req.params.route_id);
|
||||
const struct = model.getStructure(req.params.structure_id);
|
||||
|
||||
const it = {
|
||||
route: function (url) {
|
||||
return struct.route_prefix ? path.join(struct.route_prefix, url) : url;
|
||||
return withPrefix(struct.route_prefix, url);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -718,23 +669,19 @@ app.get("/workshop/:structure_id/route/:route_id/logs", (req, res) => {
|
|||
const since = req.query.since;
|
||||
let logs = [];
|
||||
if (since != undefined) {
|
||||
logs = model.getNewLogsByRoute(db, req.params.route_id, since);
|
||||
logs = model.getNewLogsByRoute(req.params.route_id, since);
|
||||
} else {
|
||||
logs = model.getLogsByRoute(db, req.params.route_id);
|
||||
logs = model.getLogsByRoute(req.params.route_id);
|
||||
}
|
||||
|
||||
const lastId = model.getMostRecentLogIdByRoute(db, req.params.route_id);
|
||||
const lastId = model.getMostRecentLogIdByRoute(req.params.route_id);
|
||||
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/logs", {
|
||||
return renderWorkshop(res, "workshop/logs", {
|
||||
logs: logs,
|
||||
structId: req.params.structure_id,
|
||||
routeId: req.params.route_id,
|
||||
since: lastId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// POST route to handle file upload
|
||||
|
|
@ -766,34 +713,26 @@ function prefixUrlWithHost(req, path) {
|
|||
app.get("/workshop/:structure_id/files", (req, res) => {
|
||||
const { structure_id } = req.params;
|
||||
|
||||
const files = model.getFilesForStruct(db, structure_id);
|
||||
const files = model.getFilesForStruct(structure_id);
|
||||
files.map((it) => {
|
||||
it.url = prefixUrlWithHost(req, it.path);
|
||||
});
|
||||
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/files", {
|
||||
return renderWorkshop(res, "workshop/files", {
|
||||
files,
|
||||
...sidebarStuff(db, structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
...sidebarStuff(structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/settings", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/settings", {
|
||||
...sidebarStuff(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/settings", {
|
||||
...sidebarStuff(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.put("/workshop/:structure_id/settings", (req, res) => {
|
||||
let structId = req.params.structure_id;
|
||||
let struct = model.getStructure(db, structId);
|
||||
let struct = model.getStructure(structId);
|
||||
let routePrefix = req.body.route_prefix;
|
||||
|
||||
if (routePrefix[0] != "/") {
|
||||
|
|
@ -803,59 +742,43 @@ app.put("/workshop/:structure_id/settings", (req, res) => {
|
|||
routePrefix = routePrefix.substring(0, routePrefix.length - 1);
|
||||
}
|
||||
|
||||
model.updateStruct(db, {
|
||||
model.updateStruct({
|
||||
...struct,
|
||||
route_prefix: routePrefix,
|
||||
head_injection: req.body.head_injection,
|
||||
});
|
||||
struct = model.getStructure(db, structId);
|
||||
struct = model.getStructure(structId);
|
||||
|
||||
res.send("success!");
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/new_template_modal", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/new_template_modal", {
|
||||
structure: model.getStructure(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/new_template_modal", {
|
||||
structure: model.getStructure(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/new_route_modal", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/new_route_modal", {
|
||||
structure: model.getStructure(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/new_route_modal", {
|
||||
structure: model.getStructure(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/new_db_modal", (req, res) => {
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/new_db_modal", {
|
||||
structure: model.getStructure(db, req.params.structure_id),
|
||||
}),
|
||||
),
|
||||
);
|
||||
return renderWorkshop(res, "workshop/new_db_modal", {
|
||||
structure: model.getStructure(req.params.structure_id),
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/workshop/:structure_id/clone_modal", (req, res) => {
|
||||
const structure = model.getStructure(db, req.params.structure_id);
|
||||
const dbs = model.getDbsForStructure(db, req.params.structure_id);
|
||||
const structure = model.getStructure(req.params.structure_id);
|
||||
const dbs = model.getDbsForStructure(req.params.structure_id);
|
||||
|
||||
return res.send(
|
||||
bootstrapTemplateWithHTMXetc(
|
||||
eta.render("workshop/clone_structure_modal", {
|
||||
return renderWorkshop(res, "workshop/clone_structure_modal", {
|
||||
structure,
|
||||
dbs,
|
||||
defaultRoutePrefix: path.join(structure.route_prefix || "", "clone"),
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function embedHTML(url) {
|
||||
|
|
@ -882,13 +805,13 @@ app.all("*", async (req, res) => {
|
|||
.prepare("SELECT * FROM routes WHERE id = ?")
|
||||
.get(routeMatch.id);
|
||||
|
||||
const structure = model.getStructure(db, route.structure_id);
|
||||
const structure = model.getStructure(route.structure_id);
|
||||
const __urlPrefix = structure.route_prefix;
|
||||
|
||||
try {
|
||||
// todo: only add req res to contexst when running the handler()
|
||||
// which means moving to runscript instead of runincontext for that
|
||||
let context = bootstrapContext(db, route.structure_id, route.id, {
|
||||
let context = bootstrapContext(route.structure_id, route.id, {
|
||||
req,
|
||||
res,
|
||||
eta,
|
||||
|
|
@ -897,7 +820,7 @@ app.all("*", async (req, res) => {
|
|||
res.render = (template, context) => {
|
||||
context = context || {};
|
||||
context.route = function (url) {
|
||||
return __urlPrefix ? path.join(__urlPrefix, url) : url;
|
||||
return withPrefix(__urlPrefix, url);
|
||||
};
|
||||
const eta = model.getTemplater(route.structure_id);
|
||||
res.send(
|
||||
|
|
@ -918,7 +841,7 @@ app.all("*", async (req, res) => {
|
|||
const result = await executionScript.runInContext(context);
|
||||
} catch (e) {
|
||||
const error = e.stack ? `${e}\n\n${e.stack}` : `${e}`;
|
||||
model.createLog(db, structure.id, route.id, error, true);
|
||||
model.createLog(structure.id, route.id, error, true);
|
||||
return res.status(500).send(error);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue