Adding Dockerfile and build script

This commit is contained in:
Fabian 2019-10-01 17:45:38 +02:00
parent 65588f4b98
commit 405e589328
11 changed files with 98 additions and 45 deletions

32
Dockerfile Normal file
View File

@ -0,0 +1,32 @@
FROM node:12
LABEL maintainer="Fabian Stamm <dev@fabianstamm.de>"
RUN apt-get update
# for https
RUN apt-get install -yyq ca-certificates
# install libraries
RUN apt-get install -yyq libappindicator1 libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6
# tools
RUN apt-get install -yyq gconf-service lsb-release wget xdg-utils
# and fonts
RUN apt-get install -yyq fonts-liberation
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app
COPY package-lock.json /usr/src/app
COPY lib/ /usr/src/app
RUN npm install
VOLUME [ "/usr/src/app/databases", "/usr/src/app/logs" ]
EXPOSE 5000/tcp
CMD ["npm", "run", "start"]

View File

@ -1,5 +0,0 @@
[general]
dev=true
port=5013
admin=admin
access_log=true

16
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "@hibas123/realtime-db", "name": "@hibas123/realtimedb",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
@ -76,6 +76,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/dotenv": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
"integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/events": { "@types/events": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@ -930,6 +939,11 @@
"is-obj": "^1.0.0" "is-obj": "^1.0.0"
} }
}, },
"dotenv": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz",
"integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA=="
},
"duplexer3": { "duplexer3": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",

View File

@ -9,11 +9,13 @@
"watch-ts": "tsc -w", "watch-ts": "tsc -w",
"watch-node": "nodemon --ignore *.ts lib/index.js", "watch-node": "nodemon --ignore *.ts lib/index.js",
"watch": "concurrently \"npm:watch-*\"", "watch": "concurrently \"npm:watch-*\"",
"build-docker": "npm run build && docker build -t realtimedb .",
"prepublishOnly": "tsc" "prepublishOnly": "tsc"
}, },
"author": "Fabian Stamm <dev@fabianstamm.de>", "author": "Fabian Stamm <dev@fabianstamm.de>",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/dotenv": "^6.1.1",
"@types/ini": "^1.3.30", "@types/ini": "^1.3.30",
"@types/leveldown": "^4.0.0", "@types/leveldown": "^4.0.0",
"@types/levelup": "^3.1.1", "@types/levelup": "^3.1.1",
@ -30,6 +32,7 @@
"@hibas123/utils": "^2.1.1", "@hibas123/utils": "^2.1.1",
"@types/koa": "^2.0.49", "@types/koa": "^2.0.49",
"@types/koa-router": "^7.0.42", "@types/koa-router": "^7.0.42",
"dotenv": "^8.1.0",
"handlebars": "^4.2.0", "handlebars": "^4.2.0",
"ini": "^1.3.5", "ini": "^1.3.5",
"koa": "^2.8.1", "koa": "^2.8.1",

View File

@ -1,16 +1,20 @@
import * as ini from "ini";
import * as fs from "fs";
import Logging from "@hibas123/nodelogging"; import Logging from "@hibas123/nodelogging";
import * as dotenv from "dotenv";
interface IConfig { interface IConfig {
general: { port: number;
port: string;
admin: string; admin: string;
access_log: boolean; access_log: boolean;
dev: boolean dev: boolean
}
} }
const config = ini.parse(fs.readFileSync("config.ini").toString()) as IConfig; const config: IConfig = {
Logging.debug("Config:", config); port: Number(process.env.PORT),
access_log: (process.env.ACCESS_LOG || "").toLowerCase() === "true",
admin: process.env.ADMIN_KEY,
dev: (process.env.DEV || "").toLowerCase() === "true"
}
dotenv.config()
export default config; export default config;

View File

@ -13,7 +13,7 @@ export class DatabaseManager {
let databases = await Settings.getDatabases(); let databases = await Settings.getDatabases();
databases.forEach(dbconfig => { databases.forEach(dbconfig => {
let db = new Database(dbconfig.name, dbconfig.accesskey, dbconfig.rules, dbconfig.publickey); let db = new Database(dbconfig.name, dbconfig.accesskey, dbconfig.rules, dbconfig.publickey, dbconfig.rootkey);
this.databases.set(dbconfig.name, db); this.databases.set(dbconfig.name, db);
}) })
} }
@ -68,7 +68,7 @@ export class Database {
} }
} }
constructor(public name: string, public accesskey?: string, rawRules?: string, public publickey?: string) { constructor(public name: string, public accesskey?: string, rawRules?: string, public publickey?: string, public rootkey?: string) {
if (rawRules) if (rawRules)
this.rules = new Rules(rawRules); this.rules = new Rules(rawRules);
} }
@ -84,11 +84,17 @@ export class Database {
this.accesskey = key; this.accesskey = key;
} }
async setRootKey(key: string) {
await Settings.setDatabaseAccessKey(this.name, key);
this.accesskey = key;
}
async setPublicKey(key: string) { async setPublicKey(key: string) {
await Settings.setDatabasePublicKey(this.name, key); await Settings.setDatabasePublicKey(this.name, key);
this.publickey = key; this.publickey = key;
} }
getQuery(path: string[]) { getQuery(path: string[]) {
return new Query(this, path); return new Query(this, path);
} }

View File

@ -1,18 +1,14 @@
import Logging from "@hibas123/nodelogging"; import Logging from "@hibas123/nodelogging";
// import getLevelDB from "./storage";
// import Settings from "./settings";
import Web from "./web"; import Web from "./web";
import config from "./config"; import config from "./config";
import { DatabaseManager } from "./database/database"; import { DatabaseManager } from "./database/database";
import { createServer } from "http"; import { createServer } from "http";
import { ConnectionManager } from "./connection"; import { ConnectionManager } from "./connection";
// Logging.debug(getLevelDB("system"));
DatabaseManager.init().then(() => { DatabaseManager.init().then(() => {
const http = createServer(Web.callback()); const http = createServer(Web.callback());
ConnectionManager.bind(http); ConnectionManager.bind(http);
const port = Number(config.general.port) || 5013; const port = config.port || 5000;
http.listen(port, () => Logging.log("Listening on port:", port)) http.listen(port, () => Logging.log("Listening on port:", port))
}).catch(err => { }).catch(err => {
Logging.error(err); Logging.error(err);

View File

@ -1,7 +1,4 @@
import getLevelDB, { resNull } from "./storage"; import getLevelDB, { resNull } from "./storage";
import Encoder, { DataTypes } from "@hibas123/binary-encoder";
import Logging from "@hibas123/nodelogging";
import { Lock } from "@hibas123/utils"; import { Lock } from "@hibas123/utils";
interface IDatabaseConfig { interface IDatabaseConfig {
@ -9,19 +6,9 @@ interface IDatabaseConfig {
publickey?: string; publickey?: string;
rules?: string; rules?: string;
accesskey?: string; accesskey?: string;
rootkey?: string;
} }
// const DatabaseEncoder = new Encoder<IDatabaseConfig>({
// publickey: {
// index: 1,
// type: DataTypes.STRING,
// allowNull: true
// },
// rules: {
// index: 2,
// type: DataTypes.STRING
// }
// });
class SettingComponent { class SettingComponent {
db = getLevelDB("_server"); db = getLevelDB("_server");
@ -54,7 +41,8 @@ class SettingComponent {
await Promise.all([ await Promise.all([
this.getField(database, "publickey").then(r => res.publickey = r), this.getField(database, "publickey").then(r => res.publickey = r),
this.getField(database, "rules").then(r => res.rules = r), this.getField(database, "rules").then(r => res.rules = r),
this.getField(database, "accesskey").then(r => res.accesskey = r) this.getField(database, "accesskey").then(r => res.accesskey = r),
this.getField(database, "rootkey").then(r => res.rootkey = r)
]) ])
return res; return res;
}))) })))
@ -107,6 +95,14 @@ class SettingComponent {
lock.release(); lock.release();
} }
async setDatabaseRootKey(name: string, accesskey: string) {
const lock = await this.databaseLock.getLock();
await this.setField(name, "rootkey", accesskey);
lock.release();
}
async deleteDatabase(name: string) { async deleteDatabase(name: string) {
const lock = await this.databaseLock.getLock(); const lock = await this.databaseLock.getLock();
@ -119,6 +115,7 @@ class SettingComponent {
.del(pref + ":publickey") .del(pref + ":publickey")
.del(pref + ":rules") .del(pref + ":rules")
.del(pref + ":accesskey") .del(pref + ":accesskey")
.del(pref + ":rootkey")
.write(); .write();
lock.release(); lock.release();

View File

@ -42,7 +42,7 @@ const cache = new Map<string, Handlebars.TemplateDelegate>();
export default function getTemplate(name: string) { export default function getTemplate(name: string) {
let tl: Handlebars.TemplateDelegate; let tl: Handlebars.TemplateDelegate;
if (!config.general.dev) if (!config.dev)
tl = cache.get(name); tl = cache.get(name);
if (!tl) { if (!tl) {

View File

@ -2,9 +2,9 @@ import { LoggingBase } from "@hibas123/nodelogging";
import { Context } from "koa"; import { Context } from "koa";
import config from "../../config"; import config from "../../config";
const route_logging = new LoggingBase({ name: "access", files: { errorfile: null }, console: config.general.dev }) const route_logging = new LoggingBase({ name: "access", files: { errorfile: null }, console: config.dev })
const RequestLog = async (ctx: Context, next) => { const RequestLog = async (ctx: Context, next) => {
if (!config.general.access_log) return next(); if (!config.access_log) return next();
let start = process.hrtime() let start = process.hrtime()
let to = false let to = false
let print = () => { let print = () => {

View File

@ -13,7 +13,7 @@ const AdminRoute = new Router();
AdminRoute.use(async (ctx, next) => { AdminRoute.use(async (ctx, next) => {
const { key } = ctx.query; const { key } = ctx.query;
if (key !== config.general.admin) if (key !== config.admin)
throw new NoPermissionError("No permission!"); throw new NoPermissionError("No permission!");
return next(); return next();
}) })
@ -87,7 +87,7 @@ AdminRoute
} }
}) })
.post("/database", async ctx => { .post("/database", async ctx => {
const { name, rules, publickey, accesskey } = ctx.request.body; const { name, rules, publickey, accesskey, rootkey } = ctx.request.body;
if (!name) if (!name)
throw new BadRequestError("Name must be set!"); throw new BadRequestError("Name must be set!");
@ -101,16 +101,22 @@ AdminRoute
if (rules) if (rules)
await db.setRules(rules); await db.setRules(rules);
db
if (accesskey) if (accesskey)
await db.setAccessKey(accesskey); await db.setAccessKey(accesskey);
if (rootkey)
await db.setRootKey(rootkey);
ctx.body = "Success"; ctx.body = "Success";
}) })
AdminRoute.get("/database/new", getForm("/v1/admin/database", "New/Change Database", { AdminRoute.get("/database/new", getForm("/v1/admin/database", "New/Change Database", {
name: { label: "Name", type: "text", }, name: { label: "Name", type: "text", },
accesskey: { label: "Access Key", 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}` }, rules: { label: "Rules", type: "textarea", value: `{\n ".write": true, \n ".read": true \n}` },
publickey: { label: "Public Key", type: "textarea" } publickey: { label: "Public Key", type: "textarea" }
})) }))