diff --git a/.env.sample b/.env.sample
index 2b88f4e..fec9b30 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1,9 +1,10 @@
+PORT=3000
+DOMAIN="http://localhost:3000"
DATABASE_HOST="127.0.0.1"
DATABASE_NAME=hsp-gdh
DATABASE_USER=hsp-gdh
DATABASE_PASSWORD=""
JWT_SECRET=""
-PORT=
SMTP=
MAIL=
MAIL_PASS=
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
index 2fcbea3..3495da3 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -46,5 +46,8 @@
"space-unary-ops": "error",
"spaced-comment": "error",
"yoda": "error"
+ },
+ "globals": {
+ "Bun": false
}
}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d7ff0f0..f0d9796 100644
--- a/.gitignore
+++ b/.gitignore
@@ -131,3 +131,9 @@ dist
.yarn/install-state.gz
.pnp.*
+# logs
+logs/
+*.log
+
+# token
+token/
\ No newline at end of file
diff --git a/tokensDB/000037.log b/README.md
similarity index 100%
rename from tokensDB/000037.log
rename to README.md
diff --git a/bun.lockb b/bun.lockb
index cf1eea9..e0b6543 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/database.sql b/database.sql
index 7b9d932..4170894 100644
--- a/database.sql
+++ b/database.sql
@@ -10,128 +10,28 @@ GRANT ALL PRIVILEGES ON airjet.* TO 'hsp-gdh'@'%';
USE `hsp-gdh`;
-CREATE TABLE pilots (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- first_name VARCHAR(255) NOT NULL,
- last_name VARCHAR(255) NOT NULL,
- email VARCHAR(255) NOT NULL,
- phone VARCHAR(20) NOT NULL,
- license_number VARCHAR(255) NOT NULL,
- license_expiry DATE NOT NULL,
- salary DECIMAL(10, 2) NOT NULL DEFAULT 0.0,
- status VARCHAR(255) NOT NULL,
- PRIMARY KEY (id)
-) ENGINE=InnoDB;
-
-CREATE TABLE airplanes (
+CREATE TABLE roles (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
- type VARCHAR(255) NOT NULL,
- manufacturer VARCHAR(255) NOT NULL,
- capacity INT NOT NULL,
- status VARCHAR(255) NOT NULL,
- location VARCHAR(255),
- PRIMARY KEY (id)
-) ENGINE=InnoDB;
-
-CREATE TABLE airlines (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- code VARCHAR(2) NOT NULL,
- logo VARCHAR(255),
- PRIMARY KEY (id)
-) ENGINE=InnoDB;
-
-CREATE TABLE airports (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- code VARCHAR(3) NOT NULL,
- city VARCHAR(255) NOT NULL,
- country VARCHAR(255) NOT NULL,
- latitude FLOAT NOT NULL,
- longitude FLOAT NOT NULL,
- PRIMARY KEY (id)
-) ENGINE=InnoDB;
-
-CREATE TABLE flights (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- airline_id INT UNSIGNED NOT NULL,
- pilot_id INT UNSIGNED NOT NULL,
- flight_no VARCHAR(10) NOT NULL,
- origin_id INT UNSIGNED NOT NULL,
- destination_id INT UNSIGNED NOT NULL,
- departure_time DATETIME NOT NULL,
- arrival_time DATETIME NOT NULL,
- duration_minutes INT NOT NULL,
- price_economy DECIMAL(10,2) NOT NULL,
- price_business DECIMAL(10,2) NOT NULL,
- price_first_class DECIMAL(10,2) NOT NULL,
- status VARCHAR(255) NOT NULL,
+ user_bitfield INT UNSIGNED NOT NULL,
+ role_bitfield INT UNSIGNED NOT NULL,
+ verification_code_bitfield INT UNSIGNED NOT NULL,
+ ban_bitfield INT UNSIGNED NOT NULL,
+ patient_bitfield INT UNSIGNED NOT NULL,
+ doctor_bitfield INT UNSIGNED NOT NULL,
+ service_bitfield INT UNSIGNED NOT NULL,
+ company_bitfield INT UNSIGNED NOT NULL,
+ hospital_bitfield INT UNSIGNED NOT NULL,
+ room_bitfield INT UNSIGNED NOT NULL,
+ appointment_bitfield INT UNSIGNED NOT NULL,
PRIMARY KEY (id),
- CONSTRAINT fl_airline_id
- FOREIGN KEY (airline_id)
- REFERENCES airlines(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE,
- CONSTRAINT fl_pilot_id
- FOREIGN KEY (pilot_id)
- REFERENCES pilots(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE,
- CONSTRAINT fl_origin_id
- FOREIGN KEY (origin_id)
- REFERENCES airports(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE,
- CONSTRAINT fl_destination_id
- FOREIGN KEY (destination_id)
- REFERENCES airports(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE
+ UNIQUE INDEX r_name_idx (name)
) ENGINE=InnoDB;
-CREATE TABLE user_types (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- PRIMARY KEY (id),
- UNIQUE INDEX ut_name_idx (name)
-) ENGINE=InnoDB;
-
-INSERT INTO user_types (name) VALUES ('unverified'), ('customer'), ('support'), ('pilote'), ('staff'), ('admin');
-
-CREATE TABLE permissions (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- name VARCHAR(255) NOT NULL,
- PRIMARY KEY (id),
- UNIQUE INDEX p_name_idx (name)
-) ENGINE=InnoDB;
-
-INSERT INTO permissions (name) VALUES
-('view_users'), ('add_users'), ('edit_users'), ('delete_users'),
-('view_flights'), ('add_flights'), ('edit_flights'), ('delete_flights'),
-('view_airlines'), ('add_airlines'), ('edit_airlines'), ('delete_airlines'),
-('view_airplanes'), ('add_airplanes'), ('edit_airplanes'), ('delete_airplanes'),
-('view_airports'), ('add_airports'), ('edit_airports'), ('delete_airports'),
-('view_seats'), ('add_seats'), ('edit_seats'), ('delete_seats'),
-('view_pilots'), ('add_pilots'), ('edit_pilots'), ('delete_pilots');
-
-CREATE TABLE user_type_permissions (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- user_type_id INT UNSIGNED NOT NULL,
- permission_id INT UNSIGNED NOT NULL,
- PRIMARY KEY (id),
- CONSTRAINT utp_user_type_id
- FOREIGN KEY (user_type_id)
- REFERENCES user_types(id)
- ON DELETE CASCADE
- ON UPDATE CASCADE,
- CONSTRAINT utp_permission_id
- FOREIGN KEY (permission_id)
- REFERENCES permissions(id)
- ON DELETE CASCADE
- ON UPDATE CASCADE,
- UNIQUE INDEX utp_user_type_permission_idx (user_type_id, permission_id)
-) ENGINE=InnoDB;
+INSERT INTO roles (name, user_bitfield, role_bitfield, verification_code_bitfield, ban_bitfield, patient_bitfield, doctor_bitfield, service_bitfield, company_bitfield, hospital_bitfield, room_bitfield, appointment_bitfield) VALUES
+ ('Admin', 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7),
+ ('Doctor', 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0),
+ ('Patient', 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0);
CREATE TABLE users (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -140,19 +40,31 @@ CREATE TABLE users (
username VARCHAR(64) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(128) NOT NULL,
+ email_verified BOOLEAN NOT NULL DEFAULT FALSE,
phone VARCHAR(32) DEFAULT 'None',
- user_type_id INT UNSIGNED NOT NULL,
- is_banned BOOLEAN NOT NULL DEFAULT 0,
+ phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (id),
- CONSTRAINT u_user_type_id
- FOREIGN KEY (user_type_id)
- REFERENCES user_types(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE,
- INDEX ur_user_type_idx (user_type_id)
) ENGINE=InnoDB;
-CREATE TABLE user_email_verifications (
+CREATE TABLE user_roles (
+ user_id INT UNSIGNED NOT NULL,
+ role_id INT UNSIGNED NOT NULL,
+ PRIMARY KEY (user_id, role_id),
+ CONSTRAINT ur_user_id
+ FOREIGN KEY (user_id)
+ REFERENCES users(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ur_user_id_idx (user_id),
+ CONSTRAINT ur_role_id
+ FOREIGN KEY (role_id)
+ REFERENCES roles(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ur_role_id_idx (role_id)
+) ENGINE=InnoDB;
+
+CREATE TABLE verification_codes (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id INT UNSIGNED NOT NULL,
verification_code VARCHAR(255),
@@ -167,108 +79,169 @@ CREATE TABLE user_email_verifications (
INDEX mv_user_id_idx (user_id)
) ENGINE=InnoDB;
-CREATE TABLE seats (
+CREATE TABLE bans (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- user_id INT UNSIGNED,
- flight_id INT UNSIGNED NOT NULL,
- place_no INT UNSIGNED NOT NULL,
- class VARCHAR(32) NOT NULL,
- bought_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ user_id INT UNSIGNED NOT NULL,
+ reason TEXT NOT NULL,
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
- CONSTRAINT ps_user_id
+ CONSTRAINT bn_user_id
FOREIGN KEY (user_id)
REFERENCES users(id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
- CONSTRAINT ps_flight_id
- FOREIGN KEY (flight_id)
- REFERENCES flights(id)
- ON DELETE RESTRICT
- ON UPDATE CASCADE,
- INDEX ps_user_id_idx (user_id),
- INDEX ps_flight_id_idx (flight_id)
+ INDEX bn_user_id_idx (user_id)
) ENGINE=InnoDB;
-INSERT INTO pilots (id, first_name, last_name, email, phone, license_number, license_expiry, salary, status) VALUES
-(1, 'John', 'Doe', 'john.doe@example.com', '555-1234', '1234-5678', '2025-06-30', 75000.00, 'Active'),
-(2, 'Jane', 'Smith', 'jane.smith@example.com', '555-5678', '9876-5432', '2024-12-31', 80000.00, 'Active'),
-(3, 'Bob', 'Johnson', 'bob.johnson@example.com', '555-7890', '3456-7890', '2023-09-30', 70000.00, 'Inactive'),
-(4, 'Alice', 'Lee', 'alice.lee@example.com', '555-2345', '5678-1234', '2024-03-31', 85000.00, 'Active'),
-(5, 'David', 'Nguyen', 'david.nguyen@example.com', '555-5678', '1234-5678', '2023-12-31', 75000.00, 'Active'),
-(6, 'Maria', 'Garcia', 'maria.garcia@example.com', '555-7890', '9876-5432', '2024-06-30', 70000.00, 'Active'),
-(7, 'Mohammed', 'Ali', 'mohammed.ali@example.com', '555-2345', '3456-7890', '2025-09-30', 85000.00, 'Inactive'),
-(8, 'Sofia', 'Martinez', 'sofia.martinez@example.com', '555-3456', '5678-1234', '2022-03-31', 80000.00, 'Active');
+CREATE TABLE doctors (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ user_id INT UNSIGNED NOT NULL,
+ email VARCHAR(255) NOT NULL,
+ phone VARCHAR(20) NOT NULL,
+ speciality VARCHAR(255) NOT NULL,
+ status VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id),
+ CONSTRAINT dt_user_id
+ FOREIGN KEY (user_id)
+ REFERENCES users(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX dt_user_id_idx (user_id)
+) ENGINE=InnoDB;
-INSERT INTO airplanes (id, name, type, manufacturer, capacity, status, location) VALUES
-(1, 'Boeing 747', 'Jet', 'Boeing', 660, 'Active', 'Los Angeles'),
-(2, 'Airbus A320', 'Jet', 'Airbus', 180, 'Active', 'New York'),
-(3, 'Embraer E175', 'Jet', 'Embraer', 78, 'Inactive', 'Miami'),
-(4, 'Boeing 737', 'Jet', 'Boeing', 160, 'Active', 'Chicago'),
-(5, 'Airbus A330', 'Jet', 'Airbus', 440, 'Active', 'Paris'),
-(6, 'Bombardier CRJ900', 'Jet', 'Bombardier', 88, 'Active', 'Montreal'),
-(7, 'Boeing 777', 'Jet', 'Boeing', 396, 'Inactive', 'London'),
-(8, 'Airbus A380', 'Jet', 'Airbus', 853, 'Active', 'Dubai'),
-(9, 'Embraer E190', 'Jet', 'Embraer', 114, 'Active', 'Buenos Aires'),
-(10, 'Boeing 787', 'Jet', 'Boeing', 335, 'Active', 'Tokyo'),
-(11, 'Boeing 747-8', 'Jet', 'Boeing', 605, 'Active', 'Hong Kong'),
-(12, 'Airbus A350', 'Jet', 'Airbus', 440, 'Active', 'Dublin'),
-(13, 'Embraer E195', 'Jet', 'Embraer', 124, 'Inactive', 'Rio de Janeiro'),
-(14, 'Boeing 737 MAX', 'Jet', 'Boeing', 230, 'Active', 'Seattle'),
-(15, 'Airbus A321', 'Jet', 'Airbus', 236, 'Active', 'Shanghai');
+CREATE TABLE patients (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ user_id INT UNSIGNED NOT NULL,
+ date_of_birth DATE NOT NULL,
+ gender ENUM('M', 'F', 'O') NOT NULL,
+ address VARCHAR(255) NOT NULL,
+ social_security_number VARCHAR(128) NOT NULL,
+ insurance_number VARCHAR(128) NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
-INSERT INTO airlines (id, name, code, logo) VALUES
-(1, 'Delta Air Lines', 'DL', 'https://www.delta.com/content/dam/delta-com/brand-icons/Delta_Icon_blue_72.png'),
-(2, 'American Airlines', 'AA', 'https://www.aa.com/content/dam/aa-com/logo-web/aa-logo-blue-and-red-horz.png'),
-(3, 'United Airlines', 'UA', 'https://www.united.com/web/en-US/content/images/global/header/header-united-logo.png'),
-(4, 'Southwest Airlines', 'WN', 'https://www.southwest.com/etc/designs/southwest/v2/images/swa-logo--mod.svg'),
-(5, 'Alaska Airlines', 'AS', 'https://www.alaskaair.com/content/dam/alaskaair/logo/2016/alaska-airlines-horiz-white-blue-1x.png'),
-(6, 'JetBlue Airways', 'B6', 'https://www.jetblue.com/etc/designs/jetblue/clientlibs/dist/images/svg/jetblue-logo.svg'),
-(7, 'Spirit Airlines', 'NK', 'https://www.spirit.com/images/spirit-logo.png'),
-(8, 'Frontier Airlines', 'F9', 'https://www.flyfrontier.com/etc/designs/frontier-airlines/clientlibs/dist/images/f9-logo-horz.svg'),
-(9, 'Air France', 'AF', 'https://www.airfrance.com/etc/designs/airfrance/clientlibs/dist/images/global/logo/airfrance-logo-blue.svg'),
-(10, 'Transavia France', 'TO', 'https://www.transavia.com/content/dam/airlines/tv/fra/fr/common/img/logo.svg'),
-(11, 'EasyJet France', 'U2', 'https://www.easyjet.com/etc/designs/easyjet/clientlibs/dist/images/logo.svg'),
-(12, 'Corsair International', 'SS', 'https://www.corsair.fr/etc/designs/corsair/clientlibs/dist/images/logo.svg'),
-(13, 'XL Airways France', 'SE', 'https://www.xl.com/assets/images/XL-logo.png'),
-(14, 'Aigle Azur', 'ZI', 'https://www.aigle-azur.com/site/sites/all/themes/aigle-azur/images/logo-aigle-azur-160x80.png');
+CREATE TABLE services (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ description TEXT NOT NULL,
+ price DECIMAL(10, 2) NOT NULL,
+ status VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
-INSERT INTO airports (id, name, code, city, country, latitude, longitude) VALUES
-(1, 'John F. Kennedy International Airport', 'JFK', 'New York', 'United States', 40.6413, -73.7781),
-(2, 'Los Angeles International Airport', 'LAX', 'Los Angeles', 'United States', 33.9416, -118.4085),
-(3, 'London Heathrow Airport', 'LHR', 'London', 'United Kingdom', 51.4700, -0.4543),
-(4, 'Paris-Charles de Gaulle Airport', 'CDG', 'Paris', 'France', 49.0097, 2.5479),
-(5, 'Tokyo Haneda Airport', 'HND', 'Tokyo', 'Japan', 35.5532, 139.7818),
-(6, 'Dubai International Airport', 'DXB', 'Dubai', 'United Arab Emirates', 25.2528, 55.3644),
-(7, 'Sydney Kingsford Smith Airport', 'SYD', 'Sydney', 'Australia', -33.9461, 151.1772),
-(8, 'São Paulo-Guarulhos International Airport', 'GRU', 'São Paulo', 'Brazil', -23.4356, -46.4731),
-(9, 'Jomo Kenyatta International Airport', 'NBO', 'Nairobi', 'Kenya', -1.3192, 36.9258),
-(10, 'Cairo International Airport', 'CAI', 'Cairo', 'Egypt', 30.1111, 31.4139),
-(11, 'Paris Orly Airport', 'ORY', 'Paris', 'France', 48.7262, 2.3650),
-(12, 'Nice Côte d''Azur Airport', 'NCE', 'Nice', 'France', 43.6584, 7.2157),
-(13, 'Marseille Provence Airport', 'MRS', 'Marseille', 'France', 43.4393, 5.2214),
-(14, 'Lyon-Saint-Exupéry Airport', 'LYS', 'Lyon', 'France', 45.7216, 5.0790),
-(15, 'Bordeaux-Mérignac Airport', 'BOD', 'Bordeaux', 'France', 44.8283, -0.7156),
-(16, 'Toulouse-Blagnac Airport', 'TLS', 'Toulouse', 'France', 43.6356, 1.3678),
-(17, 'Nantes Atlantique Airport', 'NTE', 'Nantes', 'France', 47.1567, -1.6114),
-(18, 'Strasbourg Airport', 'SXB', 'Strasbourg', 'France', 48.5442, 7.6277),
-(19, 'Lille Airport', 'LIL', 'Lille', 'France', 50.5633, 3.0897),
-(20, 'Brest Bretagne Airport', 'BES', 'Brest', 'France', 48.4472, -4.4228),
-(21, 'Vienna International Airport', 'VIE', 'Vienna', 'Austria', 48.1197, 16.5667),
-(22, 'Zürich Airport', 'ZRH', 'Zürich', 'Switzerland', 47.4502, 8.5618),
-(23, 'Amsterdam Airport Schiphol', 'AMS', 'Amsterdam', 'Netherlands', 52.3081, 4.7642),
-(24, 'Frankfurt Airport', 'FRA', 'Frankfurt', 'Germany', 50.0379, 8.5622),
-(25, 'Barcelona-El Prat Airport', 'BCN', 'Barcelona', 'Spain', 41.2974, 2.0833),
-(26, 'Adolfo Suárez Madrid-Barajas Airport', 'MAD', 'Madrid', 'Spain', 40.4936, -3.5668),
-(27, 'Leonardo da Vinci-Fiumicino Airport', 'FCO', 'Rome', 'Italy', 41.8003, 12.2388),
-(28, 'Stockholm Arlanda Airport', 'ARN', 'Stockholm', 'Sweden', 59.6519, 17.9186);
+CREATE TABLE service_doctors (
+ service_id INT UNSIGNED NOT NULL,
+ doctor_id INT UNSIGNED NOT NULL,
+ PRIMARY KEY (service_id, doctor_id),
+ CONSTRAINT sd_service_id
+ FOREIGN KEY (service_id)
+ REFERENCES services(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX sd_service_id_idx (service_id),
+ CONSTRAINT sd_doctor_id
+ FOREIGN KEY (doctor_id)
+ REFERENCES doctors(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX sd_doctor_id_idx (doctor_id)
+) ENGINE=InnoDB;
-INSERT INTO flights (id, airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status) VALUES
-(1, 1, 1, 'BA123', 1, 2, '2023-03-15 08:00:00', '2023-03-15 10:00:00', 120, 100, 200, 300, 'scheduled'),
-(2, 2, 2, 'AF456', 2, 3, '2023-03-16 14:00:00', '2023-03-16 16:30:00', 150, 80, 150, 250, 'scheduled'),
-(3, 3, 3, 'LH789', 4, 5, '2023-03-17 09:30:00', '2023-03-17 12:00:00', 150, 90, 170, 280, 'scheduled'),
-(4, 1, 4, 'BA345', 2, 5, '2023-03-18 07:00:00', '2023-03-18 10:00:00', 180, 120, 240, 400, 'scheduled'),
-(5, 4, 5, 'EZY678', 3, 1, '2023-03-19 11:00:00', '2023-03-19 13:00:00', 120, 60, 120, 200, 'scheduled'),
-(6, 2, 6, 'AF901', 5, 6, '2023-03-20 16:30:00', '2023-03-20 19:00:00', 150, 100, 200, 350, 'scheduled');
+CREATE TABLE companies (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ code VARCHAR(2) NOT NULL,
+ logo VARCHAR(255),
+ PRIMARY KEY (id)
+) ENGINE=InnoDB;
-INSERT INTO `user_type_permissions` (`user_type_id`, `permission_id`) VALUES
-('6', '1'),('6', '2'),('6', '3'),('6', '4'),('6', '5'),('6', '6'),('6', '7'),('6', '8'),('6', '9'),('6', '10'),('6', '11'),('6', '12'),('6', '13'),('6', '14'),('6', '15'),('6', '16'),('6', '17'),('6', '18'),('6', '19'),('6', '20'),('6', '21'),('6', '22'),('6', '23'),('6', '24'),('6', '25'),('6', '26'),('6', '27'),('6', '28');
\ No newline at end of file
+CREATE TABLE hospitals (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ company_id INT UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ code VARCHAR(3) NOT NULL,
+ country VARCHAR(255) NOT NULL,
+ region VARCHAR(255) NOT NULL,
+ city VARCHAR(255) NOT NULL,
+ address VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id),
+ CONSTRAINT hs_company_id
+ FOREIGN KEY (company_id)
+ REFERENCES companies(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX hs_company_id_idx (company_id)
+) ENGINE=InnoDB;
+
+CREATE TABLE rooms (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ hospital_id INT UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id)
+ CONSTRAINT ch_hospital_id
+ FOREIGN KEY (hospital_id)
+ REFERENCES hospitals(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+) ENGINE=InnoDB;
+
+CREATE TABLE hospital_doctors (
+ hospital_id INT UNSIGNED NOT NULL,
+ doctor_id INT UNSIGNED NOT NULL,
+ PRIMARY KEY (hospital_id, doctor_id),
+ CONSTRAINT hd_hospital_id
+ FOREIGN KEY (hospital_id)
+ REFERENCES hospitals(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX hd_hospital_id_idx (hospital_id),
+ CONSTRAINT hd_doctor_id
+ FOREIGN KEY (doctor_id)
+ REFERENCES doctors(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX hd_doctor_id_idx (doctor_id)
+) ENGINE=InnoDB;
+
+CREATE TABLE appointments (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ patient_id INT UNSIGNED NOT NULL,
+ doctor_id INT UNSIGNED NOT NULL,
+ service_id INT UNSIGNED NOT NULL,
+ hospital_id INT UNSIGNED DEFAULT NULL,
+ room_id INT UNSIGNED DEFAULT NULL,
+ date DATE NOT NULL,
+ time TIME NOT NULL,
+ status VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id)
+ CONSTRAINT ap_patient_id
+ FOREIGN KEY (patient_id)
+ REFERENCES patients(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ap_patient_id_idx (patient_id),
+ CONSTRAINT ap_doctor_id
+ FOREIGN KEY (doctor_id)
+ REFERENCES doctors(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ap_doctor_id_idx (doctor_id),
+ CONSTRAINT ap_service_id
+ FOREIGN KEY (service_id)
+ REFERENCES services(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ap_service_id_idx (service_id),
+ CONSTRAINT ap_hospital_id
+ FOREIGN KEY (hospital_id)
+ REFERENCES hospitals(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ap_hospital_id_idx (hospital_id),
+ CONSTRAINT ap_room_id
+ FOREIGN KEY (room_id)
+ REFERENCES rooms(id)
+ ON DELETE RESTRICT
+ ON UPDATE CASCADE,
+ INDEX ap_room_id_idx (room_id)
+) ENGINE=InnoDB;
\ No newline at end of file
diff --git a/index.js b/index.js
index a887282..e24077a 100644
--- a/index.js
+++ b/index.js
@@ -5,17 +5,19 @@ import logger from 'morgan';
import express from 'express';
import cookieParser from 'cookie-parser';
-import { log } from './modules/log';
+import { log } from './modules/logManager';
import { speedLimiter, checkSystemLoad } from './modules/requestHandler';
import testRouter from './routes/test';
import usersRouter from './routes/users';
-import pilotsRouter from './routes/pilots';
-import airplanesRouter from './routes/airplanes';
-import airlinesRouter from './routes/airlines';
-import airportsRouter from './routes/airports';
-import flightsRouter from './routes/flights';
-import seatsRouter from './routes/seats';
+import doctorsRouter from './routes/doctors';
+import patientsRouter from './routes/patients';
+import companiesRouter from './routes/companies';
+import hospitalsRouter from './routes/hospitals';
+
+// create logs directory if it doesn't exist
+const logsDir = path.join(import.meta.dir, 'logs');
+if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir);
const app = express();
app.set('trust proxy', 1);
@@ -25,7 +27,7 @@ app.use(cookieParser());
app.use(speedLimiter);
app.use(checkSystemLoad);
app.use(logger('dev'));
-app.use(logger('combined', { stream: fs.createWriteStream(path.join(__dirname, 'logs/access.log'), { flags: 'a' }) }));
+app.use(logger('combined', { stream: fs.createWriteStream(path.join(import.meta.dir, 'logs/access.log'), { flags: 'a' }) }));
app.use(cors({
origin: '*',
}));
@@ -35,12 +37,10 @@ app.use(express.static('public'));
// routes
app.use('/api/test', testRouter);
app.use('/api/users', usersRouter);
-app.use('/api/pilots', pilotsRouter);
-app.use('/api/airplanes', airplanesRouter);
-app.use('/api/airlines', airlinesRouter);
-app.use('/api/airports', airportsRouter);
-app.use('/api/flights', flightsRouter);
-app.use('/api/seats', seatsRouter);
+app.use('/api/doctors', doctorsRouter);
+app.use('/api/patients', patientsRouter);
+app.use('/api/companies', companiesRouter);
+app.use('/api/hospitals', hospitalsRouter);
// run the API
app.listen(process.env.PORT, async () => {
diff --git a/logs/access.log b/logs/access.log
deleted file mode 100644
index 691dca0..0000000
--- a/logs/access.log
+++ /dev/null
@@ -1,187 +0,0 @@
-::ffff:127.0.0.1 - - [15/Feb/2023:20:21:41 +0000] "OPTIONS /user/login HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:21:41 +0000] "POST /user/login HTTP/1.1" 200 261 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:21:41 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:21:41 +0000] "GET /user/profile HTTP/1.1" 401 27 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:22:00 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:22:00 +0000] "GET /user/profile HTTP/1.1" 200 180 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:22:55 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:22:55 +0000] "GET /user/profile HTTP/1.1" 404 29 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:23:05 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:23:06 +0000] "GET /user/profile HTTP/1.1" 401 27 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:23:39 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:23:39 +0000] "POST /user/register HTTP/1.1" 500 35 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:21 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:21 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:41 +0000] "OPTIONS /user/login HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:41 +0000] "POST /user/login HTTP/1.1" 200 261 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:41 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:24:41 +0000] "GET /user/profile HTTP/1.1" 200 190 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:27:36 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:28:16 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:28:16 +0000] "GET /user/profile HTTP/1.1" 304 - "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:08 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:08 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:18 +0000] "OPTIONS /user/login HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:18 +0000] "POST /user/login HTTP/1.1" 200 258 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:18 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:30:18 +0000] "GET /user/profile HTTP/1.1" 200 177 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:33:05 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:33:05 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:34:37 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:34:38 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:36:28 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:36:28 +0000] "POST /user/register HTTP/1.1" 401 29 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:39:25 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:39:25 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:34 +0000] "OPTIONS /user/register HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:34 +0000] "POST /user/register HTTP/1.1" 200 37 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:44 +0000] "OPTIONS /user/login HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:44 +0000] "POST /user/login HTTP/1.1" 200 275 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:44 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:40:44 +0000] "GET /user/profile HTTP/1.1" 200 194 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:44:45 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:44:45 +0000] "GET /user/profile HTTP/1.1" 304 - "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:46:02 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:46:02 +0000] "GET /user/profile HTTP/1.1" 304 - "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:46:46 +0000] "OPTIONS /user/profile HTTP/1.1" 204 0 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [15/Feb/2023:20:46:47 +0000] "GET /user/profile HTTP/1.1" 304 - "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:11 +0000] "GET / HTTP/1.1" 404 139 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:23 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:35 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:35 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:35 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:35 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:35 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:36 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:36 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:36 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:36 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:36 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:37 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:37 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:37 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:37 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:38 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:38 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:39 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:39 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:39 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:40 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:41 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:42 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:43 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:43 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:43 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:43 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:43 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:44 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:45 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:46 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:47 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:48 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:06:49 +0000] "GET /users HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:07:53 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:44 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:44 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:45 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:45 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:45 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:46 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:46 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:46 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:46 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:46 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:47 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:47 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:47 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:47 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:47 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:48 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:48 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:48 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:48 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:49 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:08:49 +0000] "GET /users/register HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:09:04 +0000] "GET /users/login HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:09:07 +0000] "GET /users/loginn HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:09:11 +0000] "GET /users/login HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:11:15 +0000] "GET /users/login HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:12:03 +0000] "POST /users/login HTTP/1.1" 500 35 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:14:49 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:15:14 +0000] "GET /users/login HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:15:44 +0000] "GET /users/verify HTTP/1.1" 401 31 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:16:30 +0000] "GET /users/verify HTTP/1.1" 500 35 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:16:31 +0000] "GET /users/verify HTTP/1.1" 500 35 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:16:31 +0000] "GET /users/verify HTTP/1.1" 500 35 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:18:28 +0000] "GET /users/verify HTTP/1.1" 400 25 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:18:37 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:18:55 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:19:37 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:20:01 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:42 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:51 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:52 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:52 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:53 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:53 +0000] "POST /users/login HTTP/1.1" 429 54 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:54 +0000] "POST /users/login HTTP/1.1" 429 54 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:23:55 +0000] "POST /users/login HTTP/1.1" 429 54 "-" "Wget/1.21.3"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:25:02 +0000] "GET /users/verify HTTP/1.1" 400 25 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:26:29 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "curl/7.84.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:28:59 +0000] "GET /test HTTP/1.1" 200 45 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/110.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:29:30 +0000] "POST /test HTTP/1.1" 200 46 "-" "curl/7.84.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:30:08 +0000] "POST /test HTTP/1.1" 200 46 "-" "curl/7.84.0"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:31:49 +0000] "POST /test HTTP/1.1" 200 46 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:32:12 +0000] "POST /users/login HTTP/1.1" 400 41 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:32:54 +0000] "POST /users/login HTTP/1.1" 400 41 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:33:50 +0000] "POST /users/login HTTP/1.1" 400 25 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:34:25 +0000] "POST /users/login HTTP/1.1" 400 41 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
-::ffff:127.0.0.1 - - [12/Mar/2023:13:35:27 +0000] "POST /users/login HTTP/1.1" 400 41 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"
diff --git a/modules/cronManager.js b/modules/cronManager.js
new file mode 100644
index 0000000..cb434ff
--- /dev/null
+++ b/modules/cronManager.js
@@ -0,0 +1,48 @@
+import cron from 'node-cron';
+
+const daysOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
+const hoursOfDay = [...Array(24).keys()].map(hour => hour.toString());
+const minutesOfHour = [...Array(60).keys()].map(minute => minute.toString());
+
+function toCronExpression(str) {
+ switch (str.toLowerCase()) {
+ case 'everyfiveseconds': {
+ return '*/5 * * * * *';
+ }
+ case 'everythirtyseconds': {
+ return '*/30 * * * * *';
+ }
+ case 'everyminute': {
+ return '* * * * *';
+ }
+ case 'everyfiveminutes': {
+ return '*/5 * * * *';
+ }
+ case 'everyfifteenminutes': {
+ return '*/15 * * * *';
+ }
+ case 'everythirtyminutes': {
+ return '*/30 * * * *';
+ }
+ case 'everyday': {
+ return '0 0 * * *';
+ }
+ case 'weekdays': {
+ return '0 0 * * 1-5';
+ }
+ case 'weekends': {
+ return '0 0 * * 6,7';
+ }
+ default: {
+ const [day, hour, minute] = str.toLowerCase().split(' ');
+ const dayIndex = daysOfWeek.indexOf(day);
+ const hourIndex = hoursOfDay.indexOf(hour);
+ const minuteIndex = minutesOfHour.indexOf(minute);
+ return `${minuteIndex} ${hourIndex} * * ${dayIndex}`;
+ }
+ }
+}
+
+export function createCron(rate, exec) {
+ cron.schedule(toCronExpression(rate), exec);
+}
\ No newline at end of file
diff --git a/modules/database.js b/modules/databaseManager.js
similarity index 100%
rename from modules/database.js
rename to modules/databaseManager.js
diff --git a/modules/fetcher.js b/modules/fetchManager.js
similarity index 84%
rename from modules/fetcher.js
rename to modules/fetchManager.js
index 499f12a..adb4a23 100644
--- a/modules/fetcher.js
+++ b/modules/fetchManager.js
@@ -1,7 +1,7 @@
import fetch from 'node-fetch';
-import { error } from './log';
+import { error } from './logManager';
-async function get(url, token) {
+async function get(url, token = 'none') {
const options = {
method: 'GET',
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
@@ -15,7 +15,7 @@ async function get(url, token) {
.catch(err => error(err));
}
-async function post(url, body, token) {
+async function post(url, body, token = 'none') {
const options = {
method: 'POST',
mode: 'cors',
@@ -31,7 +31,7 @@ async function post(url, body, token) {
.catch(err => error(err));
}
-async function patch(url, body, token) {
+async function patch(url, body, token = 'none') {
const options = {
method: 'PATCH',
mode: 'cors',
@@ -47,7 +47,7 @@ async function patch(url, body, token) {
.catch(err => error(err));
}
-async function put(url, body, token) {
+async function put(url, body, token = 'none') {
const options = {
method: 'PUT',
mode: 'cors',
diff --git a/modules/fileManager.js b/modules/fileManager.js
index 558c972..f1d1918 100644
--- a/modules/fileManager.js
+++ b/modules/fileManager.js
@@ -1,6 +1,7 @@
import fs from 'fs';
-import download from 'download';
-import random from './random';
+import { Readable } from 'stream';
+import { finished } from 'stream/promises';
+import { random } from './random';
function fileExist(path) {
try {
@@ -22,9 +23,11 @@ function fileDelete(path) {
}
}
-function fileDownload(url, name) {
+async function fileDownload(url, path) {
try {
- download(url, '../cdn/images/', { filename: name });
+ const stream = fs.createWriteStream(path);
+ const { body } = await fetch(url);
+ await finished(Readable.fromWeb(body).pipe(stream));
return true;
}
catch (err) {
@@ -32,6 +35,7 @@ function fileDownload(url, name) {
}
}
+
function folderExist(path) {
try {
if (fs.existsSync(path)) {
@@ -58,7 +62,7 @@ function getFilesFromFolder(path) {
function randomFileFromFolder(path) {
try {
if (getFilesFromFolder(path)) {
- return random.random(0, getFilesFromFolder(path).length);
+ return random(0, getFilesFromFolder(path).length);
}
else {
return false;
diff --git a/modules/formatHandler.js b/modules/formatManager.js
similarity index 83%
rename from modules/formatHandler.js
rename to modules/formatManager.js
index a28c1fd..9ac8207 100644
--- a/modules/formatHandler.js
+++ b/modules/formatManager.js
@@ -22,4 +22,9 @@ export function isValidEmail(email) {
export function isNumber(x) {
return /^-?[\d.]+(?:e-?\d+)?$/.test(x);
+}
+
+export function isPhoneNumber(phone) {
+ const phoneRegex = /^\d{10}$/;
+ return phoneRegex.test(phone);
}
\ No newline at end of file
diff --git a/modules/log.js b/modules/logManager.js
similarity index 100%
rename from modules/log.js
rename to modules/logManager.js
diff --git a/modules/mailHandler.js b/modules/mailHandler.js
index e1f63b6..08b2648 100644
--- a/modules/mailHandler.js
+++ b/modules/mailHandler.js
@@ -1,9 +1,6 @@
/* eslint-disable no-undef */
import nodemailer from 'nodemailer';
import { random } from './random';
-import { createPool } from './database';
-
-const pool = createPool('localhost', 'root', '', 'postfixadmin');
const transporter = nodemailer.createTransport({
host: process.env.SMTP,
@@ -15,29 +12,6 @@ const transporter = nodemailer.createTransport({
},
});
-async function createAddress(username, domain, password, name = '', backup_email = '', phone = '') {
- try {
- const hashedPassword = await Bun.password.hash(password, {
- type: Bun.password.argon2i,
- memoryCost: 2 ** 15,
- hashLength: 32,
- timeCost: 5,
- });
- const [result] = await pool.execute(
- 'INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `created`, `modified`, `active`, `phone`, `email_other`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [ username, hashedPassword, name, `${domain}/${username}`, 0, username, domain, Date.now(), Date.now(), '1', phone, backup_email],
- );
- if (result.affectedRows === 0) {
- return false;
- }
- return true;
- }
- catch (error) {
- console.error(error);
- return false;
- }
-}
-
function sendMail(email, head, body) {
try {
// setup email data
@@ -63,30 +37,29 @@ function sendMail(email, head, body) {
}
}
-function sendVerification(email, userId) {
+function sendVerification(email, userId, type = 'register', code = null) {
try {
- const code = random(100000, 999999);
- if (sendMail(email, 'Your verification code for AirJet', `Verification code: ${code}\nLink: https://aostia.me/api/users/verify?u=${userId}&c=${code}`)) {
- return code;
- }
- else {
+ code ? code : random(100000, 999999);
+ let title, body;
+ switch (type) {
+ case 'email':
+ title = 'Your verification code for HSP-GDH';
+ body = `Verification code: ${code}\nLink: ${process.env.DOMAIN}/email/verify?code=${code}`;
+ break;
+ case 'password':
+ title = 'Your password reset code for HSP-GDH';
+ body = `Verification code: ${code}\nLink: ${process.env.DOMAIN}/password/reset?u=${userId}&c=${code}`;
+ break;
+ case '2fa':
+ title = 'Your 2FA code for HSP-GDH';
+ body = `Verification code: ${code}`;
+ break;
+ default:
return false;
}
- }
- catch (err) {
+ if (sendMail(email, title, body)) return code;
return false;
}
-}
-
-function sendResetVerification(email, code = random(100000, 999999)) {
- try {
- if (sendMail(email, 'Your reset verification code for AirJet', `Verification code: ${code}`)) {
- return code;
- }
- else {
- return false;
- }
- }
catch (err) {
return false;
}
@@ -95,6 +68,4 @@ function sendResetVerification(email, code = random(100000, 999999)) {
export {
sendMail,
sendVerification,
- sendResetVerification,
- createAddress,
};
\ No newline at end of file
diff --git a/modules/permission.js b/modules/permission.js
deleted file mode 100644
index 16718d1..0000000
--- a/modules/permission.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { pool } from '../modules/database.js';
-import { respondWithStatus } from './requestHandler.js';
-
-// Middleware to verify the user permissions
-async function verifyPermissions(userId, perms_req) {
-
- try {
- // Query the database to get the user
- const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (user.length === 0) {
- return false;
- }
-
- // Query the database to get the perms and verify
- const [hasPerm] = await pool.execute(
- 'SELECT COUNT(*) AS count FROM user_type_permissions WHERE user_type_id = ? AND permission_id = (SELECT id FROM permissions WHERE name = ?) LIMIT 1',
- [ user[0].user_type_id, perms_req ],
- );
- if (hasPerm.length === 0) {
- return false;
- }
- else {
- return true;
- }
- }
- catch (error) {
- return false;
- }
-}
-
-const hasPermission = (perms_req) => async (req, res, next) => {
- try {
- const userId = req.userId;
-
- // Query the database to get the user
- const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (user.length === 0) {
- return await respondWithStatus(res, 401, 'User is invalid');
- }
- // Query the database to get the perms and verify
- const [hasPerm] = await pool.execute(
- 'SELECT COUNT(*) AS count FROM user_type_permissions WHERE user_type_id = ? AND permission_id = (SELECT id FROM permissions WHERE name = ?) LIMIT 1',
- [ user[0].user_type_id, perms_req ],
- );
- if (req.originalUrl == '/api/users/me') {
- next();
- return;
- }
- if (hasPerm.length === 0) {
- return await respondWithStatus(res, 403, 'Missing permission');
- }
- else if (hasPerm[0].count == 0) {
- return await respondWithStatus(res, 403, 'Missing permission');
- }
- else {next();}
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-};
-
-async function checkBanned(req, res, next) {
- const userId = req.userId;
- try {
- const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (user.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
- }
- if (user[0].is_banned) {
- return await respondWithStatus(res, 403, 'User is banned');
- }
- next();
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-}
-
-async function isBanned(userId) {
- try {
- // Query the database to get the user
- const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (user.length === 0) {
- return true;
- }
- else if (user[0].is_banned == 1) {
- return true;
- }
- else {
- return false;
- }
- }
- catch (error) {
- console.error(error);
- return true;
- }
-}
-
-export { verifyPermissions, hasPermission, checkBanned, isBanned };
\ No newline at end of file
diff --git a/modules/permissionManager.js b/modules/permissionManager.js
new file mode 100644
index 0000000..f9cc1e6
--- /dev/null
+++ b/modules/permissionManager.js
@@ -0,0 +1,65 @@
+import { pool } from './databaseManager.js';
+import { respondWithStatus } from './requestHandler.js';
+import { error } from './logManager.js';
+
+
+export async function userExists(userId) {
+ try {
+ const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
+ if (user.length === 0) return false;
+ return true;
+ }
+ catch (err) {
+ error(err);
+ return false;
+ }
+}
+
+export async function isBanned(userId) {
+ try {
+ const [bannedUser] = await pool.execute('SELECT * FROM bans WHERE user_id = ? LIMIT 1', [userId]);
+ if (bannedUser.length > 0) return true;
+ return false;
+ }
+ catch (err) {
+ error(err);
+ return true;
+ }
+}
+
+// permissionType is bitfield
+// 1 = read
+// 2 = write
+// 4 = delete
+export async function verifyPermissions(userId, permissionName, permissionType) {
+ try {
+ const [perms] = await pool.execute(`SELECT r.${permissionName}_bitfield AS permissionBitfield FROM users u INNER JOIN user_roles ur ON u.id = ur.user_id INNER JOIN roles r ON ur.role_id = r.id WHERE u.id = ?`, [userId]);
+ for (const row of perms) {
+ if (row.permissionBitfield & permissionType) {
+ return true;
+ }
+ }
+ }
+ catch (err) {
+ error(err);
+ return false;
+ }
+}
+
+export async function checkUserExists(req, res, next) {
+ const userId = req.userId;
+ if (!userExists(userId)) return await respondWithStatus(res, 404, 'User not found');
+ next();
+}
+
+export async function checkBanned(req, res, next) {
+ const userId = req.userId;
+ if (isBanned(userId)) return await respondWithStatus(res, 403, 'User is banned');
+ next();
+}
+
+export const checkPermissions = (permissionName, permissionType) => async (req, res, next) => {
+ const userId = req.userId;
+ if (!verifyPermissions(userId, permissionName, permissionType)) return await respondWithStatus(res, 403, 'Missing permission');
+ next();
+};
\ No newline at end of file
diff --git a/modules/random.js b/modules/random.js
index 75ac3b6..2fe78ff 100644
--- a/modules/random.js
+++ b/modules/random.js
@@ -1,9 +1,22 @@
import crypto from 'crypto';
export function random(x, y) {
- return crypto.randomInt(x, y);
+ return crypto.randomInt(x, y + 1);
+}
+
+export function random2(min, max) {
+ const range = max - min + 1;
+ const byteLength = Math.ceil(Math.log2(range) / 8);
+ let randomValue;
+
+ do {
+ const randomBytes = crypto.randomBytes(byteLength);
+ randomValue = parseInt(randomBytes.toString('hex'), 16);
+ } while (randomValue >= range);
+
+ return randomValue + min;
}
export function randomHEX(x) {
return crypto.randomBytes(x).toString('hex');
-}
+}
\ No newline at end of file
diff --git a/modules/requestHandler.js b/modules/requestHandler.js
index bef8a75..bdf33a3 100644
--- a/modules/requestHandler.js
+++ b/modules/requestHandler.js
@@ -2,6 +2,7 @@ import rateLimit from 'express-rate-limit';
import slowDown from 'express-slow-down';
import http from 'http';
import os from 'os';
+import { log } from './logManager';
const requestLimiter = rateLimit({
windowMs: 60 * 1000,
@@ -23,7 +24,7 @@ function checkSystemLoad(req, res, next) {
const threshold = cores * 0.7;
if (load > threshold) {
- console.log('System load too high, please try again later');
+ log('System load too high, please try again later');
return res.status(503).send(http.STATUS_CODES[503]);
}
diff --git a/modules/token.js b/modules/token.js
deleted file mode 100644
index 589ee35..0000000
--- a/modules/token.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* eslint-disable no-undef */
-import jwt from 'jsonwebtoken';
-import levelup from 'levelup';
-import leveldown from 'leveldown';
-import { respondWithStatus } from './requestHandler';
-import { pool } from './database';
-
-
-// Set up LevelDB instance
-const db = levelup(leveldown('./tokensDB'));
-
-// Generate a new JWT
-const generateToken = async (userId, password) => {
- const token = jwt.sign({ userId: userId, password: password }, process.env.JWT_SECRET, { expiresIn: '7d' });
- await db.put(token, 'valid');
- return token;
-};
-
-// Middleware to verify the JWT and set req.userId
-const verifyToken = async (req, res, next) => {
- const token = req.headers.authorization;
- if (!token) {
- return await respondWithStatus(res, 401, 'No token provided');
- }
-
- try {
- const decoded = jwt.verify(token, process.env.JWT_SECRET);
- req.userId = decoded.userId;
-
- const [rows] = await pool.execute(
- 'SELECT * FROM users WHERE id = ? LIMIT 1',
- [req.userId],
- );
- if (!rows.length) {
- return await respondWithStatus(res, 404, 'User not found!');
- }
- const passwordMatch = await Bun.password.verify(decoded.password, rows[0].password);
- if (!passwordMatch) {
- return await respondWithStatus(res, 401, 'Token is invalid');
- }
- // Check if the token is close to expiring
- const now = Date.now().valueOf() / 1000;
- if (decoded.exp - now < 36000) {
- // Generate a new token if the old one is close to expiring
- const newToken = generateToken(req.userId, decoded.password);
- res.cookie('token', newToken, {
- expires: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
- httpOnly: true,
- secure: true,
- sameSite: 'strict',
- });
- res.set('Authorization', newToken);
- }
-
- // Check if the token has been revoked
- const tokenStatus = await db.get(token);
- if (tokenStatus != 'valid') {
- return await respondWithStatus(res, 401, 'Token has been revoked ');
- }
- next();
- }
- catch (error) {
- return await respondWithStatus(res, 401, 'Invalid user');
- }
-};
-
-// Function to revoke a token
-const revokeToken = (token) => {
- return new Promise((resolve, reject) => {
- db.put(token, 'revoked', (error) => {
- if (error) {
- reject(error);
- }
- else {
- resolve();
- }
- });
- });
-};
-
-// Function to revoke all tokens of a user
-const revokeUserTokens = (userId) => {
- return new Promise((resolve, reject) => {
- const tokensToRevoke = [];
- db.createReadStream()
- .on('data', (data) => {
- const token = data.key;
- const decoded = jwt.decode(token);
- if (decoded.userId === userId) {
- tokensToRevoke.push(token);
- }
- })
- .on('end', () => {
- Promise.all(tokensToRevoke.map(revokeToken))
- .then(resolve)
- .catch(reject);
- });
- });
-};
-
-export { generateToken, verifyToken, revokeToken, revokeUserTokens };
diff --git a/modules/tokenManager.js b/modules/tokenManager.js
new file mode 100644
index 0000000..aa52b49
--- /dev/null
+++ b/modules/tokenManager.js
@@ -0,0 +1,40 @@
+/* eslint-disable no-undef */
+import jwt from 'jsonwebtoken';
+import { Level } from 'level';
+import { respondWithStatus } from './requestHandler';
+import { userExists } from './permissionManager';
+
+const db = new Level('tokens', { valueEncoding: 'json' });
+
+export async function generateToken(userId, password) {
+ const token = jwt.sign({ userId: userId, password: password }, process.env.JWT_SECRET, { expiresIn: '7d' });
+ await db.put(token, 'valid');
+ return token;
+}
+
+
+export async function verifyToken(req, res, next) {
+ const token = req.headers.authorization;
+ if (!token) return await respondWithStatus(res, 401, 'No token provided');
+
+ try {
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
+ req.userId = decoded.userId;
+
+ if (!userExists(userId)) return await respondWithStatus(res, 404, 'User not found');
+ const passwordMatch = await Bun.password.verify(decoded.password, rows[0].password);
+ if (!passwordMatch) return await respondWithStatus(res, 401, 'Token is invalid');
+ const tokenStatus = await db.get(token);
+ if (tokenStatus != 'valid') {
+ return await respondWithStatus(res, 401, 'Token has been revoked ');
+ }
+ next();
+ }
+ catch (error) {
+ return await respondWithStatus(res, 401, 'Invalid user');
+ }
+}
+
+export async function revokeToken(token) {
+ db.put(token, 'revoked');
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 39a17db..e6af035 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,11 @@
"module": "index.js",
"type": "module",
"devDependencies": {
+ "@types/cookie-parser": "^1.4.6",
+ "@types/cors": "^2.8.17",
+ "@types/express": "^4.17.21",
+ "@types/morgan": "^1.9.9",
+ "@types/nodemailer": "^6.4.14",
"bun-types": "latest",
"eslint": "^8.54.0"
},
@@ -16,12 +21,11 @@
"express-rate-limit": "^7.1.4",
"express-slow-down": "^2.0.1",
"jsonwebtoken": "^9.0.2",
- "level": "^8.0.0",
- "leveldown": "^6.1.1",
- "levelup": "^5.1.1",
+ "level": "^8.0.1",
"morgan": "^1.10.0",
"mysql2": "^3.6.4",
- "nodemailer": "^6.9.7",
+ "node-cron": "^3.0.3",
+ "nodemailer": "^6.9.10",
"path": "^0.12.7",
"pino": "^8.16.2"
}
diff --git a/public/index.html b/public/index.html
index 06a40a2..7c26739 100644
--- a/public/index.html
+++ b/public/index.html
@@ -29,14 +29,13 @@
-
AOSTIA Jet
+
HSP-GDH
@@ -53,10 +52,15 @@
-
Fly with us,
- the sky's the limit!
+ Stayin' Alive!
-
Fly with ease and control your journey with our airline's innovative flight management software. Whether you're booking your flights or making changes to your itinerary, our platform provides you with the flexibility and convenience you need to manage your flights with confidence.
+
Whether you're a brother or whether you're a mother
+ You're stayin' alive, stayin' alive
+ Feel the city breakin' and everybody shakin'
+ And we're stayin' alive, stayin' alive
+ Ah, ha, ha, ha, stayin' alive, stayin' alive
+ Ah, ha, ha, ha, stayin' alive
+ Oh when you walk
@@ -95,24 +99,24 @@
Register
- Username:
+ Username* :
- Email:
+ Email* :
- Password:
+ Password* :
- First name:
+ First name* :
- Last name:
+ Last name* :
Phone:
-
+
Next
diff --git a/public/scripts/app.js b/public/scripts/app.js
index 1dff992..b4e74d9 100644
--- a/public/scripts/app.js
+++ b/public/scripts/app.js
@@ -1,4 +1,5 @@
-const api = 'https://aostia.me/api';
+/* eslint-disable no-undef */
+const api = window.location.origin + '/api';
const loginForm = document.getElementById('loginForm');
const registerForm = document.getElementById('registerForm');
const resetPasswordForm = document.getElementById('resetPasswordForm');
@@ -11,7 +12,7 @@ const register = document.getElementById('register');
const forgot = document.getElementById('forgotPassword');
function events() {
- document.querySelector('.toggleThemeBtn').addEventListener('click', toggleTheme);
+ document.querySelector('.toggleThemeBtn').addEventListener('click', toggleTheme);
document.querySelectorAll('.toggleForgotPasswordBtn').forEach(button => button.addEventListener('click', () => togglePage('forgotPassword')));
document.querySelectorAll('.toggleRegisterBtn').forEach(button => button.addEventListener('click', () => togglePage('register')));
document.querySelectorAll('.toggleLoginBtn').forEach(button => button.addEventListener('click', () => togglePage('login')));
@@ -84,7 +85,7 @@ function dbox(msg) {
window.onload = (event) => {
events();
togglePage(localStorage.getItem('page') || 'home');
- loginForm.addEventListener('submit', async (event) => {
+ loginForm.addEventListener('submit', async () => {
event.preventDefault();
const { status, data } = await post(`${api}/users/login`, { usernameOrEmail: loginForm.elements['usernameOrEmail'].value, password: loginForm.elements['password'].value });
@@ -101,7 +102,7 @@ window.onload = (event) => {
}
});
- registerForm.addEventListener('submit', async (event) => {
+ registerForm.addEventListener('submit', async () => {
event.preventDefault();
const username = registerForm.elements['username'].value;
@@ -114,7 +115,6 @@ window.onload = (event) => {
const { status, data } = await post(`${api}/users/register`, { username: username, email: email, password: password, first_name: first_name, last_name: last_name, phone: phone });
if (status != 200) {
- const data = await response.json();
dbox(`${data.message}`);
console.error(data);
}
@@ -125,7 +125,7 @@ window.onload = (event) => {
}
});
- requestResetPasswordForm.addEventListener('submit', async (event) => {
+ requestResetPasswordForm.addEventListener('submit', async () => {
event.preventDefault();
const { status, data } = await post(`${api}/users/changepassword`, { usernameOrEmail: requestResetPasswordForm.elements['usernameOrEmail'].value });
@@ -140,7 +140,7 @@ window.onload = (event) => {
}
});
- verifyResetPasswordForm.addEventListener('submit', async (event) => {
+ verifyResetPasswordForm.addEventListener('submit', async () => {
event.preventDefault();
const { status, data } = await get(`${api}/users/verify?c=${verifyResetPasswordForm.elements['code'].value}&u=${requestResetPasswordForm.elements['usernameOrEmail'].value}`);
if (status != 200) {
@@ -153,8 +153,8 @@ window.onload = (event) => {
verifyResetPasswordForm.classList.add('hidden');
}
});
-
- resetPasswordForm.addEventListener('submit', async (event) => {
+
+ resetPasswordForm.addEventListener('submit', async () => {
event.preventDefault();
const { status, data } = await patch(`${api}/users/changepassword`, { code: verifyResetPasswordForm.elements['code'].value, usernameOrEmail: requestResetPasswordForm.elements['usernameOrEmail'].value, password: resetPasswordForm.elements['password'].value });
diff --git a/routes/airlines.js b/routes/airlines.js
deleted file mode 100644
index 7ddc566..0000000
--- a/routes/airlines.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_airlines'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM airlines WHERE 1');
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airlines not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_airlines'), async (req, res) => {
- const { name, code, logo } = req.body;
- if ([ name, code, logo ].every(Boolean)) {
- try {
- const [result] = await pool.execute(
- 'INSERT INTO airlines (name, code, logo) VALUES (?, ?, ?)',
- [ name, code, logo ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing airline');
- }
- return await respondWithStatus(res, 200, 'Airline created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:airlineId', verifyToken, checkBanned, hasPermission('view_airlines'), async (req, res) => {
- try {
- const id = req.params.airlineId;
- const [rows] = await pool.execute('SELECT * FROM airlines WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airline not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:airlineId', verifyToken, checkBanned, hasPermission('edit_airlines'), async (req, res) => {
- try {
- const id = req.params.airlineId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM airlines WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airline not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE airlines SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airline');
- }
- return await respondWithStatus(res, 200, 'Airline updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:airlineId', verifyToken, checkBanned, hasPermission('edit_airlines'), async (req, res) => {
- const id = req.params.airlineId;
- const { name, code, logo } = req.body;
- if ([name, code, logo].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM airlines WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airline not found');
- }
- const [result] = await pool.execute(
- 'UPDATE airlines SET name = ?, code = ?, logo = ? WHERE id = ?',
- [name, code, logo, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airline');
- }
- return await respondWithStatus(res, 200, 'Airline updated successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:airlineId', verifyToken, checkBanned, hasPermission('delete_airlines'), async (req, res) => {
- try {
- const id = req.params.airlineId;
- const [rows] = await pool.execute('SELECT * FROM airlines WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airline not found');
- }
-
- const [result] = await pool.execute('DELETE FROM airlines WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing airline');
- }
- return await respondWithStatus(res, 200, 'Airline removed successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
\ No newline at end of file
diff --git a/routes/airplanes.js b/routes/airplanes.js
deleted file mode 100644
index 4415b0e..0000000
--- a/routes/airplanes.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_airplanes'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM airplanes WHERE 1');
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airplanes not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_airplanes'), async (req, res) => {
- const { name, type, manufacturer, capacity, status, location } = req.body;
- if ([name, type, manufacturer, capacity, status, location].every(Boolean)) {
- try {
- const [result] = await pool.execute(
- 'INSERT INTO airplanes (name, type, manufacturer, capacity, status, location) VALUES (?, ?, ?, ?, ?, ?)',
- [ name, type, manufacturer, capacity, status, location ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing airplane');
- }
- return await respondWithStatus(res, 200, 'Airplane created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:airplaneId', verifyToken, checkBanned, hasPermission('view_airplanes'), async (req, res) => {
- try {
- const id = req.params.airplaneId;
- const [rows] = await pool.execute('SELECT * FROM airplanes WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airplane not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:airplaneId', verifyToken, checkBanned, hasPermission('edit_airplanes'), async (req, res) => {
- try {
- const id = req.params.airplaneId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM airplanes WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airplane not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE airplanes SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airplane');
- }
- return await respondWithStatus(res, 200, 'Airplane updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:airplaneId', verifyToken, checkBanned, hasPermission('edit_airplanes'), async (req, res) => {
- const id = req.params.airplaneId;
- const { name, type, manufacturer, capacity, status, location } = req.body;
- if ([name, type, manufacturer, capacity, status, location].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM airplanes WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airplane not found');
- }
- const [result] = await pool.execute(
- 'UPDATE airplanes SET name = ?, type = ?, manufacturer = ?, capacity = ?, status = ?, location = ? WHERE id = ?',
- [name, type, manufacturer, capacity, status, location, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airplane');
- }
- return await respondWithStatus(res, 200, 'Airplane updated successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:airplaneId', verifyToken, checkBanned, hasPermission('delete_airplanes'), async (req, res) => {
- try {
- const id = req.params.airplaneId;
- const [rows] = await pool.execute('SELECT * FROM airplanes WHERE id = ? LIMIT', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airplane not found');
- }
-
- const [result] = await pool.execute('DELETE FROM airplanes WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing airplane');
- }
- return await respondWithStatus(res, 200, 'Airplane deleted successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
diff --git a/routes/airports.js b/routes/airports.js
deleted file mode 100644
index d95593e..0000000
--- a/routes/airports.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_airports'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM airports WHERE 1');
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airports not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_airports'), async (req, res) => {
- const { name, code, city, country, latitude, longitude } = req.body;
- if ([name, code, city, country, latitude, longitude].every(Boolean)) {
- try {
- const [result] = await pool.execute(
- 'INSERT INTO airports (name, code, city, country, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?)',
- [ name, code, city, country, latitude, longitude ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing airport');
- }
- return await respondWithStatus(res, 200, 'Airport created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:airportId', verifyToken, checkBanned, hasPermission('view_airports'), async (req, res) => {
- try {
- const id = req.params.airportId;
- const [rows] = await pool.execute('SELECT * FROM airports WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airports not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:airportId', verifyToken, checkBanned, hasPermission('edit_airports'), async (req, res) => {
- try {
- const id = req.params.airportId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM airports WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airport not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE airports SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airport');
- }
- return await respondWithStatus(res, 200, 'Airport updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:airportId', verifyToken, checkBanned, hasPermission('edit_airports'), async (req, res) => {
- const id = req.params.airportId;
- const { name, code, city, country, latitude, longitude } = req.body;
- if ([name, code, city, country, latitude, longitude].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM airports WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airport not found');
- }
- const [result] = await pool.execute(
- 'UPDATE airports SET name = ?, code = ?, city = ?, country = ?, latitude = ?, longitude = ? WHERE id = ?',
- [name, code, city, country, latitude, longitude, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating airport');
- }
- return await respondWithStatus(res, 200, 'Airport updated successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:airportId', verifyToken, checkBanned, hasPermission('delete_airports'), async (req, res) => {
- try {
- const id = req.params.airportId;
- const [rows] = await pool.execute('SELECT * FROM airports WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Airport not found');
- }
-
- const [result] = await pool.execute('DELETE FROM airports WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing airport');
- }
- return await respondWithStatus(res, 200, 'Airport deleted successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
diff --git a/routes/companies.js b/routes/companies.js
new file mode 100644
index 0000000..02313d6
--- /dev/null
+++ b/routes/companies.js
@@ -0,0 +1,128 @@
+import express from 'express';
+import { pool } from '../modules/databaseManager';
+import { verifyToken } from '../modules/tokenManager';
+import { checkPermissions, checkBanned } from '../modules/permissionManager';
+import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
+
+const router = express.Router();
+
+router.get('/', verifyToken, checkBanned, checkPermissions('companies', 1), async (req, res) => {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM companies WHERE 1');
+
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Companies not found');
+ return await respondWithStatusJSON(res, 200, rows);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.post('/', verifyToken, checkBanned, checkPermissions('companies', 2), async (req, res) => {
+ const { name, code, logo } = req.body;
+ if ([ name, code, logo ].every(Boolean)) {
+ try {
+ const [result] = await pool.execute(
+ 'INSERT INTO companies (name, code, logo) VALUES (?, ?, ?)',
+ [ name, code, logo ],
+ );
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing companie');
+ return await respondWithStatus(res, 200, 'Company created successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.get('/:companyId', verifyToken, checkBanned, checkPermissions('companies', 1), async (req, res) => {
+ try {
+ const id = req.params.companyId;
+ const [rows] = await pool.execute('SELECT * FROM companies WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Company not found');
+ return await respondWithStatusJSON(res, 200, rows[0]);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.patch('/:companyId', verifyToken, checkBanned, checkPermissions('companies', 2), async (req, res) => {
+ try {
+ const id = req.params.companyId;
+ const { type, value } = req.body;
+ const [rows] = await pool.execute('SELECT * FROM companies WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Company not found');
+
+ const excludedKeys = ['id'];
+ const fields = rows.map(row => Object.keys(row).filter(key => !excludedKeys.includes(key)));
+ if (fields[0].includes(type)) {
+ const [result] = await pool.execute(`UPDATE companies SET ${type} = ? WHERE id = ?`, [value, id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating companie');
+ return await respondWithStatus(res, 200, 'Company updated successfully');
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Invalid type');
+ }
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.put('/:companyId', verifyToken, checkBanned, checkPermissions('companies', 2), async (req, res) => {
+ const id = req.params.companyId;
+ const { name, code, logo } = req.body;
+ if ([name, code, logo].every(Boolean)) {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM companies WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Company not found');
+
+ const [result] = await pool.execute(
+ 'UPDATE companies SET name = ?, code = ?, logo = ? WHERE id = ?',
+ [name, code, logo, id],
+ );
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating companie');
+ return await respondWithStatus(res, 200, 'Company updated successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.delete('/:companyId', verifyToken, checkBanned, checkPermissions('companies', 4), async (req, res) => {
+ try {
+ const id = req.params.companyId;
+ const [rows] = await pool.execute('SELECT * FROM companies WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Company not found');
+
+ const [result] = await pool.execute('DELETE FROM companies WHERE id = ?', [id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing companie');
+ return await respondWithStatus(res, 200, 'Company removed successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+// Hospital endpoints
+// GET /:companyId/hospitals
+
+
+export default router;
\ No newline at end of file
diff --git a/routes/doctors.js b/routes/doctors.js
new file mode 100644
index 0000000..38598b4
--- /dev/null
+++ b/routes/doctors.js
@@ -0,0 +1,151 @@
+import express from 'express';
+import { pool } from '../modules/databaseManager';
+import { verifyToken } from '../modules/tokenManager';
+import { verifyPermissions, checkPermissions, checkBanned } from '../modules/permissionManager';
+import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
+
+const router = express.Router();
+
+/**
+ * Retrieves all doctors from the database.
+ *
+ * @returns {Promise
} A promise that resolves to an array of doctor objects.
+ */
+router.get('/', verifyToken, checkBanned, checkPermissions('doctors', 1), async (req, res) => {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM doctors WHERE 1');
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Doctors not found');
+ return await respondWithStatusJSON(res, 200, rows);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+/**
+ * Inserts a new doctor record into the database.
+ *
+ * @param {number} user_id - The ID of the user associated with the doctor.
+ * @param {date} date_of_birth - The date of birth of the doctor.
+ * @param {string} gender - The gender of the doctor.
+ * @param {string} address - The address of the doctor.
+ * @param {string} social_security_number - The social security number of the doctor.
+ * @param {string} insurance_number - The insurance number of the doctor.
+ * @returns {Promise} - A promise that resolves with the result of the insertion.
+ */
+router.post('/', verifyToken, checkBanned, checkPermissions('doctors', 2), async (req, res) => {
+ const { user_id, date_of_birth, gender, address, social_security_number, insurance_number } = req.body;
+ if ([ user_id, date_of_birth, gender, address, social_security_number, insurance_number ].every(Boolean)) {
+ try {
+ const [result] = await pool.execute(
+ 'INSERT INTO doctors (user_id, date_of_birth, gender, address, social_security_number, insurance_number) VALUES (?, ?, ?, ?, ?, ?)',
+ [ user_id, date_of_birth, gender, address, social_security_number, insurance_number ],
+ );
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing doctor');
+ return await respondWithStatus(res, 200, 'Doctor created successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.get('/:doctorId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.doctorId;
+ const [rows] = await pool.execute('SELECT * FROM doctors WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Doctor not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'doctors', 1)) return await respondWithStatus(res, 403, 'Missing permission');
+ return await respondWithStatusJSON(res, 200, rows[0]);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.patch('/:doctorId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.doctorId;
+ const { type, value } = req.body;
+ const [rows] = await pool.execute('SELECT * FROM doctors WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Doctor not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'doctors', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const excludedKeys = [ 'id', 'user_id' ];
+ const fields = rows.map(row => Object.keys(row).filter(key => !excludedKeys.includes(key)));
+ if (fields[0].includes(type)) {
+ const [result] = await pool.execute(`UPDATE doctors SET ${type} = ? WHERE id = ?`, [value, id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating doctor');
+ return await respondWithStatus(res, 200, 'Doctor updated successfully');
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Invalid type');
+ }
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.put('/:doctorId', verifyToken, checkBanned, async (req, res) => {
+ const id = req.params.doctorId;
+ const { user_id, date_of_birth, gender, address, social_security_number, insurance_number } = req.body;
+ if ([ user_id, date_of_birth, gender, address, social_security_number, insurance_number ].every(Boolean)) {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM doctors WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Doctor not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'doctors', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const [result] = await pool.execute(
+ 'UPDATE doctors SET name = ?, type = ?, manufacturer = ?, capacity = ?, status = ?, location = ? WHERE id = ?',
+ [user_id, date_of_birth, gender, address, social_security_number, insurance_number, id],
+ );
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating doctor');
+ return await respondWithStatus(res, 200, 'Doctor updated successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.delete('/:doctorId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.doctorId;
+ const [rows] = await pool.execute('SELECT * FROM doctors WHERE id = ? LIMIT', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Doctor not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'doctors', 4)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const [result] = await pool.execute('DELETE FROM doctors WHERE id = ?', [id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing doctor');
+ return await respondWithStatus(res, 200, 'Doctor deleted successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+// Appointments endpoints
+// GET /:doctorId/appointments
+// POST /:doctorId/appointments
+// GET /:doctorId/appointments/:appointmentId
+// PATCH /:doctorId/appointments/:appointmentId
+// PUT /:doctorId/appointments/:appointmentId
+// DELETE /:doctorId/appointments/:appointmentId
+
+export default router;
diff --git a/routes/flights.js b/routes/flights.js
deleted file mode 100644
index 3306cd2..0000000
--- a/routes/flights.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_flights'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM flights');
-
- if (!rows.length) {
- return await respondWithStatus(res, 404, 'Flights not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_flights'), async (req, res) => {
- const { airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status } = req.body;
- if ([airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status].every(Boolean)) {
- try {
- const [result] = await pool.execute(
- 'INSERT INTO flights (airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [ airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing flight');
- }
- return await respondWithStatus(res, 200, 'Flight created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:flightId', verifyToken, checkBanned, hasPermission('view_flights'), async (req, res) => {
- try {
- const id = req.params.flightId;
- const [rows] = await pool.execute('SELECT * FROM flights WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Flight not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:flightId', verifyToken, checkBanned, hasPermission('edit_flights'), async (req, res) => {
- try {
- const id = req.params.flightId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM flights WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Flight not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE flights SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating flight');
- }
- return await respondWithStatus(res, 200, 'Flight updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:flightId', verifyToken, checkBanned, hasPermission('edit_flights'), async (req, res) => {
- const id = req.params.flightId;
- const { airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status } = req.body;
- if ([airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM flights WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Flight not found');
- }
- const [result] = await pool.execute(
- 'UPDATE flights SET airline_id = ?, pilot_id = ?, flight_no = ?, origin_id = ?, destination_id = ?, departure_time = ?, arrival_time = ?, duration_minutes= ?, price_economy = ?, price_business = ?, price_first_class = ?, status = ? WHERE id = ?',
- [airline_id, pilot_id, flight_no, origin_id, destination_id, departure_time, arrival_time, duration_minutes, price_economy, price_business, price_first_class, status, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating flight');
- }
- return await respondWithStatus(res, 200, 'Flight updated successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:flightId', verifyToken, checkBanned, hasPermission('delete_flights'), async (req, res) => {
- try {
- const id = req.params.flightId;
- const [rows] = await pool.execute('SELECT * FROM flights WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Flight not found');
- }
-
- const [result] = await pool.execute('DELETE FROM flights WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing flight');
- }
- return await respondWithStatus(res, 200, 'Flight deleted successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
diff --git a/routes/hospitals.js b/routes/hospitals.js
new file mode 100644
index 0000000..841ba1c
--- /dev/null
+++ b/routes/hospitals.js
@@ -0,0 +1,156 @@
+import express from 'express';
+import { pool } from '../modules/databaseManager';
+import { verifyToken } from '../modules/tokenManager';
+import { checkPermissions, checkBanned } from '../modules/permissionManager';
+import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
+
+const router = express.Router();
+
+router.get('/', verifyToken, checkBanned, checkPermissions('hospitals', 1), async (req, res) => {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM hospitals WHERE 1');
+
+ if (rows.length === 0) {
+ return await respondWithStatus(res, 404, 'Hospitals not found');
+ }
+ return await respondWithStatusJSON(res, 200, rows);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.post('/', verifyToken, checkBanned, checkPermissions('hospitals', 2), async (req, res) => {
+ const { company_id, name, code, country, region, city, address } = req.body;
+ if ([ company_id, name, code, country, region, city, address ].every(Boolean)) {
+ try {
+ const [result] = await pool.execute(
+ 'INSERT INTO hospitals (company_id, name, code, country, region, city, address) VALUES (?, ?, ?, ?, ?, ?, ?)',
+ [ company_id, name, code, country, region, city, address ],
+ );
+ if (result.affectedRows === 0) {
+ return await respondWithStatus(res, 500, 'Error storing hospital');
+ }
+ return await respondWithStatus(res, 200, 'Hospital created successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.get('/:hospitalId', verifyToken, checkBanned, checkPermissions('hospitals', 1), async (req, res) => {
+ try {
+ const id = req.params.hospitalId;
+ const [rows] = await pool.execute('SELECT * FROM hospitals WHERE id = ? LIMIT 1', [id]);
+
+ if (rows.length === 0) {
+ return await respondWithStatus(res, 404, 'Hospitals not found');
+ }
+ return await respondWithStatusJSON(res, 200, rows[0]);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.patch('/:hospitalId', verifyToken, checkBanned, checkPermissions('hospitals', 2), async (req, res) => {
+ try {
+ const id = req.params.hospitalId;
+ const { type, value } = req.body;
+ const [rows] = await pool.execute('SELECT * FROM hospitals WHERE id = ? LIMIT 1', [id]);
+
+ if (rows.length === 0) {
+ return await respondWithStatus(res, 404, 'Hospital not found');
+ }
+ const fields = rows.map(row => Object.keys(row));
+ if (fields[0].includes(type)) {
+ const [result] = await pool.execute(`UPDATE hospitals SET ${type} = ? WHERE id = ?`, [value, id]);
+
+ if (result.affectedRows === 0) {
+ return await respondWithStatus(res, 500, 'Error updating hospital');
+ }
+ return await respondWithStatus(res, 200, 'Hospital updated successfully');
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Invalid type');
+ }
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.put('/:hospitalId', verifyToken, checkBanned, checkPermissions('hospitals', 2), async (req, res) => {
+ const id = req.params.hospitalId;
+ const { company_id, name, code, country, region, city, address } = req.body;
+ if ([company_id, name, code, country, region, city, address].every(Boolean)) {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM hospitals WHERE id = ? LIMIT 1', [id]);
+
+ if (rows.length === 0) {
+ return await respondWithStatus(res, 404, 'Hospital not found');
+ }
+ const [result] = await pool.execute(
+ 'UPDATE hospitals SET company_id = ?, name = ?, country = ?, region = ?, city = ?, address = ? WHERE id = ?',
+ [company_id, name, code, country, region, city, address, id],
+ );
+
+ if (result.affectedRows === 0) {
+ return await respondWithStatus(res, 500, 'Error updating hospital');
+ }
+ return await respondWithStatus(res, 200, 'Hospital updated successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.delete('/:hospitalId', verifyToken, checkBanned, checkPermissions('hospitals', 4), async (req, res) => {
+ try {
+ const id = req.params.hospitalId;
+ const [rows] = await pool.execute('SELECT * FROM hospitals WHERE id = ? LIMIT 1', [id]);
+
+ if (rows.length === 0) {
+ return await respondWithStatus(res, 404, 'Hospital not found');
+ }
+
+ const [result] = await pool.execute('DELETE FROM hospitals WHERE id = ?', [id]);
+
+ if (result.affectedRows === 0) {
+ return await respondWithStatus(res, 500, 'Error removing hospital');
+ }
+ return await respondWithStatus(res, 200, 'Hospital deleted successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+// Doctor endpoints
+// GET all doctors in a hospital
+// POST a new doctor to a hospital
+// DELETE a doctor from a hospital
+
+// Service endpoints
+// GET all services in a hospital
+
+// Room endpoints
+// GET all rooms in a hospital
+// POST a new room to a hospital
+// DELETE a room from a hospital
+
+export default router;
diff --git a/routes/patients.js b/routes/patients.js
new file mode 100644
index 0000000..574deb7
--- /dev/null
+++ b/routes/patients.js
@@ -0,0 +1,151 @@
+import express from 'express';
+import { pool } from '../modules/databaseManager';
+import { verifyToken } from '../modules/tokenManager';
+import { verifyPermissions, checkPermissions, checkBanned } from '../modules/permissionManager';
+import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
+
+const router = express.Router();
+
+/**
+ * Retrieves all patients from the database.
+ *
+ * @returns {Promise} A promise that resolves to an array of patient objects.
+ */
+router.get('/', verifyToken, checkBanned, checkPermissions('patients', 1), async (req, res) => {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM patients WHERE 1');
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Patients not found');
+ return await respondWithStatusJSON(res, 200, rows);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+/**
+ * Inserts a new patient record into the database.
+ *
+ * @param {number} user_id - The ID of the user associated with the patient.
+ * @param {date} date_of_birth - The date of birth of the patient.
+ * @param {string} gender - The gender of the patient.
+ * @param {string} address - The address of the patient.
+ * @param {string} social_security_number - The social security number of the patient.
+ * @param {string} insurance_number - The insurance number of the patient.
+ * @returns {Promise} - A promise that resolves with the result of the insertion.
+ */
+router.post('/', verifyToken, checkBanned, checkPermissions('patients', 2), async (req, res) => {
+ const { user_id, date_of_birth, gender, address, social_security_number, insurance_number } = req.body;
+ if ([ user_id, date_of_birth, gender, address, social_security_number, insurance_number ].every(Boolean)) {
+ try {
+ const [result] = await pool.execute(
+ 'INSERT INTO patients (user_id, date_of_birth, gender, address, social_security_number, insurance_number) VALUES (?, ?, ?, ?, ?, ?)',
+ [ user_id, date_of_birth, gender, address, social_security_number, insurance_number ],
+ );
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing patient');
+ return await respondWithStatus(res, 200, 'Patient created successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.get('/:patientId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.patientId;
+ const [rows] = await pool.execute('SELECT * FROM patients WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Patient not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'patients', 1)) return await respondWithStatus(res, 403, 'Missing permission');
+ return await respondWithStatusJSON(res, 200, rows[0]);
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.patch('/:patientId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.patientId;
+ const { type, value } = req.body;
+ const [rows] = await pool.execute('SELECT * FROM patients WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Patient not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'patients', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const excludedKeys = [ 'id', 'user_id' ];
+ const fields = rows.map(row => Object.keys(row).filter(key => !excludedKeys.includes(key)));
+ if (fields[0].includes(type)) {
+ const [result] = await pool.execute(`UPDATE patients SET ${type} = ? WHERE id = ?`, [value, id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating patient');
+ return await respondWithStatus(res, 200, 'Patient updated successfully');
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Invalid type');
+ }
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+router.put('/:patientId', verifyToken, checkBanned, async (req, res) => {
+ const id = req.params.patientId;
+ const { user_id, date_of_birth, gender, address, social_security_number, insurance_number } = req.body;
+ if ([ user_id, date_of_birth, gender, address, social_security_number, insurance_number ].every(Boolean)) {
+ try {
+ const [rows] = await pool.execute('SELECT * FROM patients WHERE id = ? LIMIT 1', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Patient not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'patients', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const [result] = await pool.execute(
+ 'UPDATE patients SET name = ?, type = ?, manufacturer = ?, capacity = ?, status = ?, location = ? WHERE id = ?',
+ [user_id, date_of_birth, gender, address, social_security_number, insurance_number, id],
+ );
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating patient');
+ return await respondWithStatus(res, 200, 'Patient updated successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+ }
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
+ }
+});
+
+router.delete('/:patientId', verifyToken, checkBanned, async (req, res) => {
+ try {
+ const id = req.params.patientId;
+ const [rows] = await pool.execute('SELECT * FROM patients WHERE id = ? LIMIT', [id]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Patient not found');
+ if (rows[0].userId != req.userId && !verifyPermissions(req.userId, 'patients', 4)) return await respondWithStatus(res, 403, 'Missing permission');
+
+ const [result] = await pool.execute('DELETE FROM patients WHERE id = ?', [id]);
+
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing patient');
+ return await respondWithStatus(res, 200, 'Patient deleted successfully');
+ }
+ catch (err) {
+ console.error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
+});
+
+// Appointments endpoints
+// GET /:patientId/appointments
+// POST /:patientId/appointments
+// GET /:patientId/appointments/:appointmentId
+// PATCH /:patientId/appointments/:appointmentId
+// PUT /:patientId/appointments/:appointmentId
+// DELETE /:patientId/appointments/:appointmentId
+
+export default router;
diff --git a/routes/pilots.js b/routes/pilots.js
deleted file mode 100644
index 54c20c9..0000000
--- a/routes/pilots.js
+++ /dev/null
@@ -1,144 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_pilots'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM pilots WHERE 1');
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Pilots not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_pilots'), async (req, res) => {
- const { first_name, last_name, email, phone, license_number, license_expiry, salary, status } = req.body;
- if ([first_name, last_name, email, phone, license_number, license_expiry, salary, status].every(Boolean)) {
- try {
- const [result] = await pool.execute(
- 'INSERT INTO pilots (first_name, last_name, email, phone, license_number, license_expiry, salary, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
- [ first_name, last_name, email, phone, license_number, license_expiry, salary, status ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing pilot');
- }
- return await respondWithStatus(res, 200, 'Pilot created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:pilotId', verifyToken, checkBanned, hasPermission('view_pilots'), async (req, res) => {
- try {
- const id = req.params.pilotId;
- const [rows] = await pool.execute('SELECT * FROM pilots WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Pilot not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:pilotId', verifyToken, checkBanned, hasPermission('edit_pilots'), async (req, res) => {
- try {
- const id = req.params.pilotId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM pilots WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Pilot not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE pilots SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating pilot');
- }
- return await respondWithStatus(res, 200, 'Pilot updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:pilotId', verifyToken, checkBanned, hasPermission('edit_pilots'), async (req, res) => {
- const id = req.params.pilotId;
- const { first_name, last_name, email, phone, license_number, license_expiry, salary, status } = req.body;
- if ([first_name, last_name, email, phone, license_number, license_expiry, salary, status].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM pilots WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Pilot not found');
- }
- const [result] = await pool.execute(
- 'UPDATE pilots SET first_name = ?, last_name = ?, email = ?, phone = ?, license_number = ?, license_expiry = ?, salary = ?, status = ? WHERE id = ?',
- [first_name, last_name, email, phone, license_number, license_expiry, salary, status, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating pilot');
- }
- return await respondWithStatus(res, 200, 'Pilot updated successfully');
- }
-
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:pilotId', verifyToken, checkBanned, hasPermission('delete_pilots'), async (req, res) => {
- try {
- const id = req.params.pilotId;
- const [rows] = await pool.execute('SELECT * FROM pilots WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Pilot not found');
- }
-
- const [result] = await pool.execute('DELETE FROM pilots WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing pilot');
- }
- return await respondWithStatus(res, 200, 'Pilot removed successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
diff --git a/routes/seats.js b/routes/seats.js
deleted file mode 100644
index 46af92c..0000000
--- a/routes/seats.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import express from 'express';
-import { pool } from '../modules/database';
-import { verifyToken } from '../modules/token';
-import { hasPermission, checkBanned } from '../modules/permission';
-import { respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
-
-const router = express.Router();
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_seats'), async (req, res) => {
- try {
- const [rows] = await pool.execute('SELECT * FROM seats WHERE 1');
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Seats not found');
- }
- return await respondWithStatusJSON(res, 200, rows);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.post('/', verifyToken, checkBanned, hasPermission('add_seats'), async (req, res) => {
- const { user_id, flight_id, place_no, seat_class } = req.body;
- if ([ user_id, flight_id, place_no, seat_class ].every(Boolean)) {
- try {
- const [user] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [user_id]);
-
- if (user.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
- }
-
- const [flight] = await pool.execute('SELECT * FROM flights WHERE id = ? LIMIT 1', [flight_id]);
-
- if (flight.length === 0) {
- return await respondWithStatus(res, 404, 'Flight not found');
- }
-
- const [result] = await pool.execute(
- 'INSERT INTO seats (user_id, flight_id, place_no, class) VALUES (?, ?, ?, ?)',
- [ user_id, flight_id, place_no, seat_class ],
- );
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing seat');
- }
- return await respondWithStatus(res, 200, 'Seat created successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.get('/:seatId', verifyToken, checkBanned, hasPermission('view_seats'), async (req, res) => {
- try {
- const id = req.params.seatId;
- const [rows] = await pool.execute('SELECT * FROM seats WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Seat not found');
- }
- return await respondWithStatusJSON(res, 200, rows[0]);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.patch('/:seatId', verifyToken, checkBanned, hasPermission('edit_seats'), async (req, res) => {
- try {
- const id = req.params.seatId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM seats WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Seat not found');
- }
- const fields = rows.map(row => Object.keys(row));
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE seats SET ${type} = ? WHERE id = ?`, [value, id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating seat');
- }
- return await respondWithStatus(res, 200, 'Seat updated successfully');
- }
- else {
- return await respondWithStatus(res, 400, 'Invalid type');
- }
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/:seatId', verifyToken, checkBanned, hasPermission('edit_seats'), async (req, res) => {
- const id = req.params.seatId;
- const { user_id, flight_id, place_no, seat_class } = req.body;
- if ([ user_id, flight_id, place_no, seat_class ].every(Boolean)) {
- try {
- const [rows] = await pool.execute('SELECT * FROM seats WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Seat not found');
- }
- const [result] = await pool.execute(
- 'UPDATE seats SET user_id = ?, flight_id = ?, place_no = ?, class = ? WHERE id = ?',
- [user_id, flight_id, place_no, seat_class, id],
- );
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating seat');
- }
- return await respondWithStatus(res, 200, 'Seat updated successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.delete('/:seatId', verifyToken, checkBanned, hasPermission('delete_seats'), async (req, res) => {
- try {
- const id = req.params.seatId;
- const [rows] = await pool.execute('SELECT * FROM seats WHERE id = ? LIMIT 1', [id]);
-
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Seat not found');
- }
-
- const [result] = await pool.execute('DELETE FROM seats WHERE id = ?', [id]);
-
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing seat');
- }
- return await respondWithStatus(res, 200, 'Seat removed successfully');
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-export default router;
diff --git a/routes/users.js b/routes/users.js
index 41ee0cb..e94731c 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -1,49 +1,46 @@
import express from 'express';
-import { pool } from '../modules/database';
-import { sendVerification, sendResetVerification } from '../modules/mailHandler';
-import { isEmailDomainValid, isValidEmail, isNumber } from '../modules/formatHandler';
-import { hasPermission, checkBanned, isBanned } from '../modules/permission';
-import { verifyToken, generateToken, revokeUserTokens } from '../modules/token';
+import { error } from '../modules/logManager';
+import { pool } from '../modules/databaseManager';
+import { sendVerification } from '../modules/mailHandler';
+import { isEmailDomainValid, isValidEmail, isPhoneNumber } from '../modules/formatManager';
+import { checkBanned, checkPermissions, userExists, isBanned, verifyPermissions } from '../modules/permissionManager';
+import { verifyToken, generateToken } from '../modules/tokenManager';
import { requestLimiter, respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler';
const router = express.Router();
router.post('/register', requestLimiter, async (req, res) => {
- const { username, email, password, first_name, last_name, phone = 'none' } = req.body;
+ const { username, email, password, first_name, last_name, phone = null } = req.body;
if ([ username, email, password, first_name, last_name ].every(Boolean)) {
try {
if (isValidEmail(email) && isEmailDomainValid(email)) {
const [existingUsername] = await pool.execute('SELECT * FROM users WHERE username = ? LIMIT 1', [username]);
- if (existingUsername.length) {
- return await respondWithStatus(res, 400, 'Username is already taken');
- }
+ if (existingUsername.length) return await respondWithStatus(res, 400, 'Username is already taken');
const [existingEmail] = await pool.execute('SELECT * FROM users WHERE email = ? LIMIT 1', [email]);
- if (existingEmail.length) {
- return await respondWithStatus(res, 400, 'Email is already taken');
- }
+ if (existingEmail.length) return await respondWithStatus(res, 400, 'Email is already taken');
+
+ if (!isPhoneNumber(phone)) return await respondWithStatus(res, 400, 'Invalid phone number');
+
const hashedPassword = await Bun.password.hash(password);
- const [unverifiedId] = await pool.execute(
- 'SELECT id FROM user_types WHERE name = \'unverified\' LIMIT 1',
- );
+
const [result] = await pool.execute(
- 'INSERT INTO users (first_name, last_name, username, email, password, user_type_id, phone) VALUES (?, ?, ?, ?, ?, ?, ?)',
- [ first_name, last_name, username, email, hashedPassword, unverifiedId[0].id, phone ],
+ 'INSERT INTO users (first_name, last_name, username, email, password, phone) VALUES (?, ?, ?, ?, ?, ?)',
+ [ first_name, last_name, username, email, hashedPassword, phone ? phone : 'None'],
);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error storing user');
- }
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error storing user');
+
const [rows] = await pool.execute('SELECT id FROM users WHERE email = ? LIMIT 1', [email]);
- const code = sendVerification(email, rows[0].id);
- pool.execute('INSERT INTO user_email_verifications (user_id, verification_code, type) VALUES (?, ?, ?)', [ rows[0].id, code, 'register' ]);
+ const code = sendVerification(email, rows[0].id, 'email');
+ pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ rows[0].id, code, 'email' ]);
return await respondWithStatus(res, 200, 'Successfully registered');
}
else {
return await respondWithStatus(res, 400, 'Invalid email address');
}
}
- catch (error) {
- console.error(error);
+ catch (err) {
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
}
@@ -60,39 +57,28 @@ router.post('/login', requestLimiter, async (req, res) => {
'SELECT * FROM users WHERE username = ? OR email = ? LIMIT 1',
[usernameOrEmail, usernameOrEmail],
);
- if (!rows.length) {
- return await respondWithStatus(res, 404, 'Incorrect username or email');
- }
+ if (!rows.length) return await respondWithStatus(res, 404, 'Incorrect username or email');
+
const user = rows[0];
const passwordMatch = await Bun.password.verify(password, user.password);
- if (!passwordMatch) {
- return await respondWithStatus(res, 401, 'Incorrect password');
- }
- if (isBanned(user.id)) {
- const token = await generateToken(user.id, password);
- res.cookie('token', token, {
- expires: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
- httpOnly: true,
- secure: true,
- sameSite: 'strict',
- });
- return await respondWithStatusJSON(res, 200, {
- message: 'Login successful',
- token: token,
- user: {
- id: user.id,
- username: user.username,
- email: user.email,
- name: user.name,
- },
- });
- }
- else {
- return await respondWithStatus(res, 403, 'User is banned or an issue occured');
- }
+ if (!passwordMatch) return await respondWithStatus(res, 401, 'Incorrect password');
+
+ if (isBanned(user.id)) return await respondWithStatus(res, 403, 'User is banned or an issue occured');
+ const token = await generateToken(user.id, password);
+ return await respondWithStatusJSON(res, 200, {
+ message: 'Login successful',
+ token: token,
+ user: {
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ first_name: user.first_name,
+ last_name: user.last_name,
+ },
+ });
}
- catch (error) {
- console.error(error);
+ catch (err) {
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
}
@@ -101,177 +87,27 @@ router.post('/login', requestLimiter, async (req, res) => {
}
});
-router.get('/verify', async (req, res) => {
- const { c, u } = req.query;
- if ([c, u].every(Boolean)) {
- try {
- let userId = u;
- if (!isNumber(u)) {
- const [user] = await pool.execute(
- 'SELECT id FROM users WHERE username = ? OR email = ? LIMIT 1',
- [u, u],
- );
- if (!user.length) {
- return await respondWithStatus(res, 404, 'Incorrect username or email');
- }
- userId = user[0].id;
- }
- const [rows] = await pool.execute(
- 'SELECT * FROM user_email_verifications WHERE user_id = ? AND verification_code = ? LIMIT 1',
- [userId, c],
- );
- if (!rows.length) {
- return await respondWithStatus(res, 400, 'Invalid code');
- }
-
- if (rows[0].type == 'register') {
- const [customerId] = await pool.execute(
- 'SELECT id FROM user_types WHERE name = \'customer\' LIMIT 1',
- );
- const [result] = await pool.execute('UPDATE users SET user_type_id = ? WHERE id = ?', [ customerId[0].id, userId ]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating user');
- }
- }
- return await respondWithStatus(res, 200, 'Successfully verified user');
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.post('/verify', verifyToken, checkBanned, async (req, res) => {
- const { code } = req.body;
- const userId = req.userId;
- if ([code, userId].every(Boolean)) {
- try {
- const [rows] = await pool.execute(
- 'SELECT * FROM user_email_verifications WHERE user_id = ? AND verification_code = ? LIMIT 1',
- [ userId, code ],
- );
- if (!rows.length) {
- return await respondWithStatus(res, 400, 'Invalid code');
- }
- const [customerId] = await pool.execute(
- 'SELECT id FROM user_types WHERE name = \'customer\' LIMIT 1',
- );
- const [result] = await pool.execute('UPDATE users SET user_type_id = ? WHERE userId = ?', [ customerId[0].id, userId ]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating user');
- }
- return await respondWithStatus(res, 200, 'Successfully verified user');
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- else {
- return await respondWithStatus(res, 400, 'Missing fields');
- }
-});
-
-router.post('/changepassword', async (req, res) => {
- const { usernameOrEmail } = req.body;
- if ([ usernameOrEmail ].every(Boolean)) {
- try {
- const [user] = await pool.execute('SELECT * FROM users WHERE email = ? OR username = ? LIMIT 1', [usernameOrEmail, usernameOrEmail]);
- if (user.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
- }
-
- let code;
- const [rows] = await pool.execute(
- 'SELECT * FROM user_email_verifications WHERE user_id = ? AND type = \'password\' LIMIT 1',
- [user[0].id],
- );
- if (!rows.length) {
- code = sendResetVerification(user[0].email);
- }
- else {
- code = sendResetVerification(user[0].email, rows[0].verification_code);
- }
-
- if (code) {
- pool.execute('INSERT INTO user_email_verifications (user_id, verification_code, type) VALUES (?, ?, ?)', [ user[0].id, code, 'password' ]);
- return await respondWithStatus(res, 200, 'Successfully sent password reset email');
- }
- else {
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
- }
-});
-
-router.patch('/changepassword', async (req, res) => {
- const { usernameOrEmail, password, code } = req.body;
- try {
- const [user] = await pool.execute(
- 'SELECT id FROM users WHERE username = ? OR email = ? LIMIT 1',
- [usernameOrEmail, usernameOrEmail],
- );
- if (!user.length) {
- return await respondWithStatus(res, 404, 'Incorrect username or email');
- }
- const [rows] = await pool.execute(
- 'SELECT * FROM user_email_verifications WHERE user_id = ? AND verification_code = ? AND type = \'password\' ORDER BY 1 DESC LIMIT 1',
- [user[0].id, code],
- );
- if (!rows.length) {
- return await respondWithStatus(res, 400, 'Invalid code');
- }
- const [result] = await pool.execute('DELETE FROM user_email_verifications WHERE user_id = ? AND verification_code = ?', [ user[0].id, code ]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing verification');
- }
- revokeUserTokens(user[0].id);
- const token = generateToken(user[0].id, password);
- res.cookie('token', token, {
- expires: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
- httpOnly: true,
- secure: true,
- sameSite: 'strict',
- });
- return userPATCH(res, user[0].id, 'password', password);
- }
- catch (error) {
- console.error(error);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.get('/', verifyToken, checkBanned, hasPermission('view_users'), async (req, res) => {
+router.get('/', verifyToken, checkBanned, checkPermissions('user', 1), async (req, res) => {
try {
const [rows] = await pool.execute('SELECT * FROM users WHERE 1');
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'Users not found');
- }
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'Users not found');
return await respondWithStatusJSON(res, 200, rows);
}
catch (err) {
- console.error(err);
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
});
-router.post('/', verifyToken, checkBanned, hasPermission('add_users'), async (req, res) => {
- const { first_name, last_name, username, email, password, user_type } = req.body;
- if ([first_name, last_name, username, email, password, user_type].every(Boolean)) {
+router.post('/', verifyToken, checkBanned, checkPermissions('user', 2), async (req, res) => {
+ const { first_name, last_name, username, email, password, phone = 'None' } = req.body;
+ if ([ first_name, last_name, username, email, password ].every(Boolean)) {
try {
const hashedPassword = await Bun.password.hash(password);
await pool.execute(
- 'INSERT INTO users (first_name, last_name, username, email, password, user_type) VALUES (?, ?, ?, ?, ?, ?)',
- [ first_name, last_name, username, email, hashedPassword, user_type ],
+ 'INSERT INTO users (first_name, last_name, username, email, password, phone) VALUES (?, ?, ?, ?, ?, ?)',
+ [ first_name, last_name, username, email, hashedPassword, phone ],
);
return await respondWithStatus(res, 200, 'User created successfully');
}
@@ -285,48 +121,10 @@ router.post('/', verifyToken, checkBanned, hasPermission('add_users'), async (re
}
});
-router.patch('/', verifyToken, checkBanned, async (req, res) => {
+router.get('/:userId', verifyToken, checkBanned, async (req, res) => {
try {
- const userId = req.userId;
- const { type, value } = req.body;
- userPATCH(res, userId, type, value);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.put('/', verifyToken, checkBanned, async (req, res) => {
- try {
- const userId = req.userId;
- const { first_name, last_name, username, email } = req.body;
- userPUT(res, userId, first_name, last_name, username, email);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.delete('/', verifyToken, checkBanned, async (req, res) => {
- try {
- const userId = req.userId;
- userDELETE(res, userId);
- }
- catch (err) {
- console.error(err);
- return await respondWithStatus(res, 500, 'An error has occured');
- }
-});
-
-router.get('/:userId', verifyToken, checkBanned, hasPermission('view_users'), async (req, res) => {
- try {
- let userId = req.params.userId;
- if (req.params.userId == 'me') {
- userId = req.userId;
- }
- const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
+ if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 1)) return await respondWithStatus(res, 403, 'Missing permission');
+ const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [req.params.userId]);
if (rows.length === 0) {
return await respondWithStatus(res, 404, 'User not found');
@@ -336,30 +134,24 @@ router.get('/:userId', verifyToken, checkBanned, hasPermission('view_users'), as
return await respondWithStatusJSON(res, 200, user);
}
catch (err) {
- console.error(err);
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
});
-router.patch('/:userId', verifyToken, checkBanned, hasPermission('edit_users'), async (req, res) => {
+router.patch('/:userId', verifyToken, checkBanned, async (req, res) => {
try {
- const userId = req.params.userId;
- const { type, value } = req.body;
- const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
- }
+ if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+ const { type } = req.body;
+ let { value } = req.body;
+ const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [req.params.userId]);
+ if (rows.length === 0) return await respondWithStatus(res, 404, 'User not found');
const excludedKeys = ['id'];
- const fields = rows.map(row =>
- Object.keys(row)
- .filter(key => !excludedKeys.includes(key)),
- );
- console.log(fields[0]);
+ const fields = rows.map(row => Object.keys(row).filter(key => !excludedKeys.includes(key)));
if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE users SET ${type} = ? WHERE id = ?`, [value, userId]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating user');
- }
+ if (type === 'password') value = await Bun.password.hash(value);
+ const [result] = await pool.execute(`UPDATE users SET ${type} = ? WHERE id = ?`, [value, req.params.userId]);
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user');
return respondWithStatus(res, 200, 'User updated successfully');
}
else {
@@ -367,93 +159,170 @@ router.patch('/:userId', verifyToken, checkBanned, hasPermission('edit_users'),
}
}
catch (err) {
- console.error(err);
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
});
-router.put('/:userId', verifyToken, checkBanned, hasPermission('edit_users'), async (req, res) => {
+router.put('/:userId', verifyToken, checkBanned, async (req, res) => {
try {
- const userId = req.params.userId;
- const { first_name, last_name, username, email } = req.body;
+ if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 2)) return await respondWithStatus(res, 403, 'Missing permission');
+ const { first_name, last_name, username, password = null, email, phone = null } = req.body;
if ([first_name, last_name, username, email].every(Boolean)) {
- userPUT(res, userId, first_name, last_name, username, email);
+ let sqlQuery = 'UPDATE users SET first_name = ?, last_name = ?, username = ?, email = ?';
+ const queryParams = [first_name, last_name, username, email];
+ if (password) {
+ const hashedPassword = await Bun.password.hash(password);
+ sqlQuery = +' password = ?';
+ queryParams.append(hashedPassword);
+ }
+ else if (phone && isPhoneNumber(phone)) {
+ sqlQuery = ' phone = ?';
+ queryParams.append(phone);
+ }
+ const [result] = await pool.execute(sqlQuery + ' WHERE id = ?', queryParams.append(req.params.userId));
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user');
+ return respondWithStatus(res, 200, 'User updated successfully');
}
+ if (!userExists(req.params.userId)) return await respondWithStatus(res, 404, 'User not found');
}
catch (err) {
- console.error(err);
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
});
-router.delete('/:userId', verifyToken, checkBanned, hasPermission('delete_users'), async (req, res) => {
+router.delete('/:userId', verifyToken, checkBanned, async (req, res) => {
try {
- const userId = req.params.userId;
- userDELETE(res, userId);
+ if (req.params.userId != req.userId && !verifyPermissions(req.userId, 'user', 4)) return await respondWithStatus(res, 403, 'Missing permission');
+ if (!userExists(req.params.userId)) return await respondWithStatus(res, 404, 'User not found');
+ const [result] = await pool.execute('DELETE FROM users WHERE id = ?', [ req.params.userId ]);
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error removing user');
+ return respondWithStatus(res, 200, 'User deleted successfully');
}
catch (err) {
- console.error(err);
+ error(err);
return await respondWithStatus(res, 500, 'An error has occured');
}
});
-async function userPATCH(res, id, type, value) {
- const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [id]);
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
+
+// Email verification endpoints
+router.get('/email/request', verifyToken, checkBanned, async (req, res) => {
+ const userId = req.userId;
+ try {
+ const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
+ if (!rows.length) return await respondWithStatus(res, 404, 'User not found');
+ const user = rows[0];
+ if (user.email_verified) return await respondWithStatus(res, 400, 'Email is already verified');
+ const code = sendVerification(user.email, userId, 'email');
+ pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ userId, code, 'email' ]);
+ return await respondWithStatus(res, 200, 'Successfully sent verification email');
}
- const excludedKeys = ['id', 'user_type_id', 'is_banned'];
- const fields = rows.map(row =>
- Object.keys(row)
- .filter(key => !excludedKeys.includes(key)),
- );
- if (type == 'password') {
- value = await Bun.password.hash(value);
+ catch (err) {
+ error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
}
- if (fields[0].includes(type)) {
- const [result] = await pool.execute(`UPDATE users SET ${type} = ? WHERE id = ?`, [value, id]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating user');
+});
+
+router.get('/email/verify', verifyToken, checkBanned, async (req, res) => {
+ const { code } = req.query;
+ const userId = req.userId;
+ if (code) {
+ try {
+ const [rows] = await pool.execute(
+ 'SELECT * FROM verification_codes WHERE user_id = ? AND verification_code = ? AND type = ? AND created_at >= NOW() - INTERVAL 10 MINUTE LIMIT 1',
+ [userId, code, 'email'],
+ );
+ if (!rows.length) return await respondWithStatus(res, 400, 'Invalid code');
+
+ const [result] = await pool.execute('UPDATE users SET email_verified = ? WHERE id = ?', [ true, userId ]);
+ if (result.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user');
+
+ return await respondWithStatus(res, 200, 'Successfully verified user');
+ }
+ catch (err) {
+ error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
}
- return respondWithStatus(res, 200, 'User updated successfully');
}
else {
- return await respondWithStatus(res, 400, 'Invalid type or disallowed');
+ return await respondWithStatus(res, 400, 'Missing code');
}
-}
+});
-async function userPUT(res, userId, first_name, last_name, username, email, password = false) {
- const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
- }
- let sqlQuery, queryParams;
- if (password) {
- const hashedPassword = await Bun.password.hash(password);
- sqlQuery = 'UPDATE users SET first_name = ?, last_name = ?, username = ?, email = ?, password = ? WHERE id = ?';
- queryParams = [first_name, last_name, username, email, hashedPassword, userId];
+// Phone verification endpoints (requires a mobile providers API)
+// POST /phone/request
+// PATCH /phone/verify
+
+// Password reset endpoints
+router.post('/password/request', async (req, res) => {
+ const { usernameOrEmail } = req.body;
+ if (usernameOrEmail) {
+ try {
+ const [user] = await pool.execute('SELECT * FROM users WHERE email = ? OR username = ? LIMIT 1', [usernameOrEmail, usernameOrEmail]);
+ if (user.length === 0) return await respondWithStatus(res, 404, 'User not found');
+
+ let code;
+ const [rows] = await pool.execute(
+ 'SELECT * FROM verification_codes WHERE user_id = ? AND type = ? AND created_at >= NOW() - INTERVAL 10 MINUTE LIMIT 1', [user[0].id, 'password'],
+ );
+ if (!rows.length) {
+ code = sendVerification(user[0].email, user[0].id, 'password');
+ pool.execute('INSERT INTO verification_codes (user_id, verification_code, type) VALUES (?, ?, ?)', [ user[0].id, code, 'password' ]);
+ return await respondWithStatus(res, 200, 'Successfully sent password reset email');
+ }
+ else {
+ code = sendVerification(user[0].email, user[0].id, 'password', rows[0].verification_code);
+ return await respondWithStatus(res, 200, 'Successfully sent password reset email');
+ }
+ }
+ catch (err) {
+ error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
}
else {
- sqlQuery = 'UPDATE users SET first_name = ?, last_name = ?, username = ?, email = ? WHERE id = ?';
- queryParams = [first_name, last_name, username, email, userId];
+ return await respondWithStatus(res, 400, 'Missing username or email');
}
- const [result] = await pool.execute(sqlQuery, queryParams);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error updating user');
- }
- return respondWithStatus(res, 200, 'User updated successfully');
-}
+});
-async function userDELETE(res, userId) {
- const [rows] = await pool.execute('SELECT * FROM users WHERE id = ? LIMIT 1', [userId]);
- if (rows.length === 0) {
- return await respondWithStatus(res, 404, 'User not found');
+router.patch('/password/verify', async (req, res) => {
+ const { usernameOrEmail, password, code } = req.body;
+ if ([usernameOrEmail, password, code].every(Boolean)) {
+ try {
+ const [user] = await pool.execute(
+ 'SELECT id FROM users WHERE username = ? OR email = ? LIMIT 1',
+ [usernameOrEmail, usernameOrEmail],
+ );
+ if (!user.length) return await respondWithStatus(res, 404, 'Incorrect username or email');
+
+ const [rows] = await pool.execute(
+ 'SELECT * FROM verification_codes WHERE user_id = ? AND verification_code = ? AND type = ? AND created_at >= NOW() - INTERVAL 10 MINUTE ORDER BY 1 DESC LIMIT 1',
+ [user[0].id, code, 'password'],
+ );
+ if (!rows.length) return await respondWithStatus(res, 400, 'Invalid code');
+
+ const [result] = await pool.execute('DELETE FROM verification_codes WHERE user_id = ? AND verification_code = ?', [ user[0].id, code ]);
+ if (result.affectedRows === 0) {
+ return await respondWithStatus(res, 500, 'Error removing verification');
+ }
+ const token = generateToken(user[0].id, password);
+ const [rest] = await pool.execute('UPDATE users SET password = ? WHERE id = ?', [await Bun.password.hash(password), user[0].id]);
+ if (rest.affectedRows === 0) return await respondWithStatus(res, 500, 'Error updating user');
+ return await respondWithStatusJSON(res, 200, {
+ message: 'Password reset successful',
+ token: token,
+ });
+ }
+ catch (err) {
+ error(err);
+ return await respondWithStatus(res, 500, 'An error has occured');
+ }
}
- const [result] = await pool.execute('DELETE FROM users WHERE id = ?', [ userId ]);
- if (result.affectedRows === 0) {
- return await respondWithStatus(res, 500, 'Error removing user');
+ else {
+ return await respondWithStatus(res, 400, 'Missing fields');
}
- return respondWithStatus(res, 200, 'User deleted successfully');
-}
+});
export default router;
\ No newline at end of file
diff --git a/tokensDB/CURRENT b/tokensDB/CURRENT
deleted file mode 100644
index ecb0b4b..0000000
--- a/tokensDB/CURRENT
+++ /dev/null
@@ -1 +0,0 @@
-MANIFEST-000036
diff --git a/tokensDB/LOCK b/tokensDB/LOCK
deleted file mode 100644
index e69de29..0000000
diff --git a/tokensDB/LOG b/tokensDB/LOG
deleted file mode 100644
index c0af2d8..0000000
--- a/tokensDB/LOG
+++ /dev/null
@@ -1,3 +0,0 @@
-2023/11/21-19:14:47.768569 7fc5f7fff6c0 Recovering log #35
-2023/11/21-19:14:47.781678 7fc5f7fff6c0 Delete type=0 #35
-2023/11/21-19:14:47.781920 7fc5f7fff6c0 Delete type=3 #34
diff --git a/tokensDB/LOG.old b/tokensDB/LOG.old
deleted file mode 100644
index 12e41b5..0000000
--- a/tokensDB/LOG.old
+++ /dev/null
@@ -1,3 +0,0 @@
-2023/03/12-14:35:27.203048 7f7857bff6c0 Recovering log #33
-2023/03/12-14:35:27.221582 7f7857bff6c0 Delete type=0 #33
-2023/03/12-14:35:27.221647 7f7857bff6c0 Delete type=3 #32
diff --git a/tokensDB/MANIFEST-000036 b/tokensDB/MANIFEST-000036
deleted file mode 100644
index 7cff18e..0000000
Binary files a/tokensDB/MANIFEST-000036 and /dev/null differ