UNPKG

@wocker/pgsql-plugin

Version:
469 lines (468 loc) 20.2 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PgSqlService = void 0; const core_1 = require("@wocker/core"); const utils_1 = require("@wocker/utils"); const cli_table3_1 = __importDefault(require("cli-table3")); const Config_1 = require("../makes/Config"); const Service_1 = require("../makes/Service"); let PgSqlService = class PgSqlService { constructor(appConfigService, pluginConfigService, dockerService, proxyService) { this.appConfigService = appConfigService; this.pluginConfigService = pluginConfigService; this.dockerService = dockerService; this.proxyService = proxyService; this.adminContainerName = "dbadmin-pgsql.workspace"; } get config() { if (!this._config) { const fs = this.fs, data = fs.exists("config.json") ? fs.readJSON("config.json") : {}; this._config = new class extends Config_1.Config { save() { if (!fs.exists("")) { fs.mkdir("", { recursive: true }); } fs.writeJSON("config.json", this.toJSON()); } }(data); } return this._config; } get services() { return this.config.services; } get fs() { let fs = this.pluginConfigService.fs; if (!fs) { fs = new core_1.FileSystem(this.pluginConfigService.dataPath()); } return fs; } get dbFs() { return new core_1.FileSystem(this.appConfigService.dataPath("db/pgsql")); } dbPath(service) { return this.appConfigService.dataPath("db/pgsql", service); } init(admin) { return __awaiter(this, void 0, void 0, function* () { if (typeof admin.enabled === "undefined") { admin.enabled = yield (0, utils_1.promptConfirm)({ message: "Enable admin?" }); } else { this.config.admin.enabled = admin.enabled; } if (this.config.admin.enabled) { if (!admin.email) { this.config.admin.email = yield (0, utils_1.promptInput)({ message: "Email", type: "text", default: this.config.admin.email || "root@pgsql.ws" }); } else { this.config.admin.email = admin.email; } if (!admin.password) { this.config.admin.password = yield (0, utils_1.promptInput)({ message: "Password", type: "text", default: this.config.admin.password || "toor" }); } else { this.config.admin.password = admin.password; } if (typeof admin.skipPassword === "undefined") { this.config.admin.skipPassword = yield (0, utils_1.promptConfirm)({ message: "Skip password", default: this.config.admin.skipPassword }); } else { this.config.admin.skipPassword = admin.skipPassword; } } this.config.save(); }); } create() { return __awaiter(this, arguments, void 0, function* (serviceProps = {}) { if (serviceProps.name && this.config.hasService(serviceProps.name)) { console.info(`Service "${serviceProps.name}" is already exists`); delete serviceProps.name; } if (!serviceProps.name) { serviceProps.name = yield (0, utils_1.promptInput)({ message: "Service name", validate: (name) => { if (!name) { return "Service name is required"; } if (this.config.getService(name)) { return `Service "${name}" is already exists`; } return true; } }); } if (!serviceProps.user) { serviceProps.user = yield (0, utils_1.promptInput)({ message: "Database user", type: "text", required: true, default: serviceProps.user }); } if (!serviceProps.password) { serviceProps.password = yield (0, utils_1.promptInput)({ message: "Database password", type: "password", required: true, }); const confirmPassword = yield (0, utils_1.promptInput)({ message: "Confirm password", type: "password", required: true }); if (serviceProps.password !== confirmPassword) { throw new Error("Passwords do not match"); } } if (!serviceProps.storage || ![Service_1.STORAGE_VOLUME, Service_1.STORAGE_FILESYSTEM].includes(serviceProps.storage)) { serviceProps.storage = yield (0, utils_1.promptSelect)({ message: "Storage:", options: [Service_1.STORAGE_VOLUME, Service_1.STORAGE_FILESYSTEM] }); } if (!serviceProps.containerPort) { const needPort = yield (0, utils_1.promptConfirm)({ message: "Do you need to expose container port?", default: false }); if (needPort) { serviceProps.containerPort = yield (0, utils_1.promptInput)({ required: true, message: "Container port:", type: "number", min: 1, default: 5432 }); } } this.config.setService(new Service_1.Service(serviceProps)); this.config.save(); }); } upgrade(serviceProps) { return __awaiter(this, void 0, void 0, function* () { const service = this.config.getServiceOrDefault(serviceProps.name); let changed = false; if (serviceProps.imageName) { service.imageName = serviceProps.imageName; changed = true; } if (serviceProps.imageVersion) { service.imageVersion = serviceProps.imageVersion; changed = true; } if (serviceProps.containerPort) { service.containerPort = serviceProps.containerPort; changed = true; } if (changed) { this.config.setService(service); this.config.save(); } }); } destroy(name, yes, force) { return __awaiter(this, void 0, void 0, function* () { const service = this.config.getServiceOrDefault(name); if (!force && service.name === this.config.default) { throw new Error(`Can't delete default service.`); } if (!yes) { const confirm = yield (0, utils_1.promptConfirm)({ message: `Are you sure you want to delete "${service.name}" service?`, default: false }); if (!confirm) { throw new Error("Aborted"); } } if (!service.host) { yield this.dockerService.removeContainer(service.containerName); switch (service.storage) { case Service_1.STORAGE_VOLUME: if (service.volume !== service.defaultVolume) { console.info(`Deletion of custom volume "${service.volume}" skipped.`); break; } if (!this.pluginConfigService.isVersionGTE("1.0.19")) { throw new Error("Please update wocker for using volume storage"); } if (yield this.dockerService.hasVolume(service.volume)) { yield this.dockerService.rmVolume(service.volume); } break; case Service_1.STORAGE_FILESYSTEM: this.dbFs.rm(service.name, { recursive: true, force: true }); break; default: throw new Error(`Unknown storage type "${service.storage}"`); } } this.config.unsetService(service.name); this.config.save(); }); } listTable() { return __awaiter(this, void 0, void 0, function* () { const table = new cli_table3_1.default({ head: ["Name", "Host"] }); for (const service of this.config.services) { table.push([ service.name + (this.config.default === service.name ? " (default)" : ""), service.host || service.containerName ]); } return table.toString(); }); } start(name, restart) { return __awaiter(this, void 0, void 0, function* () { if (!name && !this.config.default) { yield this.create(); } const service = this.config.getServiceOrDefault(name); if (restart) { yield this.dockerService.removeContainer(service.containerName); } let container = yield this.dockerService.getContainer(service.containerName); if (!container) { const { user = "root", password = "root" } = service; const volumes = []; switch (service.storage) { case Service_1.STORAGE_VOLUME: if (!this.pluginConfigService.isVersionGTE("1.0.19")) { throw new Error("Please update wocker for using volume storage"); } if (!(yield this.dockerService.hasVolume(service.volume))) { yield this.dockerService.createVolume(service.volume); } volumes.push(`${service.volume}:/var/lib/postgresql/data`); break; case Service_1.STORAGE_FILESYSTEM: volumes.push(`${this.dbPath(service.name)}:/var/lib/postgresql/data`); break; default: throw new Error(`Unknown storage type "${service.storage}"`); } container = yield this.dockerService.createContainer({ name: service.containerName, image: service.imageTag, restart: "always", volumes: volumes, env: { POSTGRES_USER: user, POSTGRES_PASSWORD: password }, ports: service.containerPort ? [`${service.containerPort}:5432`] : undefined }); } const { State: { Running } } = yield container.inspect(); if (!Running) { yield container.start(); } console.info(`Started ${service.name} at ${service.containerName}`); }); } stop(name) { return __awaiter(this, void 0, void 0, function* () { const service = this.config.getServiceOrDefault(name); yield this.dockerService.removeContainer(service.containerName); }); } pgsql(name) { return __awaiter(this, void 0, void 0, function* () { const service = this.config.getServiceOrDefault(name); const container = yield this.dockerService.getContainer(service.containerName); if (!container) { throw new Error(`Service "${service.name}" isn't started`); } yield this.dockerService.exec(service.containerName, { tty: true, cmd: ["psql"] }); }); } dump(name) { return __awaiter(this, void 0, void 0, function* () { const service = this.config.getServiceOrDefault(name); const container = yield this.dockerService.getContainer(service.containerName); if (!container) { throw new Error(`Service "${service.name}" isn't started`); } yield this.dockerService.exec(service.containerName, { tty: true, cmd: ["pg_dump"] }); }); } admin() { return __awaiter(this, void 0, void 0, function* () { const config = this.config; if (!config.admin.email || !config.admin.password) { console.info("Can't start admin credentials missed"); return; } const servers = []; const passwords = {}; for (const service of config.services || []) { let host; let port = 5432; if (service.host) { host = service.host; if (service.port) { port = service.port; } } else { const container = yield this.dockerService.getContainer(service.containerName); if (!container) { continue; } const { State: { Running } } = yield container.inspect(); if (!Running) { continue; } host = service.containerName; } passwords[service.name] = `${host}:${port}:postgres:${service.user || ""}:${service.password || ""}`; servers.push({ Group: "Servers", Name: service.name, Host: host, Port: 5432, MaintenanceDB: "postgres", Username: service.user, PassFile: `/var/lib/pgadmin/storage/passwords/${service.name}.pgpass`, SSLMode: "prefer" }); } yield this.dockerService.removeContainer(this.adminContainerName); if (!this.config.admin.enabled || servers.length === 0) { return; } this.fs.writeJSON("servers.json", { Servers: servers.reduce((res, server, index) => { return Object.assign(Object.assign({}, res), { [`${index}`]: server }); }, {}) }); let container = yield this.dockerService.getContainer(this.adminContainerName); if (!container) { container = yield this.dockerService.createContainer({ name: this.adminContainerName, image: "dpage/pgadmin4:latest", user: "root:root", restart: "always", entrypoint: [ "/bin/sh", "-c", [ "mkdir -p /var/lib/pgadmin/storage/passwords", ...Object.keys(passwords).map((name) => { return `echo '${passwords[name]}' > /var/lib/pgadmin/storage/passwords/${name}.pgpass`; }), "chmod -R 600 /var/lib/pgadmin/storage/passwords/", "chown -R root:root /var/lib/pgadmin/storage/passwords", "/entrypoint.sh" ].join(";") + ";" ], volumes: [ "wocker-pgadmin:/var/lib/pgadmin", `${this.fs.path("servers.json")}:/pgadmin4/servers.json` ], env: Object.assign({ VIRTUAL_HOST: this.adminContainerName, PGADMIN_DEFAULT_EMAIL: config.admin.email || "", PGADMIN_DEFAULT_PASSWORD: config.admin.password || "" }, config.admin.skipPassword ? { PGADMIN_CONFIG_SERVER_MODE: "False", PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False" } : {}) }); } const { State: { Running } } = yield container.inspect(); if (!Running) { yield container.start(); try { yield this.proxyService.start(); } catch (err) { // } } console.info(`Admin started at ${this.adminContainerName}`); if (!config.admin.skipPassword) { console.info(`Login: ${config.admin.email}`); console.info(`Password: ****`); } else { console.info("Password skipped"); } }); } setDefault(name) { return __awaiter(this, void 0, void 0, function* () { const config = this.config; if (!config.getService(name)) { throw new Error(`Service "${name}" not found`); } config.default = name; config.save(); }); } getServices() { return __awaiter(this, void 0, void 0, function* () { return this.services.map((service) => { return service.name; }); }); } }; exports.PgSqlService = PgSqlService; exports.PgSqlService = PgSqlService = __decorate([ (0, core_1.Injectable)(), __metadata("design:paramtypes", [core_1.AppConfigService, core_1.PluginConfigService, core_1.DockerService, core_1.ProxyService]) ], PgSqlService);