add: postgresql!

pull/245/head
xwashere 11 months ago
parent 9c9f2ac768
commit 0d1172d332
No known key found for this signature in database
GPG Key ID: 042F8BFA1B0EF93B

@ -1,4 +1,4 @@
import { UserConfiguration, UserConfigTypeChecker } from 'ass';
import { UserConfiguration, UserConfigTypeChecker, PostgresConfiguration } from 'ass';
import fs from 'fs-extra';
import { path } from '@tycrek/joint';
@ -56,7 +56,10 @@ const Checkers: UserConfigTypeChecker = {
host: basicStringChecker,
user: basicStringChecker,
password: basicStringChecker,
database: basicStringChecker
database: basicStringChecker,
},
postgres: {
port: (val) => numChecker(val) && val >= 1 && val <= 65535
}
},
@ -111,6 +114,11 @@ export class UserConfig {
if (!Checkers.sql.mySql.user(config.database.options.user)) throw new Error('Invalid databse user');
if (!Checkers.sql.mySql.password(config.database.options.password)) throw new Error('Invalid database password');
if (!Checkers.sql.mySql.database(config.database.options.database)) throw new Error('Invalid database');
if (config.database.kind == 'postgres') {
if (!Checkers.sql.postgres.port((config.database.options as PostgresConfiguration).port)) {
throw new Error("Invalid database port");
}
}
} else throw new Error('Database options missing');
}
}

@ -1,20 +1,144 @@
import { PostgresConfiguration } from 'ass';
import { Client } from 'pg';
import { log } from '../log';
import { Database, DatabaseTable, DatabaseValue } from './database';
import { UserConfig } from '../UserConfig';
/**
* database adapter for postgresql
*/
export class PostgreSQLDatabase implements Database {
private _client: Client;
public open(): Promise<void> { return Promise.resolve(); }
public close(): Promise<void> { return Promise.resolve(); }
/**
* validate config
*/
private _validateConfig(): string | undefined {
// make sure the configuration exists
if (!UserConfig.ready) return 'User configuration not ready';
if (typeof UserConfig.config.database != 'object') return 'PostgreSQL configuration missing';
if (UserConfig.config.database.kind != "postgres") return 'Database not set to PostgreSQL, but PostgreSQL is in use, something has gone terribly wrong';
if (typeof UserConfig.config.database.options != 'object') return 'PostgreSQL configuration missing';
let config = UserConfig.config.database.options;
// check the postgres config
const checker = (val: string) => val != null && val !== '';
const issue =
!checker(config.host) ? 'Missing PostgreSQL Host'
: !checker(config.user) ? 'Missing PostgreSQL User'
: !checker(config.password) ? 'Missing PostgreSQL Password'
: !checker(config.database) ? 'Missing PostgreSQL Database'
// ! Blame VS Code for this weird indentation
: undefined;
return issue;
}
public open(): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
// config check
let configError = this._validateConfig();
if (configError) throw new Error(configError);
// grab the config
let config = UserConfig.config.database!.options! as PostgresConfiguration;
// set up the client
this._client = new Client({
host: config.host,
port: config.port,
user: config.user,
password: config.password,
database: config.database,
});
// connect to the database
log.info('PostgreSQL', `connecting to ${config.host}:${config.port}`);
await this._client.connect();
log.success('PostgreSQL', 'ok');
resolve();
} catch (err) {
log.error('PostgreSQL', 'failed to connect');
console.error(err);
reject(err);
}
});
}
public close(): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
// gracefully disconnect
await this._client.end();
resolve();
} catch (err) {
log.error('PostgreSQL', 'failed to disconnect');
console.error(err);
reject(err);
}
});
}
public configure(): Promise<void> {
return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
try {
await this._client.query(
`CREATE TABLE IF NOT EXISTS asstables (
name TEXT PRIMARY KEY,
version INT NOT NULL
);`);
log.info('PostgreSQL', 'checking database');
// update tables
let seenRows = new Set<string>();
let versions = await this._client.query('SELECT * FROM asstables;');
for (let row of versions.rows) {
seenRows.add(row.name);
}
const assTableSchema = '(id TEXT PRIMARY KEY, data JSON NOT NULL)'
// add missing tables
if (!seenRows.has('assfiles')) {
log.warn('PostgreSQL', 'assfiles missing, repairing...')
await this._client.query(
`CREATE TABLE assfiles ${assTableSchema};` +
`INSERT INTO asstables (name, version) VALUES ('assfiles', 1);`
);
log.success('PostgreSQL', 'ok');
}
if (!seenRows.has('assusers')) {
log.warn('PostgreSQL', 'asstokens missing, repairing...')
await this._client.query(
`CREATE TABLE assusers ${assTableSchema};` +
`INSERT INTO asstables (name, version) VALUES ('assusers', 1);`
);
log.success('PostgreSQL', 'ok');
}
if (!seenRows.has('asstokens')) {
log.warn('PostgreSQL', 'asstokens missing, repairing...')
await this._client.query(
`CREATE TABLE asstokens ${assTableSchema};` +
`INSERT INTO asstables (name, version) VALUES ('asstokens', 1);`
);
log.success('PostgreSQL', 'ok');
}
log.success('PostgreSQL', 'database is ok').callback(() => {
resolve();
});
} catch (err) {
log.error('PostgreSQL', 'failed to initialize');
log.error('PostgreSQL', 'failed to set up');
console.error(err);
reject(err);
}
@ -22,14 +146,56 @@ export class PostgreSQLDatabase implements Database {
}
public put(table: DatabaseTable, key: string, data: DatabaseValue): Promise<void> {
throw new Error("Method not implemented.");
return new Promise(async (resolve, reject) => {
try {
const queries = {
assfiles: 'INSERT INTO assfiles (id, data) VALUES ($1, $2)',
assusers: 'INSERT INTO assusers (id, data) VALUES ($1, $2)',
asstokens: 'INSERT INTO asstokens (id, data) VALUES ($1, $2)'
};
let result = await this._client.query(queries[table], [key, data]);
resolve();
} catch (err) {
reject(err);
}
});
}
public get(table: DatabaseTable, key: string): Promise<DatabaseValue | undefined> {
throw new Error("Method not implemented.");
return new Promise(async (resolve, reject) => {
try {
const queries = {
assfiles: 'SELECT data FROM assfiles WHERE id = $1::text',
assusers: 'SELECT data FROM assusers WHERE id = $1::text',
asstokens: 'SELECT data FROM asstokens WHERE id = $1::text'
};
let result = await this._client.query(queries[table], [key]);
resolve(result.rowCount ? result.rows[0].data : void 0);
} catch (err) {
reject(err);
}
});
}
public getAll(table: DatabaseTable): Promise<{ [index: string]: DatabaseValue; }> {
throw new Error("Method not implemented.");
return new Promise(async (resolve, reject) => {
try {
const queries = {
assfiles: 'SELECT json_object_agg(id, data) AS stuff FROM assfiles;',
assusers: 'SELECT json_object_agg(id, data) AS stuff FROM assusers;',
asstokens: 'SELECT json_object_agg(id, data) AS stuff FROM asstokens;'
};
let result = await this._client.query(queries[table]);
resolve(result.rowCount ? result.rows[0].stuff : void 0);
} catch (err) {
reject(err);
}
});
}
}

4
common/types.d.ts vendored

@ -65,6 +65,7 @@ declare module 'ass' {
interface PostgresConfiguration {
host: string;
port: number;
user: string;
password: string;
database: string;
@ -129,6 +130,9 @@ declare module 'ass' {
password: (val: any) => boolean;
database: (val: any) => boolean;
}
postgres: {
port: (val: any) => boolean;
}
}
rateLimit: {
endpoint: (val: any) => boolean;

@ -51,6 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
pgsqlTab: document.querySelector('#pgsql-tab') as SlTab,
pgsqlHost: document.querySelector('#pgsql-host') as SlInput,
pgsqlPort: document.querySelector('#pgsql-port') as SlInput,
pgsqlUser: document.querySelector('#pgsql-user') as SlInput,
pgsqlPassword: document.querySelector('#pgsql-password') as SlInput,
pgsqlDatabase: document.querySelector('#pgsql-database') as SlInput,
@ -120,6 +121,7 @@ document.addEventListener('DOMContentLoaded', () => {
kind: 'postgres',
options: {
host: Elements.pgsqlHost.value,
port: parseInt(Elements.pgsqlPort.value),
user: Elements.pgsqlUser.value,
password: Elements.pgsqlPassword.value,
database: Elements.pgsqlDatabase.value

@ -56,6 +56,8 @@ block content
sl-tab-panel(name='pgsql')
h3.setup-text-item-title Host
sl-input#pgsql-host(type='text' placeholder='postgres.example.com' clearable): sl-icon(slot='prefix' name='fas-server' library='fa')
h3.setup-text-item-title Port
sl-input#pgsql-port(type='number' placeholder='5432' min='1' max='65535' no-spin-buttons clearable): sl-icon(slot='prefix' name='fas-server' library='fa')
h3.setup-text-item-title User
sl-input#pgsql-user(type='text' placeholder='posgrassql' clearable): sl-icon(slot='prefix' name='fas-user' library='fa')
h3.setup-text-item-title Password

Loading…
Cancel
Save