make em private
This commit is contained in:
parent
1394ce1e0c
commit
02cdca748d
4 changed files with 123 additions and 37 deletions
105
db.js
105
db.js
|
|
@ -126,7 +126,10 @@ function inspectableContext(data) {
|
|||
function annotateTemplateHtml(html, template, data) {
|
||||
const context = inspectableContext(data);
|
||||
const contextId = randomUUID();
|
||||
templateContextCache.set(contextId, { templateId: String(template.id), context });
|
||||
templateContextCache.set(contextId, {
|
||||
templateId: String(template.id),
|
||||
context,
|
||||
});
|
||||
function annotate($, element) {
|
||||
const current = $(element).attr("data-bliss-templates");
|
||||
let templates = [];
|
||||
|
|
@ -147,7 +150,10 @@ function annotateTemplateHtml(html, template, data) {
|
|||
} catch (_) {}
|
||||
}
|
||||
contextIds[String(template.id)] = contextId;
|
||||
$(element).attr("data-bliss-template-context-ids", JSON.stringify(contextIds));
|
||||
$(element).attr(
|
||||
"data-bliss-template-context-ids",
|
||||
JSON.stringify(contextIds),
|
||||
);
|
||||
if (!$(element).attr("data-bliss-template-id")) {
|
||||
$(element).attr({
|
||||
"data-bliss-template-id": String(template.id),
|
||||
|
|
@ -207,14 +213,20 @@ function createStructure(name, userId) {
|
|||
return info.lastInsertRowid; // Returns the structure_id of the newly created structure
|
||||
}
|
||||
|
||||
// ---- access control (private structures) --------------------------------
|
||||
// ---- access control -------------------------------------------------------
|
||||
//
|
||||
// A structure is public unless `private` is set. Access to a private structure
|
||||
// is exactly its member list (structure_members) — a flat set of equals, no
|
||||
// creator or owner. Anyone on the list can reach it, edit it, manage the list,
|
||||
// or make it public again. This is the single source of truth the platform
|
||||
// consults before serving a user route, mounting a db, or opening the workshop
|
||||
// — never the structure's own code.
|
||||
// Two independent flags, three tiers of reach:
|
||||
//
|
||||
// public = 1 -> anyone, no login (opt-in, with a warning)
|
||||
// public = 0, private = 0 -> any logged-in user (the default)
|
||||
// public = 0, private = 1 -> invited members only
|
||||
//
|
||||
// So login is required by default; making a structure public is the explicit
|
||||
// way back onto the open web. `private` layers a stricter members-only gate on
|
||||
// top: access is then exactly its member list (structure_members) — a flat set
|
||||
// of equals, no creator or owner. This is the single source of truth the
|
||||
// platform consults before serving a user route, mounting a db, or opening the
|
||||
// workshop — never the structure's own code.
|
||||
|
||||
function isMember(structureId, userId) {
|
||||
return !!db
|
||||
|
|
@ -227,9 +239,10 @@ function isMember(structureId, userId) {
|
|||
function canAccessStructure(userId, structureId) {
|
||||
const s = getStructure(structureId);
|
||||
if (!s) return false;
|
||||
if (!s.private) return true;
|
||||
if (userId == null) return false;
|
||||
return isMember(structureId, userId);
|
||||
if (s.public) return true; // open web, no login
|
||||
if (userId == null) return false; // everything else needs a login
|
||||
if (!s.private) return true; // any logged-in user
|
||||
return isMember(structureId, userId); // members-only
|
||||
}
|
||||
|
||||
// Members of a structure, with their usernames, for the settings UI.
|
||||
|
|
@ -271,16 +284,28 @@ function setStructurePrivacy(structureId, isPrivate, actorUserId) {
|
|||
if (isPrivate && actorUserId != null) addMember(structureId, actorUserId);
|
||||
}
|
||||
|
||||
// Put a structure on the open web (public = 1) or take it back off (login
|
||||
// required). Independent of the members-only `private` flag; public wins in
|
||||
// canAccessStructure, so a public structure is reachable regardless of private.
|
||||
function setStructurePublic(structureId, isPublic) {
|
||||
const s = getStructure(structureId);
|
||||
if (!s) return;
|
||||
db.prepare("UPDATE structures SET public = ? WHERE id = ?").run(
|
||||
isPublic ? 1 : 0,
|
||||
structureId,
|
||||
);
|
||||
}
|
||||
|
||||
function createRoute(verb, path, structureId, handler) {
|
||||
path = encodeURI(path);
|
||||
|
||||
const stmt = db.prepare(
|
||||
"INSERT INTO routes (verb, path, structure_id, handler) VALUES (?, ?, ?, ?)"
|
||||
"INSERT INTO routes (verb, path, structure_id, handler) VALUES (?, ?, ?, ?)",
|
||||
);
|
||||
const info = stmt.run(verb, path, structureId, handler);
|
||||
const routeId = info.lastInsertRowid; // Get the newly created route ID
|
||||
|
||||
if (verb !== 'GET') {
|
||||
if (verb !== "GET") {
|
||||
createScaffoldPage(routeId);
|
||||
}
|
||||
|
||||
|
|
@ -306,13 +331,17 @@ const ROUTE_SELECT = `
|
|||
|
||||
function getRoutes(structureId) {
|
||||
return db
|
||||
.prepare(`${ROUTE_SELECT} WHERE r.structure_id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC`)
|
||||
.prepare(
|
||||
`${ROUTE_SELECT} WHERE r.structure_id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC`,
|
||||
)
|
||||
.all(structureId);
|
||||
}
|
||||
|
||||
function getRoute(routeId) {
|
||||
return db
|
||||
.prepare(`${ROUTE_SELECT} WHERE r.id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC LIMIT 1`)
|
||||
.prepare(
|
||||
`${ROUTE_SELECT} WHERE r.id = ? ORDER BY sp.created_at DESC, sp2.created_at DESC LIMIT 1`,
|
||||
)
|
||||
.get(routeId);
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +350,9 @@ function update(table, fields, obj) {
|
|||
const placeholders = fields.map((field) => `${field} = ?`).join(", ");
|
||||
const values = fields.map((field) => obj[field]);
|
||||
values.push(obj.id);
|
||||
return db.prepare(`UPDATE ${table} SET ${placeholders} WHERE id = ?`).run(...values);
|
||||
return db
|
||||
.prepare(`UPDATE ${table} SET ${placeholders} WHERE id = ?`)
|
||||
.run(...values);
|
||||
}
|
||||
|
||||
// ---- version history (append-only) --------------------------------------
|
||||
|
|
@ -337,7 +368,11 @@ function snapshotFor(entityType, obj) {
|
|||
case "route":
|
||||
return { verb: obj.verb, path: obj.path, handler: obj.handler };
|
||||
case "template":
|
||||
return { name: obj.name, content: obj.content, test_object: obj.test_object };
|
||||
return {
|
||||
name: obj.name,
|
||||
content: obj.content,
|
||||
test_object: obj.test_object,
|
||||
};
|
||||
case "db":
|
||||
return { name: obj.name, library: obj.library };
|
||||
default:
|
||||
|
|
@ -377,7 +412,11 @@ function getVersion(versionId) {
|
|||
}
|
||||
|
||||
function updateRoute(route) {
|
||||
update("routes", ["verb", "path", "structure_id", "handler", "updated_at", "error"], route);
|
||||
update(
|
||||
"routes",
|
||||
["verb", "path", "structure_id", "handler", "updated_at", "error"],
|
||||
route,
|
||||
);
|
||||
recordVersion("route", route.id, route.structure_id, route);
|
||||
}
|
||||
|
||||
|
|
@ -689,18 +728,25 @@ function getMostRecentLogIdByRoute(routeId) {
|
|||
}
|
||||
|
||||
function buildScaffoldUrl(endpoint, urlParams, queryString) {
|
||||
let populatedUrl = endpoint.replace(/:([^/]+)/g, () => urlParams.shift() || '');
|
||||
let populatedUrl = endpoint.replace(
|
||||
/:([^/]+)/g,
|
||||
() => urlParams.shift() || "",
|
||||
);
|
||||
return queryString ? `${populatedUrl}?${queryString}` : populatedUrl;
|
||||
}
|
||||
|
||||
function createScaffoldPage(routeId, content=null) {
|
||||
function createScaffoldPage(routeId, content = null) {
|
||||
if (!content) {
|
||||
const route = getRoute(routeId)
|
||||
const endpoint = buildScaffoldUrl(route.path, route.url_params, route.query_params);
|
||||
const route = getRoute(routeId);
|
||||
const endpoint = buildScaffoldUrl(
|
||||
route.path,
|
||||
route.url_params,
|
||||
route.query_params,
|
||||
);
|
||||
content = getDefaultScaffoldContentByVerb(endpoint, route.verb);
|
||||
}
|
||||
const stmt = db.prepare(
|
||||
"INSERT INTO scaffold_pages (route_id, content) VALUES (?, ?)"
|
||||
"INSERT INTO scaffold_pages (route_id, content) VALUES (?, ?)",
|
||||
);
|
||||
const info = stmt.run(routeId, content);
|
||||
return info.lastInsertRowid; // Returns the scaffold_page id of the newly created scaffold page
|
||||
|
|
@ -708,7 +754,7 @@ function createScaffoldPage(routeId, content=null) {
|
|||
|
||||
function getLatestScaffoldPage(routeId) {
|
||||
const stmt = db.prepare(
|
||||
"SELECT * FROM scaffold_pages WHERE route_id = ? ORDER BY created_at DESC LIMIT 1"
|
||||
"SELECT * FROM scaffold_pages WHERE route_id = ? ORDER BY created_at DESC LIMIT 1",
|
||||
);
|
||||
return stmt.get(routeId);
|
||||
}
|
||||
|
|
@ -819,11 +865,9 @@ function generateWSPage(endpoint) {
|
|||
function getDefaultScaffoldContentByVerb(url, verb) {
|
||||
if (verb == "POST") {
|
||||
return generatePostForm(url);
|
||||
}
|
||||
else if (verb == "PUT" || verb == "DELETE") {
|
||||
} else if (verb == "PUT" || verb == "DELETE") {
|
||||
return generateModifyForm(url, verb);
|
||||
}
|
||||
else if (verb == "WS") {
|
||||
} else if (verb == "WS") {
|
||||
return generateWSPage(url);
|
||||
}
|
||||
}
|
||||
|
|
@ -845,6 +889,7 @@ module.exports = {
|
|||
addMember,
|
||||
removeMember,
|
||||
setStructurePrivacy,
|
||||
setStructurePublic,
|
||||
createRoute,
|
||||
getRoutes,
|
||||
getRoute,
|
||||
|
|
@ -877,5 +922,5 @@ module.exports = {
|
|||
getMostRecentLogIdByRoute,
|
||||
createScaffoldPage,
|
||||
getLatestScaffoldPage,
|
||||
updateScaffoldPage
|
||||
updateScaffoldPage,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue