Signup tentatively works
This commit is contained in:
27
_test/OrderedObject.test.js
Normal file
27
_test/OrderedObject.test.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import OrderedObject from "../server/db/model/OrderedObject.js"
|
||||||
|
|
||||||
|
window.testSuites.push(
|
||||||
|
|
||||||
|
class testOrderedObject {
|
||||||
|
|
||||||
|
async addShouldFailIfKeyIsDuplicate() {
|
||||||
|
class Test extends OrderedObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let test = new Test()
|
||||||
|
|
||||||
|
test.add("1", {name: "hello"})
|
||||||
|
|
||||||
|
try {
|
||||||
|
test.add("1", {name: "bye"})
|
||||||
|
} catch(e){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Received no error!"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
)
|
||||||
57
_test/test.js
Normal file
57
_test/test.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
let scriptToPaste = `
|
||||||
|
<script type="module" src="./_test/test.js"></script>
|
||||||
|
`;
|
||||||
|
console.log("Tests initializing.")
|
||||||
|
window.testSuites = [];
|
||||||
|
|
||||||
|
/* Server - DB */
|
||||||
|
import ("./OrderedObject.test.js")
|
||||||
|
|
||||||
|
window.test = async function() {
|
||||||
|
// window.testSuites.sort();
|
||||||
|
window.alert = () => true;
|
||||||
|
window.confirm = () => true;
|
||||||
|
|
||||||
|
console.clear();
|
||||||
|
|
||||||
|
let failed = 0;
|
||||||
|
let success = 0;
|
||||||
|
|
||||||
|
var start = new Date();
|
||||||
|
for(let j=0; j<window.testSuites.length; j++) {
|
||||||
|
let testSuite = window.testSuites[j];
|
||||||
|
console.log(`%c ➽ ${j+1} ${testSuite.name.replace("test", "")}`, 'color: #ffffff; font-size: 17px; padding-left: -20px; padding-top: 10px; padding-bottom: 10px; text-align: right;')
|
||||||
|
let suite = new testSuite();
|
||||||
|
let testNum = 0;
|
||||||
|
let suiteContents = Object.getOwnPropertyNames(testSuite.prototype)
|
||||||
|
for(let i=0; i<suiteContents.length; i++) {
|
||||||
|
let test = suiteContents[i];
|
||||||
|
if(typeof suite[test] === 'function' && test !== "constructor") {
|
||||||
|
testNum++;
|
||||||
|
let fail = await suite[test]();
|
||||||
|
if(fail) {
|
||||||
|
failed++;
|
||||||
|
console.log(`%c ${testNum}. ${test}: ${fail}`, 'background: #222; color: rgb(254, 62, 43)');
|
||||||
|
} else {
|
||||||
|
success++;
|
||||||
|
console.log(`%c ${testNum}. ${test}`, 'background: #222; color: #00FF00');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("")
|
||||||
|
console.log("")
|
||||||
|
let elapsed = new Date() - start;
|
||||||
|
if(failed === 0) {
|
||||||
|
console.log(`%cRan ${failed+success} tests in ${elapsed}ms`, 'background: #222; color: #00FF00');
|
||||||
|
} else {
|
||||||
|
console.log(`%cRan ${failed+success} tests in ${elapsed}ms`, 'background: #222; color: rgb(254, 62, 43)');
|
||||||
|
}
|
||||||
|
console.log(`%c ${success} passed`, 'background: #222; color: #00FF00');
|
||||||
|
console.log(`%c ${failed} failed`, 'background: #222; color: rgb(254, 62, 43)');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.wait = ms => new Promise(res => setTimeout(res, ms));
|
||||||
|
|
||||||
|
window.__defineGetter__("test", test);
|
||||||
11
index.html
Normal file
11
index.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Node Terminal</title>
|
||||||
|
<!-- <script type="module" src="./_test/test.js"></script> -->
|
||||||
|
</head>
|
||||||
|
<body style="background-color: rgb(32, 33, 36)">
|
||||||
|
<script type="module" src="./server/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
main.js
Normal file
30
main.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// main.js
|
||||||
|
const { app, BrowserWindow, nativeImage } = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
setTimeout(() => {
|
||||||
|
app.dock.bounce('informational'); // or 'critical'
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
show: false, // window is hidden
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
icon: path.join(__dirname, '_', 'fabric.png')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
win.loadFile(path.join(__dirname, 'index.html'));
|
||||||
|
win.webContents.openDevTools({ mode: 'undocked' });
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(createWindow);
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') app.quit();
|
||||||
|
});
|
||||||
15
package.json
15
package.json
@@ -1,25 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "Hyperia",
|
"name": "Hyperia",
|
||||||
"type": "module",
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"main": "main.js",
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server/index.js",
|
"start": "electron ."
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
},
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argon2": "^0.44.0",
|
"argon2": "^0.44.0",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^4.1.2",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"ws": "^8.18.3"
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "^25.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import chalk from 'chalk'
|
const chalk = require('chalk');
|
||||||
import path from 'path';
|
const path = require('path');
|
||||||
import fs from 'fs/promises';
|
const fs = require('fs/promises');
|
||||||
import { pathToFileURL } from 'url';
|
const { pathToFileURL } = require('url');
|
||||||
|
|
||||||
function Node(node) {
|
function Node(node) {
|
||||||
let traits = [
|
let traits = [
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import dotenv from 'dotenv';
|
const dotenv = require("dotenv")
|
||||||
import chalk from 'chalk';
|
const jwt = require('jsonwebtoken');
|
||||||
import jwt from 'jsonwebtoken'
|
const argon2 = require('argon2');
|
||||||
import argon2 from 'argon2'
|
|
||||||
import { randomUUID } from 'node:crypto'
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
export default class AuthHandler {
|
export default class AuthHandler {
|
||||||
|
|||||||
119
server/db/db.js
119
server/db/db.js
@@ -1,29 +1,21 @@
|
|||||||
|
const fs = require('fs/promises');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const path = require('path');
|
||||||
import QuillDB from "../_/quilldb.js"
|
import QuillDB from "../_/quilldb.js"
|
||||||
import fs from 'fs/promises'
|
import Titles from "./model/Titles.js"
|
||||||
import path from 'path'
|
import Members from './model/Members.js'
|
||||||
import Title from "./model/Title.js"
|
import Tokens from './model/Tokens.js'
|
||||||
import Member from './model/Member.js'
|
|
||||||
import Token from './model/Token.js'
|
|
||||||
|
|
||||||
export default class Database {
|
export default class Database {
|
||||||
nodes = [];
|
titles = new Titles()
|
||||||
types = [
|
members = new Members()
|
||||||
{
|
tokens = new Tokens()
|
||||||
validate: Title,
|
|
||||||
start: 0,
|
fromID = {
|
||||||
end: null,
|
"HY": this.titles,
|
||||||
},
|
"MEMBER": this.members,
|
||||||
{
|
"TOKEN": this.tokens
|
||||||
validate: Member,
|
}
|
||||||
start: null,
|
|
||||||
end: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
validate: Token,
|
|
||||||
start: null,
|
|
||||||
end: null,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
@@ -38,82 +30,25 @@ export default class Database {
|
|||||||
dbJson = []
|
dbJson = []
|
||||||
}
|
}
|
||||||
let nodes = dbJson["nodes"];
|
let nodes = dbJson["nodes"];
|
||||||
this.validateNodes(nodes)
|
let entries = Object.entries(nodes)
|
||||||
}
|
|
||||||
|
|
||||||
validateNodes(nodes) {
|
for(let i=0; i<entries.length; i++) {
|
||||||
nodes = Object.entries(nodes)
|
let entry = entries[i]
|
||||||
|
let id = entry[0]; let node = entry[1];
|
||||||
let t = 0
|
let type = id.split("-")[0]
|
||||||
|
try {
|
||||||
let currentType = () => {return this.types[t]}
|
let collection = this.fromID[type]
|
||||||
let nextType = () => {return this.types[t+1]}
|
if(collection) {
|
||||||
let selectNextType = () => {
|
collection.add(node)
|
||||||
currentType().end = t
|
|
||||||
t += 1;
|
|
||||||
currentType().start = t
|
|
||||||
}
|
|
||||||
let lastNode = (i=null) => {
|
|
||||||
if(i == null) throw new Error("must pass a param to lastNode()")
|
|
||||||
return i+1 === nodes.length
|
|
||||||
}
|
|
||||||
|
|
||||||
for(let i=0; i<nodes.length; i++) {
|
|
||||||
if(this.validateNode(currentType(), nodes[i])) {
|
|
||||||
if(lastNode(i)) {
|
|
||||||
currentType().end = i
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
throw new Error("Type does not exist for node: ", id)
|
||||||
}
|
}
|
||||||
} else if(this.validateNode(nextType(), nodes[i])) {
|
} catch(e) {
|
||||||
selectNextType()
|
throw e
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
throw new Error("Nodes are out of order or corrupted!")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validateNode(type, node) {
|
|
||||||
let [key, value] = node
|
|
||||||
return type.validate(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
get = {
|
|
||||||
user: (id) => {
|
|
||||||
return this.nodes[id]
|
|
||||||
},
|
|
||||||
userByEmail: (email) => {
|
|
||||||
for (const id of this.labels["User"]) {
|
|
||||||
const user = this.get.user(id);
|
|
||||||
if (user.email === email) {
|
|
||||||
return { id, ...user }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
token: (id) => {
|
|
||||||
return this.nodes[`TOKEN-${id}`]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add = {
|
|
||||||
user: (node) => {
|
|
||||||
let lastUser = {}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
submitNewUser(qrCodeID, userInfo) {
|
|
||||||
let newUser = {
|
|
||||||
labels: ["User"],
|
|
||||||
...userInfo
|
|
||||||
}
|
|
||||||
if(User(newUser))
|
|
||||||
this.add.user(newUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
generateUserID() {
|
generateUserID() {
|
||||||
let id = this.labels["User"].length + 1;
|
let id = this.labels["User"].length + 1;
|
||||||
while (this.get.user(`user-${id}`)) {
|
while (this.get.user(`user-${id}`)) {
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
export default function Member(id, node) {
|
|
||||||
let idTraits = {
|
|
||||||
firstWord: "MEMBER",
|
|
||||||
length: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
let fields = [
|
|
||||||
"firstName",
|
|
||||||
"lastName",
|
|
||||||
"email",
|
|
||||||
"password"
|
|
||||||
]
|
|
||||||
|
|
||||||
let checkID = () => {
|
|
||||||
let split = id.split("-")
|
|
||||||
return (
|
|
||||||
split.length === idTraits.length
|
|
||||||
&& split[0] === idTraits.firstWord
|
|
||||||
&& !isNaN(Number(split[1]))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let idres = checkID()
|
|
||||||
if(!idres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let checkFields = () => {
|
|
||||||
for(let i = 0; i < fields.length; i++) {
|
|
||||||
if(!node[fields[i]]) {
|
|
||||||
throw new Error(`Member ${node.email} is missing trait ${fields[i]}`)
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let fieldres = checkFields()
|
|
||||||
if(!fieldres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
64
server/db/model/Members.js
Normal file
64
server/db/model/Members.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import OrderedObject from "./OrderedObject.js"
|
||||||
|
|
||||||
|
export default class Members extends OrderedObject {
|
||||||
|
|
||||||
|
add(newMember) {
|
||||||
|
console.log("adding ", newMember)
|
||||||
|
let id = `MEMBER-${newMember.email}`
|
||||||
|
if(this.validate(id, newMember)) {
|
||||||
|
try {
|
||||||
|
super.add(id, newMember)
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new global.ServerError(400, "Invalid Member Data!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(id, node) {
|
||||||
|
let idTraits = {
|
||||||
|
firstWord: "MEMBER"
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = [
|
||||||
|
"firstName",
|
||||||
|
"lastName",
|
||||||
|
"email",
|
||||||
|
"password"
|
||||||
|
]
|
||||||
|
|
||||||
|
let checkID = () => {
|
||||||
|
let split = id.split("-")
|
||||||
|
return (
|
||||||
|
split[0] === idTraits.firstWord
|
||||||
|
&& split[1].includes("@")
|
||||||
|
&& split[1].includes(".")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let idres = checkID()
|
||||||
|
if(!idres) {
|
||||||
|
console.log("id failed: ", id)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkFields = () => {
|
||||||
|
for(let i = 0; i < fields.length; i++) {
|
||||||
|
if(!node[fields[i]]) {
|
||||||
|
throw new Error(`Member ${node.email} is missing trait ${fields[i]}`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fieldres = checkFields()
|
||||||
|
if(!fieldres) {
|
||||||
|
console.log("fields failed")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
29
server/db/model/OrderedObject.js
Normal file
29
server/db/model/OrderedObject.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
export default class OrderedObject {
|
||||||
|
entries = []
|
||||||
|
ids = {}
|
||||||
|
|
||||||
|
add(id, data) {
|
||||||
|
if(this.get(id)) {
|
||||||
|
console.error(`Can't add item ${id}: id already exists`)
|
||||||
|
throw new global.ServerError(400, `Member with this email already exists`)
|
||||||
|
}
|
||||||
|
this.entries.push(data)
|
||||||
|
this.ids[id] = this.entries.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
if (typeof key === "number") {
|
||||||
|
return this.entries[key]
|
||||||
|
} else {
|
||||||
|
return this.entries[this.ids[key]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
if (typeof key === "number") {
|
||||||
|
return this.entries[key]
|
||||||
|
} else {
|
||||||
|
return this.entries[this.ids[key]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
export default function Title(id, node) {
|
|
||||||
let checkID = () => {
|
|
||||||
let split = id.split("-")
|
|
||||||
return (
|
|
||||||
split.length === 2
|
|
||||||
&& split[0] === "HY"
|
|
||||||
&& !isNaN(Number(split[1]))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let idres = checkID()
|
|
||||||
if(!idres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let checkFields = () => {
|
|
||||||
let fields = [
|
|
||||||
"fullName",
|
|
||||||
]
|
|
||||||
for(let i = 0; i < fields.length; i++) {
|
|
||||||
if(!node[fields[i]]) {
|
|
||||||
throw new Error(`Title ${id} is missing trait ${fields[i]}`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
let fieldres = checkFields()
|
|
||||||
if(!fieldres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
53
server/db/model/Titles.js
Normal file
53
server/db/model/Titles.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import OrderedObject from "./OrderedObject.js"
|
||||||
|
|
||||||
|
export default class Titles extends OrderedObject {
|
||||||
|
|
||||||
|
add(newTitle) {
|
||||||
|
let id = `HY-${this.entries.length+1}`
|
||||||
|
console.log(id)
|
||||||
|
if(this.validate(id, newTitle)) {
|
||||||
|
try {
|
||||||
|
super.add(id, newTitle)
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new global.ServerError(400, "Invalid Member Data!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(id, node) {
|
||||||
|
let checkID = () => {
|
||||||
|
let split = id.split("-")
|
||||||
|
return (
|
||||||
|
split.length === 2
|
||||||
|
&& split[0] === "HY"
|
||||||
|
&& !isNaN(Number(split[1]))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let idres = checkID()
|
||||||
|
if(!idres) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkFields = () => {
|
||||||
|
let fields = [
|
||||||
|
"fullName",
|
||||||
|
]
|
||||||
|
for(let i = 0; i < fields.length; i++) {
|
||||||
|
if(!node[fields[i]]) {
|
||||||
|
throw new Error(`Title ${id} is missing trait ${fields[i]}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let fieldres = checkFields()
|
||||||
|
if(!fieldres) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
export default function Token(id, node) {
|
|
||||||
let idTraits = {
|
|
||||||
firstWord: "TOKEN"
|
|
||||||
}
|
|
||||||
|
|
||||||
let fields = [
|
|
||||||
"index",
|
|
||||||
"url",
|
|
||||||
"used"
|
|
||||||
]
|
|
||||||
|
|
||||||
let checkID = () => {
|
|
||||||
let split = id.split("-")
|
|
||||||
return (
|
|
||||||
split[0] === idTraits.firstWord
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let idres = checkID()
|
|
||||||
if(!idres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
let checkFields = () => {
|
|
||||||
for(let i = 0; i < fields.length; i++) {
|
|
||||||
if(!node[fields[i]]) {
|
|
||||||
throw new Error(`Token ${node.email} is missing trait ${fields[i]}`)
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let fieldres = checkFields()
|
|
||||||
if(!fieldres) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
62
server/db/model/Tokens.js
Normal file
62
server/db/model/Tokens.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import OrderedObject from "./OrderedObject.js"
|
||||||
|
|
||||||
|
export default class Tokens extends OrderedObject {
|
||||||
|
|
||||||
|
add(token) {
|
||||||
|
let id = `TOKEN-${token.uuid}`
|
||||||
|
if(this.validate(id, token)) {
|
||||||
|
try {
|
||||||
|
super.add(id, token)
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new global.ServerError(400, "Invalid Member Data!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(uuid) {
|
||||||
|
return super.get(`TOKEN-${uuid}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(id, node) {
|
||||||
|
let idTraits = {
|
||||||
|
firstWord: "TOKEN"
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = [
|
||||||
|
"index",
|
||||||
|
"url",
|
||||||
|
"used"
|
||||||
|
]
|
||||||
|
|
||||||
|
let checkID = () => {
|
||||||
|
let split = id.split("-")
|
||||||
|
return (
|
||||||
|
split[0] === idTraits.firstWord
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let idres = checkID()
|
||||||
|
if(!idres) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkFields = () => {
|
||||||
|
for(let i = 0; i < fields.length; i++) {
|
||||||
|
if(!node[fields[i]]) {
|
||||||
|
throw new Error(`Token ${node.email} is missing trait ${fields[i]}`)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fieldres = checkFields()
|
||||||
|
if(!fieldres) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
116
server/index.js
116
server/index.js
@@ -1,27 +1,22 @@
|
|||||||
import express from 'express';
|
const express = require('express');
|
||||||
import cors from 'cors'
|
const cors = require('cors');
|
||||||
import cookieParser from 'cookie-parser'
|
const cookieParser = require('cookie-parser');
|
||||||
import http from 'http'
|
const http = require('http');
|
||||||
import fs from 'fs'
|
const fs = require('fs');
|
||||||
import chalk from 'chalk'
|
const chalk = require('chalk');
|
||||||
import moment from 'moment'
|
const moment = require('moment');
|
||||||
import path from 'path';
|
const path = require('path');
|
||||||
import { initWebSocket } from './ws.js'
|
|
||||||
|
|
||||||
|
import { initWebSocket } from './ws.js'
|
||||||
import Database from "./db/db.js"
|
import Database from "./db/db.js"
|
||||||
import AuthHandler from './auth.js';
|
import AuthHandler from './auth.js';
|
||||||
import handlers from "./handlers.js";
|
import handlers from "./handlers.js";
|
||||||
|
|
||||||
// Get __dirname in ES6 environment
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
db;
|
db;
|
||||||
auth;
|
auth;
|
||||||
UIPath = path.join(__dirname, '../ui')
|
UIPath = path.join(__dirname, './ui')
|
||||||
DBPath = path.join(__dirname, '../db')
|
DBPath = path.join(__dirname, './db')
|
||||||
|
|
||||||
registerRoutes(router) {
|
registerRoutes(router) {
|
||||||
// router.post('/api/location', handlers.updateLocation)
|
// router.post('/api/location', handlers.updateLocation)
|
||||||
@@ -39,7 +34,7 @@ class Server {
|
|||||||
if (!token) {
|
if (!token) {
|
||||||
return res.status(400).json({ error: 'Token is required' });
|
return res.status(400).json({ error: 'Token is required' });
|
||||||
}
|
}
|
||||||
let fromDB = this.db.get.token(token)
|
let fromDB = this.db.tokens.get(token)
|
||||||
if (!fromDB) {
|
if (!fromDB) {
|
||||||
return res.status(403).json({ error: 'Invalid or expired token' });
|
return res.status(403).json({ error: 'Invalid or expired token' });
|
||||||
} else if(fromDB.used) {
|
} else if(fromDB.used) {
|
||||||
@@ -50,8 +45,12 @@ class Server {
|
|||||||
|
|
||||||
newUserSubmission = (req, res) => {
|
newUserSubmission = (req, res) => {
|
||||||
const { token } = req.query;
|
const { token } = req.query;
|
||||||
db.submitNewUser(token, req.body)
|
try {
|
||||||
return res.status(400).json({ error: 'Haven\t finished this bruh' });
|
db.members.add(req.body)
|
||||||
|
return res.redirect(`/signin/?new=true`);
|
||||||
|
} catch(e) {
|
||||||
|
return res.status(e.status).json({ error: 'Error adding new member' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authMiddleware = (req, res, next) => {
|
authMiddleware = (req, res, next) => {
|
||||||
@@ -92,23 +91,14 @@ class Server {
|
|||||||
|
|
||||||
let url = req.url
|
let url = req.url
|
||||||
|
|
||||||
let publicPage = () => {
|
let publicSite = () => {
|
||||||
url = "/index.html"
|
|
||||||
let filePath = path.join(this.UIPath, "public", url);
|
|
||||||
res.sendFile(filePath, (err) => {
|
|
||||||
if (err) {
|
|
||||||
console.log("File not found, sending fallback:", filePath);
|
|
||||||
res.redirect("/");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let publicFile = () => {
|
|
||||||
let filePath;
|
let filePath;
|
||||||
if(url.startsWith("/_")) {
|
if(url.startsWith("/_")) {
|
||||||
filePath = path.join(this.UIPath, url);
|
filePath = path.join(this.UIPath, url);
|
||||||
|
} else if(url.includes("75820185")) {
|
||||||
|
filePath = path.join(this.UIPath, "public", url.split("75820185")[1]);
|
||||||
} else {
|
} else {
|
||||||
filePath = path.join(this.UIPath, "public", url);
|
filePath = path.join(this.UIPath, "public", "index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sendFile(filePath);
|
res.sendFile(filePath);
|
||||||
@@ -128,11 +118,7 @@ class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!this.auth.isLoggedInUser(req, res)) {
|
if(!this.auth.isLoggedInUser(req, res)) {
|
||||||
if(!url.includes(".")) {
|
publicSite()
|
||||||
publicPage()
|
|
||||||
} else {
|
|
||||||
publicFile()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
privateSite()
|
privateSite()
|
||||||
}
|
}
|
||||||
@@ -142,10 +128,10 @@ class Server {
|
|||||||
const formattedDate = moment().format('M.D');
|
const formattedDate = moment().format('M.D');
|
||||||
const formattedTime = moment().format('h:mma');
|
const formattedTime = moment().format('h:mma');
|
||||||
if(req.url.includes("/api/")) {
|
if(req.url.includes("/api/")) {
|
||||||
console.log(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
console.logclean(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||||
} else {
|
} else {
|
||||||
if(req.url === "/")
|
if(req.url === "/")
|
||||||
console.log(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
console.logclean(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@@ -154,9 +140,9 @@ class Server {
|
|||||||
const originalSend = res.send;
|
const originalSend = res.send;
|
||||||
res.send = function (body) {
|
res.send = function (body) {
|
||||||
if(res.statusCode >= 400) {
|
if(res.statusCode >= 400) {
|
||||||
console.log(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`));
|
console.logclean(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`));
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`));
|
console.logclean(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`));
|
||||||
}
|
}
|
||||||
originalSend.call(this, body);
|
originalSend.call(this, body);
|
||||||
};
|
};
|
||||||
@@ -184,16 +170,16 @@ class Server {
|
|||||||
initWebSocket(server);
|
initWebSocket(server);
|
||||||
const PORT = 3003;
|
const PORT = 3003;
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log("\n")
|
console.logclean("\n")
|
||||||
console.log(chalk.yellow("*************** Hyperia ***************"))
|
console.logclean(chalk.yellow("*************** Hyperia ***************"))
|
||||||
console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
console.logclean(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
||||||
console.log(chalk.yellow("***************************************"))
|
console.logclean(chalk.yellow("***************************************"))
|
||||||
console.log("\n")
|
console.logclean("\n")
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', async () => {
|
||||||
console.log(chalk.red('Closing server...'));
|
console.logclean(chalk.red('Closing server...'));
|
||||||
console.log(chalk.green('Database connection closed.'));
|
console.logclean(chalk.green('Database connection closed.'));
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -201,4 +187,38 @@ class Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _log = console.log;
|
||||||
|
|
||||||
|
console.logclean = function (...args) {
|
||||||
|
_log.call(console, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log = function (...args) {
|
||||||
|
// // Get the caller location
|
||||||
|
// const stack = new Error().stack.split("\n")[2];
|
||||||
|
// const match = stack.match(/(\/.*:\d+:\d+)/);
|
||||||
|
// let location = match ? match[1] : "unknown";
|
||||||
|
|
||||||
|
// // Remove CWD prefix
|
||||||
|
// while (location.startsWith("/")) {
|
||||||
|
// location = location.slice(1);
|
||||||
|
// }
|
||||||
|
// location = "/" + location
|
||||||
|
|
||||||
|
// let cwd = process.cwd();
|
||||||
|
// if (location.startsWith(cwd)) {
|
||||||
|
// location = location.slice(cwd.length);
|
||||||
|
// if (location.startsWith("/")) location = location.slice(1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _log.call(console, `[${location}]`, ...args);
|
||||||
|
// };
|
||||||
|
|
||||||
|
global.ServerError = class extends Error {
|
||||||
|
constructor(status, msg) {
|
||||||
|
super(msg);
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const server = new Server()
|
const server = new Server()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { WebSocket, WebSocketServer } from 'ws';
|
const { WebSocket, WebSocketServer } = require('ws');
|
||||||
|
|
||||||
let wss;
|
let wss;
|
||||||
|
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ class SignupForm extends Shadow {
|
|||||||
|
|
||||||
VStack(() => {
|
VStack(() => {
|
||||||
input("First Name")
|
input("First Name")
|
||||||
.attr({name: "firstname", type: "name"})
|
.attr({name: "firstName", type: "name"})
|
||||||
.styles(this.inputStyles)
|
.styles(this.inputStyles)
|
||||||
|
|
||||||
input("Last Name")
|
input("Last Name")
|
||||||
.attr({name: "lastname", type: "name"})
|
.attr({name: "lastName", type: "name"})
|
||||||
.styles(this.inputStyles)
|
.styles(this.inputStyles)
|
||||||
|
|
||||||
input("Email")
|
input("Email")
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
<title>Hyperia</title>
|
<title>Hyperia</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" href="/_/icons/logo.svg">
|
<link rel="icon" href="/_/icons/logo.svg">
|
||||||
<link rel="stylesheet" href="_/code/shared.css">
|
<link rel="stylesheet" href="/_/code/shared.css">
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
background-image: url("_/images/fabric.png");
|
background-image: url("/_/images/fabric.png");
|
||||||
background-size: 33vw auto; /* width height of each tile */
|
background-size: 33vw auto; /* width height of each tile */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="_/code/quill.js"></script>
|
<script src="/_/code/quill.js"></script>
|
||||||
<script type="module" src="index.js"></script>
|
<script type="module" src="75820185/index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Home extends Shadow {
|
|||||||
|
|
||||||
NavBar()
|
NavBar()
|
||||||
|
|
||||||
img("_/icons/logo.svg", "2.5em")
|
img("/_/icons/logo.svg", "2.5em")
|
||||||
.onClick((done) => {
|
.onClick((done) => {
|
||||||
if(!done) return
|
if(!done) return
|
||||||
window.navigateTo("/")
|
window.navigateTo("/")
|
||||||
@@ -24,7 +24,7 @@ class Home extends Shadow {
|
|||||||
|
|
||||||
switch(window.location.pathname) {
|
switch(window.location.pathname) {
|
||||||
case "/":
|
case "/":
|
||||||
img("_/images/knight.png", "29vmax")
|
img("/_/images/knight.png", "29vmax")
|
||||||
.position("absolute")
|
.position("absolute")
|
||||||
.left(50, vw).top(50, vh)
|
.left(50, vw).top(50, vh)
|
||||||
.center()
|
.center()
|
||||||
@@ -51,13 +51,12 @@ class Home extends Shadow {
|
|||||||
case "/join":
|
case "/join":
|
||||||
Join()
|
Join()
|
||||||
break;
|
break;
|
||||||
case "/signin":
|
|
||||||
SignIn()
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(window.location.pathname.startsWith("/signup")) {
|
if(window.location.pathname.startsWith("/signup")) {
|
||||||
SignupForm()
|
SignupForm()
|
||||||
|
} else if(window.location.pathname.startsWith("/signin")) {
|
||||||
|
SignIn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ class SignIn extends Shadow {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
ZStack(() => {
|
ZStack(() => {
|
||||||
|
if(window.location.search.includes("new")) {
|
||||||
|
p("Welcome to Hyperia! You may now log in.")
|
||||||
|
.x(50, vw).y(40, vh)
|
||||||
|
.center()
|
||||||
|
}
|
||||||
|
|
||||||
form(() => {
|
form(() => {
|
||||||
input("Email")
|
input("Email")
|
||||||
.attr({name: "email", type: "email"})
|
.attr({name: "email", type: "email"})
|
||||||
|
|||||||
Reference in New Issue
Block a user