don't allow users to just mount the prime db i guess for privacy reasons (may undo this someday)
This commit is contained in:
parent
464774ef4b
commit
5d7bcddcd1
4 changed files with 95 additions and 7 deletions
25
db.js
25
db.js
|
|
@ -7,6 +7,7 @@ const { Eta } = require("eta");
|
||||||
const cheerio = require("cheerio");
|
const cheerio = require("cheerio");
|
||||||
|
|
||||||
const db = betterSqlite3("./dbs/0.sqlite");
|
const db = betterSqlite3("./dbs/0.sqlite");
|
||||||
|
const PRIME_DB_ID = 0;
|
||||||
|
|
||||||
db.pragma("journal_mode = WAL");
|
db.pragma("journal_mode = WAL");
|
||||||
|
|
||||||
|
|
@ -587,6 +588,7 @@ function getDbsForStructure(structureId) {
|
||||||
FROM structure_dbs
|
FROM structure_dbs
|
||||||
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
||||||
WHERE structure_dbs.structure_id = ?
|
WHERE structure_dbs.structure_id = ?
|
||||||
|
AND NOT (structure_dbs.db_id = ${PRIME_DB_ID} AND structure_dbs.structure_id != ${PRIME_DB_ID})
|
||||||
ORDER BY structure_dbs.created_at, is_aliased ASC;
|
ORDER BY structure_dbs.created_at, is_aliased ASC;
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
|
|
@ -607,11 +609,16 @@ function getDb(dbId) {
|
||||||
function getDbForStructure(structureId, dbId) {
|
function getDbForStructure(structureId, dbId) {
|
||||||
return db
|
return db
|
||||||
.prepare(
|
.prepare(
|
||||||
`SELECT *
|
`SELECT
|
||||||
|
dbs.*,
|
||||||
|
structure_dbs.alias,
|
||||||
|
structure_dbs.structure_id AS alias_struct_id,
|
||||||
|
dbs.structure_id AS db_struct_id
|
||||||
FROM structure_dbs
|
FROM structure_dbs
|
||||||
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
INNER JOIN dbs ON structure_dbs.db_id = dbs.id
|
||||||
WHERE structure_dbs.structure_id = ?
|
WHERE structure_dbs.structure_id = ?
|
||||||
AND structure_dbs.db_id = ?;
|
AND structure_dbs.db_id = ?
|
||||||
|
AND NOT (structure_dbs.db_id = ${PRIME_DB_ID} AND structure_dbs.structure_id != ${PRIME_DB_ID});
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.get(structureId, dbId);
|
.get(structureId, dbId);
|
||||||
|
|
@ -644,6 +651,9 @@ function createDb(structId, name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachDb(structId, dbId, alias) {
|
function attachDb(structId, dbId, alias) {
|
||||||
|
if (Number(dbId) === PRIME_DB_ID && Number(structId) !== PRIME_DB_ID) {
|
||||||
|
throw new Error("prime db cannot be attached to other structures");
|
||||||
|
}
|
||||||
const insertStructureDbStmt = db.prepare(`
|
const insertStructureDbStmt = db.prepare(`
|
||||||
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
INSERT INTO structure_dbs (db_id, structure_id, alias)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
|
|
@ -698,10 +708,17 @@ function cloneStructure(
|
||||||
).lastInsertRowid;
|
).lastInsertRowid;
|
||||||
|
|
||||||
const dbIds = db
|
const dbIds = db
|
||||||
.prepare(`SELECT db_id FROM structure_dbs WHERE structure_id = ?;`)
|
.prepare(`SELECT db_id FROM structure_dbs WHERE structure_id = ? AND db_id != ${PRIME_DB_ID};`)
|
||||||
.all(structId);
|
.all(structId);
|
||||||
|
|
||||||
const toClone = new Set(cloneDbs);
|
const requestedCloneDbs = Array.isArray(cloneDbs)
|
||||||
|
? cloneDbs
|
||||||
|
: cloneDbs
|
||||||
|
? [cloneDbs]
|
||||||
|
: [];
|
||||||
|
const toClone = new Set(
|
||||||
|
requestedCloneDbs.filter((dbId) => Number(dbId) !== PRIME_DB_ID),
|
||||||
|
);
|
||||||
const toAlias = new Set();
|
const toAlias = new Set();
|
||||||
|
|
||||||
for (let { db_id } of dbIds) {
|
for (let { db_id } of dbIds) {
|
||||||
|
|
|
||||||
38
index.js
38
index.js
|
|
@ -12,11 +12,18 @@ const { match } = require("path-to-regexp");
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
const cheerio = require("cheerio");
|
const cheerio = require("cheerio");
|
||||||
const webPush = require("web-push");
|
const webPush = require("web-push");
|
||||||
|
const { randomUUID } = require("node:crypto");
|
||||||
const app = express();
|
const app = express();
|
||||||
const _expressWs = require("express-ws")(app);
|
const _expressWs = require("express-ws")(app);
|
||||||
const bodyParser = require("body-parser");
|
const bodyParser = require("body-parser");
|
||||||
const model = require("./db");
|
const model = require("./db");
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
const SESSION_SECRET = process.env.SESSION_SECRET || randomUUID();
|
||||||
|
if (!process.env.SESSION_SECRET) {
|
||||||
|
console.warn(
|
||||||
|
"SESSION_SECRET is not set; using an ephemeral secret and invalidating sessions on restart.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const db = model.db;
|
const db = model.db;
|
||||||
const wsRouter = express.Router();
|
const wsRouter = express.Router();
|
||||||
|
|
@ -62,7 +69,7 @@ app.use(fileUpload());
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
store: new SQLiteStore({ client: db, expired: { clear: true } }),
|
store: new SQLiteStore({ client: db, expired: { clear: true } }),
|
||||||
secret: "your secret key",
|
secret: SESSION_SECRET,
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: true,
|
||||||
cookie: { secure: false },
|
cookie: { secure: false },
|
||||||
|
|
@ -229,6 +236,26 @@ function currentUserFor(req) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dbForWorkshopRequest(req, res) {
|
||||||
|
const appDb = model.getDbForStructure(
|
||||||
|
req.params.structure_id,
|
||||||
|
req.params.db_id,
|
||||||
|
);
|
||||||
|
if (!appDb) {
|
||||||
|
res.status(404).send("Not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const ownDb = String(appDb.db_struct_id) === String(req.params.structure_id);
|
||||||
|
if (
|
||||||
|
!ownDb &&
|
||||||
|
!model.canAccessStructure(req.session.userId, appDb.db_struct_id)
|
||||||
|
) {
|
||||||
|
res.status(404).send("Not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return appDb;
|
||||||
|
}
|
||||||
|
|
||||||
function userIdFrom(value) {
|
function userIdFrom(value) {
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
if (typeof value === "object" && value.id != null) return Number(value.id);
|
if (typeof value === "object" && value.id != null) return Number(value.id);
|
||||||
|
|
@ -925,6 +952,9 @@ app.post("/workshop/:structure_id/db", (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/workshop/:structure_id/db/attach", (req, res) => {
|
app.post("/workshop/:structure_id/db/attach", (req, res) => {
|
||||||
|
if (Number(req.body.db_id) === 0 && Number(req.params.structure_id) !== 0) {
|
||||||
|
return res.status(400).send("prime db cannot be attached");
|
||||||
|
}
|
||||||
model.attachDb(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}`;
|
const redirectUrl = `/workshop/${req.params.structure_id}/db/${req.body.db_id}`;
|
||||||
return smartRedirect(req, res, redirectUrl);
|
return smartRedirect(req, res, redirectUrl);
|
||||||
|
|
@ -993,7 +1023,8 @@ app.put("/workshop/:structure_id/route/:route_id", (req, res) => {
|
||||||
app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
||||||
try {
|
try {
|
||||||
let dbId = req.params.db_id;
|
let dbId = req.params.db_id;
|
||||||
let appDb = model.getDb(dbId);
|
let appDb = dbForWorkshopRequest(req, res);
|
||||||
|
if (!appDb) return;
|
||||||
model.updateDb({ ...appDb, library: req.body.library });
|
model.updateDb({ ...appDb, library: req.body.library });
|
||||||
appDb = model.getDb(dbId);
|
appDb = model.getDb(dbId);
|
||||||
|
|
||||||
|
|
@ -1018,7 +1049,8 @@ app.put("/workshop/:structure_id/db/:db_id/library", (req, res) => {
|
||||||
app.post("/workshop/:structure_id/db/:db_id/repl", (req, res) => {
|
app.post("/workshop/:structure_id/db/:db_id/repl", (req, res) => {
|
||||||
try {
|
try {
|
||||||
let dbId = req.params.db_id;
|
let dbId = req.params.db_id;
|
||||||
let appDb = model.getDb(dbId);
|
let appDb = dbForWorkshopRequest(req, res);
|
||||||
|
if (!appDb) return;
|
||||||
let dbInstance = model.getDbInstance(dbId);
|
let dbInstance = model.getDbInstance(dbId);
|
||||||
let capturedOutput = [];
|
let capturedOutput = [];
|
||||||
let context = vm.createContext({
|
let context = vm.createContext({
|
||||||
|
|
|
||||||
3
migrations/008_block_prime_mounts.sql
Normal file
3
migrations/008_block_prime_mounts.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
DELETE FROM structure_dbs
|
||||||
|
WHERE db_id = 0
|
||||||
|
AND structure_id != 0;
|
||||||
|
|
@ -70,6 +70,42 @@ else
|
||||||
echo "Dependencies unchanged; skipping npm install."
|
echo "Dependencies unchanged; skipping npm install."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f .env ]]; then
|
||||||
|
install -m 600 /dev/null .env
|
||||||
|
else
|
||||||
|
chmod 600 .env || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -Eq '^SESSION_SECRET=.+$' .env; then
|
||||||
|
SESSION_SECRET_VALUE="$(
|
||||||
|
node -e 'process.stdout.write(require("crypto").randomBytes(48).toString("base64url"))'
|
||||||
|
)"
|
||||||
|
if grep -Eq '^SESSION_SECRET=' .env; then
|
||||||
|
ENV_TMP="$(mktemp)"
|
||||||
|
awk -v secret="$SESSION_SECRET_VALUE" '
|
||||||
|
BEGIN { replaced = 0 }
|
||||||
|
/^SESSION_SECRET=/ {
|
||||||
|
if (!replaced) {
|
||||||
|
print "SESSION_SECRET=" secret
|
||||||
|
replaced = 1
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
{ print }
|
||||||
|
END {
|
||||||
|
if (!replaced) print "SESSION_SECRET=" secret
|
||||||
|
}
|
||||||
|
' .env >"$ENV_TMP"
|
||||||
|
cat "$ENV_TMP" >.env
|
||||||
|
rm -f "$ENV_TMP"
|
||||||
|
else
|
||||||
|
printf '\nSESSION_SECRET=%s\n' "$SESSION_SECRET_VALUE" >>.env
|
||||||
|
fi
|
||||||
|
echo "Generated SESSION_SECRET in $APP_DIR/.env"
|
||||||
|
else
|
||||||
|
echo "SESSION_SECRET already present in $APP_DIR/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
PM2_TARGET=""
|
PM2_TARGET=""
|
||||||
if command -v pm2 >/dev/null 2>&1; then
|
if command -v pm2 >/dev/null 2>&1; then
|
||||||
PM2_TARGET="$(pm2 jlist 2>/dev/null | APP_DIR="$APP_DIR" node -e '
|
PM2_TARGET="$(pm2 jlist 2>/dev/null | APP_DIR="$APP_DIR" node -e '
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue