Compare commits
133 Commits
6c601e0b42
...
deadline
| Author | SHA1 | Date | |
|---|---|---|---|
|
7fead5486b
|
|||
|
5cc214a29b
|
|||
|
65a6ae2e3c
|
|||
|
83d07a2812
|
|||
|
e8acfd7b30
|
|||
|
438ae4b5d0
|
|||
|
6ccc899320
|
|||
|
5afb6e22bf
|
|||
|
bdba2f6e29
|
|||
|
30c6f2e3f1
|
|||
|
b47ec6c440
|
|||
|
b4f200cb32
|
|||
|
ec53fcb247
|
|||
|
371d960cf3
|
|||
|
3525fb12e6
|
|||
|
82afff9878
|
|||
|
fef2bda082
|
|||
|
f87aecaf75
|
|||
|
170b9a5693
|
|||
|
c9ca39ddfa
|
|||
|
da028ea2c4
|
|||
|
1c643b3625
|
|||
|
33d6793758
|
|||
|
c95ac03680
|
|||
|
03fc5307e6
|
|||
|
b6a2a74ba0
|
|||
|
f310bedeff
|
|||
|
838ea4ad22
|
|||
|
00064fd054
|
|||
|
70a6e5df54
|
|||
|
7f52a9d75e
|
|||
|
62742e6afe
|
|||
|
bdfc598218
|
|||
|
1cbc771251
|
|||
|
8644b5add6
|
|||
|
b66a5aff6d
|
|||
|
3d3da77df0
|
|||
|
abaeafea8a
|
|||
|
cb41c68f77
|
|||
|
e98729c9e5
|
|||
|
cd09a0c61b
|
|||
|
1723a8588a
|
|||
|
3fe6453b0c
|
|||
|
98477c5f27
|
|||
|
3472c59ac2
|
|||
|
3b6726113d
|
|||
|
d78b0aec4c
|
|||
|
ae6b25fbd6
|
|||
|
d7f9cb0b37
|
|||
|
cb1c2ee87c
|
|||
|
23ce32cb6f
|
|||
|
f6d18fc58d
|
|||
|
5163d79056
|
|||
|
ea3b7aa68b
|
|||
|
bc12f94e41
|
|||
|
df28d3aa52
|
|||
|
3d5ea6ac30
|
|||
|
2fb6cd6e83
|
|||
|
34f028ef9f
|
|||
|
0635c512cc
|
|||
|
3232e5fac1
|
|||
|
9225337e95
|
|||
|
f1272ca2c8
|
|||
|
4fd6c11326
|
|||
|
355cb0ec90
|
|||
|
19d265a0e6
|
|||
|
a6593cb76f
|
|||
|
28671146d1
|
|||
|
56bfd8cd0d
|
|||
|
cda313866f
|
|||
|
91b88ea592
|
|||
|
44b5745f3b
|
|||
|
a9cf48b04a
|
|||
|
16a8e892f9
|
|||
|
89ba2cb6d1
|
|||
|
47bb7aacdf
|
|||
|
88e89f0551
|
|||
|
9a6d7a73b2
|
|||
|
89d9fc47b2
|
|||
|
37cfaf4bbd
|
|||
|
8138231112
|
|||
|
edf2f6880c
|
|||
|
cd4b8479d2
|
|||
|
d05c7efadc
|
|||
|
8fe0fa57d8
|
|||
|
2796b514eb
|
|||
|
0053c0ce19
|
|||
|
041e77efcd
|
|||
|
915b205b6e
|
|||
|
4ff1aa852d
|
|||
|
c024770b4a
|
|||
|
37ec62405e
|
|||
|
57151ec777
|
|||
|
0f8fd9a3e8
|
|||
|
80dff138cc
|
|||
|
0887fe213f
|
|||
|
3231f916f8
|
|||
|
a74731a49e
|
|||
|
9bdcdef88f
|
|||
|
5d53cd28f8
|
|||
|
8711b3530a
|
|||
|
13d72ad529
|
|||
|
f23aabccd4
|
|||
|
c70bcef352
|
|||
|
30bd5a0dbe
|
|||
|
61c546459a
|
|||
|
adaf4e30db
|
|||
|
2f4ad31edf
|
|||
|
c25b204c67
|
|||
|
0a6321deb0
|
|||
|
a811a2ece2
|
|||
|
85adbbcdb3
|
|||
|
5c0d266002
|
|||
|
6c626e0b18
|
|||
|
aecaf83d85
|
|||
|
33d44ee4b6
|
|||
|
90cd80e540
|
|||
|
f3bddc7170
|
|||
|
789d62ea62
|
|||
|
debb30b896
|
|||
|
6e429f4f27
|
|||
|
b1dacb750a
|
|||
|
0ed130f7b8
|
|||
|
50c1ff797f
|
|||
|
2640ebbc70
|
|||
|
a992868eb6
|
|||
|
f3fc63502d
|
|||
|
44b04459fb
|
|||
|
64aa814d2c
|
|||
|
016f7fa9d4
|
|||
|
01241dff4b
|
|||
|
dda3eea9e1
|
|||
|
a430044d95
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
.env
|
||||
dist
|
||||
mariadb
|
||||
|
||||
8
.idea/biome.xml
generated
Normal file
8
.idea/biome.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="BiomeSettings">
|
||||
<option name="configPath" value="$PROJECT_DIR$" />
|
||||
<option name="configurationMode" value="MANUAL" />
|
||||
<option name="formatOnSave" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
22
.idea/dataSources.local.xml
generated
Normal file
22
.idea/dataSources.local.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="DB-241.14494.283">
|
||||
<data-source name="@docker/brief05" uuid="1f2800b5-8649-4a80-a9ec-b7b2a24623b4">
|
||||
<database-info product="MariaDB" version="10.3.39-MariaDB-1:10.3.39+maria~ubu2004" jdbc-version="4.2" driver-name="MariaDB Connector/J" driver-version="3.3.3" dbms="MARIADB" exact-version="10.3.39" exact-driver-version="3.3">
|
||||
<extra-name-characters>#@</extra-name-characters>
|
||||
<identifier-quote-string>`</identifier-quote-string>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="exact" quoted-identifiers="exact" />
|
||||
<secret-storage>master_key</secret-storage>
|
||||
<user-name>user_brief05</user-name>
|
||||
<schema-mapping>
|
||||
<introspection-scope>
|
||||
<node kind="schema">
|
||||
<name qname="@" />
|
||||
<name qname="brief_05" />
|
||||
</node>
|
||||
</introspection-scope>
|
||||
</schema-mapping>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="@docker/brief05" uuid="1f2800b5-8649-4a80-a9ec-b7b2a24623b4">
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mariadb://localhost:3434/brief_05</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
1449
.idea/dataSources/1f2800b5-8649-4a80-a9ec-b7b2a24623b4.xml
generated
Normal file
1449
.idea/dataSources/1f2800b5-8649-4a80-a9ec-b7b2a24623b4.xml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
#n:brief_05
|
||||
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
||||
BIN
.idea/dataSources/1f2800b5-8649-4a80-a9ec-b7b2a24623b4/storage_v2/_src_/schema/brief_05.6nelCw.zip
generated
Normal file
BIN
.idea/dataSources/1f2800b5-8649-4a80-a9ec-b7b2a24623b4/storage_v2/_src_/schema/brief_05.6nelCw.zip
generated
Normal file
Binary file not shown.
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -3,7 +3,7 @@
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<Languages>
|
||||
<language minSize="114" name="TypeScript" />
|
||||
<language minSize="174" name="TypeScript" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
|
||||
7
.idea/sqldialects.xml
generated
Normal file
7
.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/1f2800b5-8649-4a80-a9ec-b7b2a24623b4/console.sql" dialect="MariaDB" />
|
||||
<file url="PROJECT" dialect="MariaDB" />
|
||||
</component>
|
||||
</project>
|
||||
15
biome.json
15
biome.json
@@ -3,6 +3,15 @@
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"files": {
|
||||
"include": [
|
||||
"./src/**/*.ts"
|
||||
]
|
||||
},
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
@@ -17,8 +26,8 @@
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"indentStyle": "space",
|
||||
"indentStyle": "tab",
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 15
|
||||
"lineWidth": 80
|
||||
}
|
||||
}
|
||||
}
|
||||
155
db.sql
Normal file
155
db.sql
Normal file
@@ -0,0 +1,155 @@
|
||||
-- MariaDB dump 10.19-11.3.2-MariaDB, for Linux (x86_64)
|
||||
--
|
||||
-- Host: 127.0.0.1 Database: brief_05
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.3.39-MariaDB-1:10.3.39+maria~ubu2004
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `brands`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `brands`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `brands` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`slug_name` varchar(32) NOT NULL,
|
||||
`display_name` varchar(32) NOT NULL DEFAULT `slug_name`,
|
||||
`image_blob` blob DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `slug_name` (`slug_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `categories`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `categories`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `categories` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`slug_name` varchar(32) NOT NULL,
|
||||
`display_name` varchar(32) NOT NULL DEFAULT `slug_name`,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `categories_pk` (`slug_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `models`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `models`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `models` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`slug_name` varchar(32) NOT NULL,
|
||||
`display_name` varchar(32) NOT NULL DEFAULT `slug_name`,
|
||||
`brand_id` varchar(36) DEFAULT NULL,
|
||||
`category_id` varchar(36) DEFAULT NULL,
|
||||
`image_blob` blob DEFAULT NULL,
|
||||
`is_trending` tinyint(1) DEFAULT 0,
|
||||
`base_price` float NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `slug_name` (`slug_name`),
|
||||
KEY `models_brands_id_fk` (`brand_id`),
|
||||
KEY `models_categories_id_fk` (`category_id`),
|
||||
CONSTRAINT `models_brands_id_fk` FOREIGN KEY (`brand_id`) REFERENCES `brands` (`id`) ON UPDATE CASCADE,
|
||||
CONSTRAINT `models_categories_id_fk` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `rents`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `rents`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `rents` (
|
||||
`vehicle_id` varchar(36) NOT NULL,
|
||||
`user_id` varchar(36) NOT NULL,
|
||||
`active` tinyint(1) NOT NULL,
|
||||
`iat` date NOT NULL,
|
||||
`eat` date NOT NULL,
|
||||
`need_survey` tinyint(1) DEFAULT NULL,
|
||||
`km_at_start` int(11) DEFAULT NULL,
|
||||
`id` varchar(36) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `rent_vehicles_id_fk` (`vehicle_id`),
|
||||
KEY `rent_users_id_fk` (`user_id`),
|
||||
CONSTRAINT `rent_users_id_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE,
|
||||
CONSTRAINT `rent_vehicles_id_fk` FOREIGN KEY (`vehicle_id`) REFERENCES `vehicles` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `users` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`username` varchar(12) NOT NULL,
|
||||
`firstname` varchar(16) DEFAULT NULL,
|
||||
`lastname` varchar(16) DEFAULT NULL,
|
||||
`dob` date DEFAULT NULL,
|
||||
`email` varchar(32) NOT NULL,
|
||||
`is_admin` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`gdpr` date NOT NULL,
|
||||
`hash` varchar(97) NOT NULL,
|
||||
`is_email_verified` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `users_pk` (`id`),
|
||||
UNIQUE KEY `users_pk_2` (`id`),
|
||||
UNIQUE KEY `username` (`username`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `vehicles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `vehicles`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `vehicles` (
|
||||
`id` varchar(36) NOT NULL,
|
||||
`plate_number` varchar(8) DEFAULT NULL,
|
||||
`model_id` varchar(36) NOT NULL,
|
||||
`odometer` int(11) NOT NULL DEFAULT 0,
|
||||
`health_state` float NOT NULL,
|
||||
`isAvailable` tinyint(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `vehicles_models_id_fk` (`model_id`),
|
||||
CONSTRAINT `vehicles_models_id_fk` FOREIGN KEY (`model_id`) REFERENCES `models` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2024-05-03 14:56:07
|
||||
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
|
||||
#serveur de base de donnees
|
||||
database:
|
||||
image: 'mariadb:10.3'
|
||||
container_name: database
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_USER: 'user_brief05'
|
||||
MYSQL_PASSWORD: '1234567890'
|
||||
MYSQL_DATABASE: 'brief_05'
|
||||
MYSQL_ROOT_PASSWORD: '0987654321'
|
||||
|
||||
ports:
|
||||
- '3434:3306'
|
||||
volumes:
|
||||
- ${PWD}/mariadb/:/var/lib/mysql/
|
||||
10
package.json
10
package.json
@@ -1,18 +1,22 @@
|
||||
{
|
||||
"name": "brief-05-back",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "",
|
||||
"main": "dist/app.js",
|
||||
"main": "src/app.ts",
|
||||
"keywords": [],
|
||||
"author": "Mathis HERRIOT",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"bun:dev": "bun run --watch src/app.ts",
|
||||
"bun:check": "bunx biome check --skip-errors --apply src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@node-rs/argon2": "^1.8.3",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"express-validator": "^7.0.1",
|
||||
"express-xss-sanitizer": "^1.2.0",
|
||||
"helmet": "^7.1.0",
|
||||
"jose": "^5.2.4",
|
||||
"morgan": "^1.10.0",
|
||||
"mysql2": "^3.9.7",
|
||||
|
||||
BIN
schema-db.png
Normal file
BIN
schema-db.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 198 KiB |
54
src/app.ts
54
src/app.ts
@@ -1,17 +1,22 @@
|
||||
import express, { type Express } from 'express';
|
||||
import cors from 'cors';
|
||||
import compression from 'compression';
|
||||
import {Logger} from "tslog";
|
||||
import * as process from "node:process";
|
||||
import AuthRouter from "@routes/auth/authRouter";
|
||||
import CatalogRouter from "@routes/catalog/catalogRouter";
|
||||
import RentRouter from "@routes/rent/rentRouter";
|
||||
import compression from "compression";
|
||||
import cors from "cors";
|
||||
import express, { type Express } from "express";
|
||||
import helmet from "helmet";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
|
||||
const logger = new Logger({ name: "App" });
|
||||
const logger = new Logger({
|
||||
name: "App",
|
||||
});
|
||||
|
||||
const app: Express = express();
|
||||
|
||||
// enable cors
|
||||
app.use(cors());
|
||||
app.options('*', cors());
|
||||
app.options("*", cors());
|
||||
|
||||
// enable xss sanitizer
|
||||
app.use(
|
||||
@@ -19,18 +24,41 @@ app.use(
|
||||
xXssProtection: true,
|
||||
}),
|
||||
);
|
||||
app.use(helmet.xXssProtection())
|
||||
app.use(helmet.xXssProtection());
|
||||
|
||||
// parse json request body
|
||||
app.use(express.json());
|
||||
|
||||
// parse urlencoded request body
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(
|
||||
express.urlencoded({
|
||||
extended: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// gzip compression
|
||||
app.use(compression())
|
||||
app.use(compression());
|
||||
|
||||
//app.use('/auth', AuthRoutes)
|
||||
try {
|
||||
app.use("/auth", AuthRouter);
|
||||
app.use("/catalog", CatalogRouter);
|
||||
app.use("/rent", RentRouter);
|
||||
logger.info("Routers loaded !");
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
throw null;
|
||||
}
|
||||
|
||||
//app.listen(3333)
|
||||
logger.info('Server is running !')
|
||||
try {
|
||||
app.listen(process.env["APP_PORT"]);
|
||||
logger.info(
|
||||
`Server is running !\n >> Memory total: ${Math.round(
|
||||
process.memoryUsage().rss / 1_000_000,
|
||||
)} Mio\n >> Memory heap: ${Math.round(
|
||||
process.memoryUsage().heapUsed / 1_000_000,
|
||||
)} Mio\n`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(`Server failed to start: ${error}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -1,83 +1,96 @@
|
||||
import JwtService from "@services/jwt.service";
|
||||
|
||||
|
||||
import type {IReqEditUserData} from "@interfaces/IReqEditUserData";
|
||||
import type { IReqEditUserData } from "@interfaces/IReqEditUserData";
|
||||
import { HttpStatusCode } from "@interfaces/requests/HttpStatusCode";
|
||||
import type { IReqRegister } from "@interfaces/requests/IReqRegister";
|
||||
import UserService from "@services/user.service";
|
||||
import type {Request, Response} from "express";
|
||||
import {Logger} from "tslog";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { isEmail } from "@utils/validators/email";
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "AuthController",
|
||||
});
|
||||
|
||||
const logger = new Logger({ name: "AuthController" });
|
||||
|
||||
//FIX Better return object interface
|
||||
/**
|
||||
* Registers a user with the given request data.
|
||||
* Registers a user.
|
||||
*
|
||||
* @param {Request} req - The request object containing user data.
|
||||
* @param {Response} res - The response object to send the registration result.
|
||||
* @param {Request} req - The request object.
|
||||
* @param {Response} res - The response object.
|
||||
*
|
||||
* @return {Promise} A promise that resolves to the registration result.
|
||||
* It can have the following properties:
|
||||
* - error: "gdprNotApproved" if GDPR is not approved
|
||||
* - error: "exist" if the user already exists
|
||||
* - Otherwise, the registered user data
|
||||
* @return {Promise} - A promise that resolves with the registration result or rejects with an error.
|
||||
*/
|
||||
async function registerUser(req: Request, res: Response): Promise<unknown> {
|
||||
const body = req.body;
|
||||
async function registerUser(req: Request, res: Response): Promise<Response> {
|
||||
const body: IReqRegister = req.body;
|
||||
if (!body) {
|
||||
logger.warn(`Invalid input data (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid input data",
|
||||
});
|
||||
}
|
||||
if (!body.password || !body.username || !body.firstName || !body.lastName || !body.displayName) {
|
||||
if (
|
||||
!body.password ||
|
||||
!body.username ||
|
||||
!body.firstName ||
|
||||
!body.lastName ||
|
||||
!body.email
|
||||
) {
|
||||
logger.warn(`Field(s) missing (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "Field(s) missing",
|
||||
});
|
||||
}
|
||||
|
||||
let gdpr = false
|
||||
if (body.gdpr === true) {gdpr = true}
|
||||
const sanitizeData= {
|
||||
username: `${body.username}`,
|
||||
displayName: `${body.displayName}`,
|
||||
gdpr: gdpr,
|
||||
password: `${body.password}`,
|
||||
firstName: `${body.firstName}`,
|
||||
lastName: `${body.lastName}`,
|
||||
if (!isEmail(body.email)) {
|
||||
logger.warn(`Invalid email format (${req.ip})`);
|
||||
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid email format",
|
||||
});
|
||||
}
|
||||
|
||||
let gdpr = false;
|
||||
if (body.gdpr === true) {
|
||||
gdpr = true;
|
||||
}
|
||||
const sanitizeData: IReqRegister = {
|
||||
username: `${body.username}`,
|
||||
email: `${body.email.toLowerCase()}`,
|
||||
gdpr: gdpr,
|
||||
password: `${body.password}`,
|
||||
firstName: `${body.firstName}`,
|
||||
lastName: `${body.lastName}`,
|
||||
};
|
||||
|
||||
const RegisterServiceResult = await UserService.register(sanitizeData)
|
||||
const RegisterServiceResult = await UserService.register(sanitizeData);
|
||||
|
||||
if (RegisterServiceResult.error === "gdprNotApproved") {
|
||||
if (
|
||||
typeof RegisterServiceResult !== "string" &&
|
||||
RegisterServiceResult.message === "GDPR acceptance is required."
|
||||
) {
|
||||
logger.warn(`GDPR not approved (${req.ip})`);
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "GDPR not accepted."
|
||||
});
|
||||
return res.status(HttpStatusCode.BadRequest).json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "GDPR not accepted.",
|
||||
});
|
||||
}
|
||||
if (RegisterServiceResult.error === "exist") {
|
||||
logger.warn(`The user already exists (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "The user already exists."
|
||||
});
|
||||
if (
|
||||
typeof RegisterServiceResult !== "string" &&
|
||||
RegisterServiceResult.error === 5
|
||||
) {
|
||||
logger.warn(`The user already exists (${sanitizeData.email})`);
|
||||
return res.type("application/json").status(HttpStatusCode.Conflict).json({
|
||||
error: RegisterServiceResult.error,
|
||||
message: "The user already exists.",
|
||||
});
|
||||
}
|
||||
|
||||
// SUCCESS
|
||||
logger.info(`User registered successfully (${req.ip})`);
|
||||
//logger.info(`User registered successfully (${sanitizeData.username})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(201)
|
||||
.json(RegisterServiceResult);
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Ok)
|
||||
.json({ token: RegisterServiceResult });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,198 +102,201 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
|
||||
* @return {Promise<void>} A promise that resolves when the user is logged in or rejects with an error.
|
||||
*/
|
||||
async function loginUser(req: Request, res: Response): Promise<void> {
|
||||
|
||||
const body = req.body;
|
||||
if (!body) {
|
||||
res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Invalid input data' });
|
||||
res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid input data",
|
||||
});
|
||||
}
|
||||
if (!body.password || !body.username) {
|
||||
if (!body.password || !body.email) {
|
||||
logger.warn(`Field(s) missing (${req.ip})`);
|
||||
res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "Field(s) missing",
|
||||
});
|
||||
}
|
||||
|
||||
const loginData = {
|
||||
username: `${body.username}`,
|
||||
password: `${body.password}`
|
||||
email: `${body.email}`,
|
||||
password: `${body.password}`,
|
||||
};
|
||||
console.log(body)
|
||||
console.log(body);
|
||||
const LoginServiceResult = await UserService.login(loginData);
|
||||
console.log(LoginServiceResult)
|
||||
console.log(LoginServiceResult);
|
||||
|
||||
if (LoginServiceResult.error === "userNotFound") {
|
||||
console.log('POOL')
|
||||
res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "User not found."
|
||||
});
|
||||
if (
|
||||
typeof LoginServiceResult !== "string" &&
|
||||
LoginServiceResult.error === 3
|
||||
) {
|
||||
res.type("application/json").status(HttpStatusCode.NotFound).json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "User not found.",
|
||||
});
|
||||
}
|
||||
if (LoginServiceResult.error === "invalidPassword") {
|
||||
res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "Invalid password."
|
||||
});
|
||||
if (
|
||||
typeof LoginServiceResult !== "string" &&
|
||||
LoginServiceResult.error === 5
|
||||
) {
|
||||
res.type("application/json").status(HttpStatusCode.NotAcceptable).json({
|
||||
error: LoginServiceResult.error,
|
||||
message: "Invalid password.",
|
||||
});
|
||||
}
|
||||
res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(LoginServiceResult);
|
||||
res.type("application/json").status(200).json(LoginServiceResult);
|
||||
}
|
||||
|
||||
//ToTest
|
||||
async function getAllUsers(req: Request, res: Response) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
|
||||
error: "Invalid token",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload) {
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
|
||||
error: "Invalid token",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub)
|
||||
const sourceUser = await UserService.getFromId(payload.sub);
|
||||
if (!sourceUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
if (!sourceUser.is_admin) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
if ("id" in sourceUser && !sourceUser.is_admin) {
|
||||
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const AllUserResponse = await UserService.getAll()
|
||||
if (!AllUserResponse.users) {
|
||||
const AllUserResponse = await UserService.getAll();
|
||||
if (typeof AllUserResponse === "object") {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.InternalServerError)
|
||||
.json({
|
||||
error: "Internal server error",
|
||||
});
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Found)
|
||||
.json(AllUserResponse);
|
||||
}
|
||||
|
||||
//ToTest
|
||||
async function getUser(req: Request, res: Response) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Unauthorized)
|
||||
.json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload) {
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Unauthorized)
|
||||
.json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub)
|
||||
const sourceUser = await UserService.getFromId(payload.sub);
|
||||
if (!sourceUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
if (!sourceUser.is_admin) {
|
||||
if ("username" in sourceUser && !sourceUser.is_admin) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Unauthorized)
|
||||
.json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const userId = req.params["id"];
|
||||
if (!userId) {
|
||||
logger.warn(`User ID not provided (${req.ip})`);
|
||||
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
|
||||
error: "User ID not provided",
|
||||
});
|
||||
}
|
||||
const dbUser = await UserService.getFromId(userId);
|
||||
if (!dbUser) {
|
||||
logger.warn(`User not found (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
return res.type("application/json").status(HttpStatusCode.NotFound).json({
|
||||
error: "User not found",
|
||||
});
|
||||
}
|
||||
// @ts-ignore
|
||||
delete dbUser.passwordHash
|
||||
delete dbUser.passwordHash;
|
||||
// @ts-ignore
|
||||
delete dbUser._id
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(dbUser);
|
||||
delete dbUser._id;
|
||||
return res.type("application/json").status(HttpStatusCode.Found).json(dbUser);
|
||||
}
|
||||
|
||||
//FEAT - Implement re-auth by current password in case of password change
|
||||
//ToTest
|
||||
async function editUser(req: Request, res: Response) {
|
||||
const body: IReqEditUserData | null = req.body;
|
||||
if (!body) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(400)
|
||||
.json({ error: 'Field(s) missing' });
|
||||
return res.type("application/json").status(400).json({
|
||||
error: "Field(s) missing",
|
||||
});
|
||||
}
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload) {
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub)
|
||||
const sourceUser = await UserService.getFromId(payload.sub);
|
||||
|
||||
//@ts-ignore
|
||||
const targetUserId = req.params.id || payload.sub
|
||||
console.log(targetUserId)
|
||||
const targetUserId = req.params.id || payload.sub;
|
||||
console.log(targetUserId);
|
||||
|
||||
if (!sourceUser) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
return res.type("application/json").status(404).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
if (sourceUser.is_admin || sourceUser.id === payload.sub) {
|
||||
if (
|
||||
("id" in sourceUser && sourceUser.is_admin) ||
|
||||
("id" in sourceUser && sourceUser.id === payload.sub)
|
||||
) {
|
||||
if (sourceUser.is_admin) {
|
||||
logger.info(`EDIT :> Source user is an admin (${sourceUser.firstname} ${sourceUser.lastname})`)
|
||||
logger.info(
|
||||
`EDIT :> Source user is an admin (${sourceUser.firstname} ${sourceUser.lastname})`,
|
||||
);
|
||||
} else {
|
||||
logger.info(`EDIT :> Source user modify itself (${sourceUser.firstname} ${sourceUser.lastname})`)
|
||||
logger.info(
|
||||
`EDIT :> Source user modify itself (${sourceUser.firstname} ${sourceUser.lastname})`,
|
||||
);
|
||||
}
|
||||
|
||||
//TODO Interface
|
||||
const modifiedData = {
|
||||
}
|
||||
const modifiedData = {};
|
||||
//@ts-ignore
|
||||
if (body.firstName) modifiedData.firstName = `${body.firstName}`;
|
||||
//@ts-ignore
|
||||
@@ -291,175 +307,169 @@ async function editUser(req: Request, res: Response) {
|
||||
//if (body.password) modifiedData.password = `${body.password}`;
|
||||
|
||||
//Call service
|
||||
const EditUserServiceResult = await UserService.edit(`${targetUserId}`, modifiedData);
|
||||
if (EditUserServiceResult.error === 'userNotFound') {
|
||||
const EditUserServiceResult = await UserService.edit(
|
||||
`${targetUserId}`,
|
||||
modifiedData,
|
||||
);
|
||||
if (
|
||||
typeof EditUserServiceResult !== "boolean" &&
|
||||
EditUserServiceResult.error === 3
|
||||
) {
|
||||
logger.warn(`User not found (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
return res.type("application/json").status(404).json({
|
||||
error: "User not found",
|
||||
});
|
||||
}
|
||||
if (EditUserServiceResult.error !== 'none') {
|
||||
if (
|
||||
typeof EditUserServiceResult !== "boolean" &&
|
||||
EditUserServiceResult.error
|
||||
) {
|
||||
logger.error(`Error occurred during user edit (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
return res.type("application/json").status(500).json({
|
||||
error: "Internal server error",
|
||||
});
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json(EditUserServiceResult);
|
||||
return res.type("application/json").status(200).json(EditUserServiceResult);
|
||||
}
|
||||
//Not itself or
|
||||
logger.warn(`Unauthorized access attempt, not self or admin (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
|
||||
|
||||
return res.type("application/json").status(403).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
//ToTest
|
||||
async function deleteUser(req: Request, res: Response): Promise<Response> {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
|
||||
if (!payload) {
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Invalid token (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Invalid token' });
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Invalid token",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload?.sub)
|
||||
const targetUserId = req.params["id"]
|
||||
const sourceUser = await UserService.getFromId(payload?.sub);
|
||||
const targetUserId = req.params["id"];
|
||||
if (!sourceUser) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
return res.type("application/json").status(404).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
if (sourceUser.is_admin || sourceUser.id === payload.sub) {
|
||||
const deleteUserServiceResult = await UserService.delete(`${targetUserId}`);
|
||||
if (
|
||||
("id" in sourceUser && sourceUser.is_admin) ||
|
||||
("id" in sourceUser && sourceUser.id === payload.sub)
|
||||
) {
|
||||
const deleteUserServiceResult = await UserService.delete(`${targetUserId}`);
|
||||
if (!deleteUserServiceResult) {
|
||||
logger.error(`Error occurred during user delete (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Internal server error' });
|
||||
return res.type("application/json").status(500).json({
|
||||
error: "Internal server error",
|
||||
});
|
||||
}
|
||||
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ message: 'User deleted successfully' });
|
||||
return res.type("application/json").status(200).json({
|
||||
message: "User deleted successfully",
|
||||
});
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
return res.type("application/json").status(403).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
//ToTest
|
||||
async function deleteSelf(req: Request, res: Response) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub)
|
||||
if (!sourceUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'You dont exist anymore' });
|
||||
}
|
||||
if (sourceUser.id !== req.params["id"]) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(403)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
const deleteResult = await UserService.delete(sourceUser.id);
|
||||
if (!deleteResult) {
|
||||
logger.error(`Failed to delete user (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(500)
|
||||
.json({ error: 'Failed to delete user' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({ message: 'User deleted successfully' });
|
||||
}
|
||||
|
||||
async function getSelf(req: Request, res: Response) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(' ')[1];
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(401)
|
||||
.json({ error: 'Unauthorized' });
|
||||
}
|
||||
const dbUser = await UserService.getFromId(payload.sub)
|
||||
if (!dbUser) {
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(404)
|
||||
.json({ error: 'User not found' });
|
||||
}
|
||||
return res
|
||||
.type('application/json')
|
||||
.status(200)
|
||||
.json({
|
||||
id: dbUser.id,
|
||||
username: dbUser.username,
|
||||
firstName: dbUser.firstname,
|
||||
lastName: dbUser.firstname,
|
||||
isAdmin: dbUser.firstname
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub);
|
||||
if (!sourceUser) {
|
||||
return res.type("application/json").status(404).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
if ("id" in sourceUser && sourceUser.id !== req.params["id"]) {
|
||||
return res.type("application/json").status(403).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
if ("id" in sourceUser) {
|
||||
const deleteResult = await UserService.delete(sourceUser.id);
|
||||
|
||||
if (!deleteResult) {
|
||||
logger.error(`Failed to delete user (${req.ip})`);
|
||||
return res.type("application/json").status(500).json({
|
||||
error: "Failed to delete user",
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.type("application/json").status(200).json({
|
||||
message: "User deleted successfully",
|
||||
});
|
||||
}
|
||||
|
||||
//ToTest
|
||||
async function getSelf(req: Request, res: Response) {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res.type("application/json").status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const GetUserResult = await UserService.getFromId(payload.sub);
|
||||
if ("id" in GetUserResult) {
|
||||
return res.type("application/json").status(200).json({
|
||||
id: GetUserResult.id,
|
||||
username: GetUserResult.username,
|
||||
firstName: GetUserResult.firstname,
|
||||
lastName: GetUserResult.lastname,
|
||||
isAdmin: GetUserResult.is_admin,
|
||||
});
|
||||
}
|
||||
return res.type("application/json").status(404).json({
|
||||
error: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const AuthController = {
|
||||
register: registerUser,
|
||||
login: loginUser,
|
||||
getAllUsers,
|
||||
getUser,
|
||||
editUser,
|
||||
deleteUser,
|
||||
login: loginUser,
|
||||
getAllUsers,
|
||||
getUser,
|
||||
editUser,
|
||||
deleteUser,
|
||||
deleteSelf,
|
||||
getSelf
|
||||
}
|
||||
getSelf,
|
||||
};
|
||||
|
||||
export default AuthController;
|
||||
export default AuthController;
|
||||
|
||||
185
src/controllers/brand.controller.ts
Normal file
185
src/controllers/brand.controller.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
import type IDbBrand from "@interfaces/database/IDbBrand";
|
||||
import BrandService from "@services/brand.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
//import {body} from "express-validator";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "BrandController",
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new brand.
|
||||
*
|
||||
* @param req - The request object containing the brand information.
|
||||
* @param res - The response object used to send the response.
|
||||
*
|
||||
* @returns A Promise that resolves to the response object with a status code and a JSON message indicating the success or failure of brand creation.
|
||||
*/
|
||||
async function createBrand(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbBrand = req.body;
|
||||
const doesExist = await BrandService.getBySlug(`${body.slug_name}`);
|
||||
if (doesExist) {
|
||||
logger.error("Brand already exists");
|
||||
return res.status(400).json({
|
||||
error: "Brand already exists",
|
||||
});
|
||||
}
|
||||
const createResult = await BrandService.create({
|
||||
slug_name: `${body.slug_name}`,
|
||||
display_name: `${body.display_name}`,
|
||||
image_blob: `${body.image_blob}`,
|
||||
});
|
||||
if (!createResult) {
|
||||
logger.error("Failed to create brand");
|
||||
return res.status(500).json({
|
||||
error: "Failed to create brand",
|
||||
});
|
||||
}
|
||||
logger.info(`Brand created successfully ! (${body.slug_name})`);
|
||||
return res.status(201).json({
|
||||
message: "Brand created successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a brand in the database.
|
||||
*
|
||||
* @param {Request} req - The HTTP request object.
|
||||
* @param {Response} res - The HTTP response object.
|
||||
* @return {Promise<Response>} A promise that resolves with the HTTP response.
|
||||
*/
|
||||
async function updateBrand(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbBrand = req.body;
|
||||
const brandSlug = req.params["brandSlug"];
|
||||
if (!brandSlug) {
|
||||
logger.error("Brand slug is missing");
|
||||
return res.status(400).json({
|
||||
error: "Brand slug is missing",
|
||||
});
|
||||
}
|
||||
const doesExist = await BrandService.getBySlug(brandSlug);
|
||||
if (!doesExist) {
|
||||
logger.error("Brand not found");
|
||||
return res.status(404).json({
|
||||
error: "Brand not found",
|
||||
});
|
||||
}
|
||||
const updateResult = await BrandService.update({
|
||||
slug_name: `${body.slug_name}`,
|
||||
display_name: `${body.display_name}`,
|
||||
image_blob: `${body.image_blob}`,
|
||||
});
|
||||
if (!updateResult) {
|
||||
logger.error("Failed to update brand");
|
||||
return res.status(500).json({
|
||||
error: "Failed to update brand",
|
||||
});
|
||||
}
|
||||
logger.info(`Brand updated successfully ! (${brandSlug})`);
|
||||
return res.status(200).json({
|
||||
message: "Brand updated successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a brand by its slug.
|
||||
*
|
||||
* @param {Request} req - The request object containing the brand slug in the parameters.
|
||||
* @param {Response} res - The response object to send the result.
|
||||
* @returns {Promise<Response>} - A promise that resolves to the response with the retrieved brand.
|
||||
*/
|
||||
async function getBySlugBrand(req: Request, res: Response): Promise<Response> {
|
||||
const brandSlug = req.params["brandSlug"];
|
||||
if (!brandSlug) {
|
||||
logger.error("Brand slug is missing");
|
||||
return res.status(400).json({
|
||||
error: "Brand slug is missing",
|
||||
});
|
||||
}
|
||||
const brand = await BrandService.getBySlug(brandSlug);
|
||||
if (!brand) {
|
||||
logger.error("Brand not found");
|
||||
return res.status(404).json({
|
||||
error: "Brand not found",
|
||||
});
|
||||
}
|
||||
logger.info(`Brand retrieved successfully ! (${brandSlug})`);
|
||||
return res.status(200).json(brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all brands.
|
||||
*
|
||||
* @param {Request} _req - The request object.
|
||||
* @param {Response} res - The response object.
|
||||
* @returns {Promise<Response>} - A promise with the response object.
|
||||
*/
|
||||
async function getAllBrand(_req: Request, res: Response): Promise<Response> {
|
||||
const brands = await BrandService.getAll();
|
||||
if (!brands) {
|
||||
logger.error("Failed to retrieve brands");
|
||||
return res.status(500).json({
|
||||
error: "Failed to retrieve brands",
|
||||
});
|
||||
}
|
||||
logger.info("Brands retrieved successfully !");
|
||||
return res.status(200).json({
|
||||
uat: Date.now(),
|
||||
brands: brands,
|
||||
total: brands.length,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a brand.
|
||||
*
|
||||
* @async
|
||||
* @param {Request} req - The request object.
|
||||
* @param {Response} res - The response object.
|
||||
* @returns {Promise<Response>} - The response object indicating the success or failure of the delete operation.
|
||||
*/
|
||||
async function deleteBrand(req: Request, res: Response): Promise<Response> {
|
||||
const brandSlug = req.params["brandSlug"];
|
||||
if (!brandSlug) {
|
||||
logger.error("Brand slug is missing");
|
||||
return res.status(400).json({
|
||||
error: "Brand slug is missing",
|
||||
});
|
||||
}
|
||||
//TODO verify if models linked to brand
|
||||
const doesExist = await BrandService.getBySlug(brandSlug);
|
||||
if (!doesExist) {
|
||||
logger.error("Brand not found");
|
||||
return res.status(404).json({
|
||||
error: "Brand not found",
|
||||
});
|
||||
}
|
||||
const deleteResult = await BrandService.delete(brandSlug);
|
||||
if (!deleteResult) {
|
||||
logger.error("Failed to delete brand");
|
||||
return res.status(500).json({
|
||||
error: "Failed to delete brand",
|
||||
});
|
||||
}
|
||||
logger.info(`Brand deleted successfully ! (${brandSlug})`);
|
||||
return res.status(200).json({
|
||||
message: "Brand deleted successfully",
|
||||
});
|
||||
}
|
||||
|
||||
//TODO get models of the brand
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const BrandController = {
|
||||
create: createBrand,
|
||||
update: updateBrand,
|
||||
getBySlug: getBySlugBrand,
|
||||
getAll: getAllBrand,
|
||||
delete: deleteBrand,
|
||||
};
|
||||
|
||||
export default BrandController;
|
||||
@@ -1,12 +1,13 @@
|
||||
import type {Request, Response} from "express";
|
||||
import {Logger} from "tslog";
|
||||
import type IDbCategory from "@interfaces/database/IDbCategory";
|
||||
import CategoryService from "@services/category.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
//import {validationResult} from "express-validator";
|
||||
|
||||
|
||||
const logger = new Logger({ name: "CategoryController" });
|
||||
|
||||
const logger = new Logger({
|
||||
name: "CategoryController",
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new category.
|
||||
@@ -16,22 +17,28 @@ const logger = new Logger({ name: "CategoryController" });
|
||||
* @returns {Promise<Response>} The response object indicating the outcome of the category creation.
|
||||
*/
|
||||
async function createCategory(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbCategory = req.body
|
||||
const doesExist = await CategoryService.getBySlug(`${body.slug_name}`)
|
||||
const body: IDbCategory = req.body;
|
||||
const doesExist = await CategoryService.getBySlug(`${body.slug_name}`);
|
||||
if (doesExist) {
|
||||
logger.error("Category already exists");
|
||||
return res.status(400).json({ error: "Category already exists" });
|
||||
}
|
||||
logger.error("Category already exists");
|
||||
return res.status(400).json({
|
||||
error: "Category already exists",
|
||||
});
|
||||
}
|
||||
const createResult = await CategoryService.create({
|
||||
display_name: `${body.display_name}`,
|
||||
slug_name: `${body.slug_name}`
|
||||
})
|
||||
slug_name: `${body.slug_name}`,
|
||||
});
|
||||
if (!createResult) {
|
||||
logger.error("Failed to create category");
|
||||
return res.status(500).json({ error: "Failed to create category" });
|
||||
return res.status(500).json({
|
||||
error: "Failed to create category",
|
||||
});
|
||||
}
|
||||
logger.info(`Category created successfully ! (${body.slug_name})`)
|
||||
return res.status(201).json({ message: "Category created successfully" });
|
||||
logger.info(`Category created successfully ! (${body.slug_name})`);
|
||||
return res.status(201).json({
|
||||
message: "Category created successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,29 +49,37 @@ async function createCategory(req: Request, res: Response): Promise<Response> {
|
||||
*
|
||||
* @return {Promise<Response>} - A promise that will be resolved with the result of the update operation.
|
||||
*/
|
||||
async function updateCategory(req: Request, res:Response): Promise<Response> {
|
||||
async function updateCategory(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbCategory = req.body;
|
||||
const categoryId = req.params["categorySlug"];
|
||||
const categoryId = req.params["categorySlug"];
|
||||
if (!categoryId) {
|
||||
logger.error("Category slug is missing");
|
||||
return res.status(400).json({ error: "Category slug is missing" });
|
||||
return res.status(400).json({
|
||||
error: "Category slug is missing",
|
||||
});
|
||||
}
|
||||
const doesExist = await CategoryService.getById(`${categoryId}`)
|
||||
if (!doesExist || !doesExist.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({ error: "Category not found" });
|
||||
}
|
||||
const updateResult = await CategoryService.update({
|
||||
const doesExist = await CategoryService.getById(`${categoryId}`);
|
||||
if (!doesExist || !doesExist.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({
|
||||
error: "Category not found",
|
||||
});
|
||||
}
|
||||
const updateResult = await CategoryService.update({
|
||||
id: doesExist.id,
|
||||
slug_name: `${body.slug_name}`,
|
||||
display_name: `${body.display_name}`
|
||||
})
|
||||
if (!updateResult) {
|
||||
logger.error("Failed to update category");
|
||||
return res.status(500).json({ error: "Failed to update category" });
|
||||
}
|
||||
logger.info(`Category updated successfully! (${categoryId})`);
|
||||
return res.status(200).json({ message: "Category updated successfully" });
|
||||
display_name: `${body.display_name}`,
|
||||
});
|
||||
if (!updateResult) {
|
||||
logger.error("Failed to update category");
|
||||
return res.status(500).json({
|
||||
error: "Failed to update category",
|
||||
});
|
||||
}
|
||||
logger.info(`Category updated successfully! (${categoryId})`);
|
||||
return res.status(200).json({
|
||||
message: "Category updated successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,73 +91,101 @@ async function updateCategory(req: Request, res:Response): Promise<Response> {
|
||||
*/
|
||||
async function deleteCategory(req: Request, res: Response): Promise<Response> {
|
||||
const categorySlug = req.params["categorySlug"];
|
||||
if (!categorySlug) {
|
||||
logger.error("Category slug is missing");
|
||||
return res.status(400).json({ error: "Category slug is missing" });
|
||||
}
|
||||
const doesExist = await CategoryService.getBySlug(`${categorySlug}`);
|
||||
if (!doesExist || !doesExist.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({ error: "Category not found" });
|
||||
}
|
||||
const deleteResult = await CategoryService.delete(`${doesExist.id}`);
|
||||
if (!deleteResult) {
|
||||
logger.error("Failed to delete category");
|
||||
return res.status(500).json({ error: "Failed to delete category" });
|
||||
}
|
||||
logger.info(`Category deleted successfully! (${categorySlug})`);
|
||||
return res.status(200).json({ message: "Category deleted successfully" });
|
||||
if (!categorySlug) {
|
||||
logger.error("Category slug is missing");
|
||||
return res.status(400).json({
|
||||
error: "Category slug is missing",
|
||||
});
|
||||
}
|
||||
const doesExist = await CategoryService.getBySlug(`${categorySlug}`);
|
||||
if (!doesExist || !doesExist.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({
|
||||
error: "Category not found",
|
||||
});
|
||||
}
|
||||
const deleteResult = await CategoryService.delete(`${doesExist.id}`);
|
||||
if (!deleteResult) {
|
||||
logger.error("Failed to delete category");
|
||||
return res.status(500).json({
|
||||
error: "Failed to delete category",
|
||||
});
|
||||
}
|
||||
logger.info(`Category deleted successfully! (${categorySlug})`);
|
||||
return res.status(200).json({
|
||||
message: "Category deleted successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all categories.
|
||||
*
|
||||
* @param _req
|
||||
* @param {Response} res - The response object.
|
||||
* @return {Promise<Response>} - A promise that resolves to the response object.
|
||||
*/
|
||||
async function getAllCategory(res: Response): Promise<Response> {
|
||||
async function getAllCategory(_req: Request, res: Response): Promise<Response> {
|
||||
const categories = await CategoryService.getAll();
|
||||
if (!categories) {
|
||||
logger.error("Failed to get categories");
|
||||
return res.status(500).json({ error: "Failed to get categories" });
|
||||
}
|
||||
logger.info("Categories retrieved successfully");
|
||||
//ToTest categories output type
|
||||
return res.status(200).json({
|
||||
if (!categories) {
|
||||
logger.error("Failed to get categories");
|
||||
return res.status(500).json({
|
||||
error: "Failed to get categories",
|
||||
});
|
||||
}
|
||||
logger.info("Categories retrieved successfully");
|
||||
return res.status(200).json({
|
||||
iat: Date.now(),
|
||||
categories: categories.map((category: IDbCategory) => ({
|
||||
id: category.id,
|
||||
display_name: category.display_name,
|
||||
slug_name: category.slug_name
|
||||
}))
|
||||
id: category.id,
|
||||
display_name: category.display_name,
|
||||
slug_name: category.slug_name,
|
||||
})),
|
||||
total: categories.length,
|
||||
});
|
||||
}
|
||||
|
||||
async function getBySlugCategory(req: Request, res:Response) {
|
||||
/**
|
||||
* Get category by slug
|
||||
*
|
||||
* @param {Request} req - The request object containing category slug
|
||||
* @param {Response} res - The response object to send back the category
|
||||
*
|
||||
* @return {Promise<Response>} - The response with category data or error message
|
||||
*/
|
||||
async function getBySlugCategory(
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<Response> {
|
||||
const categorySlug = req.params["categorySlug"];
|
||||
if (!categorySlug) {
|
||||
logger.error("Category slug is missing");
|
||||
return res.status(400).json({ error: "Category slug is missing" });
|
||||
}
|
||||
const category = await CategoryService.getBySlug(`${categorySlug}`);
|
||||
if (!category || !category.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({ error: "Category not found" });
|
||||
}
|
||||
logger.info(`Category retrieved successfully! (${categorySlug})`);
|
||||
return res.status(200).json({
|
||||
id: category.id,
|
||||
display_name: category.display_name,
|
||||
slug_name: category.slug_name
|
||||
});
|
||||
if (!categorySlug) {
|
||||
logger.error("Category slug is missing");
|
||||
return res.status(400).json({
|
||||
error: "Category slug is missing",
|
||||
});
|
||||
}
|
||||
const category = await CategoryService.getBySlug(`${categorySlug}`);
|
||||
if (!category || !category.id) {
|
||||
logger.error("Category not found");
|
||||
return res.status(404).json({
|
||||
error: "Category not found",
|
||||
});
|
||||
}
|
||||
logger.info(`Category retrieved successfully! (${categorySlug})`);
|
||||
return res.status(200).json({
|
||||
id: category.id,
|
||||
display_name: category.display_name,
|
||||
slug_name: category.slug_name,
|
||||
});
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const CategoryController = {
|
||||
create: createCategory,
|
||||
update: updateCategory,
|
||||
delete: deleteCategory,
|
||||
getAll: getAllCategory,
|
||||
getBySlug: getBySlugCategory
|
||||
}
|
||||
getBySlug: getBySlugCategory,
|
||||
};
|
||||
|
||||
export default CategoryController;
|
||||
export default CategoryController;
|
||||
|
||||
145
src/controllers/model.controller.ts
Normal file
145
src/controllers/model.controller.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import type IDbModel from "@interfaces/database/IDbModel";
|
||||
import CategoryService from "@services/category.service";
|
||||
import ModelService from "@services/model.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
//import {validationResult} from "express-validator";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "ModelController",
|
||||
});
|
||||
|
||||
async function createModel(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbModel = req.body;
|
||||
const doesExist = await CategoryService.getBySlug(`${body.slug_name}`);
|
||||
if (doesExist) {
|
||||
logger.error("Category already exists");
|
||||
return res.status(400).json({
|
||||
error: "Category already exists",
|
||||
});
|
||||
}
|
||||
const createResult = await ModelService.create({
|
||||
display_name: `${body.display_name}`,
|
||||
slug_name: `${body.slug_name}`,
|
||||
category_id: `${body.category_id}`,
|
||||
base_price: Number.parseFloat(`${body.base_price}`),
|
||||
brand_id: `${body.brand_id}`,
|
||||
image_blob: `${body.image_blob}`,
|
||||
is_trending: !!body.is_trending,
|
||||
});
|
||||
if (!createResult) {
|
||||
logger.error("Failed to create category");
|
||||
return res.status(500).json({
|
||||
error: "Failed to create category",
|
||||
});
|
||||
}
|
||||
logger.info(`Category created successfully ! (${body.slug_name})`);
|
||||
return res.status(201).json({
|
||||
message: "Category created successfully",
|
||||
});
|
||||
}
|
||||
|
||||
async function updateModel(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbModel = req.body;
|
||||
const doesExist = await ModelService.getBySlug(`${req.params["modelSlug"]}`);
|
||||
if (!doesExist) {
|
||||
logger.error("Model does not exist");
|
||||
return res.status(404).json({
|
||||
error: "Model does not exist",
|
||||
});
|
||||
}
|
||||
const updateResult = await ModelService.update({
|
||||
id: `${body.id}`,
|
||||
display_name: `${body.display_name}`,
|
||||
slug_name: `${body.slug_name}`,
|
||||
category_id: `${body.category_id}`,
|
||||
base_price: Number.parseFloat(`${body.base_price}`),
|
||||
brand_id: `${body.brand_id}`,
|
||||
image_blob: `${body.image_blob}`,
|
||||
is_trending: !!body.is_trending,
|
||||
});
|
||||
if (!updateResult) {
|
||||
logger.error("Failed to update model");
|
||||
return res.status(500).json({
|
||||
error: "Failed to update model",
|
||||
});
|
||||
}
|
||||
logger.info(`Model updated successfully! (${body.slug_name})`);
|
||||
return res.status(200).json({
|
||||
message: "Model updated successfully",
|
||||
});
|
||||
}
|
||||
|
||||
async function getAllModel(res: Response): Promise<Response> {
|
||||
const models = await ModelService.getAll();
|
||||
if (!models) {
|
||||
logger.error("Failed to get all models");
|
||||
return res.status(500).json({
|
||||
error: "Failed to get all models",
|
||||
});
|
||||
}
|
||||
return res.status(200).json({
|
||||
uat: Date.now(),
|
||||
models: models,
|
||||
total: models.length,
|
||||
});
|
||||
}
|
||||
|
||||
async function getModelBySlug(req: Request, res: Response): Promise<Response> {
|
||||
const slug = req.params["modelSlug"];
|
||||
if (!slug) {
|
||||
logger.error("Invalid slug");
|
||||
return res.status(400).json({
|
||||
error: "Invalid slug",
|
||||
});
|
||||
}
|
||||
const model = await ModelService.getBySlug(slug);
|
||||
if (!model) {
|
||||
logger.error("Model not found");
|
||||
return res.status(404).json({
|
||||
error: "Model not found",
|
||||
});
|
||||
}
|
||||
return res.status(200).json({
|
||||
model,
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteModel(req: Request, res: Response): Promise<Response> {
|
||||
const modelSlug = req.params["modelSlug"];
|
||||
if (!modelSlug) {
|
||||
logger.error("Invalid model slug");
|
||||
return res.status(400).json({
|
||||
error: "Invalid model slug",
|
||||
});
|
||||
}
|
||||
//TODO Check if vehicle related to model
|
||||
const deleteResult = await ModelService.delete(modelSlug);
|
||||
if (!deleteResult) {
|
||||
logger.error("Failed to delete model");
|
||||
return res.status(500).json({
|
||||
error: "Failed to delete model",
|
||||
});
|
||||
}
|
||||
logger.info(`Model deleted successfully! (SLUG: ${modelSlug})`);
|
||||
return res.status(200).json({
|
||||
message: "Model deleted successfully",
|
||||
});
|
||||
}
|
||||
|
||||
//TODO get all vehicles of an model by slug
|
||||
|
||||
//TODO get model with vehicle available.
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const ModelController = {
|
||||
create: createModel,
|
||||
update: updateModel,
|
||||
getAll: getAllModel,
|
||||
getBySlug: getModelBySlug,
|
||||
delete: deleteModel,
|
||||
};
|
||||
|
||||
export default ModelController;
|
||||
150
src/controllers/rent.controller.ts
Normal file
150
src/controllers/rent.controller.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
import RentService from "@services/rent.service";
|
||||
import {HttpStatusCode} from "@interfaces/requests/HttpStatusCode";
|
||||
import type IDbRent from "@interfaces/database/IDbRent";
|
||||
import JwtService from "@services/jwt.service";
|
||||
import UserService from "@services/user.service";
|
||||
|
||||
|
||||
const logger = new Logger({
|
||||
name: "RentController",
|
||||
});
|
||||
|
||||
async function createRent(req: Request, res: Response): Promise<Response> {
|
||||
try {
|
||||
const rentData: IDbRent = req.body;
|
||||
if (!rentData.active || !rentData.need_survey || !rentData.eat || !rentData.iat || !rentData.user_id || !rentData.vehicle_id || !rentData.km_at_start || !rentData.active) {
|
||||
logger.error("Invalid rent data");
|
||||
return res.status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid rent data",
|
||||
});
|
||||
}
|
||||
const rent = await RentService.create(rentData);
|
||||
logger.info(`\n\n> Rent created successfully! (ID: ${rentData.vehicle_id})\n`);
|
||||
return res.status(201).json({
|
||||
message: "Rent created successfully",
|
||||
rent,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Failed to create rent !\n${error}\n`);
|
||||
return res.status(500).json({
|
||||
error: "Failed to create rent",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateRent(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbRent = req.body;
|
||||
if (!body.vehicle_id || !body.user_id || !body.active || !body.need_survey || !body.eat || !body.iat || !body.km_at_start) {
|
||||
logger.error("Invalid rent data");
|
||||
return res.status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid rent data",
|
||||
});
|
||||
}
|
||||
const rentId = req.params["rentId"];
|
||||
if (!rentId || rentId.length !== 36) {
|
||||
logger.error("Invalid rent ID");
|
||||
return res.status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid rent ID",
|
||||
});
|
||||
}
|
||||
const result = await RentService.update({
|
||||
id: rentId,
|
||||
vehicle_id: ``,
|
||||
user_id: ``,
|
||||
active: !!body.active,
|
||||
need_survey: !!body.need_survey,
|
||||
eat: body.eat,
|
||||
iat: body.iat,
|
||||
km_at_start: body.km_at_start,
|
||||
})
|
||||
if (!result) {
|
||||
logger.error(`Failed to update rent with ID: ${rentId}`);
|
||||
return res.status(HttpStatusCode.InternalServerError).json({
|
||||
error: `Failed to update rent with ID: ${rentId}`,
|
||||
});
|
||||
}
|
||||
logger.info(`Rent with ID: ${rentId} updated successfully`);
|
||||
return res.status(HttpStatusCode.Ok).json({
|
||||
message: `Rent with ID: ${rentId} updated successfully`,
|
||||
});
|
||||
}
|
||||
|
||||
async function getAllAssigned(res: Response): Promise<Response> {
|
||||
const rents = await RentService.getAll();
|
||||
if (rents.length === 0) {
|
||||
return res.status(HttpStatusCode.NotFound).json({
|
||||
error: "No assigned rents found",
|
||||
});
|
||||
}
|
||||
return res.status(HttpStatusCode.Ok).json({
|
||||
iat: Date.now(),
|
||||
rents: rents,
|
||||
total: rents.length
|
||||
});
|
||||
}
|
||||
|
||||
async function getAssignedToUser(req: Request, res: Response): Promise<Response> {
|
||||
const authHeader = req.headers.authorization;
|
||||
const bearerToken = authHeader?.split(" ")[1];
|
||||
if (!bearerToken) {
|
||||
logger.warn(`Bearer token not provided (${req.ip})`);
|
||||
return res
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Unauthorized)
|
||||
.json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const payload = await JwtService.verify(bearerToken);
|
||||
if (!payload || !payload.sub) {
|
||||
logger.warn(`Unauthorized access attempt (${req.ip})`);
|
||||
return res
|
||||
.type("application/json")
|
||||
.status(HttpStatusCode.Unauthorized)
|
||||
.json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const sourceUser = await UserService.getFromId(payload.sub);
|
||||
if (!sourceUser) {
|
||||
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
|
||||
error: "You dont exist anymore",
|
||||
});
|
||||
}
|
||||
const userId: string = payload.sub;
|
||||
if (!userId || userId.length !== 36) {
|
||||
logger.error("Invalid user ID");
|
||||
return res.status(HttpStatusCode.BadRequest).json({
|
||||
error: "Invalid user ID",
|
||||
});
|
||||
}
|
||||
let targetId = userId
|
||||
if ("is_admin" in sourceUser && sourceUser.is_admin) {
|
||||
targetId = req.body.targetId || userId
|
||||
}
|
||||
const rents = await RentService.getUserRent(targetId);
|
||||
if (!rents) {
|
||||
return res.status(HttpStatusCode.NotFound).json({
|
||||
error: "No assigned rents found for the user",
|
||||
});
|
||||
}
|
||||
return res.status(HttpStatusCode.Ok).json({
|
||||
iat: Date.now(),
|
||||
rents: rents,
|
||||
total: rents.length,
|
||||
});
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const RentController = {
|
||||
create: createRent,
|
||||
update: updateRent,
|
||||
getAll: getAllAssigned,
|
||||
getAssignedToUser,
|
||||
};
|
||||
|
||||
export default RentController;
|
||||
174
src/controllers/vehicle.controller.ts
Normal file
174
src/controllers/vehicle.controller.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
|
||||
import VehicleService from "@services/vehicle.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import type { Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
//import {validationResult} from "express-validator";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "VehicleController",
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new vehicle in the database.
|
||||
*
|
||||
* @param {Request} req - The request object containing the vehicle details in the body.
|
||||
* @param {Response} res - The response object used to send the result of the operation.
|
||||
* @returns {Promise<Response>} The response with the result of the operation.
|
||||
*/
|
||||
async function createVehicle(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbVehicle = req.body;
|
||||
const createResult = await VehicleService.create({
|
||||
plate_number: `${body.plate_number}`,
|
||||
model_id: `${body.plate_number}`,
|
||||
odometer: Number.parseInt(`${body.odometer}`),
|
||||
health_state: Number.parseInt(`${body.health_state}`),
|
||||
});
|
||||
if (!createResult) {
|
||||
logger.error("Failed to create vehicle");
|
||||
return res.status(500).json({
|
||||
error: "Failed to create vehicle",
|
||||
});
|
||||
}
|
||||
logger.info(`Vehicle created successfully ! (${body.plate_number})`);
|
||||
return res.status(201).json({
|
||||
message: "Vehicle created successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a vehicle in the database.
|
||||
*
|
||||
* @param {Request} req - The request object containing the vehicle data in the request body and the vehicle ID in the request parameters.
|
||||
* @param {Response} res - The response object used to send the result of the update operation.
|
||||
*
|
||||
* @return {Promise<Response>} A promise that resolves to the response object with a status and a JSON body indicating the result of the update operation.
|
||||
*/
|
||||
async function updateVehicle(req: Request, res: Response): Promise<Response> {
|
||||
const body: IDbVehicle = req.body;
|
||||
const vehicleId = req.params["vehicleId"];
|
||||
if (!vehicleId || vehicleId.length !== 36) {
|
||||
if (isDebugMode()) logger.error("Vehicle ID is missing");
|
||||
return res.status(400).json({
|
||||
error: "Vehicle ID is missing or not valid",
|
||||
});
|
||||
}
|
||||
const updateResult = await VehicleService.update({
|
||||
plate_number: `${body.plate_number}`,
|
||||
model_id: `${body.plate_number}`,
|
||||
odometer: Number.parseInt(`${body.odometer}`),
|
||||
health_state: Number.parseInt(`${body.health_state}`),
|
||||
});
|
||||
if (!updateResult) {
|
||||
logger.error("Failed to update vehicle");
|
||||
return res.status(500).json({
|
||||
error: "Failed to update vehicle",
|
||||
});
|
||||
}
|
||||
logger.info(`Vehicle updated successfully ! (${body.plate_number})`);
|
||||
return res.status(200).json({
|
||||
message: "Vehicle updated successfully",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all vehicles from the vehicle service.
|
||||
*
|
||||
* @param {Response} res - The response object from the client.
|
||||
* @returns {Promise<Response>} A promise that resolves to a response object containing the result of the operation. The result is a JSON object containing all vehicles.
|
||||
*/
|
||||
async function getAllVehicle(res: Response): Promise<Response> {
|
||||
const getAllVehicleResult = await VehicleService.getAll();
|
||||
if (!getAllVehicleResult) {
|
||||
logger.error("Failed to get all vehicles");
|
||||
return res.status(500).json({
|
||||
error: "Failed to get all vehicles",
|
||||
});
|
||||
}
|
||||
return res.status(200).json(getAllVehicleResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the available vehicles from the VehicleService
|
||||
*
|
||||
* @param {Response} res - The Response object to send the result to
|
||||
* @returns {Promise<Response<any, Record<string, any>>>} - A promise that resolves to a Response object containing the available vehicles or an error message
|
||||
*/
|
||||
async function getAvailableVehicle(res: Response): Promise<Response<any, Record<string, any>>> {
|
||||
const getAvailableVehicleResult = await VehicleService.getAvailable();
|
||||
if (!getAvailableVehicleResult) {
|
||||
logger.error("Failed to get available vehicles");
|
||||
return res.status(500).json({
|
||||
error: "Failed to get available vehicles",
|
||||
});
|
||||
}
|
||||
return res.status(200).json(getAvailableVehicleResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a vehicle by its ID.
|
||||
*
|
||||
* @param {Request} req - The request object containing the vehicle ID.
|
||||
* @param {Response} res - The response object used to send the result.
|
||||
*
|
||||
* @return {Promise<Response>} A promise that resolves to the result of the retrieval operation.
|
||||
*/
|
||||
async function getVehicleById(req: Request, res: Response): Promise<Response> {
|
||||
const vehicleId = req.params["vehicleId"];
|
||||
if (!vehicleId || vehicleId.length !== 36) {
|
||||
if (isDebugMode()) logger.error("Vehicle ID is missing or not valid");
|
||||
return res.status(400).json({
|
||||
error: "Vehicle ID is missing or not valid",
|
||||
});
|
||||
}
|
||||
const getVehicleResult = await VehicleService.getById(vehicleId);
|
||||
if (!getVehicleResult) {
|
||||
logger.error(`Failed to get vehicle by ID: ${vehicleId}`);
|
||||
return res.status(500).json({
|
||||
error: `Failed to get vehicle by ID: ${vehicleId}`,
|
||||
});
|
||||
}
|
||||
return res.status(200).json(getVehicleResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a vehicle.
|
||||
*
|
||||
* @param {Object} req - The request object.
|
||||
* @param {Object} res - The response object.
|
||||
*
|
||||
* @return {Promise<Response>} A promise that resolves to the response JSON object.
|
||||
*/
|
||||
async function deleteVehicle(req: Request, res: Response): Promise<Response> {
|
||||
const vehicleId = req.params["vehicleId"];
|
||||
if (!vehicleId || vehicleId.length !== 36) {
|
||||
if (isDebugMode()) logger.error("Vehicle ID is missing or not valid");
|
||||
return res.status(400).json({
|
||||
error: "Vehicle ID is missing or not valid",
|
||||
});
|
||||
}
|
||||
const deleteResult = await VehicleService.delete(vehicleId);
|
||||
if (!deleteResult) {
|
||||
logger.error(`Failed to delete vehicle with ID: ${vehicleId}`);
|
||||
return res.status(500).json({
|
||||
error: `Failed to delete vehicle with ID: ${vehicleId}`,
|
||||
});
|
||||
}
|
||||
logger.info(`Vehicle deleted successfully ! (ID: ${vehicleId})`);
|
||||
return res.status(200).json({
|
||||
message: "Vehicle deleted successfully",
|
||||
});
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nController loaded.");
|
||||
|
||||
const VehicleController = {
|
||||
create: createVehicle,
|
||||
update: updateVehicle,
|
||||
getAll: getAllVehicle,
|
||||
delete: deleteVehicle,
|
||||
getById: getVehicleById,
|
||||
getAvailable: getAvailableVehicle,
|
||||
};
|
||||
|
||||
export default VehicleController;
|
||||
@@ -3,4 +3,4 @@ export interface IReqEditUserData {
|
||||
lastName?: string;
|
||||
displayName?: string;
|
||||
password?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ interface DbUserData {
|
||||
isDisabled: boolean;
|
||||
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpires?: Date;
|
||||
resetPasswordExpires?: Date;
|
||||
|
||||
dob: Date;
|
||||
gdpr: Date;
|
||||
@@ -20,4 +20,4 @@ interface DbUserData {
|
||||
uat: Date;
|
||||
}
|
||||
|
||||
export default DbUserData
|
||||
export default DbUserData;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export interface IDbBrand {
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string;
|
||||
image_blob: BinaryType;
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string;
|
||||
image_blob: BinaryType;
|
||||
}
|
||||
|
||||
export default IDbBrand;
|
||||
export default IDbBrand;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface IDbCategory {
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export default IDbCategory;
|
||||
export default IDbCategory;
|
||||
|
||||
54
src/interfaces/database/IDbFactorize.ts
Normal file
54
src/interfaces/database/IDbFactorize.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Represents the output of the factorization function.
|
||||
*/
|
||||
export interface IDbFactorizeOutput {
|
||||
/**
|
||||
* Description: The variable `_valuesArray` is an array that can contain values of type `string`, `boolean`, `number`, or `Date`.
|
||||
* (The value associated with the keys of `_keysTemplate`)
|
||||
*
|
||||
* @type {Array<string | boolean | number | Date>}
|
||||
*/
|
||||
_valuesArray: Array<string | boolean | number | Date>;
|
||||
/**
|
||||
* Represents the SQL Query template for the keys.
|
||||
* @type {string}
|
||||
*/
|
||||
_keysTemplate: string;
|
||||
|
||||
/**
|
||||
* The list of ? for the "VALUE" section
|
||||
*/
|
||||
_questionMarksFields: string;
|
||||
|
||||
/**
|
||||
* The total number of fields.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
totalFields: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface IDbFactorizeInput represents the input required to factorize a SQL query.
|
||||
*/
|
||||
export interface IDbFactorizeInput {
|
||||
/**
|
||||
* An object containing values that will be in a SQL Query.
|
||||
*
|
||||
* @type {Array<string | boolean | number | Date>}
|
||||
*/
|
||||
values: object;
|
||||
/**
|
||||
* Represents the name of the action that will result of the prepared SQL Query.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
actionName: string;
|
||||
/**
|
||||
* Indicates whether an error should be thrown when encountering an error.
|
||||
* If set to true, an error will be thrown. If set to false or not provided, the error will not be thrown.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
throwOnError?: true;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
export interface IDbModel {
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string;
|
||||
brand_id: string;
|
||||
category_id: string;
|
||||
image_blob: BinaryType;
|
||||
is_trending: boolean;
|
||||
base_price: number;
|
||||
id?: string;
|
||||
slug_name: string;
|
||||
display_name: string;
|
||||
brand_id: string;
|
||||
category_id: string;
|
||||
image_blob: BinaryType;
|
||||
is_trending: boolean;
|
||||
base_price: number;
|
||||
}
|
||||
|
||||
export default IDbModel;
|
||||
export default IDbModel;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
export interface IDbRent {
|
||||
vehicle_id: string;
|
||||
user_id: string;
|
||||
active: boolean;
|
||||
iat: Date;
|
||||
eat: Date;
|
||||
need_survey: boolean;
|
||||
km_at_start: number
|
||||
id?: string;
|
||||
vehicle_id: string;
|
||||
user_id: string;
|
||||
active: boolean;
|
||||
iat: Date;
|
||||
eat: Date;
|
||||
need_survey: boolean;
|
||||
km_at_start: number;
|
||||
}
|
||||
|
||||
export default IDbRent;
|
||||
export default IDbRent;
|
||||
|
||||
9
src/interfaces/database/IDbStatusResult.ts
Normal file
9
src/interfaces/database/IDbStatusResult.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface IDbStatusResult {
|
||||
fieldCount: number;
|
||||
affectedRows: number;
|
||||
insertId: number;
|
||||
info: string;
|
||||
serverStatus: number;
|
||||
warningStatus: number;
|
||||
changedRows: number;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
export interface IDbUser {
|
||||
id?: string;
|
||||
username: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
dob: Date;
|
||||
email: string;
|
||||
is_mail_verified: boolean;
|
||||
is_admin: boolean;
|
||||
gdpr: Date;
|
||||
hash: string
|
||||
}
|
||||
id?: string;
|
||||
username: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
dob: Date;
|
||||
email: string;
|
||||
is_email_verified: boolean;
|
||||
is_admin: boolean;
|
||||
gdpr: Date;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface IDbVehicle {
|
||||
id?: string;
|
||||
plate_number: string;
|
||||
model_id: string;
|
||||
odometer: number;
|
||||
health_state: number;
|
||||
isAvailable?: boolean;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from './UserData'
|
||||
export * from "./UserData";
|
||||
|
||||
269
src/interfaces/requests/HttpStatusCode.ts
Normal file
269
src/interfaces/requests/HttpStatusCode.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
export enum HttpStatusCode {
|
||||
/**
|
||||
* Status code for a request indicating that the server received the request headers
|
||||
* and the client should proceed to send the request body.
|
||||
* @enum {number}
|
||||
*/
|
||||
Continue = 100,
|
||||
|
||||
/**
|
||||
* Status code for a request indicating that the requester has asked the server to switch protocols.
|
||||
* @enum {number}
|
||||
*/
|
||||
SwitchingProtocols = 101,
|
||||
|
||||
/**
|
||||
* Status code for a request indicating that the server has received and is processing the request,
|
||||
* but no response is available yet.
|
||||
* @enum {number}
|
||||
*/
|
||||
Processing = 102,
|
||||
|
||||
/**
|
||||
* Successful HTTP response
|
||||
* @enum {number}
|
||||
*/
|
||||
Ok = 200,
|
||||
/**
|
||||
* Request has been fulfilled; new resource created as a result.
|
||||
* @enum {number}
|
||||
*/
|
||||
Created = 201,
|
||||
/**
|
||||
* Request accepted, but not yet processed.
|
||||
* @enum {number}
|
||||
*/
|
||||
Accepted = 202,
|
||||
/**
|
||||
* Request successful. Meta-information returned is from original server, not local.
|
||||
* @enum {number}
|
||||
*/
|
||||
NonAuthoritativeInformation = 203,
|
||||
/**
|
||||
* Request processed. No content returned.
|
||||
* @enum {number}
|
||||
*/
|
||||
NoContent = 204,
|
||||
/**
|
||||
* Server specifies part of document should be reset.
|
||||
* @enum {number}
|
||||
*/
|
||||
ResetContent = 205,
|
||||
/**
|
||||
* Partial content returned due to GET.
|
||||
* @enum {number}
|
||||
*/
|
||||
PartialContent = 206,
|
||||
/**
|
||||
* Multiple Resource Meta-Data: Could be used for collection instances.
|
||||
* @enum {number}
|
||||
*/
|
||||
MultiStatus = 207,
|
||||
/**
|
||||
* Status code used to indicate that a certain request has been already reported.
|
||||
* @enum {number}
|
||||
*/
|
||||
AlreadyReported = 208,
|
||||
/**
|
||||
* The server has fulfilled the request for the content, and the content is being conveyed in a manner described by the Content-Encoding, Content-Range, and Content-Type headers.
|
||||
* @enum {number}
|
||||
*/
|
||||
ImUsed = 226,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for multiple choices
|
||||
*/
|
||||
MultipleChoices = 300,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for moved permanently
|
||||
*/
|
||||
MovedPermanently = 301,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for found
|
||||
*/
|
||||
Found = 302,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for see other
|
||||
*/
|
||||
SeeOther = 303,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for not modified
|
||||
*/
|
||||
NotModified = 304,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for use proxy
|
||||
*/
|
||||
UseProxy = 305,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for temporary redirect
|
||||
*/
|
||||
TemporaryRedirect = 307,
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @description HTTP status code for permanent redirect
|
||||
*/
|
||||
PermanentRedirect = 308,
|
||||
|
||||
/** @description Client error: The server could not understand the request due to invalid syntax. */
|
||||
BadRequest = 400,
|
||||
|
||||
/** @description Client error: The client must authenticate itself to get the requested response. */
|
||||
Unauthorized = 401,
|
||||
|
||||
/** @description Client error: This response code is reserved for future use. */
|
||||
PaymentRequired = 402,
|
||||
|
||||
/** @description Client error: The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. */
|
||||
Forbidden = 403,
|
||||
|
||||
/** @description Client error: The server cannot find requested resource. */
|
||||
NotFound = 404,
|
||||
|
||||
/** @description Client error: The request method is not supported by the server and cannot be handled. */
|
||||
MethodNotAllowed = 405,
|
||||
|
||||
/** @description Client error: This response is sent when the web server, after performing server-driven content negotiation, doesn't find any content following the criteria given by the user agent. */
|
||||
NotAcceptable = 406,
|
||||
|
||||
/** @description Client error: This is similar to 401 (Unauthorised), but indicates that the client must authenticate itself to get the requested response. */
|
||||
ProxyAuthenticationRequired = 407,
|
||||
|
||||
/** @description Client error: This response is sent on an idle connection by some servers, even without any previous request by the client. */
|
||||
RequestTimeout = 408,
|
||||
|
||||
/** @description Client error: This response is sent when a request conflicts with the current state of the server. */
|
||||
Conflict = 409,
|
||||
|
||||
/** @description Client error: This response is sent when the requested content has been permanently deleted from server, with no forwarding address. */
|
||||
Gone = 410,
|
||||
|
||||
/** @description Client error: The server refuses to accept the request without a defined Content- Length. */
|
||||
LengthRequired = 411,
|
||||
|
||||
/** @description Client error: The precondition given in the request evaluated to false by the server. */
|
||||
PreconditionFailed = 412,
|
||||
|
||||
/** @description Client error: Request entity is larger than limits defined by server. */
|
||||
PayloadTooLarge = 413,
|
||||
|
||||
/** @description Client error: The URI requested by the client is longer than the server is willing to interpret. */
|
||||
UriTooLong = 414,
|
||||
|
||||
/** @description Client error: The media format of the requested data is not supported by the server, so the server is rejecting the request. */
|
||||
UnsupportedMediaType = 415,
|
||||
|
||||
/** @description Client error: The range specified by the Range header field in the request can't be fulfilled. */
|
||||
RangeNotSatisfiable = 416,
|
||||
|
||||
/** @description Client error: This response code means the expectation indicated by the Expect request header field can't be met by the server. */
|
||||
ExpectationFailed = 417,
|
||||
|
||||
/** @description Client error: The server refuses to brew coffee because it is, permanently, a teapot. */
|
||||
ImATeapot = 418,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name MisdirectedRequest
|
||||
* @description Represents HTTP status code 421: Misdirected Request.
|
||||
* The client should switch to a different protocol such as TLS/1.0.
|
||||
*/
|
||||
MisdirectedRequest = 421,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name UnprocessableEntity
|
||||
* @description Represents HTTP status code 422: Unprocessable Entity.
|
||||
* The request was well-formed but was unable to be followed due to semantic errors.
|
||||
*/
|
||||
UnprocessableEntity = 422,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name Locked
|
||||
* @description Represents HTTP status code 423: Locked.
|
||||
* The resource that is being accessed is locked.
|
||||
*/
|
||||
Locked = 423,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name FailedDependency
|
||||
* @description Represents HTTP status code 424: Failed Dependency.
|
||||
* The request failed because it depended on another request and that request failed.
|
||||
*/
|
||||
FailedDependency = 424,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name TooEarly
|
||||
* @description Represents HTTP status code 425: Too Early.
|
||||
* Indicates that the server is unwilling to risk processing a request that might be replayed.
|
||||
*/
|
||||
TooEarly = 425,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name UpgradeRequired
|
||||
* @description Represents HTTP status code 426: Upgrade Required.
|
||||
* The client should switch to a different protocol.
|
||||
*/
|
||||
UpgradeRequired = 426,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name PreconditionRequired
|
||||
* @description Represents HTTP status code 428: Precondition Required.
|
||||
* The client must first fulfill certain precondition.
|
||||
*/
|
||||
PreconditionRequired = 428,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name TooManyRequests
|
||||
* @description Represents HTTP status code 429: Too Many Requests.
|
||||
* The user has sent too many requests in a certain amount of time.
|
||||
*/
|
||||
TooManyRequests = 429,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name RequestHeaderFieldsTooLarge
|
||||
* @description Represents HTTP status code 431: Request Header Fields Too Large.
|
||||
* The server is unwilling to process the request because its header fields are too large.
|
||||
*/
|
||||
RequestHeaderFieldsTooLarge = 431,
|
||||
|
||||
/**
|
||||
* @enum
|
||||
* @name UnavailableForLegalReasons
|
||||
* @description Represents HTTP status code 451: Unavailable For Legal Reasons.
|
||||
* The server is denying access for legal reasons.
|
||||
*/
|
||||
UnavailableForLegalReasons = 451,
|
||||
|
||||
InternalServerError = 500,
|
||||
NotImplemented = 501,
|
||||
BadGateway = 502,
|
||||
ServiceUnavailable = 503,
|
||||
GatewayTimeout = 504,
|
||||
HttpVersionNotSupported = 505,
|
||||
VariantAlsoNegotiates = 506,
|
||||
InsufficientStorage = 507,
|
||||
LoopDetected = 508,
|
||||
NotExtended = 510,
|
||||
NetworkAuthenticationRequired = 511,
|
||||
}
|
||||
4
src/interfaces/requests/IReqLogin.ts
Normal file
4
src/interfaces/requests/IReqLogin.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface IReqLogin {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
9
src/interfaces/requests/IReqRegister.ts
Normal file
9
src/interfaces/requests/IReqRegister.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface IReqRegister {
|
||||
username: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
/*dob: Date;*/
|
||||
email: string;
|
||||
gdpr?: boolean;
|
||||
password: string;
|
||||
}
|
||||
25
src/interfaces/services/ISError.ts
Normal file
25
src/interfaces/services/ISError.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Represents an error object.
|
||||
*
|
||||
* @interface ISError
|
||||
*/
|
||||
export interface ISError {
|
||||
error: ErrorType;
|
||||
message: string;
|
||||
result?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the types of errors that can occur in the application.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
export enum ErrorType {
|
||||
InvalidData = 0,
|
||||
DatabaseError = 1,
|
||||
ServiceError = 2,
|
||||
NotFound = 3,
|
||||
PasswordInvalid = 4,
|
||||
UnAuthorized = 5,
|
||||
UnexpectedError = 6,
|
||||
}
|
||||
8
src/interfaces/services/IUserUpdate.ts
Normal file
8
src/interfaces/services/IUserUpdate.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export interface IUserUpdate {
|
||||
id?: string;
|
||||
username?: string;
|
||||
firstname?: string;
|
||||
lastname?: string;
|
||||
dob?: Date;
|
||||
gdpr?: Date;
|
||||
}
|
||||
30
src/routes/auth/authRouter.ts
Normal file
30
src/routes/auth/authRouter.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import AuthController from "@controllers/auth.controller";
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
import express, { type Router } from "express";
|
||||
|
||||
const AuthRouter: Router = express.Router();
|
||||
|
||||
AuthRouter.route("/login").post(AuthController.login);
|
||||
AuthRouter.route("/register").post(AuthController.register);
|
||||
|
||||
// PATCH
|
||||
//TODO - To test
|
||||
AuthRouter.route("/me").patch(UserGuard, AuthController.editUser);
|
||||
|
||||
// GET
|
||||
AuthRouter.route("/me").get(UserGuard, AuthController.getSelf);
|
||||
|
||||
// DELETE
|
||||
AuthRouter.route("/me").delete(UserGuard, AuthController.deleteSelf);
|
||||
|
||||
// GET
|
||||
AuthRouter.route("/all").get(AdminGuard, AuthController.getAllUsers);
|
||||
|
||||
// GET
|
||||
AuthRouter.route("/user/:targetId")
|
||||
.get(AdminGuard, AuthController.getUser)
|
||||
.patch(AdminGuard, AuthController.editUser)
|
||||
.delete(AdminGuard, AuthController.deleteUser);
|
||||
|
||||
export default AuthRouter;
|
||||
@@ -1,38 +0,0 @@
|
||||
import express, {type Router} from "express";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import AuthController from "@controllers/auth.controller";
|
||||
|
||||
|
||||
const router: Router = express.Router();
|
||||
|
||||
router.route('/login').post(AuthController.login)
|
||||
router.route('/register').post(AuthController.register)
|
||||
|
||||
// PATCH
|
||||
//TODO - To test
|
||||
router.route('/me')
|
||||
.patch(UserGuard, AuthController.editUser)
|
||||
|
||||
// GET
|
||||
router.route('/me')
|
||||
.get(UserGuard, AuthController.getSelf)
|
||||
|
||||
// DELETE
|
||||
router.route('/me')
|
||||
.delete(UserGuard, AuthController.deleteSelf)
|
||||
|
||||
|
||||
// GET
|
||||
router.route('/all')
|
||||
.get(AdminGuard, AuthController.getAllUsers)
|
||||
|
||||
|
||||
// GET
|
||||
router.route('/user/:targetId')
|
||||
.get(AdminGuard, AuthController.getUser)
|
||||
.patch(AdminGuard, AuthController.editUser)
|
||||
.delete(AdminGuard, AuthController.deleteUser)
|
||||
|
||||
|
||||
export default router
|
||||
41
src/routes/catalog/catalogRouter.ts
Normal file
41
src/routes/catalog/catalogRouter.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import BrandController from "@controllers/brand.controller";
|
||||
import CategoryController from "@controllers/category.controller";
|
||||
import ModelController from "@controllers/model.controller";
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
import express, { type Router } from "express";
|
||||
|
||||
const CatalogRouter: Router = express.Router();
|
||||
|
||||
//-- MODELS >>
|
||||
|
||||
CatalogRouter.route("/model/new").get(AdminGuard, ModelController.create);
|
||||
|
||||
CatalogRouter.route("/model/all").get(ModelController.getAll);
|
||||
|
||||
CatalogRouter.route("/model/:modelSlug")
|
||||
.get(UserGuard, ModelController.getBySlug)
|
||||
.patch(AdminGuard, ModelController.update)
|
||||
.delete(AdminGuard, ModelController.delete);
|
||||
|
||||
//-- CATEGORY >>
|
||||
|
||||
CatalogRouter.route("/category/new").get(AdminGuard, CategoryController.create);
|
||||
|
||||
CatalogRouter.route("/category/all").get(CategoryController.getAll);
|
||||
|
||||
CatalogRouter.route("/category/:categorySlug")
|
||||
.get(UserGuard, CategoryController.getBySlug)
|
||||
.patch(AdminGuard, CategoryController.update)
|
||||
.delete(AdminGuard, CategoryController.delete);
|
||||
|
||||
//-- BRAND >>
|
||||
|
||||
CatalogRouter.route("/brand/new").post(AdminGuard, BrandController.create);
|
||||
CatalogRouter.route("/brand/all").get(BrandController.getAll);
|
||||
CatalogRouter.route("/brand/:brandSlug")
|
||||
.get(UserGuard, BrandController.getBySlug)
|
||||
.patch(AdminGuard, BrandController.update)
|
||||
.delete(AdminGuard, BrandController.delete);
|
||||
|
||||
export default CatalogRouter;
|
||||
@@ -1,40 +0,0 @@
|
||||
import express, {type Router} from "express";
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
import CategoryController from "@controllers/category.controller";
|
||||
|
||||
|
||||
const router: Router = express.Router();
|
||||
|
||||
//-- MODELS >>
|
||||
|
||||
router.route('/model/new').get(AdminGuard)
|
||||
|
||||
router.route('/model/all').get()
|
||||
|
||||
router.route('/model/:modelSlug')
|
||||
.get(UserGuard)
|
||||
.patch(AdminGuard)
|
||||
.delete(AdminGuard)
|
||||
|
||||
|
||||
//-- CATEGORY >>
|
||||
|
||||
router.route('/category/new').get(AdminGuard, CategoryController.create)
|
||||
|
||||
router.route('/category/all').get(CategoryController.getAll)
|
||||
|
||||
router.route('/category/:categorySlug')
|
||||
.get(UserGuard, CategoryController.getBySlug)
|
||||
.patch(AdminGuard, CategoryController.update)
|
||||
.delete(AdminGuard, CategoryController.delete)
|
||||
|
||||
|
||||
//-- BRAND >>
|
||||
|
||||
router.route('/brand/new').post(AdminGuard)
|
||||
router.route('/brand/all').get()
|
||||
router.route('/brand/:brandSlug')
|
||||
.get(UserGuard)
|
||||
.patch(AdminGuard)
|
||||
.delete(AdminGuard)
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './auth/router'
|
||||
export * from './catalog/router'
|
||||
export * from './rent/router'
|
||||
export * from "src/routes/auth/authRouter";
|
||||
export * from "src/routes/catalog/catalogRouter";
|
||||
export * from "src/routes/rent/rentRouter";
|
||||
|
||||
29
src/routes/rent/rentRouter.ts
Normal file
29
src/routes/rent/rentRouter.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
import express, { type Router } from "express";
|
||||
import VehicleController from "@controllers/vehicle.controller";
|
||||
import RentController from "@controllers/rent.controller";
|
||||
|
||||
const RentRouter: Router = express.Router();
|
||||
|
||||
// Get rent affected to the user
|
||||
RentRouter.route("/affected").get(UserGuard, RentController.getAssignedToUser);
|
||||
|
||||
// Get all vehicle in rent (admin only)
|
||||
RentRouter.route("/affected/all").get(AdminGuard, RentController.getAll);
|
||||
|
||||
// Add a new vehicle (admin only)
|
||||
RentRouter.route("/veh/new").post(AdminGuard, VehicleController.create);
|
||||
|
||||
// Get all vehicles
|
||||
RentRouter.route("/veh/all").get(VehicleController.getAll);
|
||||
|
||||
// Rent a specific vehicle
|
||||
RentRouter.route("/veh/rent/:vehicleId").post(UserGuard);
|
||||
|
||||
RentRouter.route("/veh/:vehicleId")
|
||||
.get(UserGuard, VehicleController.getById)
|
||||
.patch(AdminGuard, VehicleController.update)
|
||||
.delete(AdminGuard, VehicleController.delete);
|
||||
|
||||
export default RentRouter;
|
||||
@@ -1,32 +0,0 @@
|
||||
import express, {type Router} from "express";
|
||||
import AdminGuard from "@validators/AdminGuard";
|
||||
import UserGuard from "@validators/UserGuard";
|
||||
|
||||
|
||||
const router: Router = express.Router();
|
||||
|
||||
// Get rent affected to the user
|
||||
router.route('/affected')
|
||||
.get(UserGuard)
|
||||
|
||||
// Get all vehicle in rent (admin only)
|
||||
router.route('/affected/all')
|
||||
.get(AdminGuard)
|
||||
|
||||
// Add a new vehicle (admin only)
|
||||
router.route('/veh/new')
|
||||
.post(AdminGuard)
|
||||
|
||||
// Get all vehicles
|
||||
router.route('/veh/all')
|
||||
.get()
|
||||
|
||||
// Rent a specific vehicle
|
||||
router.route('/veh/rent/:vehicleId')
|
||||
.post(UserGuard)
|
||||
|
||||
|
||||
router.route('/veh/:vehicleId')
|
||||
.get(UserGuard)
|
||||
.patch(AdminGuard)
|
||||
.delete(AdminGuard)
|
||||
183
src/services/brand.service.ts
Normal file
183
src/services/brand.service.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import type IDbBrand from "@interfaces/database/IDbBrand";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const DbHandler = new MysqlService.Handler("BrandService");
|
||||
const logger = new Logger({
|
||||
name: "BrandService",
|
||||
});
|
||||
|
||||
//SEC todo Blob validation
|
||||
/**
|
||||
* Creates a brand in the database with the given data.
|
||||
*
|
||||
* @param {IDbBrand} data - The data of the brand to be created.
|
||||
* @return {Promise<unknown>} A promise that resolves to the result of the operation.
|
||||
*/
|
||||
async function createBrand(data: IDbBrand): Promise<unknown> {
|
||||
const doesExist = await MysqlService.Brand.getBySlug(
|
||||
DbHandler,
|
||||
data.slug_name,
|
||||
);
|
||||
if (doesExist) {
|
||||
logger.error(`Brand already exists (${data.slug_name})`);
|
||||
return {
|
||||
error: "exist",
|
||||
};
|
||||
}
|
||||
const brandId = uuidv4();
|
||||
const createdBrand = await MysqlService.Brand.insert(DbHandler, {
|
||||
id: brandId,
|
||||
slug_name: `${data.slug_name}`,
|
||||
display_name: `${data.display_name}`,
|
||||
image_blob: data.image_blob,
|
||||
});
|
||||
if (createdBrand) {
|
||||
logger.info(`Brand created successfully (${data.slug_name})`);
|
||||
return {
|
||||
success: true,
|
||||
brand: createdBrand,
|
||||
};
|
||||
}
|
||||
logger.error(`Failed to create brand (${data.slug_name})`);
|
||||
return {
|
||||
error: "failed",
|
||||
};
|
||||
}
|
||||
|
||||
//SEC todo Blob validation
|
||||
/**
|
||||
* Updates a brand in the database.
|
||||
*
|
||||
* @param {IDbBrand} data - The brand data to update.
|
||||
* @returns {Promise<boolean>} - Indicates whether the update was successful or not.
|
||||
*/
|
||||
async function updateBrand(data: IDbBrand): Promise<boolean> {
|
||||
if (!data.id) {
|
||||
logger.error("Brand ID is missing");
|
||||
return false;
|
||||
}
|
||||
const doesExist = await MysqlService.Brand.getBySlug(
|
||||
DbHandler,
|
||||
data.slug_name,
|
||||
);
|
||||
if (doesExist && doesExist.id !== data.id) {
|
||||
logger.error(`Brand already exists (${data.slug_name})`);
|
||||
return false;
|
||||
}
|
||||
const updatedBrand = await MysqlService.Brand.update(DbHandler, {
|
||||
id: data.id,
|
||||
slug_name: `${data.slug_name}`,
|
||||
display_name: `${data.display_name}`,
|
||||
image_blob: data.image_blob,
|
||||
});
|
||||
if (updatedBrand) {
|
||||
logger.info(`Brand updated successfully (${data.slug_name})`);
|
||||
return true;
|
||||
}
|
||||
logger.error(`Failed to update brand (${data.slug_name})`);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all brands from the database.
|
||||
* @returns {Promise<Array<IDbBrand>|false>} - An array of IDbBrand objects if successful, false otherwise.
|
||||
*/
|
||||
async function getAllBrand(): Promise<Array<IDbBrand> | false> {
|
||||
const brands = await MysqlService.Brand.getAll(DbHandler);
|
||||
if (!brands) {
|
||||
logger.error("Failed to retrieve brands");
|
||||
return false;
|
||||
}
|
||||
logger.info(`Retrieved all brands successfully (${brands.length})`);
|
||||
return brands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a brand by its slug.
|
||||
*
|
||||
* @param {string} brandSlug - The slug of the brand.
|
||||
* @returns {Promise<IDbBrand|false>} - A promise that resolves to the retrieved brand object or false if the brand is not found.
|
||||
*/
|
||||
async function getBySlugBrand(brandSlug: string): Promise<IDbBrand | false> {
|
||||
if (!brandSlug) {
|
||||
logger.error("Brand slug is missing");
|
||||
return false;
|
||||
}
|
||||
const brand = await MysqlService.Brand.getBySlug(DbHandler, brandSlug);
|
||||
if (!brand) {
|
||||
logger.error(`Brand not found (${brandSlug})`);
|
||||
return false;
|
||||
}
|
||||
logger.info(`Retrieved brand by slug successfully (${brandSlug})`);
|
||||
return brand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a brand from the database based on the provided brand ID.
|
||||
*
|
||||
* @param {string} brandId - The ID of the brand to retrieve.
|
||||
*
|
||||
* @returns {Promise<IDbBrand | false>} A promise that resolves to the retrieved brand object, or false if the brand is not found or the ID is invalid.
|
||||
*/
|
||||
async function getByIdBrand(brandId: string): Promise<IDbBrand | false> {
|
||||
if (!brandId) {
|
||||
logger.error("Brand ID is missing");
|
||||
return false;
|
||||
}
|
||||
if (brandId.length !== 36) {
|
||||
logger.error("Invalid brand ID");
|
||||
return false;
|
||||
}
|
||||
const brand = await MysqlService.Brand.getById(DbHandler, brandId);
|
||||
if (!brand) {
|
||||
logger.error(`Brand not found (${brandId})`);
|
||||
return false;
|
||||
}
|
||||
logger.info(`Retrieved brand by ID successfully (${brandId})`);
|
||||
return brand;
|
||||
}
|
||||
|
||||
//TODO get models of the brand
|
||||
|
||||
//TODO get stats of the brand
|
||||
|
||||
/**
|
||||
* Deletes a brand from the database.
|
||||
*
|
||||
* @param {string} brandId - The ID of the brand to delete.
|
||||
* @return {Promise<boolean>} - A promise that resolves to true if the brand was deleted successfully, or false otherwise.
|
||||
*/
|
||||
async function deleteBrand(brandId: string): Promise<boolean> {
|
||||
if (!brandId) {
|
||||
logger.error("Brand ID is missing");
|
||||
return false;
|
||||
}
|
||||
if (brandId.length !== 36) {
|
||||
logger.error("Invalid brand ID");
|
||||
return false;
|
||||
}
|
||||
//TODO verify if as models linked
|
||||
const deletedBrand = await MysqlService.Brand.delete(DbHandler, brandId);
|
||||
if (!deletedBrand) {
|
||||
logger.error(`Failed to delete brand (${brandId})`);
|
||||
return false;
|
||||
}
|
||||
logger.info(`Brand deleted successfully (${brandId})`);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const BrandService = {
|
||||
create: createBrand,
|
||||
update: updateBrand,
|
||||
getAll: getAllBrand,
|
||||
getBySlug: getBySlugBrand,
|
||||
getById: getByIdBrand,
|
||||
delete: deleteBrand,
|
||||
};
|
||||
|
||||
export default BrandService;
|
||||
@@ -1,14 +1,13 @@
|
||||
//FEAT Create new category
|
||||
//FEAT Create new category
|
||||
import type { IDbCategory } from "@interfaces/database/IDbCategory";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import {Logger} from "tslog";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const DbHandler = new MysqlService.Handler('CategoryService')
|
||||
const logger = new Logger({name: 'CategoryService'})
|
||||
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const DbHandler = new MysqlService.Handler("CategoryService");
|
||||
const logger = new Logger({
|
||||
name: "CategoryService",
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a new category with the given data.
|
||||
@@ -18,18 +17,18 @@ const logger = new Logger({name: 'CategoryService'})
|
||||
* If an error occurs, the promise will reject with the error.
|
||||
*/
|
||||
async function createCategory(data: IDbCategory): Promise<boolean> {
|
||||
logger.info(`Creating a new category... (${data.display_name})`)
|
||||
logger.info(`Creating a new category... (${data.display_name})`);
|
||||
try {
|
||||
await MysqlService.Category.insert(DbHandler, {
|
||||
id: uuidv4(),
|
||||
display_name: data.display_name,
|
||||
slug_name: data.slug_name
|
||||
})
|
||||
slug_name: data.slug_name,
|
||||
});
|
||||
//TODO Return the new id
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error creating category: ${error}`);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,19 +44,19 @@ async function createCategory(data: IDbCategory): Promise<boolean> {
|
||||
*/
|
||||
async function updateCategory(data: IDbCategory) {
|
||||
if (!data.id) {
|
||||
logger.error("Category id is missing.")
|
||||
return false
|
||||
logger.error("Category id is missing.");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await MysqlService.Category.update(DbHandler, {
|
||||
id: data.id,
|
||||
slug_name: data.slug_name,
|
||||
display_name: data.display_name
|
||||
display_name: data.display_name,
|
||||
});
|
||||
//TODO Return id
|
||||
return true;
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
logger.error(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -69,15 +68,14 @@ async function updateCategory(data: IDbCategory) {
|
||||
*/
|
||||
async function getAll(): Promise<Promise<Array<IDbCategory>> | null> {
|
||||
try {
|
||||
logger.info("Getting all categories...");
|
||||
return await MysqlService.Category.getAll(DbHandler);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting all categories: ${error}`);
|
||||
return null;
|
||||
}
|
||||
logger.info("Getting all categories...");
|
||||
return await MysqlService.Category.getAll(DbHandler);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting all categories: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a category by its slug
|
||||
*
|
||||
@@ -86,12 +84,12 @@ async function getAll(): Promise<Promise<Array<IDbCategory>> | null> {
|
||||
*/
|
||||
async function getBySlug(slug: string): Promise<IDbCategory | null> {
|
||||
try {
|
||||
logger.info(`Getting category by slug... (${slug})`);
|
||||
return await MysqlService.Category.getBySlug(DbHandler, slug);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting category by slug: ${error}`);
|
||||
return null;
|
||||
}
|
||||
logger.info(`Getting category by slug... (${slug})`);
|
||||
return await MysqlService.Category.getBySlug(DbHandler, slug);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting category by slug: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,42 +98,45 @@ async function getBySlug(slug: string): Promise<IDbCategory | null> {
|
||||
* @param {string} id - The id of the category to retrieve.
|
||||
* @returns {Promise<IDbCategory | null>} - A Promise that resolves with the retrieved category object or null if not found.
|
||||
*/
|
||||
async function getById(id: string):Promise<IDbCategory | null> {
|
||||
async function getById(id: string): Promise<IDbCategory | null> {
|
||||
try {
|
||||
logger.info(`Getting category by id... (${id})`);
|
||||
return await MysqlService.Category.getById(DbHandler, id);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting category by id: ${error}`);
|
||||
return null;
|
||||
}
|
||||
logger.info(`Getting category by id... (${id})`);
|
||||
return await MysqlService.Category.getById(DbHandler, id);
|
||||
} catch (error) {
|
||||
logger.error(`Error getting category by id: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//FEAT Get all models in category (slug)
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a category with the given ID from the database.
|
||||
*
|
||||
* @param {string} id - The ID of the category to delete.
|
||||
* @return {Promise} - A Promise that resolves to the deleted category if successful, or null if an error occurs.
|
||||
*/
|
||||
async function deleteCategory(id:string): Promise<unknown> {
|
||||
async function deleteCategory(id: string): Promise<unknown> {
|
||||
//TODO Verify if exist
|
||||
//TODO Verify if element linked to category
|
||||
try {
|
||||
logger.info(`Deleting category... (${id})`);
|
||||
return await MysqlService.Category.delete(DbHandler, id);
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting category: ${error}`);
|
||||
return null;
|
||||
}
|
||||
logger.info(`Deleting category... (${id})`);
|
||||
return await MysqlService.Category.delete(DbHandler, id);
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting category: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const CategoryService = {
|
||||
create: createCategory,
|
||||
delete: deleteCategory,
|
||||
update: updateCategory,
|
||||
getAll,
|
||||
getBySlug,
|
||||
getById
|
||||
}
|
||||
getById,
|
||||
};
|
||||
|
||||
export default CategoryService;
|
||||
export default CategoryService;
|
||||
|
||||
@@ -2,23 +2,23 @@ import Argon2id from "@node-rs/argon2";
|
||||
|
||||
//ToTest
|
||||
export async function getHashFromPassword(password: string) {
|
||||
return await Argon2id.hash(password,{
|
||||
return await Argon2id.hash(password, {
|
||||
secret: Buffer.from(`${process.env["HASH_SECRET"]}`),
|
||||
algorithm: 2
|
||||
})
|
||||
algorithm: 2,
|
||||
});
|
||||
}
|
||||
|
||||
//ToTest
|
||||
export async function comparePassword(password: string, hash: string) {
|
||||
return await Argon2id.verify(hash, password, {
|
||||
secret: Buffer.from(`${process.env["HASH_SECRET"]}`),
|
||||
algorithm: 2
|
||||
algorithm: 2,
|
||||
});
|
||||
}
|
||||
|
||||
const CredentialService = {
|
||||
compare: comparePassword,
|
||||
hash: getHashFromPassword,
|
||||
}
|
||||
};
|
||||
|
||||
export default CredentialService;
|
||||
export default CredentialService;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './jwt.service';
|
||||
export * as MySqlService from './mysql.service'
|
||||
export * from "./jwt.service";
|
||||
export * as MySqlService from "./mysql.service";
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import Jose, {type JWTHeaderParameters, type JWTPayload} from "jose";
|
||||
import {Logger} from "tslog";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import {
|
||||
type JWTHeaderParameters,
|
||||
type JWTPayload,
|
||||
SignJWT,
|
||||
jwtVerify,
|
||||
} from "jose";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
const logger = new Logger({ name: "JwtService" });
|
||||
const logger = new Logger({
|
||||
name: "JwtService",
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify a JWT token.
|
||||
@@ -11,22 +19,22 @@ const logger = new Logger({ name: "JwtService" });
|
||||
* @returns {Promise<null | JWTPayload>}
|
||||
* - The payload of the verified JWT token or null if verification fails.
|
||||
*/
|
||||
async function JwtVerifyService(jwt: string | Uint8Array): Promise<null | JWTPayload> {
|
||||
try {
|
||||
const result = await Jose.jwtVerify(
|
||||
jwt,
|
||||
new TextEncoder()
|
||||
.encode(`${process.env["JWT_SECRET"]}`),
|
||||
{
|
||||
})
|
||||
return result.payload;
|
||||
} catch (error) {
|
||||
logger.error(error)
|
||||
return null
|
||||
}
|
||||
async function JwtVerifyService(
|
||||
jwt: string | Uint8Array,
|
||||
): Promise<null | JWTPayload> {
|
||||
try {
|
||||
const result = await jwtVerify(
|
||||
jwt,
|
||||
new TextEncoder().encode(`${process.env["JWT_SECRET"]}`),
|
||||
{},
|
||||
);
|
||||
return result.payload;
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronously signs a JWT token using the provided payload, header, expiration time, and audience.
|
||||
*
|
||||
@@ -42,19 +50,26 @@ async function JwtVerifyService(jwt: string | Uint8Array): Promise<null | JWTPay
|
||||
* @returns {Promise<string>}
|
||||
* - A promise that resolves with the signed JWT token.
|
||||
*/
|
||||
async function JwtSignService(payload: JWTPayload, pHeader: JWTHeaderParameters, expTime: string | number | Date, audience: string | string[]): Promise<string> {
|
||||
return await new Jose.SignJWT(payload)
|
||||
.setProtectedHeader(pHeader)
|
||||
.setIssuedAt(new Date())
|
||||
.setIssuer(`${process.env["JWT_SECRET"]} - Mathis HERRIOT`)
|
||||
.setAudience(audience)
|
||||
.setExpirationTime(expTime)
|
||||
.sign(new TextEncoder().encode(`${process.env["JWT_SECRET"]}`))
|
||||
async function JwtSignService(
|
||||
payload: JWTPayload,
|
||||
pHeader: JWTHeaderParameters,
|
||||
expTime: string | number | Date,
|
||||
audience: string | string[],
|
||||
): Promise<string> {
|
||||
return await new SignJWT(payload)
|
||||
.setProtectedHeader(pHeader)
|
||||
.setIssuedAt(new Date())
|
||||
.setIssuer(`${process.env["JWT_SECRET"]} - Mathis HERRIOT`)
|
||||
.setAudience(audience)
|
||||
.setExpirationTime(expTime)
|
||||
.sign(new TextEncoder().encode(`${process.env["JWT_SECRET"]}`));
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const JwtService = {
|
||||
verify: JwtVerifyService,
|
||||
sign: JwtSignService
|
||||
}
|
||||
verify: JwtVerifyService,
|
||||
sign: JwtSignService,
|
||||
};
|
||||
|
||||
export default JwtService
|
||||
export default JwtService;
|
||||
|
||||
157
src/services/model.service.ts
Normal file
157
src/services/model.service.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import type IDbModel from "@interfaces/database/IDbModel";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const DbHandler = new MysqlService.Handler("ModelService");
|
||||
const logger = new Logger({
|
||||
name: "ModelService",
|
||||
});
|
||||
|
||||
//SEC TODO validate blob
|
||||
/**
|
||||
* Creates a new model with the provided data.
|
||||
*
|
||||
* @param {IDbModel} data - The data for the new model.
|
||||
*
|
||||
* @return {Promise<boolean>} - Indicates whether the model was created successfully.
|
||||
*/
|
||||
async function createModel(data: IDbModel): Promise<boolean> {
|
||||
logger.info(`Creating a new model... (${data.display_name})`);
|
||||
//TODO Validate IDbModel data
|
||||
try {
|
||||
await MysqlService.Model.insert(DbHandler, {
|
||||
id: uuidv4(),
|
||||
display_name: data.display_name,
|
||||
slug_name: data.slug_name,
|
||||
image_blob: data.image_blob,
|
||||
brand_id: data.brand_id,
|
||||
category_id: data.category_id,
|
||||
base_price: data.base_price,
|
||||
is_trending: data.is_trending,
|
||||
});
|
||||
//TODO Return the new id
|
||||
logger.info("Success !");
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error creating category: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a model in the database.
|
||||
*
|
||||
* @param {IDbModel} data - The model data to update.
|
||||
*
|
||||
* @return {Promise<boolean>} - A promise that resolves to a boolean indicating whether the update was successful or not.
|
||||
*/
|
||||
async function updateModel(data: IDbModel): Promise<boolean> {
|
||||
logger.info(`Updating model... (${data.slug_name})`);
|
||||
try {
|
||||
await MysqlService.Model.update(DbHandler, {
|
||||
display_name: data.display_name,
|
||||
slug_name: data.slug_name,
|
||||
image_blob: data.image_blob,
|
||||
brand_id: data.brand_id,
|
||||
category_id: data.category_id,
|
||||
base_price: data.base_price,
|
||||
is_trending: data.is_trending,
|
||||
});
|
||||
logger.info("Update Successful !");
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error updating model: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a model from the database.
|
||||
*
|
||||
* @param {string} modelSlug - The slug of the model to be deleted.
|
||||
* @return {Promise<boolean>} - A promise that resolves to true if the deletion is successful, else false.
|
||||
*/
|
||||
async function deleteModel(modelSlug: string): Promise<boolean> {
|
||||
if (!modelSlug) {
|
||||
logger.error("Model slug is missing");
|
||||
return false;
|
||||
}
|
||||
logger.info(`Deleting model with ID: ${modelSlug}`);
|
||||
const doesExist = await MysqlService.Model.getBySlug(DbHandler, modelSlug);
|
||||
if (!doesExist[0]) {
|
||||
logger.warn(`Model with slug ${modelSlug} not found`);
|
||||
return false;
|
||||
}
|
||||
const target = doesExist[0]
|
||||
if (!target.id) return false;
|
||||
try {
|
||||
await MysqlService.Model.delete(DbHandler, target.id);
|
||||
logger.info("Deletion Successful !");
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Error deleting model: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a model by slug from the database.
|
||||
*
|
||||
* @param {string} modelSlug - The slug of the model to be fetched.
|
||||
* @return {Promise<IDbModel | null>} - A promise that resolves to the model if found, else null.
|
||||
*/
|
||||
async function getBySlugModel(modelSlug: string): Promise<IDbModel | null> {
|
||||
logger.info(`Fetching model with slug: ${modelSlug}`);
|
||||
try {
|
||||
const model = await MysqlService.Model.getBySlug(DbHandler, modelSlug);
|
||||
if (!model[0]) {
|
||||
logger.warn(`Model with slug ${modelSlug} not found`);
|
||||
return null;
|
||||
}
|
||||
return model[0];
|
||||
} catch (error) {
|
||||
logger.error(`Error fetching model by slug: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all models from the database.
|
||||
*
|
||||
* @return {Promise<IDbModel[] | null>} - A promise that resolves to an array of all models if found, else null.
|
||||
*/
|
||||
async function getAllModels(): Promise<IDbModel[] | null> {
|
||||
logger.info("Fetching all models from the database");
|
||||
try {
|
||||
const models = await MysqlService.Model.getAll(DbHandler);
|
||||
if (!models || models.length === 0) {
|
||||
logger.warn("No models found on the database");
|
||||
return null;
|
||||
}
|
||||
logger.info(`Found ${models.length} model(s)`);
|
||||
return models;
|
||||
} catch (error) {
|
||||
logger.error(`Error fetching all models: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
/**
|
||||
* ModelService is responsible for managing models.
|
||||
* @namespace
|
||||
*/
|
||||
const ModelService = {
|
||||
create: createModel,
|
||||
update: updateModel,
|
||||
delete: deleteModel,
|
||||
getBySlug: getBySlugModel,
|
||||
getAll: getAllModels,
|
||||
//getByCategory: getByCategoryModel,
|
||||
//getByBrand: getModelsByBrand,
|
||||
};
|
||||
|
||||
export default ModelService;
|
||||
File diff suppressed because it is too large
Load Diff
182
src/services/rent.service.ts
Normal file
182
src/services/rent.service.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
//import { ErrorType, type ISError } from "@interfaces/services/ISError";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 } from "uuid";
|
||||
import IDbRent from "@interfaces/database/IDbRent";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "RentService",
|
||||
});
|
||||
|
||||
const DbHandler = new MySqlService.Handler("RentService");
|
||||
|
||||
async function createRentService(data: IDbRent): Promise<boolean> {
|
||||
if (isDebugMode()) logger.debug(`\n\n> Creating a new rent...\n`);
|
||||
const wantedVehicleId = data.id;
|
||||
const targetUserId = data.user_id;
|
||||
if (!targetUserId || !wantedVehicleId) {
|
||||
logger.error(`Missing targetUserId or targetVehicleId`);
|
||||
return false;
|
||||
}
|
||||
const userIfExist = await MySqlService.User.getById(DbHandler, targetUserId);
|
||||
if (!userIfExist[0]) {
|
||||
logger.error(`User does not exist`);
|
||||
return false;
|
||||
}
|
||||
const targetUser = userIfExist[0]
|
||||
if (!targetUser.id) return false;
|
||||
const vehicleIfExist = await MySqlService.Vehicle.getById(DbHandler, wantedVehicleId);
|
||||
if (!vehicleIfExist[0] || !vehicleIfExist[0].id) {
|
||||
logger.error(`Vehicle does not exist`);
|
||||
return false;
|
||||
}
|
||||
const vehicleId = vehicleIfExist[0].id
|
||||
if (!vehicleIfExist[0].isAvailable) {
|
||||
logger.error(`Vehicle is not available`);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const result = await MySqlService.Rent.insert(DbHandler, {
|
||||
id: v4(),
|
||||
vehicle_id: vehicleId,
|
||||
user_id: targetUser.id,
|
||||
active: data.active,
|
||||
iat: new Date(Date.parse(`${data.iat}`)),
|
||||
eat: new Date(Date.parse(`${data.eat}`)),
|
||||
need_survey: data.need_survey,
|
||||
km_at_start: Number.parseInt(`${data.km_at_start}`)
|
||||
});
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error creating category: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateRentService(data: IDbRent) {
|
||||
if (isDebugMode()) logger.debug(`\n\n> Updating a rent...\n`);
|
||||
const wantedVehicleId = data.id;
|
||||
const targetUserId = data.user_id;
|
||||
if (!targetUserId || !wantedVehicleId || !data.id) {
|
||||
logger.error(`Missing targetUserId or targetVehicleId`);
|
||||
return false;
|
||||
}
|
||||
const rentIfExist = await MySqlService.Rent.getById(DbHandler, data.id);
|
||||
if (!rentIfExist[0]) {
|
||||
logger.error(`Rent does not exist`);
|
||||
return false;
|
||||
}
|
||||
const rentId = rentIfExist[0].id;
|
||||
if (!rentId) {
|
||||
logger.error(`RentId does not exist`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const userIfExist = await MySqlService.User.getById(DbHandler, targetUserId);
|
||||
if (!userIfExist[0]) {
|
||||
logger.error(`User does not exist`);
|
||||
return false;
|
||||
}
|
||||
const targetUser = userIfExist[0]
|
||||
if (!targetUser.id) return false;
|
||||
const vehicleIfExist = await MySqlService.Vehicle.getById(DbHandler, wantedVehicleId);
|
||||
if (!vehicleIfExist[0] || !vehicleIfExist[0].id) {
|
||||
logger.error(`Vehicle does not exist`);
|
||||
return false;
|
||||
}
|
||||
const vehicleId = vehicleIfExist[0].id
|
||||
try {
|
||||
const result = await MySqlService.Rent.update(DbHandler, {
|
||||
id: rentId,
|
||||
vehicle_id: vehicleId,
|
||||
user_id: targetUser.id,
|
||||
active: data.active,
|
||||
iat: new Date(Date.parse(`${data.iat}`)),
|
||||
eat: new Date(Date.parse(`${data.eat}`)),
|
||||
need_survey: data.need_survey,
|
||||
km_at_start: Number.parseInt(`${data.km_at_start}`)
|
||||
});
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error updating category: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllAssignedRentService() {
|
||||
const result = await MySqlService.Rent.getAllAssigned(DbHandler);
|
||||
if (result.length > 0) {
|
||||
return result;
|
||||
}
|
||||
logger.warn(`No assigned rents found`);
|
||||
return [];
|
||||
}
|
||||
|
||||
async function getRentByIdService(rentId: string) {
|
||||
if (!rentId || rentId.length !== 36) {
|
||||
logger.warn(`Id missing or not conform`)
|
||||
return false;
|
||||
}
|
||||
const rent = await MySqlService.Rent.getById(DbHandler, rentId);
|
||||
if (rent.length > 0) {
|
||||
return rent[0];
|
||||
}
|
||||
logger.warn(`Rent not found`);
|
||||
return null;
|
||||
}
|
||||
|
||||
async function deleteRentService(rentId: string) {
|
||||
const rentIfExist = await MySqlService.Rent.getById(DbHandler, rentId);
|
||||
if (!rentIfExist[0]) {
|
||||
logger.error(`Rent does not exist`);
|
||||
return false;
|
||||
}
|
||||
const target = rentIfExist[0]
|
||||
if (!target.id) return false;
|
||||
try {
|
||||
const result = await MySqlService.Rent.delete(DbHandler, target.id);
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error deleting rent: \n${error}\n`);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserRentService(userId: string) {
|
||||
if (!userId) {
|
||||
logger.warn(`Missing userId`);
|
||||
return false;
|
||||
}
|
||||
const rents = await MySqlService.Rent.getAssignedToUser(DbHandler, userId);
|
||||
if (rents.length > 0) {
|
||||
return rents;
|
||||
}
|
||||
logger.warn(`No rents found for user`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const VehicleService = {
|
||||
create: createRentService,
|
||||
update: updateRentService,
|
||||
getAll: getAllAssignedRentService,
|
||||
getById: getRentByIdService,
|
||||
delete: deleteRentService,
|
||||
getUserRent: getUserRentService,
|
||||
};
|
||||
|
||||
export default VehicleService;
|
||||
@@ -1,204 +1,322 @@
|
||||
import {Logger} from "tslog";
|
||||
|
||||
import Argon2id from "@node-rs/argon2";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import type { IDbUser } from "@interfaces/database/IDbUser";
|
||||
import type { IReqLogin } from "@interfaces/requests/IReqLogin";
|
||||
import type { IReqRegister } from "@interfaces/requests/IReqRegister";
|
||||
import { ErrorType, type ISError } from "@interfaces/services/ISError";
|
||||
import type { IUserUpdate } from "@interfaces/services/IUserUpdate";
|
||||
import CredentialService from "@services/credential.service";
|
||||
import JwtService from "@services/jwt.service";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "UserService",
|
||||
});
|
||||
|
||||
const logger = new Logger({ name: "UserService" });
|
||||
|
||||
const DbHandler = new MySqlService.Handler('UserService')
|
||||
const DbHandler = new MySqlService.Handler("UserService");
|
||||
|
||||
/**
|
||||
* Retrieves a user object from the database based on the given username.
|
||||
* Retrieves a user from the database by the given email address.
|
||||
*
|
||||
* @param {string} username - The username of the user to retrieve.
|
||||
* @returns {Promise<Object | null>} - The user object if found, or null if not found.
|
||||
* @param {string} targetEmail - The email address of the user to retrieve.
|
||||
* @returns {Promise<IDbUser | ISError>}
|
||||
* - A promise that resolves with the user.
|
||||
* - If the user is not found, an error object is returned.
|
||||
* - If an error occurs during the database operation, an error object is returned.
|
||||
*/
|
||||
async function getUserFromUsername(username: string): Promise<object | null> {
|
||||
const dbUser = await MySqlService.User.getByUsername(DbHandler, username)
|
||||
if (dbUser === undefined) return null;
|
||||
return dbUser;
|
||||
}
|
||||
|
||||
async function getUserFromIdService(id: string | undefined) {
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, id);
|
||||
if (dbUser === undefined) return null;
|
||||
return dbUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new user by creating a UserService object, generating a JWT token, and inserting the user into the database.
|
||||
*
|
||||
* @param {Object} sanitizedData - The sanitized user data.
|
||||
* @param {string} sanitizedData.username - The username of the new user.
|
||||
* @param {string} sanitizedData.displayName - The display namcoe of the new user.
|
||||
* @param {string} sanitizedData.firstName
|
||||
* @param {string} sanitizedData.lastName
|
||||
* @param {string} sanitizedData.password - The password of the new user.
|
||||
* @param {boolean} sanitizedData.gdpr - Indicates whether the new user has accepted GDPR.
|
||||
*
|
||||
* @returns {Object} - An object containing the registered user's data and JWT token.
|
||||
* @returns {string} error - The error name, if any. "none" if registration was successful.
|
||||
* @returns {string|null} jwt - The JWT token for the registered user. Null if registration was not successful.
|
||||
* @returns {Object|null} user - The registered user's data. Null if registration was not successful.
|
||||
* @returns {string|null} user.id - The ID of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.username - The username of the registered user. Null if registration was not successful.
|
||||
* @returns {string|null} user.displayName - The display name of the registered user. Null if registration was not successful.
|
||||
*/
|
||||
async function RegisterService(sanitizedData) {
|
||||
if (sanitizedData.password.length < 6) {
|
||||
logger.info(`REGISTER :> Invalid password (${sanitizedData.username})`)
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const passwordHash = await CredentialService.hash(sanitizedData.password)
|
||||
|
||||
// Does the new user has accepted GDPR ?
|
||||
if (sanitizedData.gdpr !== true) {
|
||||
logger.info(`REGISTER :> GDPR not validated (${sanitizedData.username})`)
|
||||
return { error: "gdprNotApproved" }
|
||||
}
|
||||
|
||||
// Check if exist and return
|
||||
|
||||
const dbUserIfExist = await getUserFromUsername(sanitizedData.username)
|
||||
if (dbUserIfExist) {
|
||||
logger.info(`REGISTER :> User exist (${dbUserIfExist.username})\n ID:${dbUserIfExist.id}`)
|
||||
return { error: "exist" }
|
||||
}
|
||||
|
||||
const currentDate = new Date();
|
||||
|
||||
// New UserService (class)
|
||||
|
||||
const NewUser = new User(sanitizedData.username, sanitizedData.displayName, passwordHash, currentDate);
|
||||
NewUser.setFirstName(sanitizedData.firstName);
|
||||
NewUser.setLastName(sanitizedData.lastName);
|
||||
|
||||
// JWT
|
||||
|
||||
const alg = 'HS512'
|
||||
const token = await JwtService.sign({
|
||||
sub: NewUser.id
|
||||
}, alg,
|
||||
'1d',
|
||||
'user')
|
||||
|
||||
const userData = {
|
||||
error: "none",
|
||||
jwt: token,
|
||||
user: {
|
||||
id: NewUser.id,
|
||||
username: NewUser.username,
|
||||
displayName: NewUser.displayName,
|
||||
firstName: NewUser.firstName,
|
||||
lastName: NewUser.lastName
|
||||
}};
|
||||
logger.info(userData)
|
||||
await Db.collection("users").insertOne(NewUser);
|
||||
logger.info(`REGISTER :> Inserted new user (${NewUser.username})`)
|
||||
return userData
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the login process by verifying the provided credentials.
|
||||
* @param {Object} sanitizedData - The sanitized user login data.
|
||||
* @param {string} sanitizedData.username - The username provided by the user.
|
||||
* @param {string} sanitizedData.password - The password provided by the user.
|
||||
* @returns {Object} - The login result object.
|
||||
* @returns {string} result.error - The error code if there is an error during the login process.
|
||||
* @returns {string} result.jwt - The JSON Web Token (JWT) generated upon successful login.
|
||||
* @returns {Object} result.user - The user information.
|
||||
* @returns {number} result.user.id - The ID of the user.
|
||||
* @returns {string} result.user.username - The username of the user.
|
||||
* @returns {string} result.user.displayName - The display name of the user.
|
||||
*/
|
||||
async function LoginService(sanitizedData: { username: string; password: string; }) {
|
||||
//const passwordHash = await getHashFromPassword(sanitizedData.password);
|
||||
const dbUser = await MysqlService.User.getByUsername(DbHandler, sanitizedData.username);
|
||||
if (!dbUser) {
|
||||
console.log(`LoginService :> User does not exist (${sanitizedData.username})`);
|
||||
return { error: "userNotFound" };
|
||||
}
|
||||
if (sanitizedData.password.length < 6) {
|
||||
console.log('X')
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
const isPasswordValid = await CredentialService.compare(sanitizedData.password, dbUser.hash)
|
||||
if (!isPasswordValid) {
|
||||
console.log(isPasswordValid)
|
||||
console.log(`LoginService :> Invalid password (${sanitizedData.username})`);
|
||||
return { error: "invalidPassword" };
|
||||
}
|
||||
// biome-ignore lint/style/useConst: <explanation>
|
||||
let userData = {
|
||||
error: "none",
|
||||
jwt: '',
|
||||
user: {
|
||||
id: dbUser.id,
|
||||
username: dbUser.username,
|
||||
displayName: dbUser.displayName,
|
||||
async function getUserByEmail(targetEmail: string): Promise<IDbUser | ISError> {
|
||||
try {
|
||||
const dbUser = await MySqlService.User.getByEmail(DbHandler, targetEmail);
|
||||
if (dbUser.length === 0) {
|
||||
logger.info(`\n\n> User not found (${targetEmail})\n`);
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "The user was not fund.",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
userData.jwt = await JwtService.sign({sub: dbUser.id}, {alg: 'HS512'}, '7d', 'user')
|
||||
|
||||
|
||||
console.log("USERDATA :>");
|
||||
console.log(userData);
|
||||
return userData;
|
||||
if (dbUser.length === 1 && dbUser[0]) return dbUser[0];
|
||||
return {
|
||||
error: ErrorType.ServiceError,
|
||||
message: "To many user found, suspicious.",
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user from the database based on the provided ID.
|
||||
*
|
||||
* @param {string} id - The ID of the user to retrieve.
|
||||
* @returns {Promise<IDbUser | ISError>} - A promise that resolves with the user object if found, or an error object if not.
|
||||
*/
|
||||
async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
|
||||
try {
|
||||
if (!id || id.length !== 36) {
|
||||
logger.info(`\n\n> Invalid ID (${id})\n`);
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Invalid ID length.",
|
||||
};
|
||||
}
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, id);
|
||||
if (dbUser.length === 0) {
|
||||
logger.info(`User not found (${id})`);
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "The user was not found.",
|
||||
};
|
||||
}
|
||||
const firstUser = dbUser[0];
|
||||
if (firstUser) return firstUser;
|
||||
return {
|
||||
error: ErrorType.ServiceError,
|
||||
message: "No user found.",
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
DbHandler.factorize({
|
||||
values: {
|
||||
id: '011010101',
|
||||
username: 'avnyr',
|
||||
age: 42,
|
||||
is_admin: true
|
||||
},
|
||||
actionName: "Testing"
|
||||
}).then((result)=>{
|
||||
logger.trace(`\n\n> ${result._valuesArray.join(', ')}\n\n> ${result.totalFields}\n\n> ${result._keysTemplate}\n`)
|
||||
})*/
|
||||
|
||||
/**
|
||||
* Registers a new user.
|
||||
*
|
||||
* @param {IReqRegister} inputData - The input data for registration.
|
||||
* @return {Promise<ISError | string>} - A Promise that resolves to either an error or a token.
|
||||
*/
|
||||
async function register(inputData: IReqRegister): Promise<ISError | string> {
|
||||
try {
|
||||
if (inputData.password.length < 6) {
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Password must be at least 6 characters long.",
|
||||
};
|
||||
}
|
||||
|
||||
const passwordHash = await CredentialService.hash(`${inputData.password}`);
|
||||
|
||||
// Does the new user has accepted GDPR ?
|
||||
if (inputData.gdpr !== true) {
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "GDPR acceptance is required.",
|
||||
};
|
||||
}
|
||||
const currentDate = new Date();
|
||||
|
||||
// Check if exist and return
|
||||
const dbUserIfExist: IDbUser | ISError = await getUserByEmail(
|
||||
inputData.email,
|
||||
);
|
||||
if ("username" in dbUserIfExist) {
|
||||
logger.info(
|
||||
`\n\n> User already exist for email "${inputData.email}".\n(${dbUserIfExist.username}::${dbUserIfExist.id})\n`,
|
||||
);
|
||||
return {
|
||||
error: ErrorType.UnAuthorized,
|
||||
message: "User already exists.",
|
||||
};
|
||||
}
|
||||
const currentId = v4();
|
||||
logger.info(`\n\n> Trying to insert a new user... (${currentId})\n`);
|
||||
const NewUser = await MySqlService.User.insert(DbHandler, {
|
||||
id: currentId,
|
||||
email: inputData.email,
|
||||
username: inputData.username,
|
||||
firstname: inputData.firstName,
|
||||
lastname: inputData.lastName,
|
||||
dob: new Date(),
|
||||
hash: passwordHash,
|
||||
gdpr: currentDate,
|
||||
is_admin: false,
|
||||
is_email_verified: false,
|
||||
});
|
||||
if ("error" in NewUser && NewUser.affectedRows === 0) {
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "Error when inserting user in database.",
|
||||
};
|
||||
}
|
||||
logger.info(
|
||||
`\n\n> New user created ! (${inputData.username}::${currentId})\n`,
|
||||
);
|
||||
|
||||
// JWT
|
||||
return await JwtService.sign(
|
||||
{
|
||||
sub: currentId,
|
||||
},
|
||||
{
|
||||
alg: "HS512",
|
||||
},
|
||||
"1d",
|
||||
"user",
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs in a user with the provided input data.
|
||||
*
|
||||
* @param inputData - The input data for the login operation.
|
||||
* @property email - The email of the user.
|
||||
* @property password - The password of the user.
|
||||
* @returns A promise that resolves to either an error or a token string.
|
||||
* @throws {ISError} - If an error occurs in the login process.
|
||||
* @throws {string} - If the login was successful, returns a token string.
|
||||
*/
|
||||
async function login(inputData: IReqLogin): Promise<ISError | string> {
|
||||
try {
|
||||
const dbUser = await getUserByEmail(inputData.email);
|
||||
if ("error" in dbUser) {
|
||||
return {
|
||||
error: dbUser.error,
|
||||
message: dbUser.message,
|
||||
};
|
||||
}
|
||||
if (!dbUser?.id) {
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "User not found.",
|
||||
};
|
||||
}
|
||||
const isPasswordValid = await CredentialService.compare(
|
||||
inputData.password,
|
||||
dbUser.hash,
|
||||
);
|
||||
if (!isPasswordValid) {
|
||||
return {
|
||||
error: ErrorType.UnAuthorized,
|
||||
message: "Invalid password.",
|
||||
};
|
||||
}
|
||||
// Generate the JSDoc definition for those html status code
|
||||
const token = await JwtService.sign(
|
||||
{
|
||||
sub: dbUser.id,
|
||||
p: [
|
||||
{
|
||||
isAdmin: dbUser.is_admin,
|
||||
gdpr: dbUser.gdpr,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
alg: "HS512",
|
||||
},
|
||||
"1d",
|
||||
"user",
|
||||
);
|
||||
if (isDebugMode())
|
||||
logger.trace(
|
||||
`\n\n> New token for login of "${dbUser.username}".\n${token}`,
|
||||
);
|
||||
return token;
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user from the database by email.
|
||||
*
|
||||
* @param {string} email - The email associated with the user.
|
||||
* @return {Promise<IDbUser | false>} - A Promise that resolves to the user (if found) or false (if not found).
|
||||
*/
|
||||
async function getByEmailService(email: string): Promise<IDbUser | false> {
|
||||
const dbUser = await MySqlService.User.getByEmail(DbHandler, email);
|
||||
if (dbUser.length === 0) {
|
||||
if (isDebugMode()) logger.trace(`\n\n> User not found in DB (${email})\n`);
|
||||
return false;
|
||||
}
|
||||
if (isDebugMode()) logger.trace(dbUser);
|
||||
if (dbUser.length > 1 && dbUser[0]) return dbUser[0];
|
||||
return false;
|
||||
}
|
||||
|
||||
//TOTest
|
||||
/**
|
||||
* Retrieves all users from the database.
|
||||
*
|
||||
* @async
|
||||
* @function getAllUsersService
|
||||
* @returns {Promise<{iat: number, users: Array<user>, length: number}>} - The response object containing the users array and its length.
|
||||
* @returns {Promise<Array<IDbUser> | ISError>} The list of users, or an error object if an error occurred.
|
||||
*/
|
||||
async function getAllUsersService() {
|
||||
const users = await Db.collection("users").find().toArray();
|
||||
// biome-ignore lint/complexity/noForEach: <explanation>
|
||||
users.forEach(user => {
|
||||
delete user.passwordHash
|
||||
delete user._id
|
||||
delete user.gdpr
|
||||
});
|
||||
logger.info(`Query ${users.length} user(s)`)
|
||||
return {
|
||||
iat: Date.now(),
|
||||
users: users,
|
||||
length: users.length
|
||||
async function getAllUsersService(): Promise<Array<IDbUser> | ISError> {
|
||||
try {
|
||||
const allUsers = await MySqlService.User.getAll(DbHandler);
|
||||
if (allUsers === undefined) {
|
||||
logger.error(`Error retrieving all users.`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
return allUsers;
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits a user in the database.
|
||||
*
|
||||
* @param {string} targetId - The ID of the user to be edited.
|
||||
* @param {object} sanitizedData - The sanitized data to update the user with.
|
||||
* @returns {object} - An object indicating the result of the operation.
|
||||
* If the user is not found, the error property will be a string "userNotFound".
|
||||
* Otherwise, the error property will be a string "none".
|
||||
*/
|
||||
async function editUserService(targetId, sanitizedData) {
|
||||
if (sanitizedData.password) {
|
||||
const passwordHash = await getHashFromPassword(sanitizedData.password)
|
||||
delete sanitizedData.password
|
||||
logger.info(`Changing password for user "${targetId}"`)
|
||||
sanitizedData.passwordHash = passwordHash
|
||||
async function editUserService(
|
||||
targetId: string,
|
||||
inputData: IUserUpdate,
|
||||
): Promise<ISError | boolean> {
|
||||
if (!targetId || targetId.length !== 36) {
|
||||
logger.info(`\n\n> Invalid ID (${targetId})\n`);
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Invalid ID length.",
|
||||
};
|
||||
}
|
||||
const updatedUserResult = await Db.collection("users").updateOne({id: targetId}, {$set: sanitizedData});
|
||||
if (updatedUserResult.modifiedCount === 0) {
|
||||
logger.info(`EDIT :> User not found (${targetId})`);
|
||||
return { error: "userNotFound" };
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, targetId);
|
||||
if (!dbUser[0] || !dbUser[0].id) {
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "User not found.",
|
||||
};
|
||||
}
|
||||
|
||||
logger.info(`EDIT :> User updated (${targetId})`);
|
||||
return { error: "none" };
|
||||
const result = await MySqlService.User.update(DbHandler, inputData);
|
||||
if (result.affectedRows === 0) {
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,24 +325,27 @@ async function editUserService(targetId, sanitizedData) {
|
||||
* @param {string} targetId - The ID of the user to be deleted.
|
||||
* @return {Promise<boolean>} - A promise that resolves to true if the user is successfully deleted, or false if an error occurs.
|
||||
*/
|
||||
async function deleteUserService(targetId) {
|
||||
logger.info(`Deleting user ${targetId}`)
|
||||
async function deleteUserService(targetId: string): Promise<boolean> {
|
||||
logger.info(`Deleting user ${targetId}`);
|
||||
try {
|
||||
await Db.collection("users").deleteOne({id: targetId});
|
||||
return true
|
||||
const DeleteResult = await MySqlService.User.delete(DbHandler, targetId);
|
||||
return DeleteResult.affectedRows !== 0;
|
||||
} catch (e) {
|
||||
logger.warn(e)
|
||||
return false
|
||||
logger.warn(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const UserService = {
|
||||
register: RegisterService,
|
||||
login: LoginService,
|
||||
register: register,
|
||||
login: login,
|
||||
getAll: getAllUsersService,
|
||||
getFromId: getUserFromIdService,
|
||||
getByEmail: getByEmailService,
|
||||
edit: editUserService,
|
||||
delete: deleteUserService
|
||||
}
|
||||
delete: deleteUserService,
|
||||
};
|
||||
|
||||
export default UserService;
|
||||
export default UserService;
|
||||
|
||||
125
src/services/vehicle.service.ts
Normal file
125
src/services/vehicle.service.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
|
||||
//import { ErrorType, type ISError } from "@interfaces/services/ISError";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import { isDebugMode } from "@utils/debugState";
|
||||
import { Logger } from "tslog";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const logger = new Logger({
|
||||
name: "VehicleService",
|
||||
});
|
||||
|
||||
const DbHandler = new MySqlService.Handler("VehicleService");
|
||||
|
||||
async function createVehicleService(data: IDbVehicle) {
|
||||
if (isDebugMode()) logger.debug(`\n\n> Creating a new vehicle...\n`);
|
||||
try {
|
||||
const result = await MySqlService.Vehicle.insert(DbHandler, {
|
||||
id: v4(),
|
||||
plate_number: data.plate_number,
|
||||
model_id: data.model_id,
|
||||
odometer: data.odometer | 0,
|
||||
health_state: data.health_state,
|
||||
});
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error creating category: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateVehicleService(data: IDbVehicle) {
|
||||
if (isDebugMode()) logger.debug(`\n\n> Updating vehicle...\n`);
|
||||
try {
|
||||
if (!data.id) {
|
||||
return false;
|
||||
}
|
||||
const result = await MySqlService.Vehicle.update(DbHandler, {
|
||||
id: data.id,
|
||||
plate_number: data.plate_number,
|
||||
model_id: data.model_id,
|
||||
odometer: data.odometer | 0,
|
||||
health_state: data.health_state,
|
||||
});
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error updating vehicle: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllVehiclesService() {
|
||||
try {
|
||||
const result = await MySqlService.Vehicle.getAll(DbHandler);
|
||||
return {
|
||||
iat: Date.now(),
|
||||
vehicles: result,
|
||||
total: result.length,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error getting vehicles: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getVehicleByIdService(vehicleId: string) {
|
||||
try {
|
||||
const result = await MySqlService.Vehicle.getById(DbHandler, vehicleId);
|
||||
return {
|
||||
iat: Date.now(),
|
||||
vehicle: result,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error getting vehicle by id: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getAvailableVehicleService() {
|
||||
try {
|
||||
const result = await MySqlService.Vehicle.getAvailable(DbHandler);
|
||||
return {
|
||||
iat: Date.now(),
|
||||
vehicles: result,
|
||||
total: result.length,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error getting available vehicles: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteVehicleService(vehicleId: string) {
|
||||
try {
|
||||
const result = await MySqlService.Vehicle.delete(DbHandler, vehicleId);
|
||||
if (result.affectedRows !== 0) {
|
||||
logger.info("\n\n> Success !");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
logger.error(`\n\n> Error deleting vehicle: \n${error}\n`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDebugMode()) logger.debug("\nService loaded.");
|
||||
|
||||
const VehicleService = {
|
||||
create: createVehicleService,
|
||||
update: updateVehicleService,
|
||||
getAll: getAllVehiclesService,
|
||||
getById: getVehicleByIdService,
|
||||
delete: deleteVehicleService,
|
||||
getAvailable: getAvailableVehicleService,
|
||||
};
|
||||
|
||||
export default VehicleService;
|
||||
8
src/utils/debugState.ts
Normal file
8
src/utils/debugState.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import process from "node:process";
|
||||
|
||||
export function isDebugMode() {
|
||||
if (process.env["DEBUG"] === "true") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
5
src/utils/validators/email.ts
Normal file
5
src/utils/validators/email.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function isEmail(email: string) {
|
||||
const re =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(String(email).toLowerCase());
|
||||
}
|
||||
@@ -1,39 +1,52 @@
|
||||
import JwtService from "@services/jwt.service";
|
||||
import type {NextFunction, Request, Response} from "express";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import {Logger} from "tslog";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
const DbHandler = new MySqlService.Handler('AdminGuard')
|
||||
const logger = new Logger({name: 'AdminGuard'})
|
||||
const DbHandler = new MySqlService.Handler("AdminGuard");
|
||||
const logger = new Logger({
|
||||
name: "AdminGuard",
|
||||
});
|
||||
|
||||
const UNAUTHORIZED = 401;
|
||||
const FORBIDDEN = 403;
|
||||
const UNAUTH_MESSAGE = 'Missing Authorization Header';
|
||||
const INVALID_TOKEN_MESSAGE = 'Invalid or expired token.';
|
||||
const PERMISSON_NOT_VALID = 'You are missing the required permission.'
|
||||
const UNAUTH_MESSAGE = "Missing Authorization Header";
|
||||
const INVALID_TOKEN_MESSAGE = "Invalid or expired token.";
|
||||
const PERMISSON_NOT_VALID = "You are missing the required permission.";
|
||||
|
||||
async function AdminGuard(req: Request, res: Response, next: NextFunction) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
logger.warn(`Invalid header (${req.ip})`)
|
||||
return res.status(UNAUTHORIZED).json({message: UNAUTH_MESSAGE});
|
||||
logger.warn(`Invalid header (${req.ip})`);
|
||||
return res.status(UNAUTHORIZED).json({
|
||||
message: UNAUTH_MESSAGE,
|
||||
});
|
||||
}
|
||||
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const bearerToken = authHeader.split(" ")[1];
|
||||
|
||||
if (!bearerToken) return res.status(FORBIDDEN).json({message: INVALID_TOKEN_MESSAGE});
|
||||
if (!bearerToken)
|
||||
return res.status(FORBIDDEN).json({
|
||||
message: INVALID_TOKEN_MESSAGE,
|
||||
});
|
||||
|
||||
const token = await JwtService.verify(bearerToken);
|
||||
|
||||
if (token) {
|
||||
if (token && token.sub) {
|
||||
// @ts-ignore
|
||||
const isSourceAdmin = await MysqlService.User.getAdminStateForId(DbHandler, token.sub)
|
||||
const isSourceAdmin = await MysqlService.User.getAdminStateForId(
|
||||
DbHandler,
|
||||
token.sub,
|
||||
);
|
||||
if (isSourceAdmin === true) next();
|
||||
return res.status(FORBIDDEN).json({message: PERMISSON_NOT_VALID});
|
||||
|
||||
return res.status(FORBIDDEN).json({
|
||||
message: PERMISSON_NOT_VALID,
|
||||
});
|
||||
}
|
||||
return res.status(FORBIDDEN).json({message: INVALID_TOKEN_MESSAGE});
|
||||
return res.status(FORBIDDEN).json({
|
||||
message: INVALID_TOKEN_MESSAGE,
|
||||
});
|
||||
}
|
||||
|
||||
export default AdminGuard
|
||||
export default AdminGuard;
|
||||
|
||||
@@ -1,40 +1,57 @@
|
||||
import JwtService from "@services/jwt.service";
|
||||
import type {NextFunction, Request, Response} from "express";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import {Logger} from "tslog";
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { Logger } from "tslog";
|
||||
|
||||
const DbHandler = new MySqlService.Handler('UserGuard')
|
||||
const logger = new Logger({name: 'UserGuard'})
|
||||
const DbHandler = new MySqlService.Handler("UserGuard");
|
||||
const logger = new Logger({
|
||||
name: "UserGuard",
|
||||
});
|
||||
|
||||
const UNAUTHORIZED = 401;
|
||||
const FORBIDDEN = 403;
|
||||
const UNAUTH_MESSAGE = 'Missing Authorization Header';
|
||||
const INVALID_TOKEN_MESSAGE = 'Invalid or expired token.';
|
||||
const USER_NOT_EXIST = 'You dont exist anymore'
|
||||
const UNAUTH_MESSAGE = "Missing Authorization Header";
|
||||
const INVALID_TOKEN_MESSAGE = "Invalid or expired token.";
|
||||
const USER_NOT_EXIST = "You dont exist anymore";
|
||||
|
||||
async function UserGuard(req: Request, res: Response, next: NextFunction) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) {
|
||||
return res.status(UNAUTHORIZED).json({message: UNAUTH_MESSAGE});
|
||||
return res.status(UNAUTHORIZED).json({
|
||||
message: UNAUTH_MESSAGE,
|
||||
});
|
||||
}
|
||||
|
||||
const bearerToken = authHeader.split(' ')[1];
|
||||
const bearerToken = authHeader.split(" ")[1];
|
||||
|
||||
if (!bearerToken) return res.status(FORBIDDEN).json({message: INVALID_TOKEN_MESSAGE});
|
||||
if (!bearerToken)
|
||||
return res.status(FORBIDDEN).json({
|
||||
message: INVALID_TOKEN_MESSAGE,
|
||||
});
|
||||
|
||||
const token = await JwtService.verify(bearerToken);
|
||||
|
||||
if (token) {
|
||||
// @ts-ignore
|
||||
const userId = token.sub;
|
||||
const user= await MySqlService.User.getById(DbHandler, userId);
|
||||
if (user) {
|
||||
logger.info(`An user do a request. (${user?.username})`)
|
||||
next()
|
||||
if (!userId) {
|
||||
logger.error(USER_NOT_EXIST);
|
||||
return res.status(UNAUTHORIZED).json({
|
||||
message: USER_NOT_EXIST,
|
||||
});
|
||||
}
|
||||
return res.status(UNAUTHORIZED).json({message: USER_NOT_EXIST});
|
||||
const user = await MySqlService.User.getById(DbHandler, userId);
|
||||
if (user) {
|
||||
logger.info(`An user do a request. (${user?.username})`);
|
||||
next();
|
||||
}
|
||||
return res.status(UNAUTHORIZED).json({
|
||||
message: USER_NOT_EXIST,
|
||||
});
|
||||
}
|
||||
return res.status(FORBIDDEN).json({message: INVALID_TOKEN_MESSAGE});
|
||||
return res.status(FORBIDDEN).json({
|
||||
message: INVALID_TOKEN_MESSAGE,
|
||||
});
|
||||
}
|
||||
|
||||
export default UserGuard
|
||||
export default UserGuard;
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "es6",
|
||||
"rootDir": "./src",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitAny": true,
|
||||
"strictBindCallApply": true,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"paths": {
|
||||
"@services/*": [
|
||||
"src/services/*"
|
||||
@@ -26,19 +38,11 @@
|
||||
]
|
||||
},
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"removeComments": false,
|
||||
"noEmitOnError": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
@@ -47,12 +51,10 @@
|
||||
"noUnusedParameters": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": true,
|
||||
"skipLibCheck": true
|
||||
"allowUnreachableCode": true
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user