@wocker/pgsql-plugin
Version:
PostgreSQL plugin for wocker
469 lines (468 loc) • 20.2 kB
JavaScript
;
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);