Add auto resolving fields
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Fabian Stamm
2020-03-24 18:33:47 +01:00
parent 68295c148d
commit 0bfdbce908
7 changed files with 245 additions and 180 deletions

View File

@ -154,6 +154,7 @@ export abstract class Query {
} else if (this.permission === "write" && !perm.write) {
throw new QueryError("No permission!");
}
this.query.path = perm.path;
return this._runner.call(
this,
collection,
@ -170,10 +171,12 @@ export abstract class Query {
this.query.path,
this.session
);
if (this.permission === "read" && !perm.read) {
if (!perm.read) {
throw new QueryError("No permission!");
}
this.query.path = perm.path;
const receivedChanges = (changes: Change[]) => {
let res = changes
.filter(change => this.checkChange(change))

View File

@ -61,19 +61,21 @@ export class Rules {
hasPermission(
path: string[],
session: Session
): { read: boolean; write: boolean } {
): { read: boolean; write: boolean; path: string[] } {
if (session.root)
return {
read: true,
write: true
write: true,
path: path
};
let read = this.rules[".read"] || false;
let write = this.rules[".write"] || false;
let rules = this.rules;
for (let segment of path) {
if (segment.startsWith("$") || segment.startsWith(".")) {
for (let idx in path) {
let segment = path[idx];
if (segment.startsWith(".")) {
read = false;
write = false;
Logging.log("Invalid query path (started with '$' or '.'):", path);
@ -85,6 +87,10 @@ export class Rules {
.find(e => {
switch (e) {
case "$uid":
if (segment === "$uid") {
path[idx] = session.uid;
return true;
}
if (segment === session.uid) return true;
break;
}
@ -108,7 +114,8 @@ export class Rules {
return {
read: read as boolean,
write: write as boolean
write: write as boolean,
path
};
}

View File

@ -5,16 +5,26 @@ interface IFormConfigField {
type: "text" | "number" | "boolean" | "textarea";
label: string;
value?: string;
disabled?: boolean;
}
type IFormConfig = { [name: string]: IFormConfigField }
type IFormConfig = { [name: string]: IFormConfigField };
export default function getForm(url: string, title: string, fieldConfig: IFormConfig): (ctx: Context) => void {
let fields = Object.keys(fieldConfig).map(name => ({ name, ...fieldConfig[name] }))
export default function getForm(
url: string,
title: string,
fieldConfig: IFormConfig
): (ctx: Context) => void {
let fields = Object.keys(fieldConfig).map(name => ({
name,
...fieldConfig[name],
disabled: fieldConfig.disabled ? "disabled" : ""
}));
return ctx => ctx.body = getTemplate("forms")({
url,
title,
fields
});
}
return ctx =>
(ctx.body = getTemplate("forms")({
url,
title,
fields
}));
}

View File

@ -2,7 +2,11 @@ import * as Router from "koa-router";
import Settings from "../../settings";
import getForm from "../helper/form";
import getTable from "../helper/table";
import { BadRequestError, NoPermissionError } from "../helper/errors";
import {
BadRequestError,
NoPermissionError,
NotFoundError
} from "../helper/errors";
import { DatabaseManager } from "../../database/database";
import { MP } from "../../database/query";
import config from "../../config";
@ -13,10 +17,9 @@ const AdminRoute = new Router();
AdminRoute.use(async (ctx, next) => {
const { key } = ctx.query;
if (key !== config.admin)
throw new NoPermissionError("No permission!");
if (key !== config.admin) throw new NoPermissionError("No permission!");
return next();
})
});
AdminRoute.get("/", async ctx => {
//TODO: Main Interface
@ -33,26 +36,24 @@ AdminRoute.get("/settings", async ctx => {
let res = [["key", "value"]];
stream.on("data", ({ key, value }) => {
res.push([key, value]);
})
});
stream.on("error", no);
stream.on("end", () => yes(res))
})
stream.on("end", () => yes(res));
});
if (ctx.query.view) {
return getTable("Settings", res, ctx);
} else {
ctx.body = res;
}
})
});
AdminRoute.get("/data", async ctx => {
const { database } = ctx.query;
let db = DatabaseManager.getDatabase(database);
if (!db)
throw new BadRequestError("Database not found");
if (!db) throw new BadRequestError("Database not found");
let res = await new Promise<string[][]>((yes, no) => {
const stream = db.data.createReadStream({
keys: true,
values: true,
@ -61,73 +62,71 @@ AdminRoute.get("/data", async ctx => {
limit: 1000
});
let res = [["key", "value"]];
stream.on("data", ({ key, value }: { key: string, value: Buffer }) => {
res.push([key, key.split("/").length > 2 ? value.toString() : JSON.stringify(MP.decode(value))]);
})
stream.on("data", ({ key, value }: { key: string; value: Buffer }) => {
res.push([
key,
key.split("/").length > 2
? value.toString()
: JSON.stringify(MP.decode(value))
]);
});
stream.on("error", no);
stream.on("end", () => yes(res))
})
stream.on("end", () => yes(res));
});
if (ctx.query.view) {
return getTable("Data from " + database, res, ctx);
} else {
ctx.body = res;
}
})
});
AdminRoute
.get("/database", ctx => {
const isFull = ctx.query.full === "true" || ctx.query.full === "1";
let res;
if (isFull) {
//TODO: Better than JSON.parse / JSON.stringify
res = Array.from(DatabaseManager.databases.entries()).map(([name, config]) => ({ name, ...(JSON.parse(JSON.stringify(config))) }));
} else {
res = Array.from(DatabaseManager.databases.keys());
}
AdminRoute.get("/database", ctx => {
const isFull = ctx.query.full === "true" || ctx.query.full === "1";
let res;
if (isFull) {
//TODO: Better than JSON.parse / JSON.stringify
res = Array.from(DatabaseManager.databases.entries()).map(
([name, config]) => ({
name,
...JSON.parse(JSON.stringify(config))
})
);
} else {
res = Array.from(DatabaseManager.databases.keys());
}
if (ctx.query.view) {
return getTable("Databases" + (isFull ? "" : " small"), res, ctx);
} else {
ctx.body = res;
}
})
.post("/database", async ctx => {
const { name, rules, publickey, accesskey, rootkey } = ctx.request.body;
if (ctx.query.view) {
return getTable("Databases" + (isFull ? "" : " small"), res, ctx);
} else {
ctx.body = res;
}
}).post("/database", async ctx => {
const { name, rules, publickey, accesskey, rootkey } = ctx.request.body;
if (!name)
throw new BadRequestError("Name must be set!");
if (!name) throw new BadRequestError("Name must be set!");
let db = DatabaseManager.getDatabase(name);
if (!db)
db = await DatabaseManager.addDatabase(name);
let db = DatabaseManager.getDatabase(name);
if (!db) db = await DatabaseManager.addDatabase(name);
if (publickey)
await db.setPublicKey(publickey);
if (publickey) await db.setPublicKey(publickey);
if (rules)
await db.setRules(rules);
if (rules) await db.setRules(rules);
if (accesskey)
await db.setAccessKey(accesskey);
if (accesskey) await db.setAccessKey(accesskey);
if (rootkey) await db.setRootKey(rootkey);
if (rootkey)
await db.setRootKey(rootkey);
ctx.body = "Success";
})
ctx.body = "Success";
});
AdminRoute.get("/collections", async ctx => {
const { database } = ctx.query;
let db = DatabaseManager.getDatabase(database);
if (!db)
throw new BadRequestError("Database not found");
if (!db) throw new BadRequestError("Database not found");
let res = await new Promise<string[]>((yes, no) => {
const stream = db.collections.createKeyStream({
keyAsBuffer: false,
limit: 1000
@ -135,24 +134,23 @@ AdminRoute.get("/collections", async ctx => {
let res = [];
stream.on("data", (key: string) => {
res.push(key);
})
});
stream.on("error", no);
stream.on("end", () => yes(res))
})
stream.on("end", () => yes(res));
});
if (ctx.query.view) {
return getTable("Databases", res, ctx);
} else {
ctx.body = res;
}
})
});
AdminRoute.get("/collections/cleanup", async ctx => {
const { database } = ctx.query;
let db = DatabaseManager.getDatabase(database);
if (!db)
throw new BadRequestError("Database not found");
if (!db) throw new BadRequestError("Database not found");
let deleted = await db.runCleanup();
if (ctx.query.view) {
@ -160,14 +158,55 @@ AdminRoute.get("/collections/cleanup", async ctx => {
} else {
ctx.body = deleted;
}
})
});
AdminRoute.get("/database/new", getForm("/v1/admin/database", "New/Change Database", {
name: { label: "Name", type: "text", },
accesskey: { label: "Access Key", type: "text" },
rootkey: { label: "Root access key", type: "text" },
rules: { label: "Rules", type: "textarea", value: `{\n ".write": true, \n ".read": true \n}` },
publickey: { label: "Public Key", type: "textarea" }
}))
AdminRoute.get(
"/database/new",
getForm("/v1/admin/database", "New Database", {
name: { label: "Name", type: "text" },
accesskey: { label: "Access Key", type: "text" },
rootkey: { label: "Root access key", type: "text" },
rules: {
label: "Rules",
type: "textarea",
value: `{\n ".write": true, \n ".read": true \n}`
},
publickey: { label: "Public Key", type: "textarea" }
})
);
export default AdminRoute;
AdminRoute.get("/database/update", async ctx => {
const { database } = ctx.query;
let db = DatabaseManager.getDatabase(database);
if (!db) throw new NotFoundError("Database not found!");
getForm("/v1/admin/database", "Change Database", {
name: {
label: "Name",
type: "text",
value: db.name,
disabled: true
},
accesskey: {
label: "Access Key",
type: "text",
value: db.accesskey
},
rootkey: {
label: "Root access key",
type: "text",
value: db.rootkey
},
rules: {
label: "Rules",
type: "textarea",
value: db.rules.toJSON()
},
publickey: {
label: "Public Key",
type: "textarea",
value: db.publickey
}
})(ctx);
});
export default AdminRoute;