panic push, moved to app directory, init in init.sql

This commit is contained in:
Ulysia 2025-01-07 19:09:08 +01:00
parent 6b3fa6cf22
commit bb3058b671
16 changed files with 329 additions and 370 deletions

View file

@ -2,7 +2,7 @@ config*
docker*
Docker*
README*
node_modules/
**node_modules/
.*
pnpm-lock*
dist

View file

@ -1,6 +1,6 @@
FROM node:23-alpine3.20
COPY . /app
COPY ./app/. /app/.
WORKDIR /app

View file

68
app/sql/init.sql Normal file
View file

@ -0,0 +1,68 @@
-- Enable Extensions
CREATE EXTENSION IF NOT EXISTS pg_cron;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create table
CREATE TABLE if NOT EXISTS auctions (
id UUID PRIMARY KEY,
auctionid VARCHAR(255) NOT NULL,
lbin DECIMAL(65,5),
modified TIMESTAMP(2),
UNIQUE (auctionid)
);
-- Update trigger function
CREATE OR REPLACE FUNCTION public.fn_update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Update trigger
DROP TRIGGER IF EXISTS update_auction_modtime on auctions;
CREATE TRIGGER update_auction_modtime
BEFORE UPDATE ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_update_modified_column();
-- Cleanup function
CREATE OR REPLACE FUNCTION public.cleanup_old_entries()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
temp_id UUID;
BEGIN
FOR temp_id IN
SELECT id
FROM auctions
WHERE EXTRACT(EPOCH FROM (NOW() - modified))/3600 >= 6 -- 6 hours
LOOP
DELETE FROM auctions
WHERE auctions.id = temp_id;
END LOOP;
RETURN;
END;
$$;
-- Add job to pg_cron
DO $outer$
DECLARE job_exists boolean := false;
BEGIN
SELECT (count(*) > 0)
INTO job_exists
FROM cron.job
WHERE jobname = 'cleanup_old_entries_event';
IF NOT job_exists THEN
PERFORM cron.schedule(
'cleanup_old_entries_event',
'*/2 * * * *',
$sql$ SELECT public.cleanup_old_entries(); $sql$
);
END IF;
END;
$outer$;

256
app/src/sqlFunctions.ts Normal file
View file

@ -0,0 +1,256 @@
import { Pool, PoolClient, Query, QueryResult, QueryResultRow } from 'pg';
import { loadConfig } from './configLoader';
import { unifyArrays, randomArray } from './helperFunctions'
import fs from 'fs';
const config = loadConfig();
class SqlSystem {
private static pool: Pool;
public static async InitDB() {
this.pool = new Pool({
database: 'postgres',
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: config.data.worker_count ?? 10
});
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
try {
await conn.query('BEGIN');
let qry: string = fs.readFileSync('/app/sql/init.sql', 'utf8');
await conn.query(qry);
await conn.query('COMMIT');
} catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
if (conn) conn.release();
this.pool.end();
throw error;
}
//await this.TimeoutThread(10000)
await conn.query('BEGIN');
console.log("Inserting random numbers into Pool SETUP");
let dummy_auctionid: string[] = randomArray(10, 8), dummy_lbin: number[] = randomArray(10, 5);
await this.Upsert(dummy_auctionid, dummy_lbin);
await conn.query('COMMIT');
console.log("Requesting All from table auctions PRE REMOVE");
console.log(await this.Query("SELECT * FROM auctions", false));
await conn.query('BEGIN');
await this.RemoveAll()
await conn.query('COMMIT')
console.log("Requesting All from table auctions POST REMOVE");
console.log(await this.Query("SELECT * FROM auctions", false));
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
public static async Upsert(auctionid: string | string[], lbin: number | number[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if (Array.isArray(auctionid) && Array.isArray(lbin))
if (auctionid.length === lbin.length) {
await conn.query(`
INSERT INTO auctions (id, auctionid, lbin)
SELECT uuid_generate_v4()::UUID, unnest($1::text[]), unnest($2::decimal[])
ON CONFLICT (auctionid)
DO UPDATE SET lbin = EXCLUDED.lbin;
`, [auctionid, lbin]);
}
else if (Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Upsert SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
await conn.query(`
INSERT INTO auctions (id, auctionid, lbin)
VALUES uuid_generate_v4()::UUID, $1::text, $2::decimal
ON CONFLICT (auctionid)
DO UPDATE SET lbin = EXCLUDED.lbin;
`, [auctionid, lbin]);
}
await conn.query('COMMIT');
}
catch (error) {
console.error("message_before_error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
//REMOVE ALL ELEMENTS IN AUCTIONS TABLE
public static async RemoveAll() {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
await conn.query('DELETE FROM auctions')
await conn.query('COMMIT');
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
//REMOVE ELEMENT IN AUCTIONS TABLE BY AUCTION-ID
public static async RemoveByAuctionId(auctionid: string | string[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if (Array.isArray(auctionid)) {
await conn.query('DELETE FROM auctions WHERE auctionid IN ($1);', [auctionid]);
}
else {
await conn.query('DELETE FROM auctions WHERE auctionid = $1;', [auctionid]);
}
await conn.query('COMMIT');
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
public static async Query(query: string, IsWriting: boolean = false) {
let conn: PoolClient | undefined;
let result: any;
try {
conn = await this.pool.connect()
if (IsWriting) {
await conn.query('BEGIN');
conn.query(query);
await conn.query('COMMIT');
}
else {
console.log('TRYING TO QUERY');
result = (await conn.query(query)).rows;
}
}
catch (error) {
console.error("InitDB Error: ", error);
if (IsWriting && conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
if (!IsWriting) return result;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////// NOT FIXED / UNTESTED FUNCTIONS
//MATCH PROVIDED ELEMENTS IN AUCTIONS TABLE - returns true/false
public static async Match(auctionid: string | string[], lbin: number | number[]): Promise<boolean | any> {
let conn: PoolClient | undefined;
let result;
try {
conn = await this.pool.connect();
if (Array.isArray(auctionid) && Array.isArray(lbin))
if (auctionid.length === lbin.length) {
result = await conn.query(`
CREATE TEMPORARY TABLE IF NOT EXISTS TEMP (
auctionID VARCHAR(255) PRIMARY KEY,
lbin DECIMAL(65,5)
);
INSERT INTO TEMP (auctionID, lbin) VALUES ?
SELECT tmp.*
FROM TEMP AS tmp
WHERE NOT EXISTS (
SELECT 1
FROM auctions AS auc
WHERE auc.auctionID = tmp.auctionID
AND auc.lbin = tmp.lbin);
`, [await unifyArrays(auctionid, lbin)]);
return result.rows;
}
else if (Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Match SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
result = await conn.query(`
SELECT CASE
WHEN COUNT(*) > 0
THEN true
ELSE false
END AS user_exists
FROM auctions WHERE auctionID = ?;
`, [auctionid]);
return Boolean(result.rows.length);
}
}
catch (error) {
console.error("message_before_error: ", error);
throw error;
}
finally {
if (conn) conn.release();
}
}
//INSERT ELEMENT IN AUCTIONS TABLE OR UPDATE IF IT ALREADY EXISTS
public static async deprecated_Upsert(auctionid: string | string[], lbin: number | number[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if (Array.isArray(auctionid) && Array.isArray(lbin))
if (auctionid.length === lbin.length) {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES ($1,$2)
ON CONFLICT (auctionid)
DO UPDATE SET LBin = $2;
`, [auctionid, lbin]);
}
else if (Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Upsert SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES ($1,$2)
ON CONFLICT (auctionid)
DO UPDATE SET LBin = $2;
`, [auctionid, lbin]);
}
await conn.query('COMMIT');
}
catch (error) {
console.error("message_before_error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
}
export {
SqlSystem
}

View file

@ -28,6 +28,8 @@ services:
context: .
dockerfile: Dockerfile.db
restart: unless-stopped
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: example # Change this to a secure password. Has to be identical to DB_PASSWORD in the bot service
volumes:

View file

@ -1,367 +0,0 @@
import { Pool, PoolClient, Query, QueryResult, QueryResultRow } from 'pg';
import { loadConfig } from './configLoader';
import { unifyArrays, randomArray } from './helperFunctions'
const config = loadConfig();
class SqlSystem {
private static pool: Pool;
public static async InitDB() {
this.pool = new Pool({
database: 'postgres',
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: config.data.worker_count ?? 10
});
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
//Setup tables for auctions and lifetimes
await conn.query(`
CREATE TABLE if NOT EXISTS auctions (
id UUID PRIMARY KEY,
auctionid VARCHAR(255) NOT NULL,
lbin DECIMAL(65,5),
UNIQUE (auctionid)
);
CREATE TABLE if NOT EXISTS lifetimes (id UUID PRIMARY KEY, insertedon TIMESTAMP(2));
`);
//Setup lifetime Trigger
await conn.query(`
-- Insert trigger function
CREATE OR REPLACE FUNCTION public.fn_insert_lifetime()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO lifetimes (id, insertedon)
VALUES (NEW.id, NOW());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Update trigger function
CREATE OR REPLACE FUNCTION public.fn_update_lifetime()
RETURNS TRIGGER AS $$
BEGIN
UPDATE lifetimes
SET insertedon = NOW()
WHERE id = NEW.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Remove trigger function
CREATE OR REPLACE FUNCTION public.fn_remove_lifetime()
RETURNS TRIGGER AS $$
BEGIN
DELETE FROM lifetimes
WHERE id = OLD.id;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
`);
await conn.query(`
-- Insert trigger
DROP TRIGGER IF EXISTS insert_lifetime ON auctions;
CREATE TRIGGER insert_lifetime
AFTER INSERT ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_insert_lifetime();
-- Update trigger
DROP TRIGGER IF EXISTS update_lifetime ON auctions;
CREATE TRIGGER update_lifetime
AFTER UPDATE ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_update_lifetime();
-- Remove trigger
DROP TRIGGER IF EXISTS remove_lifetime ON auctions;
CREATE TRIGGER remove_lifetime
BEFORE DELETE ON auctions
FOR EACH ROW
EXECUTE PROCEDURE public.fn_remove_lifetime();
`);
await conn.query(`
CREATE EXTENSION IF NOT EXISTS pg_cron;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
`);
//Setup Table sanitation
await conn.query(`
CREATE OR REPLACE FUNCTION public.cleanup_old_entries()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE
temp_id UUID;
BEGIN
FOR temp_id IN
SELECT id
FROM lifetimes
WHERE EXTRACT(EPOCH FROM (NOW() - insertedon))/3600 >= 6 -- 6 hours
LOOP
-- Removes matching entries from auctions
DELETE FROM auctions
WHERE auctions.id = temp_id;
END LOOP;
RETURN;
END;
$$;
`);
await conn.query(`
DO $outer$
DECLARE job_exists boolean := false;
BEGIN
SELECT (count(*) > 0)
INTO job_exists
FROM cron.job
WHERE jobname = 'cleanup_old_entries_event';
IF NOT job_exists THEN
PERFORM cron.schedule(
'cleanup_old_entries_event',
'*/2 * * * *',
$sql$ SELECT public.cleanup_old_entries(); $sql$
);
END IF;
END;
$outer$;
`);
//await this.TimeoutThread(10000)
await conn.query('BEGIN');
console.log("Inserting random numbers into Pool SETUP");
let dummy_auctionid: string[] = randomArray(10,8),dummy_lbin: number[] = randomArray(10,5);
await this.Upsert(dummy_auctionid,dummy_lbin);
await conn.query('COMMIT');
console.log("Requesting All from table auctions PRE REMOVE");
console.log(await this.Query("SELECT * FROM auctions",false));
await conn.query('BEGIN');
await this.RemoveAll()
await conn.query('COMMIT')
console.log("Requesting All from table auctions POST REMOVE");
console.log(await this.Query("SELECT * FROM auctions",false));
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
public static async Upsert(auctionid: string|string[], lbin:number|number[])
{
let conn: PoolClient | undefined;
try
{
conn = await this.pool.connect();
await conn.query('BEGIN');
if(Array.isArray(auctionid) && Array.isArray(lbin))
if(auctionid.length === lbin.length) {
await conn.query(`
INSERT INTO auctions (id, auctionid, lbin)
SELECT uuid_generate_v4()::UUID, unnest($1::text[]), unnest($2::decimal[])
ON CONFLICT (auctionid)
DO UPDATE SET lbin = EXCLUDED.lbin;
`,[auctionid,lbin]);
}
else if(Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Upsert SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
await conn.query(`
INSERT INTO auctions (id, auctionid, lbin)
VALUES uuid_generate_v4()::UUID, $1::text, $2::decimal
ON CONFLICT (auctionid)
DO UPDATE SET lbin = EXCLUDED.lbin;
`, [auctionid, lbin]);
}
await conn.query('COMMIT');
}
catch(error)
{
console.error("message_before_error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally
{
if (conn) conn.release();
}
}
//REMOVE ALL ELEMENTS IN AUCTIONS TABLE
public static async RemoveAll() {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
await conn.query('DELETE FROM auctions')
await conn.query('COMMIT');
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
//REMOVE ELEMENT IN AUCTIONS TABLE BY AUCTION-ID
public static async RemoveByAuctionId(auctionid: string|string[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if(Array.isArray(auctionid))
{
await conn.query('DELETE FROM auctions WHERE auctionid IN ($1);',[auctionid]);
}
else
{
await conn.query('DELETE FROM auctions WHERE auctionid = $1;',[auctionid]);
}
await conn.query('COMMIT');
}
catch (error) {
console.error("InitDB Error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally {
if (conn) conn.release();
}
}
public static async Query(query:string,IsWriting:boolean = false) {
let conn: PoolClient | undefined;
let result: any;
try{
conn = await this.pool.connect()
if(IsWriting)
{
await conn.query('BEGIN');
conn.query(query);
await conn.query('COMMIT');
}
else
{
console.log('TRYING TO QUERY');
result = (await conn.query(query)).rows;
}
}
catch(error) {
console.error("InitDB Error: ", error);
if (IsWriting && conn) await conn.query('ROLLBACK');
throw error;
}
finally
{
if(conn) conn.release();
if(!IsWriting) return result;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////// NOT FIXED / UNTESTED FUNCTIONS
//MATCH PROVIDED ELEMENTS IN AUCTIONS TABLE - returns true/false
public static async Match(auctionid:string|string[],lbin:number|number[]): Promise<boolean|any> {
let conn: PoolClient | undefined;
let result;
try{
conn = await this.pool.connect();
if(Array.isArray(auctionid) && Array.isArray(lbin))
if(auctionid.length === lbin.length) {
result = await conn.query(`
CREATE TEMPORARY TABLE IF NOT EXISTS TEMP (
auctionID VARCHAR(255) PRIMARY KEY,
lbin DECIMAL(65,5)
);
INSERT INTO TEMP (auctionID, lbin) VALUES ?
SELECT tmp.*
FROM TEMP AS tmp
WHERE NOT EXISTS (
SELECT 1
FROM auctions AS auc
WHERE auc.auctionID = tmp.auctionID
AND auc.lbin = tmp.lbin);
`,[await unifyArrays(auctionid,lbin)]);
return result.rows;
}
else if(Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Match SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
result = await conn.query(`
SELECT CASE
WHEN COUNT(*) > 0
THEN true
ELSE false
END AS user_exists
FROM auctions WHERE auctionID = ?;
`,[auctionid]);
return Boolean(result.rows.length);
}
}
catch(error)
{
console.error("message_before_error: ", error);
throw error;
}
finally
{
if (conn) conn.release();
}
}
//INSERT ELEMENT IN AUCTIONS TABLE OR UPDATE IF IT ALREADY EXISTS
public static async deprecated_Upsert(auctionid: string|string[], lbin: number|number[]) {
let conn: PoolClient | undefined;
try {
conn = await this.pool.connect();
await conn.query('BEGIN');
if(Array.isArray(auctionid) && Array.isArray(lbin))
if(auctionid.length === lbin.length) {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES ($1,$2)
ON CONFLICT (auctionid)
DO UPDATE SET LBin = $2;
`, [auctionid, lbin]);
}
else if(Array.isArray(auctionid) || Array.isArray(lbin))
throw Error(`Upsert SQL Function error - cannot unify collection ${Array.isArray(auctionid) ? auctionid : lbin} to singlet ${!Array.isArray(auctionid) ? auctionid : lbin}`);
else {
await conn.query(`
INSERT INTO auctions (auctionid, LBin)
VALUES ($1,$2)
ON CONFLICT (auctionid)
DO UPDATE SET LBin = $2;
`, [auctionid, lbin]);
}
await conn.query('COMMIT');
}
catch(error)
{
console.error("message_before_error: ", error);
if (conn) await conn.query('ROLLBACK');
throw error;
}
finally
{
if (conn) conn.release();
}
}
}
export {
SqlSystem
}