Fork of airjet using bun and ES6
This commit is contained in:
9
.env.sample
Normal file
9
.env.sample
Normal file
@@ -0,0 +1,9 @@
|
||||
DATABASE_HOST="127.0.0.1"
|
||||
DATABASE_NAME=hsp-gdh
|
||||
DATABASE_USER=hsp-gdh
|
||||
DATABASE_PASSWORD=""
|
||||
JWT_SECRET=""
|
||||
PORT=
|
||||
SMTP=
|
||||
MAIL=
|
||||
MAIL_PASS=
|
||||
50
.eslintrc.json
Normal file
50
.eslintrc.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"curly": ["error", "multi-line", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"handle-callback-err": "off",
|
||||
"indent": ["error", "tab"],
|
||||
"keyword-spacing": "error",
|
||||
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||
"max-statements-per-line": ["error", { "max": 2 }],
|
||||
"no-console": "off",
|
||||
"no-empty-function": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-inline-comments": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"yoda": "error"
|
||||
}
|
||||
}
|
||||
133
.gitignore
vendored
Normal file
133
.gitignore
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
# ---> Node
|
||||
# Logs
|
||||
#logs
|
||||
#*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
#.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
274
database.sql
Normal file
274
database.sql
Normal file
@@ -0,0 +1,274 @@
|
||||
SET default_storage_engine = InnoDB;
|
||||
DROP DATABASE IF EXISTS `hsp-gdh`;
|
||||
CREATE DATABASE IF NOT EXISTS `hsp-gdh`
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
DROP USER IF EXISTS 'hsp-gdh';
|
||||
CREATE USER 'hsp-gdh'@'%' IDENTIFIED BY 'PASSWORD';
|
||||
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 (
|
||||
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,
|
||||
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
|
||||
) 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;
|
||||
|
||||
CREATE TABLE users (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
first_name VARCHAR(64) NOT NULL,
|
||||
last_name VARCHAR(64) NOT NULL,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(128) NOT NULL,
|
||||
phone VARCHAR(32) DEFAULT 'None',
|
||||
user_type_id INT UNSIGNED NOT NULL,
|
||||
is_banned BOOLEAN NOT NULL DEFAULT 0,
|
||||
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 (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
verification_code VARCHAR(255),
|
||||
type VARCHAR(32) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT mv_user_id
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES users(id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE CASCADE,
|
||||
INDEX mv_user_id_idx (user_id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE seats (
|
||||
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,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT ps_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)
|
||||
) 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');
|
||||
|
||||
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');
|
||||
|
||||
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');
|
||||
|
||||
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);
|
||||
|
||||
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');
|
||||
|
||||
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');
|
||||
4
generate-secret.sh
Normal file
4
generate-secret.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
secret=$(openssl rand -base64 32)
|
||||
echo "Generated secret key $secret"
|
||||
52
index.js
Normal file
52
index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import cors from 'cors';
|
||||
import logger from 'morgan';
|
||||
import express from 'express';
|
||||
import cookieParser from 'cookie-parser';
|
||||
|
||||
import { log } from './modules/log';
|
||||
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';
|
||||
|
||||
const app = express();
|
||||
app.set('trust proxy', 1);
|
||||
|
||||
app.use(express.json());
|
||||
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(cors({
|
||||
origin: '*',
|
||||
}));
|
||||
|
||||
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);
|
||||
|
||||
// run the API
|
||||
app.listen(process.env.PORT, async () => {
|
||||
log(`running at port ${process.env.PORT}`);
|
||||
});
|
||||
|
||||
// test
|
||||
// import { post } from './modules/fetcher';
|
||||
// post('http://127.0.0.1:1109/users/login', { 'usernameOrEmail':'foo', 'password':'bar' }).then(res => console.log(res));
|
||||
22
jsconfig.json
Normal file
22
jsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
}
|
||||
}
|
||||
187
logs/access.log
Normal file
187
logs/access.log
Normal file
@@ -0,0 +1,187 @@
|
||||
::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)"
|
||||
26
modules/database.js
Normal file
26
modules/database.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import mysql from 'mysql2';
|
||||
|
||||
const connection = mysql.createConnection({
|
||||
host: process.env.DATABASE_HOST,
|
||||
user: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
database: process.env.DATABASE_NAME,
|
||||
});
|
||||
const pool = mysql.createPool({
|
||||
host: process.env.DATABASE_HOST,
|
||||
user: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
database: process.env.DATABASE_NAME,
|
||||
}).promise();
|
||||
|
||||
function createPool(host, user, password, db) {
|
||||
const newPool = mysql.createPool({
|
||||
host: host,
|
||||
user: user,
|
||||
password: password,
|
||||
database: db,
|
||||
}).promise();
|
||||
return newPool;
|
||||
}
|
||||
|
||||
export { connection, pool, createPool };
|
||||
71
modules/fetcher.js
Normal file
71
modules/fetcher.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import fetch from 'node-fetch';
|
||||
import { error } from './log';
|
||||
|
||||
async function get(url, token) {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
.catch(err => error(err));
|
||||
}
|
||||
|
||||
async function post(url, body, token) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
.catch(err => error(err));
|
||||
}
|
||||
|
||||
async function patch(url, body, token) {
|
||||
const options = {
|
||||
method: 'PATCH',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
.catch(err => error(err));
|
||||
}
|
||||
|
||||
async function put(url, body, token) {
|
||||
const options = {
|
||||
method: 'PUT',
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json', authorization: `${token}` },
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return await fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
return json;
|
||||
})
|
||||
.catch(err => error(err));
|
||||
}
|
||||
|
||||
export {
|
||||
get,
|
||||
post,
|
||||
patch,
|
||||
put,
|
||||
};
|
||||
79
modules/fileManager.js
Normal file
79
modules/fileManager.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import fs from 'fs';
|
||||
import download from 'download';
|
||||
import random from './random';
|
||||
|
||||
function fileExist(path) {
|
||||
try {
|
||||
fs.readFileSync(path);
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function fileDelete(path) {
|
||||
try {
|
||||
fs.unlinkSync(path);
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function fileDownload(url, name) {
|
||||
try {
|
||||
download(url, '../cdn/images/', { filename: name });
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function folderExist(path) {
|
||||
try {
|
||||
if (fs.existsSync(path)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getFilesFromFolder(path) {
|
||||
try {
|
||||
return fs.readdirSync(path);
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function randomFileFromFolder(path) {
|
||||
try {
|
||||
if (getFilesFromFolder(path)) {
|
||||
return random.random(0, getFilesFromFolder(path).length);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
fileExist,
|
||||
fileDelete,
|
||||
fileDownload,
|
||||
folderExist,
|
||||
getFilesFromFolder,
|
||||
randomFileFromFolder,
|
||||
};
|
||||
25
modules/formatHandler.js
Normal file
25
modules/formatHandler.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import dns from 'dns';
|
||||
|
||||
export function isEmailDomainValid(email) {
|
||||
const domain = email.split('@')[1];
|
||||
return new Promise((resolve, reject) => {
|
||||
dns.lookup(domain, (err, address) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
const isIPAddress = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(address);
|
||||
resolve(isIPAddress);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function isValidEmail(email) {
|
||||
const emailRegex = /\S+@\S+\.\S+/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
export function isNumber(x) {
|
||||
return /^-?[\d.]+(?:e-?\d+)?$/.test(x);
|
||||
}
|
||||
19
modules/log.js
Normal file
19
modules/log.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import pino from 'pino';
|
||||
|
||||
const logger = pino();
|
||||
|
||||
export function log(x) {
|
||||
logger.info(x);
|
||||
}
|
||||
|
||||
export function debug(x) {
|
||||
logger.debug(x);
|
||||
}
|
||||
|
||||
export function warn(x) {
|
||||
logger.warn(x);
|
||||
}
|
||||
|
||||
export function error(x) {
|
||||
logger.error(x);
|
||||
}
|
||||
100
modules/mailHandler.js
Normal file
100
modules/mailHandler.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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,
|
||||
port: 465,
|
||||
secure: true,
|
||||
auth: {
|
||||
user: process.env.MAIL,
|
||||
pass: process.env.MAIL_PASS,
|
||||
},
|
||||
});
|
||||
|
||||
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
|
||||
const mailOptions = {
|
||||
from: `"AirJet" <${process.env.MAIL}>`,
|
||||
to: email,
|
||||
subject: head,
|
||||
text: body,
|
||||
};
|
||||
// send mail with defined transport object
|
||||
transporter.sendMail(mailOptions, (error, info) => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
}
|
||||
else {
|
||||
console.log('Email sent: ' + info.response);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function sendVerification(email, userId) {
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
sendMail,
|
||||
sendVerification,
|
||||
sendResetVerification,
|
||||
createAddress,
|
||||
};
|
||||
101
modules/permission.js
Normal file
101
modules/permission.js
Normal file
@@ -0,0 +1,101 @@
|
||||
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 };
|
||||
9
modules/random.js
Normal file
9
modules/random.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
export function random(x, y) {
|
||||
return crypto.randomInt(x, y);
|
||||
}
|
||||
|
||||
export function randomHEX(x) {
|
||||
return crypto.randomBytes(x).toString('hex');
|
||||
}
|
||||
55
modules/requestHandler.js
Normal file
55
modules/requestHandler.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import slowDown from 'express-slow-down';
|
||||
import http from 'http';
|
||||
import os from 'os';
|
||||
|
||||
const requestLimiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 5,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: 'Too many requests from this IP, please try again later',
|
||||
});
|
||||
|
||||
const speedLimiter = slowDown({
|
||||
windowMs: 60 * 1000,
|
||||
delayAfter: 30,
|
||||
delayMs: 500,
|
||||
skipFailedRequests: true,
|
||||
});
|
||||
|
||||
function checkSystemLoad(req, res, next) {
|
||||
const load = os.loadavg()[0];
|
||||
const cores = os.cpus().length;
|
||||
const threshold = cores * 0.7;
|
||||
|
||||
if (load > threshold) {
|
||||
return res.status(503).send(http.STATUS_CODES[503]);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
function respondWithStatus(res, statusCode, message) {
|
||||
const response = { status: statusCode, message: message };
|
||||
if (statusCode >= 400 && statusCode <= 599) {
|
||||
response.error = http.STATUS_CODES[statusCode];
|
||||
}
|
||||
return res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
function respondWithStatusJSON(res, statusCode, JSON) {
|
||||
const response = { status: statusCode, JSON };
|
||||
if (statusCode >= 400 && statusCode <= 599) {
|
||||
response.error = http.STATUS_CODES[statusCode];
|
||||
}
|
||||
return res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
export {
|
||||
requestLimiter,
|
||||
speedLimiter,
|
||||
checkSystemLoad,
|
||||
respondWithStatus,
|
||||
respondWithStatusJSON,
|
||||
};
|
||||
101
modules/token.js
Normal file
101
modules/token.js
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 };
|
||||
28
package.json
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "hsp-gdh",
|
||||
"module": "index.js",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"bun-types": "latest",
|
||||
"eslint": "^8.54.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"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",
|
||||
"morgan": "^1.10.0",
|
||||
"mysql2": "^3.6.4",
|
||||
"nodemailer": "^6.9.7",
|
||||
"path": "^0.12.7",
|
||||
"pino": "^8.16.2"
|
||||
}
|
||||
}
|
||||
69
public/dash.html
Normal file
69
public/dash.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Dashboard</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="shortcut icon" href="./images/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="./styles/dash.css">
|
||||
<link rel="stylesheet" href="./styles/fontawesome.css">
|
||||
</head>
|
||||
<body class="big-wrapper">
|
||||
<main>
|
||||
<div>
|
||||
<nav>
|
||||
<div class="logo">
|
||||
<a href="javascript:window.history.replaceState({}, document.title, window.location.pathname);fetchPage()">Dashboard</a>
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#">Home</a></li>
|
||||
<li><a href="#">Dashboard</a></li>
|
||||
<li><a href="#">Settings</a></li>
|
||||
<li><a href="#">Help</a></li>
|
||||
</ul>
|
||||
<div class="notification">
|
||||
<a id="notification-icon" href="#"><i class="fas fa-bell"></i><span class="badge">3</span></a>
|
||||
<ul id="notification-menu" class="notification-menu">
|
||||
<div id="notification-loading" class="notification-loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="burger">
|
||||
<div class="line1"></div>
|
||||
<div class="line2"></div>
|
||||
<div class="line3"></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="sidebar">
|
||||
<ul class="sidebar-links">
|
||||
<li><a id="airlines-btn" class="sidebarBtn" href="?page=airlines">Airlines</a></li>
|
||||
<li><a id="airplanes-btn" class="sidebarBtn" href="?page=airplanes">Airplanes</a></li>
|
||||
<li><a id="airports-btn" class="sidebarBtn" href="?page=airports">Airports</a></li>
|
||||
<li><a id="flights-btn" class="sidebarBtn" href="?page=flights">Flights</a></li>
|
||||
<li><a id="pilots-btn" class="sidebarBtn" href="?page=pilots">Pilots</a></li>
|
||||
<li><a id="seats-btn" class="sidebarBtn" href="?page=seats">Seats</a></li>
|
||||
<li><a id="users-btn" class="sidebarBtn" href="?page=users">Users</a></li>
|
||||
<li><a id="profile-btn" class="sidebarBtn" href="?page=profile">Profile</a></li>
|
||||
</ul>
|
||||
<div class="theme-div">
|
||||
<button id="toggle-theme" class="toggleThemeBtn">
|
||||
<i class="far fa-moon" aria-hidden="true"></i>
|
||||
<i class="far fa-sun" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tiles-container"></div>
|
||||
<div id="toast"></div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="./scripts/theme.js"></script>
|
||||
<script src="./scripts/fetcher.js"></script>
|
||||
<script src="./scripts/notification.js"></script>
|
||||
<script src="./scripts/chart.js"></script>
|
||||
<script src="./scripts/dash.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/images/favicon.ico
Normal file
BIN
public/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/images/logo.png
Normal file
BIN
public/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/shape.png
Normal file
BIN
public/images/shape.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
195
public/index.html
Normal file
195
public/index.html
Normal file
@@ -0,0 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>AOSTIA</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="AOSTIA is a services provider with the main focus on users privacy, we currently provide emails, cloud and chat but there are more to come, so stay with us!">
|
||||
<meta name="keywords" content="aostia, AOSTIA, cloud, chat, mail, aostia mail, aostia cloud">
|
||||
<meta name="robots" content="index, follow">
|
||||
<link rel="canonical" href="https://aostia.me/">
|
||||
<meta name="Identifier-Url" content="https://aostia.me/">
|
||||
<meta name="Rating" content="general">
|
||||
<meta name="Distribution" content="global">
|
||||
<meta name="Category" content="internet">
|
||||
|
||||
<link rel="shortcut icon" href="./images/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="./styles/style.css">
|
||||
<link rel="stylesheet" href="./styles/w3.css">
|
||||
<link rel="stylesheet" href="./styles/fontawesome.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="big-wrapper">
|
||||
<img src="./images/shape.png" alt="" class="shape">
|
||||
|
||||
<header>
|
||||
<div class="container center">
|
||||
<div class="logo">
|
||||
<img src="./images/logo.png" alt="Logo">
|
||||
<h3>AOSTIA Jet</h3>
|
||||
</div>
|
||||
|
||||
<div class="links">
|
||||
<ul>
|
||||
<li><a href="https://mail.aostia.com/">AOSTIA Mail</a></li>
|
||||
<li><a href="https://cloud.aostia.net/">AOSTIA Cloud</a></li>
|
||||
<li><a href="https://aostia.me/">AOSTIA Jet</a></li>
|
||||
<li><a id="toggleSignUp" class="btn toggleRegisterBtn">Sign up</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="overlay"></div>
|
||||
|
||||
<div class="hamburger-menu">
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="home" class="showcase-area hidden">
|
||||
<div class="container">
|
||||
<div class="home">
|
||||
<div class="big-title">
|
||||
<h1>Fly with us,</h1>
|
||||
<h1>the sky's the limit!</h1>
|
||||
</div>
|
||||
<p class="text">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.</p>
|
||||
<div class="cta">
|
||||
<a id="toggleGetStarted" class="btn toggleLoginBtn">Get started</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="login" class="showcase-area hidden">
|
||||
<div class="container form-bg">
|
||||
<form id="loginForm" class="form">
|
||||
<div class="big-title">
|
||||
<h1>Login</h1>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label text" for="loginUsername">Username or Email:</label>
|
||||
<input class="form-input" type="text" id="loginUsername" name="usernameOrEmail" required>
|
||||
|
||||
<label class="form-label text" for="loginPassword">Password:</label>
|
||||
<input class="form-input" type="password" id="loginPassword" name="password" required>
|
||||
</div>
|
||||
|
||||
<button class="form-button" type="submit">Login</button>
|
||||
<a id="toggleForgotPassword" class="form-link text toggleForgotPasswordBtn" style="margin-bottom: 0px; cursor: pointer;">Forgot your password?</a>
|
||||
</form>
|
||||
<div class="cta">
|
||||
<a id="toggleHomeLogin" class="btn form-btn toggleHomeBtn">Back</a>
|
||||
<a id="toggleRegister" class="btn form-btn toggleRegisterBtn">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="register" class="showcase-area hidden">
|
||||
<div class="container form-bg">
|
||||
<form id="registerForm" class="form">
|
||||
<div class="big-title">
|
||||
<h1>Register</h1>
|
||||
</div>
|
||||
<div id="register-form1" class="form-group">
|
||||
<label class="form-label text" for="registerUsername">Username:</label>
|
||||
<input class="form-input" type="text" id="registerUsername" name="username" required>
|
||||
|
||||
<label class="form-label text" for="registerEmail">Email:</label>
|
||||
<input class="form-input" type="email" id="registerEmail" name="email" required>
|
||||
|
||||
<label class="form-label text" for="registerPassword">Password:</label>
|
||||
<input class="form-input" type="password" id="registerPassword" name="password" required>
|
||||
</div>
|
||||
<div id="register-form2" class="form-group hidden">
|
||||
<label class="form-label text" for="registerName">First name:</label>
|
||||
<input class="form-input" type="text" id="registerName" name="name" required>
|
||||
|
||||
<label class="form-label text" for="registerLastname">Last name:</label>
|
||||
<input class="form-input" type="text" id="registerLastname" name="lastname" required>
|
||||
|
||||
<label class="form-label text" for="registerPhone">Phone:</label>
|
||||
<input class="form-input" type="tel" id="registerPhone" name="phone" pattern="[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}">
|
||||
</div>
|
||||
<button id="register-next" class="form-button registerNextBtn" type="button">Next</button>
|
||||
<div id="register-btn-2" class="hidden">
|
||||
<button id="register-back" class="form-button form-button-2 registerBackBtn" style="max-width: fit-content;" type="button"><i class="fas fa-chevron-left" aria-hidden="true"></i></button>
|
||||
<button id="register-submit" class="form-button form-button-2" type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="cta">
|
||||
<a id="toggleHomeRegister" class="btn form-btn toggleHomeBtn">Back</a>
|
||||
<a id="toggleLogin" class="btn form-btn toggleLoginBtn">Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="forgotPassword" class="showcase-area hidden">
|
||||
<div class="container form-bg">
|
||||
<form id="requestResetPasswordForm" class="form">
|
||||
<div class="big-title">
|
||||
<h1>Reset your password</h1>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label text" for="resetUsername">Username or Email:</label>
|
||||
<input class="form-input" type="text" id="resetUsername" name="usernameOrEmail" required>
|
||||
</div>
|
||||
|
||||
<button class="form-button" type="submit">Send verification code</button>
|
||||
</form>
|
||||
<form id="verifyResetPasswordForm" class="form hidden">
|
||||
<div class="big-title">
|
||||
<h1>Reset your password</h1>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label text" for="resetCode">Verification code:</label>
|
||||
<input class="form-input" type="text" id="resetCode" name="code" required>
|
||||
</div>
|
||||
<button class="form-button" type="submit">Verify code</button>
|
||||
</form>
|
||||
<form id="resetPasswordForm" class="form hidden">
|
||||
<div class="big-title">
|
||||
<h1>Reset your password</h1>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label text" for="resetPassword">New password:</label>
|
||||
<input class="form-input" type="text" id="resetPassword" name="password" required>
|
||||
</div>
|
||||
<button class="form-button" type="submit">Reset password</button>
|
||||
</form>
|
||||
<div class="cta">
|
||||
<a id="toggleLoginReset" class="btn form-btn toggleLoginBtn">Login</a>
|
||||
<a id="toggleRegisterReset" class="btn form-btn toggleRegisterBtn">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="diag" class="hidden">
|
||||
<div class="w3-center w3-border" id="boxWrap">
|
||||
<div id="boxTxt"></div>
|
||||
<input id="btnTxt" class="w3-btn w3-border" type="button" value="OK" onclick="dbox()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="bottom-area">
|
||||
<div class="container">
|
||||
<button id="toggle-theme" class="toggleThemeBtn">
|
||||
<i class="far fa-moon" aria-hidden="true"></i>
|
||||
<i class="far fa-sun" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="./scripts/theme.js"></script>
|
||||
<script src="./scripts/fetcher.js"></script>
|
||||
<script src="./scripts/app.js"></script>
|
||||
<script src="./scripts/a81368914c.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2
public/scripts/a81368914c.js
Normal file
2
public/scripts/a81368914c.js
Normal file
File diff suppressed because one or more lines are too long
172
public/scripts/app.js
Normal file
172
public/scripts/app.js
Normal file
@@ -0,0 +1,172 @@
|
||||
const api = 'https://aostia.me/api';
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const registerForm = document.getElementById('registerForm');
|
||||
const resetPasswordForm = document.getElementById('resetPasswordForm');
|
||||
const verifyResetPasswordForm = document.getElementById('verifyResetPasswordForm');
|
||||
const requestResetPasswordForm = document.getElementById('requestResetPasswordForm');
|
||||
|
||||
const home = document.getElementById('home');
|
||||
const login = document.getElementById('login');
|
||||
const register = document.getElementById('register');
|
||||
const forgot = document.getElementById('forgotPassword');
|
||||
|
||||
function events() {
|
||||
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')));
|
||||
document.querySelectorAll('.toggleHomeBtn').forEach(button => button.addEventListener('click', () => togglePage('home')));
|
||||
document.querySelectorAll('.registerNextBtn').forEach(button => button.addEventListener('click', () => togglePage('registerNext')));
|
||||
document.querySelectorAll('.registerBackBtn').forEach(button => button.addEventListener('click', () => togglePage('registerBack')));
|
||||
document.querySelector('.hamburger-menu').addEventListener('click', () => {
|
||||
document.querySelector('.big-wrapper').classList.toggle('active');
|
||||
});
|
||||
}
|
||||
|
||||
function togglePage(page) {
|
||||
if (page == 'home') {
|
||||
localStorage.setItem('page', 'home');
|
||||
transition(home, login, register, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'login') {
|
||||
localStorage.setItem('page', 'login');
|
||||
transition(login, home, register, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'register') {
|
||||
localStorage.setItem('page', 'register');
|
||||
transition(register, home, login, forgot, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
else if (page == 'registerNext') {
|
||||
document.getElementById('register-form1').classList.add('hidden');
|
||||
document.getElementById('register-next').classList.add('hidden');
|
||||
document.getElementById('register-form2').classList.remove('hidden');
|
||||
document.getElementById('register-btn-2').classList.add('flex');
|
||||
document.getElementById('register-btn-2').classList.remove('hidden');
|
||||
}
|
||||
else if (page == 'registerBack') {
|
||||
document.getElementById('register-form2').classList.add('hidden');
|
||||
document.getElementById('register-btn-2').classList.add('hidden');
|
||||
document.getElementById('register-btn-2').classList.remove('flex');
|
||||
document.getElementById('register-form1').classList.remove('hidden');
|
||||
document.getElementById('register-next').classList.remove('hidden');
|
||||
}
|
||||
else if (page == 'forgotPassword') {
|
||||
localStorage.setItem('page', 'forgotPassword');
|
||||
transition(forgot, home, login, register, 'appear-transition', 'disintegrate-transition');
|
||||
}
|
||||
}
|
||||
|
||||
function transition(d1, d2, d3, d4, a1, a2) {
|
||||
d2.classList.add(a2);
|
||||
d3.classList.add(a2);
|
||||
d4.classList.add(a2);
|
||||
setTimeout(() => {
|
||||
d1.classList.replace('hidden', a1);
|
||||
d2.classList.replace(a2, 'hidden');
|
||||
d3.classList.replace(a2, 'hidden');
|
||||
d4.classList.replace(a2, 'hidden');
|
||||
setTimeout(() => {
|
||||
d1.classList.remove(a1);
|
||||
}, 950);
|
||||
}, 950);
|
||||
}
|
||||
|
||||
function dbox(msg) {
|
||||
if (msg !== undefined) {
|
||||
document.getElementById('boxTxt').innerHTML = msg;
|
||||
document.getElementById('diag').classList.remove('hidden');
|
||||
}
|
||||
else {
|
||||
document.getElementById('diag').classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = (event) => {
|
||||
events();
|
||||
togglePage(localStorage.getItem('page') || 'home');
|
||||
loginForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await post(`${api}/users/login`, { usernameOrEmail: loginForm.elements['usernameOrEmail'].value, password: loginForm.elements['password'].value });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Login successful!\nRedirecting...');
|
||||
localStorage.setItem('jwt', data.JSON.token);
|
||||
setTimeout(() => {
|
||||
window.location.href = './dash.html';
|
||||
}, 950);
|
||||
}
|
||||
});
|
||||
|
||||
registerForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const username = registerForm.elements['username'].value;
|
||||
const email = registerForm.elements['email'].value;
|
||||
const password = registerForm.elements['password'].value;
|
||||
const first_name = registerForm.elements['name'].value;
|
||||
const last_name = registerForm.elements['lastname'].value;
|
||||
const phone = registerForm.elements['phone'].value || 'None';
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
localStorage.setItem('jwt', data.token);
|
||||
dbox('Successfully registered!');
|
||||
togglePage('login');
|
||||
}
|
||||
});
|
||||
|
||||
requestResetPasswordForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await post(`${api}/users/changepassword`, { usernameOrEmail: requestResetPasswordForm.elements['usernameOrEmail'].value });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Reset password email has been sent!');
|
||||
verifyResetPasswordForm.classList.remove('hidden');
|
||||
requestResetPasswordForm.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
verifyResetPasswordForm.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
const { status, data } = await get(`${api}/users/verify?c=${verifyResetPasswordForm.elements['code'].value}&u=${requestResetPasswordForm.elements['usernameOrEmail'].value}`);
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Valid verification code!');
|
||||
resetPasswordForm.classList.remove('hidden');
|
||||
verifyResetPasswordForm.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
resetPasswordForm.addEventListener('submit', async (event) => {
|
||||
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 });
|
||||
|
||||
if (status != 200) {
|
||||
dbox(`${data.message}`);
|
||||
console.error(data);
|
||||
}
|
||||
else {
|
||||
dbox('Password successfully reset!');
|
||||
requestResetPasswordForm.classList.remove('hidden');
|
||||
resetPasswordForm.classList.add('hidden');
|
||||
togglePage('login');
|
||||
}
|
||||
});
|
||||
};
|
||||
20
public/scripts/chart.js
Normal file
20
public/scripts/chart.js
Normal file
File diff suppressed because one or more lines are too long
264
public/scripts/dash.js
Normal file
264
public/scripts/dash.js
Normal file
@@ -0,0 +1,264 @@
|
||||
// exemple
|
||||
const chartData = {
|
||||
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
data: [12, 19, 3, 5, 2, 3, 9],
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
{
|
||||
label: 'Dataset 2',
|
||||
data: [6, 12, 8, 2, 10, 5, 3],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
||||
borderColor: 'rgba(54, 162, 235, 1)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const api = 'https://aostia.me/api/'
|
||||
|
||||
const cookieValue = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith('token='))
|
||||
?.split('=')[1];
|
||||
|
||||
const token = cookieValue ? cookieValue : localStorage.getItem('jwt');
|
||||
if (!token) {
|
||||
toastNotifications('error', 'Missing token' + token);
|
||||
setTimeout(() => {
|
||||
window.location.href = './index.html';
|
||||
}, 950);
|
||||
}
|
||||
|
||||
fetchPage();
|
||||
|
||||
document.querySelectorAll('.sidebarBtn').forEach((btn) => {
|
||||
btn.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
const url = btn.getAttribute('href');
|
||||
window.history.pushState({}, '', url);
|
||||
fetchPage();
|
||||
});
|
||||
});
|
||||
|
||||
async function fetchPage() {
|
||||
const childElements = document.querySelector('.tiles-container').querySelectorAll('div');
|
||||
childElements.forEach((child) => {
|
||||
document.querySelector('.tiles-container').removeChild(child);
|
||||
});
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const type = params.get('page');
|
||||
if (!type) {
|
||||
try {
|
||||
const airlines = await get(api + 'airlines', token);
|
||||
const airplanes = await get(api + 'airplanes', token);
|
||||
const airports = await get(api + 'airports', token);
|
||||
const flights = await get(api + 'flights', token);
|
||||
const pilots = await get(api + 'pilots', token);
|
||||
const seats = await get(api + 'seats', token);
|
||||
if (airlines.status == 200) {
|
||||
renderTable(airlines.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (airplanes.status == 200) {
|
||||
renderTable(airplanes.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (airports.status == 200) {
|
||||
renderTable(airports.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (flights.status == 200) {
|
||||
renderTable(flights.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (pilots.status == 200) {
|
||||
renderTable(pilots.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
if (seats.status == 200) {
|
||||
renderTable(seats.data.JSON);
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
addEventTiles();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(error);
|
||||
toastNotifications('error', 'Failed to fetch data');
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
let endpoint = api + type;
|
||||
if (type === 'profile') {
|
||||
endpoint = api + 'users/me';
|
||||
}
|
||||
|
||||
const { status, data } = await get(endpoint, token);
|
||||
|
||||
if (status == 200 && type === 'profile') {
|
||||
renderProfile(data.JSON);
|
||||
} else if (status == 200) {
|
||||
renderTable(data.JSON);
|
||||
addEventTiles();
|
||||
} else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toastNotifications('error', 'Failed to fetch data');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderProfile(userData) {
|
||||
const table = document.createElement('table');
|
||||
|
||||
for (const [key, value] of Object.entries(userData)) {
|
||||
const row = document.createElement('tr');
|
||||
const dataName = document.createElement('td');
|
||||
const dataValue = document.createElement('td');
|
||||
const editCell = document.createElement('td');
|
||||
|
||||
dataName.textContent = key;
|
||||
dataValue.textContent = value;
|
||||
|
||||
const editButton = document.createElement('button');
|
||||
editButton.textContent = 'Edit';
|
||||
editButton.addEventListener('click', async () => {
|
||||
console.log('Edit button clicked for row:', key);
|
||||
const newValue = prompt(`Enter new value for ${key}:`);
|
||||
if (newValue !== null) {
|
||||
const { status, data } = await patch(api + 'users/', {type:key,value:newValue}, token);
|
||||
if (status == 200) {
|
||||
fetchPage();
|
||||
}
|
||||
else {
|
||||
toastNotifications('error', data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
editCell.appendChild(editButton);
|
||||
|
||||
row.appendChild(dataName);
|
||||
row.appendChild(dataValue);
|
||||
row.appendChild(editCell);
|
||||
table.appendChild(row);
|
||||
}
|
||||
|
||||
const tableTile = createTile('Profile', table);
|
||||
document.querySelector('.tiles-container').appendChild(tableTile);
|
||||
}
|
||||
|
||||
|
||||
function renderTable(tableData) {
|
||||
const table = document.createElement('table');
|
||||
for (const data of tableData) {
|
||||
const row = document.createElement('tr');
|
||||
for (const prop in data) {
|
||||
const cell = document.createElement('td');
|
||||
cell.innerText = data[prop];
|
||||
row.appendChild(cell);
|
||||
}
|
||||
table.appendChild(row);
|
||||
}
|
||||
|
||||
const tableTile = createTile('Table', table);
|
||||
document.querySelector('.tiles-container').appendChild(tableTile);
|
||||
}
|
||||
|
||||
function renderList(listData) {
|
||||
const list = document.createElement('ul');
|
||||
for (const data of listData) {
|
||||
const item = document.createElement('li');
|
||||
const fields = [];
|
||||
for (const key in data) {
|
||||
fields.push(data[key]);
|
||||
}
|
||||
item.innerText = fields.join(', ');
|
||||
list.appendChild(item);
|
||||
}
|
||||
const listTile = createTile('List', list);
|
||||
document.querySelector('.tiles-container').appendChild(listTile);
|
||||
}
|
||||
|
||||
function renderChart(chartData) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.id = 'chart';
|
||||
canvas.width = '1920';
|
||||
canvas.height = '1080';
|
||||
const ctx = canvas.getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
});
|
||||
const chartTile = createTile('Chart', canvas);
|
||||
document.querySelector('.tiles-container').appendChild(chartTile);
|
||||
}
|
||||
|
||||
function createTile(title, content) {
|
||||
const tile = document.createElement('div');
|
||||
tile.classList.add('tile');
|
||||
const header = document.createElement('h2');
|
||||
header.innerText = title;
|
||||
tile.appendChild(header);
|
||||
tile.appendChild(content);
|
||||
return tile;
|
||||
}
|
||||
|
||||
function addEventTiles() {
|
||||
const tiles = document.querySelectorAll('.tile');
|
||||
for (const tile of tiles) {
|
||||
tile.addEventListener('click', () => {
|
||||
if (!tile.classList.contains('fullscreen')) {
|
||||
tile.classList.add('fullscreen');
|
||||
}
|
||||
else {
|
||||
tile.classList.remove('fullscreen');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// toast
|
||||
function toastNotifications(type, msg) {
|
||||
const toast = document.getElementById('toast');
|
||||
if (type == 'success') {
|
||||
toast.innerHTML = `<div class="checkicon success"> <i class="fa fa-check-square"> ${msg}</i></div>`;
|
||||
}
|
||||
else if (type == 'error') {
|
||||
toast.innerHTML = `<div class="checkicon error"> <i class="fa fa-minus-square"> ${msg}</i></div>`;
|
||||
}
|
||||
else {
|
||||
toast.innerHTML = msg;
|
||||
}
|
||||
toast.className = 'show';
|
||||
setTimeout(function() {
|
||||
toast.className = toast.className.replace('show', '');
|
||||
}, 3000);
|
||||
}
|
||||
58
public/scripts/fetcher.js
Normal file
58
public/scripts/fetcher.js
Normal file
@@ -0,0 +1,58 @@
|
||||
async function get(url, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function post(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function patch(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
|
||||
async function put(url, json, token) {
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `${token}`
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return { status: response.status, data: data };
|
||||
}
|
||||
51
public/scripts/notification.js
Normal file
51
public/scripts/notification.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// notifications
|
||||
const notificationMenu = document.getElementById('notification-menu');
|
||||
const notificationIcon = document.getElementById('notification-icon');
|
||||
|
||||
window.addEventListener('load', generateNotifications);
|
||||
notificationIcon.addEventListener('click', generateNotifications);
|
||||
|
||||
function generateNotifications() {
|
||||
const notificationLoading = document.getElementById('notification-loading');
|
||||
notificationLoading.classList.remove('hidden');
|
||||
|
||||
const notifications = [
|
||||
{ title: 'Notification 1', link: '#' },
|
||||
{ title: 'Notification 2', link: '#' },
|
||||
{ title: 'Notification 3', link: '#' },
|
||||
];
|
||||
|
||||
const childElements = notificationMenu.querySelectorAll('li');
|
||||
childElements.forEach((child) => {
|
||||
notificationMenu.removeChild(child);
|
||||
});
|
||||
|
||||
notifications.forEach(notification => {
|
||||
const listItem = document.createElement('li');
|
||||
const link = document.createElement('a');
|
||||
link.href = notification.link;
|
||||
link.textContent = notification.title;
|
||||
listItem.appendChild(link);
|
||||
notificationMenu.appendChild(listItem);
|
||||
});
|
||||
notificationLoading.classList.add('hidden');
|
||||
}
|
||||
|
||||
|
||||
// Notification block
|
||||
let timeout;
|
||||
notificationIcon.addEventListener('mouseover', showNotificationMenu);
|
||||
notificationIcon.addEventListener('mouseout', hideNotificationMenu);
|
||||
notificationMenu.addEventListener('mouseover', showNotificationMenu);
|
||||
notificationMenu.addEventListener('mouseout', hideNotificationMenu);
|
||||
|
||||
function showNotificationMenu() {
|
||||
notificationMenu.style.display = 'block';
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
function hideNotificationMenu() {
|
||||
timeout = setTimeout(() => {
|
||||
notificationMenu.style.display = 'none';
|
||||
}, 500);
|
||||
}
|
||||
44
public/scripts/theme.js
Normal file
44
public/scripts/theme.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable no-undef */
|
||||
function checkNavTheme() {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return true; }
|
||||
else { return false; }
|
||||
}
|
||||
let dark = (localStorage.getItem('dark') == 'false') ? false : checkNavTheme();
|
||||
if (!dark) {
|
||||
document.querySelector('.big-wrapper').classList.add('light');
|
||||
}
|
||||
else {
|
||||
document.querySelector('.big-wrapper').classList.add('dark');
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
dark = !dark;
|
||||
localStorage.setItem('dark', dark);
|
||||
setTheme(dark);
|
||||
}
|
||||
|
||||
function setTheme() {
|
||||
const wrapper = document.querySelector('.big-wrapper');
|
||||
const btn = document.getElementById('toggle-theme');
|
||||
btn.setAttribute('disabled', 'disabled');
|
||||
setTimeout(() => {
|
||||
if (dark) {
|
||||
wrapper.classList.replace('light', 'dark');
|
||||
}
|
||||
else {
|
||||
wrapper.classList.replace('dark', 'light');
|
||||
}
|
||||
if (window.location.pathname.split('/').pop() == 'index.html') {
|
||||
wrapper.classList.add('appear-transition');
|
||||
setTimeout(() => {
|
||||
wrapper.classList.remove('appear-transition');
|
||||
btn.removeAttribute('disabled');
|
||||
}, 1500);
|
||||
}
|
||||
else {
|
||||
btn.removeAttribute('disabled');
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
document.querySelector('.toggleThemeBtn').addEventListener('click', toggleTheme);
|
||||
490
public/styles/dash.css
Normal file
490
public/styles/dash.css
Normal file
@@ -0,0 +1,490 @@
|
||||
.light {
|
||||
--primaryColor: #2c3e50;
|
||||
--secondaryColor: #95a5a6;
|
||||
--accentColor: #e74c3c;
|
||||
--backgroundColor: #fcfcfc;
|
||||
--foregroundColor: #ffffff;
|
||||
--textColor: #1d1d1d;
|
||||
--buttonColor: #64bcf4;
|
||||
--buttonHoverColor: #5bacdf;
|
||||
|
||||
|
||||
--darkOne: #312f3a;
|
||||
--darkTwo: #45424b;
|
||||
--lightOne: #4e4e4e;
|
||||
--lightTwo: rgb(122, 122, 122);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--primaryColor: #2c3e50;
|
||||
--secondaryColor: #95a5a6;
|
||||
--accentColor: #e74c3c;
|
||||
--backgroundColor: rgb(17, 17, 27);
|
||||
--foregroundColor: rgb(30, 30, 46);
|
||||
--textColor: #ecf0f1;
|
||||
--buttonColor: #64bcf4;
|
||||
--buttonHoverColor: #5bacdf;
|
||||
|
||||
|
||||
--darkOne: #f3f3f3;
|
||||
--darkTwo: #fff;
|
||||
--lightOne: #ccc;
|
||||
--lightTwo: #e7e3e3;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Body styles */
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: var(--backgroundColor);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* Navbar styles */
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: var(--foregroundColor);
|
||||
height: 80px;
|
||||
padding: 0 50px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
width: calc(100% - 100px);
|
||||
transition: 0.3s;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
nav a {
|
||||
text-decoration: none;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.logo a {
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.nav-links li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: var(--textColor);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 1px;
|
||||
margin: 0 20px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: var(--buttonHoverColor);
|
||||
}
|
||||
|
||||
.notification {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notification a {
|
||||
color: var(--textColor);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notification a:hover {
|
||||
color: var(--buttonHoverColor);
|
||||
}
|
||||
|
||||
.notification .badge {
|
||||
align-items: center;
|
||||
background-color: var(--accentColor);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 0.8rem;
|
||||
height: 18px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: -8px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.notification-loading {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notification-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
right: -40px;
|
||||
background-color: var(--foregroundColor);
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
min-width: 200px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.notification-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notification:hover .notification-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notification:hover .notification-menu,
|
||||
.notification-menu:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notification:hover .notification-menu:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notification-menu li {
|
||||
list-style: none;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.notification-menu a {
|
||||
text-decoration: none;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.notification-menu a:hover {
|
||||
color: var(--buttonHoverColor);
|
||||
}
|
||||
|
||||
.burger {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Sidebar styles */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: calc(100vh - 80px);
|
||||
width: 10vw;
|
||||
background-color: var(--foregroundColor);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 100px 20px 20px;
|
||||
z-index: 0;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-links {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.sidebar-links li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sidebar-links a {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
text-decoration: none;
|
||||
color: var(--textColor);
|
||||
font-size: 0.9rem;
|
||||
border-radius: 5px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.sidebar-links a:hover {
|
||||
background-color: var(--buttonHoverColor);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Tiles styles */
|
||||
.tiles-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(800px, 1fr));
|
||||
grid-gap: 20px;
|
||||
padding: 10px 10px 10px 50px;
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
max-width: 85%;
|
||||
left: 10.5vw;
|
||||
right: 1vw;
|
||||
top: 80px;
|
||||
}
|
||||
|
||||
.tile {
|
||||
background-color: var(--foregroundColor);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tile:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.tile h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 10px;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile table {
|
||||
width: 98%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tile table th {
|
||||
background-color: #f2f2f2;
|
||||
font-size: 1.2rem;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile table td {
|
||||
font-size: 1.1rem;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile li {
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
background-color: var(--backgroundColor);
|
||||
z-index: 9999;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tile.fullscreen .close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
font-size: 1.5rem;
|
||||
color: var(--textColor);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tile.fullscreen h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile.fullscreen p {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 30px;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile.fullscreen .table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tile.fullscreen .table th {
|
||||
font-size: 1.5rem;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile.fullscreen .table td {
|
||||
font-size: 1.3rem;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.tile.fullscreen canvas {
|
||||
color: var(--textColor);
|
||||
width: 100% !important;
|
||||
height: 85vh !important;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.tile.fullscreen:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#toast {
|
||||
visibility: hidden;
|
||||
min-width: 250px;
|
||||
margin-left: -200px;
|
||||
background-color: var(--foregroundColor);
|
||||
box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
|
||||
color: var(--textColor);
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
left: 55vw;
|
||||
bottom: 30px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#toast.show {
|
||||
visibility: visible;
|
||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {bottom: 0; opacity: 0;}
|
||||
to {bottom: 30px; opacity: 1;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
@keyframes fadeout {
|
||||
from {bottom: 30px; opacity: 1;}
|
||||
to {bottom: 0; opacity: 0;}
|
||||
}
|
||||
|
||||
.checkicon.success i{
|
||||
font-size: 25px;
|
||||
color: #47d764;
|
||||
}
|
||||
|
||||
.checkicon.error i{
|
||||
font-size: 25px;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.toggleThemeBtn {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
background: var(--darkTwo);
|
||||
color: var(--foregroundColor);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
height: 39px;
|
||||
width: 39px;
|
||||
border-radius: 50%;
|
||||
font-size: 1.1rem;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.toggleThemeBtn i {
|
||||
line-height: 39px;
|
||||
}
|
||||
|
||||
.toggleThemeBtn:hover {
|
||||
background: var(--buttonColor);
|
||||
}
|
||||
|
||||
.big-wrapper.light .toggleThemeBtn i:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.big-wrapper.light .toggleThemeBtn i:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.big-wrapper.dark .toggleThemeBtn i:last-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.big-wrapper.dark .toggleThemeBtn i:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-div {
|
||||
bottom: 20px;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1250px) {
|
||||
|
||||
.tiles-container {
|
||||
width: 83vw;
|
||||
}
|
||||
#toast {
|
||||
left: 62vw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 940px) {
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tiles-container {
|
||||
width: 81vw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px) {
|
||||
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tiles-container {
|
||||
padding-left: 10px;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.tile {
|
||||
max-width: 90vw;
|
||||
width: 80vw;
|
||||
}
|
||||
}
|
||||
4
public/styles/fontawesome.css
vendored
Normal file
4
public/styles/fontawesome.css
vendored
Normal file
File diff suppressed because one or more lines are too long
696
public/styles/style.css
Normal file
696
public/styles/style.css
Normal file
@@ -0,0 +1,696 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");
|
||||
|
||||
.light {
|
||||
--backgroundColor: #f1f8fc;
|
||||
--foregroundColor: #c9e0ec;
|
||||
--buttonColor: #64bcf4;
|
||||
--buttonHoverColor: #5bacdf;
|
||||
|
||||
--darkOne: #312f3a;
|
||||
--darkTwo: #45424b;
|
||||
--lightOne: #4e4e4e;
|
||||
--lightTwo: rgb(122, 122, 122);
|
||||
}
|
||||
.dark {
|
||||
--backgroundColor: rgb(17, 17, 27);
|
||||
--foregroundColor: rgb(30, 30, 46);
|
||||
--textColor: #ecf0f1;
|
||||
--buttonColor: #64bcf4;
|
||||
--buttonHoverColor: #5bacdf;
|
||||
|
||||
|
||||
--darkOne: #f3f3f3;
|
||||
--darkTwo: #fff;
|
||||
--lightOne: #ccc;
|
||||
--lightTwo: #e7e3e3;
|
||||
}
|
||||
|
||||
div.wave-transition {
|
||||
animation: wave 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
div.shake-transition {
|
||||
animation: shake 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
div.compress-transition {
|
||||
animation: compress 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
div.drop-transition {
|
||||
animation: drop 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
div.rotate-transition {
|
||||
animation: rotate 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
/* body.appear-transition {
|
||||
animation: appear 1s ease-in-out forwards;
|
||||
} */
|
||||
|
||||
div.disintegrate-transition {
|
||||
animation: appear 1s ease-in-out reverse;
|
||||
}
|
||||
|
||||
div.appear-transition {
|
||||
animation: appear 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
25% {
|
||||
transform: scale(1.1) rotate(-5deg);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.9) rotate(5deg);
|
||||
}
|
||||
75% {
|
||||
transform: scale(1.1) rotate(-5deg);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
10% {
|
||||
transform: translateX(-10px) rotate(-5deg);
|
||||
}
|
||||
20% {
|
||||
transform: translateX(10px) rotate(5deg);
|
||||
}
|
||||
30% {
|
||||
transform: translateX(-10px) rotate(-5deg);
|
||||
}
|
||||
40% {
|
||||
transform: translateX(10px) rotate(5deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-10px) rotate(-5deg);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(10px) rotate(5deg);
|
||||
}
|
||||
70% {
|
||||
transform: translateX(-10px) rotate(-5deg);
|
||||
}
|
||||
80% {
|
||||
transform: translateX(10px) rotate(5deg);
|
||||
}
|
||||
90% {
|
||||
transform: translateX(-10px) rotate(-5deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes compress {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
10% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
20% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
30% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
60% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
90% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes drop {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes disintegrate {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(0.1) rotate(720deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.1) rotate(720deg);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1) rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Poppins", sans-serif;
|
||||
background-color: var(--backgroundColor);
|
||||
}
|
||||
|
||||
.stop-scrolling {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.big-wrapper {
|
||||
position: relative;
|
||||
padding: 1.7rem 0 2rem;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: var(--backgroundColor);
|
||||
transition: background-color 1s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
max-width: 98vw;
|
||||
width: 100%;
|
||||
padding: 0 3rem;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
z-index: 70;
|
||||
}
|
||||
|
||||
header .container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 40px;
|
||||
margin-right: 0.6rem;
|
||||
margin-top: -0.6rem;
|
||||
}
|
||||
|
||||
.logo h3 {
|
||||
color: var(--darkTwo);
|
||||
font-size: 1.55rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.links ul {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--lightTwo);
|
||||
margin-left: 4.5rem;
|
||||
display: inline-block;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.links a:hover {
|
||||
color: var(--buttonHoverColor);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.9rem 1.9rem;
|
||||
color: var(--textColor);
|
||||
background-color: var(--buttonColor);
|
||||
border-radius: 16px;
|
||||
text-transform: capitalize;
|
||||
transition: 0.3s;
|
||||
margin: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggleRegisterBtn:hover {
|
||||
color: var(--lightOne) !important;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--buttonHoverColor);
|
||||
transform: scale(1) !important;
|
||||
}
|
||||
|
||||
.btn-anim {
|
||||
transition: all .2s linear;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
#diag {
|
||||
position: fixed; z-index: 999;
|
||||
top: 0; left: 0;
|
||||
width: 100vw; height: 100vh;
|
||||
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#boxWrap {
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
min-width: 320px; max-width: 600px;
|
||||
padding: 10px;
|
||||
|
||||
background: var(--backgroundColor);
|
||||
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
#boxTxt, #btnTxt {
|
||||
border-radius: 16px;
|
||||
color: var(--textColor);
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hamburger-menu .bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: var(--darkTwo);
|
||||
border-radius: 3px;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.bar::before,
|
||||
.bar::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--darkTwo);
|
||||
border-radius: 3px;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.bar::before {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.bar::after {
|
||||
transform: translateY(8px);
|
||||
}
|
||||
|
||||
.big-wrapper.active .hamburger-menu .bar {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.big-wrapper.active .bar::before {
|
||||
transform: translateY(0) rotate(-45deg);
|
||||
}
|
||||
|
||||
.big-wrapper.active .bar::after {
|
||||
transform: translateY(0) rotate(45deg);
|
||||
}
|
||||
|
||||
.showcase-area {
|
||||
margin: 0 auto;
|
||||
transition: display 2s;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.showcase-area .container {
|
||||
display: grid;
|
||||
/* grid-template-columns: repeat(1fr, 2fr); */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
right: auto;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.big-title {
|
||||
font-size: 1.4rem;
|
||||
color: var(--darkOne);
|
||||
text-transform: capitalize;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: var(--lightOne);
|
||||
font-size: 1.1rem;
|
||||
margin: 1.5rem 0;
|
||||
max-width: 600px;
|
||||
line-height: 2.3;
|
||||
}
|
||||
|
||||
.showcase-area .btn {
|
||||
box-shadow: 0 0 40px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.toggleThemeBtn {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
background: var(--darkTwo);
|
||||
color: var(--backgroundColor);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
height: 39px;
|
||||
width: 39px;
|
||||
border-radius: 50%;
|
||||
font-size: 1.1rem;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.toggleThemeBtn i {
|
||||
line-height: 39px;
|
||||
}
|
||||
|
||||
.toggleThemeBtn:hover {
|
||||
background: var(--buttonColor);
|
||||
}
|
||||
|
||||
.big-wrapper.light .toggleThemeBtn i:last-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.big-wrapper.light .toggleThemeBtn i:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.big-wrapper.dark .toggleThemeBtn i:last-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.big-wrapper.dark .toggleThemeBtn i:first-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shape {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
width: 500px;
|
||||
bottom: -180px;
|
||||
left: -15px;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.copy {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 100;
|
||||
animation: appear 1s 1 both;
|
||||
}
|
||||
|
||||
.cta {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.form-btn {
|
||||
color: white;
|
||||
width: 135px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form {
|
||||
padding: 10%;
|
||||
border-radius: 16px;
|
||||
min-width: 375px;
|
||||
min-height: fit-content;
|
||||
background-color: var(--foregroundColor);
|
||||
transition: background-color 0.5s;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
transition: display 1s;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.form-button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: var(--buttonColor);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-top: 1.9rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form-button:hover {
|
||||
background-color: var(--buttonHoverColor);
|
||||
}
|
||||
|
||||
.form-button-2 {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.form-link {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
clip-path: circle(30% at -25% -25%);
|
||||
}
|
||||
100% {
|
||||
clip-path: circle(150% at 0 0);
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#register-btn-2 {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 931px) {
|
||||
.hamburger-menu {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.links {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
max-width: 450px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--backgroundColor);
|
||||
z-index: 95;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: translateX(100%);
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.links ul {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.links a {
|
||||
color: var(--textColor);
|
||||
margin-left: 0;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.links .btn {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.big-wrapper.active .links {
|
||||
transform: translateX(0);
|
||||
box-shadow: 0 0 50px 2px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.big-wrapper.active .overlay {
|
||||
pointer-events: all;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.showcase-area {
|
||||
padding: 2.5rem 0;
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.showcase-area .container {
|
||||
grid-template-columns: 1fr;
|
||||
justify-content: center;
|
||||
grid-gap: 2rem;
|
||||
}
|
||||
|
||||
.big-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 0.95rem;
|
||||
margin: 1.4rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.person {
|
||||
width: 100%;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.logo h3 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.shape {
|
||||
bottom: -180px;
|
||||
left: -150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 470px) {
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.showcase-area {
|
||||
margin: 0;
|
||||
}
|
||||
.form {
|
||||
min-width: 320px;
|
||||
min-height: fit-content;
|
||||
padding: 5%;
|
||||
background-color: transparent;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.big-title {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin: 1.1rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.showcase-area .btn {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
235
public/styles/w3.css
Normal file
235
public/styles/w3.css
Normal file
@@ -0,0 +1,235 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
143
routes/airlines.js
Normal file
143
routes/airlines.js
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
143
routes/airplanes.js
Normal file
143
routes/airplanes.js
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
143
routes/airports.js
Normal file
143
routes/airports.js
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
143
routes/flights.js
Normal file
143
routes/flights.js
Normal file
@@ -0,0 +1,143 @@
|
||||
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;
|
||||
144
routes/pilots.js
Normal file
144
routes/pilots.js
Normal file
@@ -0,0 +1,144 @@
|
||||
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;
|
||||
155
routes/seats.js
Normal file
155
routes/seats.js
Normal file
@@ -0,0 +1,155 @@
|
||||
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;
|
||||
35
routes/test.js
Normal file
35
routes/test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import express from 'express';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received GET request' });
|
||||
|
||||
});
|
||||
|
||||
router.post('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received POST request' });
|
||||
|
||||
});
|
||||
|
||||
router.patch('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received PUT request' });
|
||||
|
||||
});
|
||||
|
||||
router.put('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received PUT request' });
|
||||
|
||||
});
|
||||
|
||||
router.delete('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received DELETE request' });
|
||||
|
||||
});
|
||||
|
||||
export default router;
|
||||
459
routes/users.js
Normal file
459
routes/users.js
Normal file
@@ -0,0 +1,459 @@
|
||||
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 { 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;
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
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 ],
|
||||
);
|
||||
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' ]);
|
||||
return await respondWithStatus(res, 200, 'Successfully registered');
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Invalid email address');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Missing fields');
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/login', requestLimiter, async (req, res) => {
|
||||
const { usernameOrEmail, password } = req.body;
|
||||
if ([usernameOrEmail, password].every(Boolean)) {
|
||||
try {
|
||||
const [rows] = await pool.execute(
|
||||
'SELECT * FROM users WHERE username = ? OR email = ? LIMIT 1',
|
||||
[usernameOrEmail, usernameOrEmail],
|
||||
);
|
||||
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');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Missing fields');
|
||||
}
|
||||
});
|
||||
|
||||
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) => {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT * FROM users WHERE 1');
|
||||
|
||||
if (rows.length === 0) {
|
||||
return await respondWithStatus(res, 404, 'Users 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_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)) {
|
||||
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 ],
|
||||
);
|
||||
return await respondWithStatus(res, 200, 'User 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.patch('/', 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 (rows.length === 0) {
|
||||
return await respondWithStatus(res, 404, 'User not found');
|
||||
}
|
||||
const user = rows[0];
|
||||
delete user.password;
|
||||
return await respondWithStatusJSON(res, 200, user);
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
});
|
||||
|
||||
router.patch('/:userId', verifyToken, checkBanned, hasPermission('edit_users'), 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');
|
||||
}
|
||||
const excludedKeys = ['id'];
|
||||
const fields = rows.map(row =>
|
||||
Object.keys(row)
|
||||
.filter(key => !excludedKeys.includes(key)),
|
||||
);
|
||||
console.log(fields[0]);
|
||||
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');
|
||||
}
|
||||
return respondWithStatus(res, 200, 'User updated successfully');
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Invalid type or disallowed');
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:userId', verifyToken, checkBanned, hasPermission('edit_users'), async (req, res) => {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const { first_name, last_name, username, email } = req.body;
|
||||
if ([first_name, last_name, username, email].every(Boolean)) {
|
||||
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('/:userId', verifyToken, checkBanned, hasPermission('delete_users'), async (req, res) => {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
userDELETE(res, userId);
|
||||
}
|
||||
catch (err) {
|
||||
console.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');
|
||||
}
|
||||
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);
|
||||
}
|
||||
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');
|
||||
}
|
||||
return respondWithStatus(res, 200, 'User updated successfully');
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Invalid type or disallowed');
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
else {
|
||||
sqlQuery = 'UPDATE users SET first_name = ?, last_name = ?, username = ?, email = ? WHERE id = ?';
|
||||
queryParams = [first_name, last_name, username, email, userId];
|
||||
}
|
||||
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');
|
||||
}
|
||||
const [result] = await pool.execute('DELETE FROM users WHERE id = ?', [ userId ]);
|
||||
if (result.affectedRows === 0) {
|
||||
return await respondWithStatus(res, 500, 'Error removing user');
|
||||
}
|
||||
return respondWithStatus(res, 200, 'User deleted successfully');
|
||||
}
|
||||
|
||||
export default router;
|
||||
0
tokensDB/000037.log
Normal file
0
tokensDB/000037.log
Normal file
1
tokensDB/CURRENT
Normal file
1
tokensDB/CURRENT
Normal file
@@ -0,0 +1 @@
|
||||
MANIFEST-000036
|
||||
0
tokensDB/LOCK
Normal file
0
tokensDB/LOCK
Normal file
3
tokensDB/LOG
Normal file
3
tokensDB/LOG
Normal file
@@ -0,0 +1,3 @@
|
||||
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
|
||||
3
tokensDB/LOG.old
Normal file
3
tokensDB/LOG.old
Normal file
@@ -0,0 +1,3 @@
|
||||
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
|
||||
BIN
tokensDB/MANIFEST-000036
Normal file
BIN
tokensDB/MANIFEST-000036
Normal file
Binary file not shown.
Reference in New Issue
Block a user