Skip to content

Commit

Permalink
Added Database creation / repair
Browse files Browse the repository at this point in the history
  • Loading branch information
RJMurg committed Sep 5, 2023
1 parent b7881aa commit aba1824
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 42 deletions.
142 changes: 142 additions & 0 deletions src/lib/database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Client } from 'pg';
import Schema from '$lib/schema.json';

interface Table {
[key: string]: {
[key: string]: string;
};
}

interface Database {
Tables: Table;
}

// This checks the Database to see if the correct tables are present, and if not, creates them
// It also checks if the correct columns are present, and if not, creates them.
// It returnes multiple values:
// 0: Database was missing tables/columns but they have been created,
// 1: Database is secure,
export const databaseIntegrity = async (databaseURL: any) => {
let returnVal = 0;
// Open and parse schema
const schema = JSON.parse(JSON.stringify(Schema));

// Connect to database
const client = new Client({
connectionString: databaseURL
});

await client.connect();

// Get list of all tables
const res = await client.query(`
SELECT *
FROM pg_catalog.pg_tables
WHERE schemaname != 'pg_catalog' AND
schemaname != 'information_schema'
`);

// Create list of all tables in db
let tables = [];
for (const row of res.rows) {
tables.push(row.tablename);
}

// Create list of all tables in schema
let schemaTables = [];
for (const table in schema.Tables) {
schemaTables.push(table.toLowerCase());
}

// Check what schema tables are missing from DB
let missingTables = [];

for (const table of schemaTables) {
if (!tables.includes(table)) {
missingTables.push(table);
}
}

if(missingTables.length === 0) {
returnVal = 1;
}

// Create missing tables using schema
for (const table of missingTables) {
// Capitalise table name
const capitalisedTable = table.charAt(0).toUpperCase() + table.slice(1);

// Create query string
let queryString = `CREATE TABLE ${capitalisedTable} (`;

// Add columns to query string
for (const column in schema.Tables[capitalisedTable]) {
queryString += `${column} ${schema.Tables[capitalisedTable][column]}, `;
}

// Remove last comma and space
queryString = queryString.slice(0, -2);

// Add closing bracket
queryString += ');';

// Execute query
await client.query(queryString);
}

// Check all tables in DB are not malformed
const newres = await client.query(`
SELECT *
FROM pg_catalog.pg_tables
WHERE schemaname != 'pg_catalog' AND
schemaname != 'information_schema'
`);

// Create list of all tables in db
let newTables = [];
for (const row of newres.rows) {
newTables.push(row.tablename);
}

// Check all tables in DB are not malformed
for (const table of newTables) {
// Capitalise table name
const capitalisedTable = table.charAt(0).toUpperCase() + table.slice(1);

// Get columns in table
const columns = await client.query(`
SELECT *
FROM information_schema.columns
WHERE table_name = '${table}'
`);

// Create list of all columns in table
let columnNames = [];
for (const row of columns.rows) {
columnNames.push(row.column_name);

// Make all lowercase
columnNames = columnNames.map((column) => {
return column.toLowerCase();
});
}

// Check all columns in table are not malformed
for (const column in schema.Tables[capitalisedTable]) {
// Make all lowercase
const capitalisedColumn = column.toLowerCase();

if (!columnNames.includes(capitalisedColumn)) {
// Create query string
let queryString = `ALTER TABLE ${capitalisedTable} ADD COLUMN ${column} ${schema.Tables[capitalisedTable][column]};`;

// Execute query
await client.query(queryString);

returnVal = 1;
}
}
}

return returnVal;
};
84 changes: 43 additions & 41 deletions src/lib/schema.json
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
{
"Players": {
"Name": "TEXT",
"StudentID": "TEXT",
"Privacy": "BOOLEAN",
"Event": "TEXT",
"Score": "INTEGER"
},
"Tables": {
"Players": {
"Name": "TEXT",
"StudentID": "TEXT",
"Privacy": "BOOLEAN",
"Event": "TEXT",
"Score": "INTEGER"
},

"Stages": {
"ID": "SERIAL PRIMARY KEY",
"Clue": "TEXT",
"Name": "TEXT",
"Event": "TEXT",
"PuzzleType": "TEXT"
},
"Stages": {
"ID": "SERIAL PRIMARY KEY",
"Clue": "TEXT",
"Name": "TEXT",
"Event": "TEXT",
"PuzzleType": "TEXT"
},

"Puzzles": {
"ID":"SERIAL PRIMARY KEY",
"Event": "TEXT",
"Type": "TEXT",
"Puzzle": "TEXT"
},
"Puzzles": {
"ID":"SERIAL PRIMARY KEY",
"Event": "TEXT",
"Type": "TEXT",
"Puzzle": "TEXT"
},

"Completion": {
"StudentID": "TEXT",
"Event": "TEXT"
},
"Completion": {
"StudentID": "TEXT",
"Event": "TEXT"
},

"Overall": {
"Event": "TEXT",
"ParticipantCount": "INTEGER",
"StageCount": "INTEGER",
"PuzzleCount": "INTEGER",
"PrizeCount": "INTEGER",
"StagesComplete": "INTEGER"
},
"Overall": {
"Event": "TEXT",
"ParticipantCount": "INTEGER",
"StageCount": "INTEGER",
"PuzzleCount": "INTEGER",
"PrizeCount": "INTEGER",
"StagesComplete": "INTEGER"
},

"Events": {
"ID":"SERIAL PRIMARY KEY",
"Name": "TEXT",
"Date": "TEXT",
"Location": "TEXT",
"Description": "TEXT",
"Prize": "TEXT",
"PrizeCount": "INTEGER",
"Active": "BOOLEAN"
"Events": {
"ID":"SERIAL PRIMARY KEY",
"Name": "TEXT",
"Date": "TEXT",
"Location": "TEXT",
"Description": "TEXT",
"Prize": "TEXT",
"PrizeCount": "INTEGER",
"Active": "BOOLEAN"
}
}
}
14 changes: 13 additions & 1 deletion src/routes/admin/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { redirect } from '@sveltejs/kit';
import { databaseIntegrity } from '$lib/database';
import type { PageServerLoad, Actions } from './$types';

// Read in info from .env file
import { config } from 'dotenv';
config();

export const load: PageServerLoad = async ({ request, cookies }) => {
// Check database integrity
const dbSecure = await databaseIntegrity(process.env.DATABASE_URL);
let dbMessage = "";

if(dbSecure == 0) {
dbMessage = "Database repaired!"
}
else{
dbMessage = "Database integrity verified!"
}

// Check if admin cookie is set
if (cookies.get('admin') === 'true') {
// Return success
return {
status: 200,
message: 'Successfully logged in'
message: 'Successfully logged in, ' + dbMessage
}
} else {
// Return failure
Expand Down
10 changes: 10 additions & 0 deletions src/routes/admin/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
LoggedIn = true;
}
else if(data.status == 500){
addToast({
message: data.message,
type: "error",
dismissible: true,
timeout: 5000
});
LoggedIn = false;
}
else{
LoggedIn = false;
}
Expand Down

0 comments on commit aba1824

Please sign in to comment.