2024-02-24 11:12:14 -05:00
|
|
|
const fs = require("fs");
|
2024-06-02 00:39:30 -04:00
|
|
|
const vm = require("node:vm");
|
2024-02-24 11:12:14 -05:00
|
|
|
const path = require("path");
|
|
|
|
|
const express = require("express");
|
|
|
|
|
const session = require("express-session");
|
2024-06-02 10:20:01 -04:00
|
|
|
const fileUpload = require("express-fileupload");
|
2024-02-24 11:12:14 -05:00
|
|
|
const SQLiteStore = require("better-sqlite3-session-store")(session);
|
|
|
|
|
const betterSqlite3 = require("better-sqlite3");
|
|
|
|
|
const { Eta } = require("eta");
|
2024-06-02 00:39:30 -04:00
|
|
|
const { match } = require("path-to-regexp");
|
2024-02-24 11:12:14 -05:00
|
|
|
const bcrypt = require("bcrypt");
|
2024-06-02 00:39:30 -04:00
|
|
|
const cheerio = require("cheerio");
|
2024-02-03 13:22:50 -05:00
|
|
|
const app = express();
|
2024-02-24 11:12:14 -05:00
|
|
|
const bodyParser = require("body-parser");
|
2024-02-03 13:22:50 -05:00
|
|
|
const PORT = 3000;
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
let viewpath = path.join(__dirname, "views");
|
2024-06-02 00:39:30 -04:00
|
|
|
let eta = new Eta({ views: viewpath, cache: false, autoEscape: true });
|
|
|
|
|
|
|
|
|
|
const routes = { GET: [], POST: [], PUT: [], DELETE: [] };
|
2024-02-03 13:22:50 -05:00
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
app.use(bodyParser.urlencoded({ extended: true }));
|
|
|
|
|
app.use(express.static("public"));
|
2024-06-02 10:20:01 -04:00
|
|
|
app.use(fileUpload());
|
2024-02-24 11:12:14 -05:00
|
|
|
|
2024-06-04 01:21:32 -04:00
|
|
|
const db = betterSqlite3("./dbs/0.sqlite");
|
2024-06-02 00:39:30 -04:00
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
db.pragma("journal_mode = WAL");
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function getAllRoutes(db) {
|
2024-06-04 03:01:19 -04:00
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
`
|
|
|
|
|
SELECT routes.*, structures.route_prefix
|
|
|
|
|
FROM routes
|
|
|
|
|
JOIN structures ON routes.structure_id = structures.id
|
|
|
|
|
ORDER BY routes.id ASC;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.all();
|
2024-06-02 00:39:30 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
app.use(
|
|
|
|
|
session({
|
2024-02-03 13:22:50 -05:00
|
|
|
store: new SQLiteStore({ client: db, expired: { clear: true } }),
|
2024-02-24 11:12:14 -05:00
|
|
|
secret: "your secret key",
|
2024-02-03 13:22:50 -05:00
|
|
|
resave: false,
|
|
|
|
|
saveUninitialized: true,
|
2024-02-24 11:12:14 -05:00
|
|
|
cookie: { secure: false },
|
|
|
|
|
})
|
|
|
|
|
);
|
2024-02-03 13:22:50 -05:00
|
|
|
|
|
|
|
|
function applyMigrations() {
|
2024-02-24 11:12:14 -05:00
|
|
|
db.exec(`
|
2024-02-03 13:22:50 -05:00
|
|
|
CREATE TABLE IF NOT EXISTS migrations (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
filename TEXT NOT NULL,
|
|
|
|
|
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
|
|
|
);
|
|
|
|
|
`);
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
const migrationsDir = path.join(__dirname, "/migrations");
|
|
|
|
|
const migrationFiles = fs
|
|
|
|
|
.readdirSync(migrationsDir)
|
|
|
|
|
.filter((file) => file.endsWith(".sql"));
|
2024-02-04 10:11:47 -05:00
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
migrationFiles.forEach((file) => {
|
|
|
|
|
const isApplied = db
|
|
|
|
|
.prepare("SELECT filename FROM migrations WHERE filename = ?")
|
|
|
|
|
.get(file);
|
2024-02-04 10:11:47 -05:00
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
if (!isApplied) {
|
|
|
|
|
const sql = fs.readFileSync(path.join(migrationsDir, file), "utf-8");
|
|
|
|
|
db.exec(sql);
|
|
|
|
|
db.prepare("INSERT INTO migrations (filename) VALUES (?)").run(file);
|
|
|
|
|
console.log(`Migration applied: ${file}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-02-03 13:22:50 -05:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function buildRoutes() {
|
|
|
|
|
for (let route of getAllRoutes(db)) {
|
2024-06-04 03:01:19 -04:00
|
|
|
const prefix = route.route_prefix;
|
|
|
|
|
console.log(route);
|
|
|
|
|
const p = prefix ? path.join(route.route_prefix, route.path) : route.path;
|
2024-06-02 00:39:30 -04:00
|
|
|
routes[route.verb].push({
|
2024-06-04 03:01:19 -04:00
|
|
|
matcher: match(p, { decode: decodeURIComponent }),
|
2024-06-02 00:39:30 -04:00
|
|
|
id: route.id,
|
|
|
|
|
path: route.path,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-03 13:22:50 -05:00
|
|
|
applyMigrations();
|
2024-06-02 00:39:30 -04:00
|
|
|
buildRoutes();
|
2024-02-03 13:22:50 -05:00
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
// __ __ _______ _______ ______
|
|
|
|
|
// | | | || || || _ |
|
|
|
|
|
// | | | || _____|| ___|| | ||
|
|
|
|
|
// | |_| || |_____ | |___ | |_||_
|
|
|
|
|
// | ||_____ || ___|| __ |
|
|
|
|
|
// | | _____| || |___ | | | |
|
|
|
|
|
// |_______||_______||_______||___| |_|
|
|
|
|
|
|
|
|
|
|
function createUser(db, username, hashedPassword) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare("INSERT INTO users (username, password) VALUES (?, ?)")
|
2024-06-02 00:39:30 -04:00
|
|
|
.run(username, hashedPassword).lastInsertRowid;
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getUser(db, username) {
|
|
|
|
|
return db.prepare("SELECT * from users where username = ?").get(username);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.post("/register", async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { username, password } = req.body;
|
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10); // 10 is the saltRounds
|
2024-06-02 00:39:30 -04:00
|
|
|
userId = createUser(db, username, hashedPassword);
|
|
|
|
|
req.session.userId = userId;
|
2024-02-24 11:12:14 -05:00
|
|
|
res.redirect("/workshop");
|
|
|
|
|
} catch (e) {
|
|
|
|
|
res.render("auth/register.html", { error: e });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.post("/login", async (req, res) => {
|
|
|
|
|
const { username, password } = req.body;
|
|
|
|
|
const user = getUser(db, username);
|
|
|
|
|
|
|
|
|
|
if (user && (await bcrypt.compare(password, user.password))) {
|
|
|
|
|
req.session.userId = user.id;
|
|
|
|
|
return res.redirect("/");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res.send(
|
|
|
|
|
eta.render("auth/login", {
|
|
|
|
|
error: "are you sure you entered that right?",
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/register", async (req, res) => {
|
|
|
|
|
if (req.session.userId) {
|
|
|
|
|
return res.redirect("/");
|
|
|
|
|
}
|
2024-06-02 00:39:30 -04:00
|
|
|
return res.send(eta.render("auth/register", { error: null }));
|
2024-02-24 11:12:14 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/login", async (req, res) => {
|
|
|
|
|
if (req.session.userId) {
|
|
|
|
|
return res.redirect("/");
|
|
|
|
|
}
|
|
|
|
|
return res.send(eta.render("auth/login", { error: null }));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.all("/logout", async (req, res) => {
|
|
|
|
|
return req.session.destroy(() => {
|
|
|
|
|
res.redirect("/");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// _ _ _______ ______ ___ _ _______ __ __ _______ _______
|
|
|
|
|
// | | _ | || || _ | | | | || || | | || || |
|
|
|
|
|
// | || || || _ || | || | |_| || _____|| |_| || _ || _ |
|
|
|
|
|
// | || | | || |_||_ | _|| |_____ | || | | || |_| |
|
|
|
|
|
// | || |_| || __ || |_ |_____ || || |_| || ___|
|
|
|
|
|
// | _ || || | | || _ | _____| || _ || || |
|
|
|
|
|
// |__| |__||_______||___| |_||___| |_||_______||__| |__||_______||___|
|
|
|
|
|
|
|
|
|
|
function getStructures(db) {
|
|
|
|
|
return db.prepare("SELECT * from structures").all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getStructure(db, id) {
|
2024-06-02 00:39:30 -04:00
|
|
|
return db.prepare("SELECT * from structures where ID = ?").get(id);
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createStructure(db, name, userId) {
|
|
|
|
|
const stmt = db.prepare(
|
|
|
|
|
"INSERT INTO structures (name, user_id) VALUES (?, ?)"
|
|
|
|
|
);
|
|
|
|
|
const info = stmt.run(name, userId);
|
|
|
|
|
return info.lastInsertRowid; // Returns the structure_id of the newly created structure
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function createRoute(db, verb, path, structureId, handler) {
|
|
|
|
|
// add default handler here
|
2024-02-24 11:12:14 -05:00
|
|
|
const stmt = db.prepare(
|
2024-06-02 00:39:30 -04:00
|
|
|
"INSERT INTO routes (verb, path, structure_id, handler) VALUES (?, ?, ?, ?)"
|
2024-02-24 11:12:14 -05:00
|
|
|
);
|
2024-06-02 00:39:30 -04:00
|
|
|
const info = stmt.run(verb, path, structureId, handler);
|
2024-02-24 11:12:14 -05:00
|
|
|
return info.lastInsertRowid; // Returns the route_id of the newly created route
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function getRoutes(db, structureId) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare("SELECT * from routes where structure_id = ?")
|
|
|
|
|
.all(structureId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getRoute(db, routeId) {
|
|
|
|
|
return db.prepare("SELECT * from routes where id = ?").get(routeId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateRoute(db, route) {
|
|
|
|
|
const fields = ["verb", "path", "structure_id", "handler", "updated_at"];
|
|
|
|
|
const values = fields.map((field) => route[field]);
|
|
|
|
|
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
|
|
|
|
|
|
|
|
|
const sql = `UPDATE routes SET ${placeholders} WHERE id = ?`;
|
|
|
|
|
values.push(route.id); // Add routeId to the end for the WHERE clause
|
|
|
|
|
db.prepare(sql).run(...values);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateDb(db, appDb) {
|
|
|
|
|
const fields = ["name", "library"];
|
|
|
|
|
const values = fields.map((field) => appDb[field]);
|
|
|
|
|
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
|
|
|
|
|
|
|
|
|
const sql = `UPDATE dbs SET ${placeholders} WHERE id = ?`;
|
|
|
|
|
values.push(appDb.id); // Add routeId to the end for the WHERE clause
|
|
|
|
|
db.prepare(sql).run(...values);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
function updateStruct(db, struct) {
|
|
|
|
|
const fields = ["name", "route_prefix"];
|
|
|
|
|
console.log(struct, fields);
|
|
|
|
|
const values = fields.map((field) => struct[field]);
|
|
|
|
|
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
|
|
|
|
|
|
|
|
|
const sql = `UPDATE structures SET ${placeholders} WHERE id = ?`;
|
|
|
|
|
values.push(struct.id); // Add routeId to the end for the WHERE clause
|
|
|
|
|
db.prepare(sql).run(...values);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 10:20:01 -04:00
|
|
|
function updateTemplate(db, template) {
|
|
|
|
|
const fields = ["content", "name", "test_object"];
|
|
|
|
|
const values = fields.map((field) => template[field]);
|
|
|
|
|
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
|
|
|
|
|
|
|
|
|
const sql = `UPDATE templates SET ${placeholders} WHERE id = ?`;
|
|
|
|
|
values.push(template.id);
|
|
|
|
|
db.prepare(sql).run(...values);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function getTemplates(db, structureId) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare("SELECT * from templates where structure_id = ?")
|
|
|
|
|
.all(structureId);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 10:20:01 -04:00
|
|
|
function getTemplate(db, templateId) {
|
|
|
|
|
return db.prepare("SELECT * from templates where id = ?").get(templateId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTemplateContentByName(db, structId, name) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
"SELECT content from templates where structure_id = ? AND name = ?"
|
|
|
|
|
)
|
|
|
|
|
.get(structId, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createTemplate(db, structureId, name, content, testObjectString) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
"INSERT INTO templates (structure_id, name, content, test_object) VALUES (?, ?, ?, ?)"
|
|
|
|
|
)
|
|
|
|
|
.run(structureId, name, content, testObjectString).lastInsertRowid;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function getDbsForStructure(db, structureId) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
`SELECT *
|
|
|
|
|
FROM structure_dbs
|
|
|
|
|
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
|
|
|
|
WHERE structure_dbs.structure_id = ?;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.all(structureId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getDb(db, dbId) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
`SELECT *
|
|
|
|
|
FROM dbs
|
|
|
|
|
WHERE id = ?;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.get(dbId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getDbForStructure(db, structureId, dbId) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
`SELECT *
|
|
|
|
|
FROM structure_dbs
|
|
|
|
|
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
|
|
|
|
WHERE structure_dbs.structure_id = ?
|
|
|
|
|
AND structure_dbs.db_id = ?;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.get(structureId, dbId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createDb(db, structId, name) {
|
|
|
|
|
const transaction = db.transaction(() => {
|
|
|
|
|
const insertDbStmt = db.prepare(`
|
|
|
|
|
INSERT INTO dbs (name, structure_id)
|
|
|
|
|
VALUES (?, ?)
|
|
|
|
|
`);
|
|
|
|
|
const result = insertDbStmt.run(name, structId);
|
|
|
|
|
const dbId = result.lastInsertRowid;
|
|
|
|
|
const insertStructureDbStmt = db.prepare(`
|
|
|
|
|
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
|
|
|
|
VALUES (?, ?, ?)
|
|
|
|
|
`);
|
|
|
|
|
insertStructureDbStmt.run(dbId, structId, name);
|
|
|
|
|
|
2024-06-04 01:21:32 -04:00
|
|
|
const newDbPath = path.join("dbs", `${dbId}.sqlite`);
|
2024-06-02 00:39:30 -04:00
|
|
|
const newDb = betterSqlite3(newDbPath);
|
|
|
|
|
newDb.close();
|
|
|
|
|
|
|
|
|
|
return dbId;
|
|
|
|
|
});
|
2024-02-24 11:12:14 -05:00
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
return transaction();
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
2024-06-04 01:21:32 -04:00
|
|
|
function attachDb(db, structId, dbId, alias) {
|
|
|
|
|
const insertStructureDbStmt = db.prepare(`
|
|
|
|
|
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
|
|
|
|
VALUES (?, ?, ?)
|
|
|
|
|
`);
|
|
|
|
|
insertStructureDbStmt.run(dbId, structId, alias);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-03 20:19:26 -04:00
|
|
|
function getFilesForStruct(db, structureId) {
|
|
|
|
|
let test = db
|
|
|
|
|
.prepare("SELECT * FROM files WHERE structure_id = ? ORDER BY id DESC")
|
|
|
|
|
.all(structureId);
|
|
|
|
|
|
|
|
|
|
console.log(db.prepare("PRAGMA table_info(files)").all());
|
|
|
|
|
|
|
|
|
|
return test;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getFile(db, fileId) {
|
|
|
|
|
return db.prepare("SELECT * FROM files WHERE id = ?").get(fileId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createFile(db, structure_id, name, filePath, mime_type, mime_subtype) {
|
|
|
|
|
return db
|
|
|
|
|
.prepare(
|
|
|
|
|
"INSERT INTO files (structure_id, name, path, mime_type, mime_subtype) VALUES (?, ?, ?, ?, ?)"
|
|
|
|
|
)
|
|
|
|
|
.run(structure_id, name, filePath, mime_type, mime_subtype).lastInsertRowid;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
const { LRUCache } = require("lru-cache");
|
2024-06-02 00:39:30 -04:00
|
|
|
const templateCache = new LRUCache({ max: 100 });
|
2024-02-24 11:12:14 -05:00
|
|
|
|
|
|
|
|
function getTemplater(structId) {
|
2024-06-02 00:39:30 -04:00
|
|
|
let etaInstance = templateCache.get(structId);
|
2024-02-24 11:12:14 -05:00
|
|
|
|
|
|
|
|
if (!etaInstance) {
|
|
|
|
|
etaInstance = new Eta({});
|
|
|
|
|
etaInstance.resolvePath = function (path, _) {
|
|
|
|
|
return path;
|
|
|
|
|
};
|
|
|
|
|
etaInstance.readFile = function (templateAlias) {
|
2024-06-02 10:20:01 -04:00
|
|
|
return getTemplateContentByName(db, structId, templateAlias).content;
|
2024-02-24 11:12:14 -05:00
|
|
|
};
|
2024-06-02 00:39:30 -04:00
|
|
|
templateCache.set(structId, etaInstance);
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return etaInstance;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
const dbCache = new LRUCache({ max: 25 });
|
|
|
|
|
|
|
|
|
|
function getDbInstance(dbId) {
|
|
|
|
|
let dbInstance = dbCache.get(dbId);
|
|
|
|
|
|
|
|
|
|
if (!dbInstance) {
|
|
|
|
|
dbInstance = betterSqlite3(`dbs/${dbId}.sqlite`);
|
|
|
|
|
dbInstance.pragma("journal_mode = WAL");
|
|
|
|
|
dbCache.set(dbId, dbInstance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dbInstance;
|
|
|
|
|
}
|
2024-02-24 11:12:14 -05:00
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
function cloneStructure(
|
|
|
|
|
structId,
|
|
|
|
|
newStructureName,
|
|
|
|
|
userId,
|
|
|
|
|
routePrefix = "",
|
|
|
|
|
cloneDb = false
|
|
|
|
|
) {
|
|
|
|
|
const transaction = db.transaction(() => {
|
|
|
|
|
const cloneStructure = db.prepare(`
|
|
|
|
|
INSERT INTO structures (name, user_id, route_prefix, cloned_from)
|
|
|
|
|
VALUES (?, ?, ?, ?);
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
const newStructId = cloneStructure.run(
|
|
|
|
|
newStructureName,
|
|
|
|
|
userId,
|
|
|
|
|
routePrefix,
|
|
|
|
|
structId
|
|
|
|
|
).lastInsertRowid;
|
|
|
|
|
|
|
|
|
|
if (cloneDb) {
|
|
|
|
|
const dbIds = db
|
|
|
|
|
.prepare(`select db_id from structure_dbs where structure_id = ?;`)
|
|
|
|
|
.all(structId);
|
|
|
|
|
|
|
|
|
|
for (let { db_id } of dbIds) {
|
|
|
|
|
const newDb = db
|
|
|
|
|
.prepare(
|
|
|
|
|
`
|
|
|
|
|
INSERT INTO dbs (name, structure_id, library)
|
|
|
|
|
SELECT name, ?, library FROM dbs WHERE id = ?;
|
|
|
|
|
`
|
|
|
|
|
)
|
|
|
|
|
.run(newStructId, db_id).lastInsertRowid;
|
|
|
|
|
|
|
|
|
|
db.prepare(
|
|
|
|
|
`
|
|
|
|
|
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
|
|
|
|
SELECT ?, ?, alias FROM structure_dbs WHERE db_id = ? AND structure_id = ?;
|
|
|
|
|
`
|
|
|
|
|
).run(newDb, newStructId, db_id, structId);
|
|
|
|
|
|
|
|
|
|
db.prepare(`select id from dbs where structure_id = ?`)
|
|
|
|
|
.all(newStructId)
|
|
|
|
|
.map((new_db) => {
|
|
|
|
|
console.log(db_id, new_db.id);
|
|
|
|
|
const srcPath = path.join(__dirname, "dbs", `${db_id}.sqlite`);
|
|
|
|
|
const destPath = path.join(__dirname, "dbs", `${new_db.id}.sqlite`);
|
|
|
|
|
fs.copyFileSync(srcPath, destPath);
|
|
|
|
|
fs.copyFileSync(srcPath + "-shm", destPath + "-shm");
|
|
|
|
|
fs.copyFileSync(srcPath + "-wal", destPath + "-wal");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
db.prepare(
|
|
|
|
|
`
|
|
|
|
|
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
|
|
|
|
SELECT db_id, ?, alias FROM structure_dbs WHERE structure_id = ?;
|
|
|
|
|
`
|
|
|
|
|
).run(newStructId, structId);
|
|
|
|
|
}
|
|
|
|
|
const cloneTemplates = db.prepare(`
|
|
|
|
|
INSERT INTO templates (name, content, structure_id, test_object, engine)
|
|
|
|
|
SELECT name, content, ?, test_object, engine FROM templates WHERE structure_id = ?;
|
|
|
|
|
`);
|
|
|
|
|
cloneTemplates.run(newStructId, structId);
|
|
|
|
|
|
|
|
|
|
const cloneRoutes = db.prepare(`
|
|
|
|
|
INSERT INTO routes (verb, path, structure_id, handler)
|
|
|
|
|
SELECT verb, path, ?, handler FROM routes WHERE structure_id = ?;
|
|
|
|
|
`);
|
|
|
|
|
cloneRoutes.run(newStructId, structId);
|
|
|
|
|
|
|
|
|
|
return newStructId;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return transaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function bootstrapTemplateWithHTMXetc(
|
|
|
|
|
htmlString,
|
|
|
|
|
blissRoute,
|
|
|
|
|
blissClone,
|
|
|
|
|
blissCopy,
|
|
|
|
|
wrapHTML
|
|
|
|
|
) {
|
2024-06-02 10:20:01 -04:00
|
|
|
if (htmlString.startsWith("<html>") || wrapHTML) {
|
2024-06-02 00:39:30 -04:00
|
|
|
const $ = cheerio.load(htmlString);
|
2024-02-24 11:12:14 -05:00
|
|
|
let head = $("head");
|
|
|
|
|
|
|
|
|
|
// If <head> does not exist, prepend it to <html>
|
|
|
|
|
if (head.length === 0) {
|
|
|
|
|
$("html").prepend("<head></head>");
|
|
|
|
|
head = $("head");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
head.append(`
|
2024-06-02 00:39:30 -04:00
|
|
|
<script src="/js/hyperscript.js"></script>
|
|
|
|
|
<script src="/js/tailwind.js"></script>
|
|
|
|
|
<script src="/js/htmx.js"></script>
|
|
|
|
|
<script src="/js/bliss_inspector.js"></script>
|
2024-02-24 11:12:14 -05:00
|
|
|
`);
|
2024-06-02 00:39:30 -04:00
|
|
|
|
|
|
|
|
if (blissRoute) {
|
2024-06-04 03:01:19 -04:00
|
|
|
$("body").attr("data-bliss-route", blissRoute);
|
|
|
|
|
$("body").attr("data-bliss-clone", blissClone);
|
|
|
|
|
if (blissCopy) $("body").attr("data-bliss-copy", blissCopy);
|
2024-06-02 00:39:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
htmlString = $.html();
|
|
|
|
|
} else if (blissRoute) {
|
|
|
|
|
const $ = cheerio.load(htmlString, null, false);
|
|
|
|
|
$.root().children().attr("data-bliss-route", blissRoute);
|
2024-06-04 03:01:19 -04:00
|
|
|
$.root().children().attr("data-bliss-clone", blissClone);
|
|
|
|
|
if (blissCopy) $.root().children().attr("data-bliss-copy", blissCopy);
|
|
|
|
|
console.log(blissCopy, "fleem")
|
2024-06-02 00:39:30 -04:00
|
|
|
htmlString = $.html();
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
return htmlString;
|
2024-02-24 11:12:14 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.post("/workshop", (req, res) => {
|
|
|
|
|
let structId = createStructure(db, req.body.name);
|
|
|
|
|
return res.redirect("/workshop/" + structId);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/workshop", (req, res) => {
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/index", {
|
|
|
|
|
structures: getStructures(db),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
function smartRedirect(req, res, redirectUrl) {
|
|
|
|
|
if (req.headers["hx-request"]) {
|
|
|
|
|
res.set("HX-Redirect", redirectUrl);
|
|
|
|
|
res.send();
|
|
|
|
|
} else {
|
|
|
|
|
res.redirect(redirectUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function sidebarStuff(db, structId) {
|
|
|
|
|
return {
|
|
|
|
|
structure: getStructure(db, structId),
|
|
|
|
|
routes: getRoutes(db, structId),
|
|
|
|
|
templates: getTemplates(db, structId),
|
|
|
|
|
dbs: getDbsForStructure(db, structId),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
app.get("/workshop/:structure_id", (req, res) => {
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
2024-06-02 00:39:30 -04:00
|
|
|
eta.render("workshop/editor", sidebarStuff(db, req.params.structure_id))
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
app.post("/workshop/:structure_id/clone", (req, res) => {
|
|
|
|
|
let routePrefix = req.body.route_prefix;
|
|
|
|
|
routePrefix = routePrefix[0] == "/" ? routePrefix : "/" + routePrefix;
|
|
|
|
|
const cloneDb = req.body.clone_db == "true";
|
|
|
|
|
let newStructureId;
|
|
|
|
|
try {
|
|
|
|
|
newStructureId = cloneStructure(
|
|
|
|
|
req.params.structure_id,
|
|
|
|
|
req.body.name,
|
|
|
|
|
1,
|
|
|
|
|
// req.session.userId,
|
|
|
|
|
routePrefix,
|
|
|
|
|
cloneDb
|
|
|
|
|
);
|
|
|
|
|
buildRoutes();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return res.send(e.stack);
|
|
|
|
|
}
|
|
|
|
|
return smartRedirect(req, res, `/workshop/${newStructureId}`);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
app.post("/workshop/:structure_id/db", (req, res) => {
|
|
|
|
|
const dbId = createDb(db, req.params.structure_id, req.body.name);
|
|
|
|
|
const redirectUrl = `/workshop/${req.params.structure_id}/db/${dbId}`;
|
|
|
|
|
return smartRedirect(req, res, redirectUrl);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-04 01:21:32 -04:00
|
|
|
app.post("/workshop/:structure_id/db/attach", (req, res) => {
|
|
|
|
|
attachDb(db, 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);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
app.get("/workshop/:structure_id/db/:db_id", (req, res) => {
|
|
|
|
|
const structdb = 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", {
|
|
|
|
|
db: structdb,
|
|
|
|
|
...sidebarStuff(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.post("/workshop/:structure_id/route", (req, res) => {
|
2024-06-02 00:44:02 -04:00
|
|
|
let path = req.body.path;
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
const route = createRoute(
|
|
|
|
|
db,
|
|
|
|
|
req.body.verb,
|
2024-06-02 00:44:02 -04:00
|
|
|
path[0] == "/" ? path : "/" + path,
|
2024-06-02 00:39:30 -04:00
|
|
|
req.params.structure_id,
|
2024-06-02 00:44:02 -04:00
|
|
|
"// put your handler code here\n\nfunction handler(req, res) {\n res.send('henlo world') \n}"
|
2024-06-02 00:39:30 -04:00
|
|
|
);
|
2024-06-04 03:01:19 -04:00
|
|
|
// todo optimize by only adding new, don't just rebuild all
|
2024-06-02 00:39:30 -04:00
|
|
|
buildRoutes();
|
|
|
|
|
return smartRedirect(
|
|
|
|
|
req,
|
|
|
|
|
res,
|
|
|
|
|
`/workshop/${req.params.structure_id}/route/${route}`
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.put("/workshop/:structure_id/route/:route_id", (req, res) => {
|
|
|
|
|
const route = getRoute(db, req.params.route_id);
|
|
|
|
|
updateRoute(db, { ...route, ...req.body });
|
|
|
|
|
return res.send("good");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
let dbId = req.params.db_id;
|
|
|
|
|
let appDb = getDb(db, dbId);
|
|
|
|
|
updateDb(db, { ...appDb, library: req.body.library });
|
|
|
|
|
appDb = getDb(db, dbId);
|
|
|
|
|
|
|
|
|
|
let dbInstance = getDbInstance(dbId);
|
|
|
|
|
let capturedOutput = [];
|
|
|
|
|
let context = vm.createContext({
|
|
|
|
|
module: { exports: null },
|
|
|
|
|
sql: dbInstance,
|
|
|
|
|
console: {
|
|
|
|
|
log: (...args) => capturedOutput.push(args.join(" ")),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
let evaledCode = vm.runInContext(appDb.library, context);
|
|
|
|
|
let stdout = capturedOutput.join("\n");
|
|
|
|
|
return res.send(`${stdout}\n\n> ${evaledCode}`.trim());
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return res.send(`${e}\n\n${e.stack}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.post("/workshop/:structure_id/db/:db_id/repl", (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
let dbId = req.params.db_id;
|
|
|
|
|
let appDb = getDb(db, dbId);
|
|
|
|
|
let dbInstance = getDbInstance(dbId);
|
|
|
|
|
let capturedOutput = [];
|
|
|
|
|
let context = vm.createContext({
|
|
|
|
|
module: { exports: null },
|
|
|
|
|
sql: dbInstance,
|
|
|
|
|
console: {
|
|
|
|
|
log: (...args) => capturedOutput.push(args.join(" ")),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const libraryScript = new vm.Script(appDb.library);
|
|
|
|
|
libraryScript.runInContext(context);
|
|
|
|
|
context.library = context.module.exports;
|
|
|
|
|
const replScript = new vm.Script(req.body.code);
|
|
|
|
|
let evaledCode = replScript.runInContext(context);
|
|
|
|
|
let stdout = capturedOutput.join("\n");
|
|
|
|
|
return res.send(`${stdout}\n\n> ${evaledCode}`.trim());
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return res.send(`${e}\n\n${e.stack}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 10:20:01 -04:00
|
|
|
app.post("/workshop/:structure_id/template", (req, res) => {
|
|
|
|
|
let name = req.body.name;
|
|
|
|
|
|
|
|
|
|
const template = createTemplate(
|
|
|
|
|
db,
|
|
|
|
|
req.params.structure_id,
|
|
|
|
|
name,
|
|
|
|
|
"<div>henlo <%= it.name %></div>",
|
|
|
|
|
"it = { name: 'templates!' };"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return smartRedirect(
|
|
|
|
|
req,
|
|
|
|
|
res,
|
|
|
|
|
`/workshop/${req.params.structure_id}/template/${template}`
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.put("/workshop/:structure_id/template/:template_id", (req, res) => {
|
|
|
|
|
let id = req.params.template_id;
|
|
|
|
|
let content = req.body.content;
|
|
|
|
|
let test_object = req.body.test_object;
|
|
|
|
|
|
|
|
|
|
updateTemplate(db, { ...getTemplate(db, id), content, test_object });
|
|
|
|
|
|
|
|
|
|
return res.send("good");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/workshop/:structure_id/template/:template_id", (req, res) => {
|
|
|
|
|
const template = getTemplate(db, req.params.template_id);
|
|
|
|
|
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/template", {
|
|
|
|
|
template: template,
|
|
|
|
|
...sidebarStuff(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/workshop/:structure_id/template/:template_id/preview", (req, res) => {
|
|
|
|
|
const template = getTemplate(db, req.params.template_id);
|
2024-06-04 03:01:19 -04:00
|
|
|
const struct = getStructure(db, req.params.structure_id);
|
2024-06-02 10:20:01 -04:00
|
|
|
const eta = getTemplater(req.params.structure_id);
|
|
|
|
|
|
|
|
|
|
const context = vm.createContext({ it: null });
|
|
|
|
|
vm.runInContext(template.test_object, context);
|
|
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
context.it.route = function (url) {
|
|
|
|
|
return struct.route_prefix ? path.join(struct.route_prefix, url) : url;
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-02 10:20:01 -04:00
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render(template.name, context.it),
|
|
|
|
|
null,
|
2024-06-04 03:01:19 -04:00
|
|
|
null,
|
|
|
|
|
null,
|
2024-06-02 10:20:01 -04:00
|
|
|
true
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
app.get("/workshop/:structure_id/route/:id", (req, res) => {
|
|
|
|
|
const route = getRoute(db, req.params.id);
|
2024-06-04 03:01:19 -04:00
|
|
|
const routePrefix = getStructure(db, req.params.structure_id).route_prefix;
|
2024-06-02 00:39:30 -04:00
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/route", {
|
|
|
|
|
route: route,
|
2024-06-04 03:01:19 -04:00
|
|
|
previewUrl:
|
|
|
|
|
route["verb"] == "GET"
|
|
|
|
|
? path.join(routePrefix, route.path)
|
|
|
|
|
: "/workshop/tip",
|
2024-06-02 00:39:30 -04:00
|
|
|
...sidebarStuff(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-03 20:19:26 -04:00
|
|
|
// POST route to handle file upload
|
|
|
|
|
app.post("/workshop/:structure_id/files", (req, res) => {
|
|
|
|
|
if (!req.files || Object.keys(req.files).length === 0) {
|
|
|
|
|
return res.status(400).send("failed to upload that file");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { structure_id } = req.params;
|
|
|
|
|
const file = req.files.file;
|
|
|
|
|
const name = file.name;
|
|
|
|
|
const [mime_type, mime_subtype] = file.mimetype.split("/");
|
|
|
|
|
const uploadPath = path.join(__dirname, "public", structure_id);
|
|
|
|
|
const storedPath = path.join(structure_id, file.name);
|
|
|
|
|
|
|
|
|
|
fs.mkdirSync(uploadPath, { recursive: true });
|
|
|
|
|
|
|
|
|
|
file.mv(path.join(uploadPath, name), (err) => {
|
|
|
|
|
try {
|
|
|
|
|
if (err) {
|
|
|
|
|
return res.status(500).send(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let id = createFile(
|
|
|
|
|
db,
|
|
|
|
|
structure_id,
|
|
|
|
|
name,
|
|
|
|
|
storedPath,
|
|
|
|
|
mime_type,
|
|
|
|
|
mime_subtype
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let file = getFile(db, id);
|
|
|
|
|
file.url = prefixUrlWithHost(req, file.path);
|
|
|
|
|
|
|
|
|
|
return res.send(eta.render("workshop/file_detail", { file: file }));
|
|
|
|
|
} catch (e) {
|
|
|
|
|
res.status(500);
|
|
|
|
|
return res.send(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function prefixUrlWithHost(req, path) {
|
|
|
|
|
return req.protocol + "://" + req.get("host") + "/" + path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// POST route to handle file upload
|
|
|
|
|
app.get("/workshop/:structure_id/files", (req, res) => {
|
|
|
|
|
const { structure_id } = req.params;
|
|
|
|
|
|
|
|
|
|
const files = getFilesForStruct(db, structure_id);
|
|
|
|
|
files.map((it) => {
|
|
|
|
|
it.url = prefixUrlWithHost(req, it.path);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/files", {
|
|
|
|
|
files,
|
|
|
|
|
...sidebarStuff(db, structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
2024-06-02 00:39:30 -04:00
|
|
|
});
|
|
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
app.get("/workshop/:structure_id/settings", (req, res) => {
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/settings", {
|
|
|
|
|
...sidebarStuff(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.put("/workshop/:structure_id/settings", (req, res) => {
|
|
|
|
|
let structId = req.params.structure_id;
|
|
|
|
|
let struct = getStructure(db, structId);
|
|
|
|
|
updateStruct(db, { ...struct, route_prefix: req.body.route_prefix });
|
|
|
|
|
struct = getStructure(db, structId);
|
|
|
|
|
|
|
|
|
|
res.send("success!");
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
app.get("/workshop/:structure_id/new_template_modal", (req, res) => {
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/new_template_modal", {
|
|
|
|
|
structure: getStructure(db, 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: getStructure(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.get("/workshop/:structure_id/new_db_modal", (req, res) => {
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/new_db_modal", {
|
2024-02-24 11:12:14 -05:00
|
|
|
structure: getStructure(db, req.params.structure_id),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2024-06-04 03:01:19 -04:00
|
|
|
app.get("/workshop/:structure_id/clone_modal", (req, res) => {
|
|
|
|
|
const structure = getStructure(db, req.params.structure_id);
|
|
|
|
|
return res.send(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render("workshop/clone_structure_modal", {
|
|
|
|
|
structure,
|
|
|
|
|
defaultRoutePrefix: path.join(structure.route_prefix || "", "clone"),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function embedHTML(url) {
|
|
|
|
|
return `<div hx-get="${url}" hx-trigger="load"></div>`;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 11:12:14 -05:00
|
|
|
app.all("*", (req, res) => {
|
2024-06-04 03:01:19 -04:00
|
|
|
const req_path = req.path;
|
2024-02-24 11:12:14 -05:00
|
|
|
const verb = req.method;
|
|
|
|
|
|
|
|
|
|
try {
|
2024-06-02 00:39:30 -04:00
|
|
|
let routeMatch = null;
|
2024-02-24 11:12:14 -05:00
|
|
|
|
2024-06-02 00:39:30 -04:00
|
|
|
for (let { matcher, id } of routes[verb]) {
|
2024-06-04 03:01:19 -04:00
|
|
|
let matchFromRoute = matcher(req_path);
|
2024-06-02 00:39:30 -04:00
|
|
|
if (matchFromRoute) {
|
|
|
|
|
routeMatch = { params: matchFromRoute.params, id: id };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (routeMatch) {
|
|
|
|
|
req.params = routeMatch.params;
|
|
|
|
|
const route = db
|
|
|
|
|
.prepare("SELECT * FROM routes WHERE id = ?")
|
|
|
|
|
.get(routeMatch.id);
|
|
|
|
|
|
|
|
|
|
let dbs = getDbsForStructure(db, route.structure_id);
|
2024-06-04 03:01:19 -04:00
|
|
|
let __urlPrefix = getStructure(db, route.structure_id).route_prefix;
|
2024-06-02 00:39:30 -04:00
|
|
|
|
|
|
|
|
const allDbInstances = {};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
let context = vm.createContext({
|
|
|
|
|
req,
|
|
|
|
|
res,
|
|
|
|
|
getDb: function (alias) {
|
|
|
|
|
return allDbInstances[alias];
|
|
|
|
|
},
|
|
|
|
|
module: { exports: null },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (let appDb of dbs) {
|
|
|
|
|
let dbInstance = getDbInstance(appDb.id);
|
|
|
|
|
context.sql = dbInstance;
|
|
|
|
|
vm.runInContext(appDb.library, context);
|
|
|
|
|
allDbInstances[appDb.alias] = {
|
|
|
|
|
library: context.module.exports,
|
|
|
|
|
sql: dbInstance,
|
|
|
|
|
};
|
|
|
|
|
context.module.exports = null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-02 00:44:02 -04:00
|
|
|
res.rawSend = res.send;
|
|
|
|
|
res.send = (...args) => {
|
|
|
|
|
res.rawSend(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
args[0],
|
2024-06-04 03:01:19 -04:00
|
|
|
`/workshop/${route.structure_id}/route/${route.id}`,
|
|
|
|
|
`/workshop/${route.structure_id}/clone/`,
|
|
|
|
|
verb == "GET" ? embedHTML(req.originalUrl) : null
|
2024-06-02 00:44:02 -04:00
|
|
|
)
|
2024-06-02 00:39:30 -04:00
|
|
|
);
|
|
|
|
|
};
|
2024-06-02 10:20:01 -04:00
|
|
|
res.render = (template, context) => {
|
2024-06-04 03:01:19 -04:00
|
|
|
context.route = function (url) {
|
|
|
|
|
return __urlPrefix ? path.join(__urlPrefix, url) : url;
|
|
|
|
|
};
|
2024-06-02 10:20:01 -04:00
|
|
|
const eta = getTemplater(route.structure_id);
|
2024-06-04 03:01:19 -04:00
|
|
|
console.log(route.method, "\n\n\n");
|
|
|
|
|
res.rawSend(
|
|
|
|
|
bootstrapTemplateWithHTMXetc(
|
|
|
|
|
eta.render(template, context),
|
|
|
|
|
`/workshop/${route.structure_id}/route/${route.id}`,
|
|
|
|
|
`/workshop/${route.structure_id}/clone_modal/`,
|
|
|
|
|
verb == "GET" ? embedHTML(req.originalUrl) : null
|
|
|
|
|
)
|
|
|
|
|
);
|
2024-06-02 10:20:01 -04:00
|
|
|
};
|
2024-06-02 00:39:30 -04:00
|
|
|
return vm.runInContext(
|
|
|
|
|
(route.handler += `\n\nhandler(req, res)`),
|
|
|
|
|
context
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return res.send(`${e}\n\n${e.stack}`);
|
|
|
|
|
}
|
2024-02-24 11:12:14 -05:00
|
|
|
} else {
|
|
|
|
|
res.status(404).json({ success: false, message: "Path not found" });
|
2024-02-03 13:22:50 -05:00
|
|
|
}
|
2024-02-24 11:12:14 -05:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
res.status(500).json({ success: false, message: "Internal server error" });
|
|
|
|
|
}
|
2024-02-03 13:22:50 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.listen(PORT, () => {
|
2024-02-24 11:12:14 -05:00
|
|
|
console.log(`Server is running on http://localhost:${PORT}`);
|
2024-02-03 13:22:50 -05:00
|
|
|
});
|