First commit
This commit is contained in:
50
.eslintrc.json
Normal file
50
.eslintrc.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2021,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"arrow-spacing": ["warn", { "before": true, "after": true }],
|
||||
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "error",
|
||||
"comma-style": "error",
|
||||
"curly": ["error", "multi-line", "consistent"],
|
||||
"dot-location": ["error", "property"],
|
||||
"handle-callback-err": "off",
|
||||
"indent": ["error", "tab"],
|
||||
"keyword-spacing": "error",
|
||||
"max-nested-callbacks": ["error", { "max": 4 }],
|
||||
"max-statements-per-line": ["error", { "max": 2 }],
|
||||
"no-console": "off",
|
||||
"no-empty-function": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-inline-comments": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }],
|
||||
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }],
|
||||
"no-trailing-spaces": ["error"],
|
||||
"no-var": "error",
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
"semi": ["error", "always"],
|
||||
"space-before-blocks": "error",
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}],
|
||||
"space-in-parens": "error",
|
||||
"space-infix-ops": "error",
|
||||
"space-unary-ops": "error",
|
||||
"spaced-comment": "error",
|
||||
"yoda": "error"
|
||||
}
|
||||
}
|
||||
6
api/.env.sample
Normal file
6
api/.env.sample
Normal file
@@ -0,0 +1,6 @@
|
||||
DATABASE_HOST="127.0.0.1"
|
||||
DATABASE_NAME=nuitdelinfo2023
|
||||
DATABASE_USER=nuitdelinfo2023
|
||||
DATABASE_PASSWORD=""
|
||||
JWT_SECRET=""
|
||||
PORT=3000
|
||||
173
api/.gitignore
vendored
Normal file
173
api/.gitignore
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Caches
|
||||
|
||||
.cache
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
15
api/README.md
Normal file
15
api/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# nuitdelinfo2023-api
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.js
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.0.13. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||
BIN
api/bun.lockb
Executable file
BIN
api/bun.lockb
Executable file
Binary file not shown.
25
api/database.sql
Normal file
25
api/database.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
SET default_storage_engine = InnoDB;
|
||||
DROP DATABASE IF EXISTS `nuitdelinfo2023`;
|
||||
CREATE DATABASE IF NOT EXISTS `nuitdelinfo2023`
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
DROP USER IF EXISTS 'nuitdelinfo2023';
|
||||
CREATE USER 'nuitdelinfo2023'@'%' IDENTIFIED BY 'PASSWORD';
|
||||
GRANT ALL PRIVILEGES ON airjet.* TO 'nuitdelinfo2023'@'%';
|
||||
|
||||
USE `nuitdelinfo2023`;
|
||||
|
||||
CREATE TABLE users (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
username VARCHAR(64) NOT NULL,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
score INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT u_user_type_id
|
||||
FOREIGN KEY (user_type_id)
|
||||
REFERENCES user_types(id)
|
||||
ON DELETE RESTRICT
|
||||
ON UPDATE CASCADE,
|
||||
INDEX ur_user_type_idx (user_type_id)
|
||||
) ENGINE=InnoDB;
|
||||
32
api/index.js
Normal file
32
api/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import cors from 'cors';
|
||||
import logger from 'morgan';
|
||||
import express from 'express';
|
||||
|
||||
import { log } from './modules/log';
|
||||
import { speedLimiter, checkSystemLoad } from './modules/requestHandler';
|
||||
|
||||
import testRouter from './routes/test';
|
||||
|
||||
const app = express();
|
||||
app.set('trust proxy', 1);
|
||||
|
||||
app.use(express.json());
|
||||
app.use(logger('dev'));
|
||||
app.use(speedLimiter);
|
||||
app.use(checkSystemLoad);
|
||||
app.use(logger('combined', { stream: fs.createWriteStream(path.join(__dirname, 'logs/access.log'), { flags: 'a' }) }));
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
}));
|
||||
|
||||
app.use(express.static('public'));
|
||||
|
||||
// routes
|
||||
app.use('/api/test', testRouter);
|
||||
|
||||
// run the API
|
||||
app.listen(process.env.PORT, async () => {
|
||||
log(`running at port ${process.env.PORT}`);
|
||||
});
|
||||
22
api/jsconfig.json
Normal file
22
api/jsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
}
|
||||
}
|
||||
0
api/logs/access.log
Normal file
0
api/logs/access.log
Normal file
26
api/modules/database.js
Normal file
26
api/modules/database.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import mysql from 'mysql2';
|
||||
|
||||
const connection = mysql.createConnection({
|
||||
host: process.env.DATABASE_HOST,
|
||||
user: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
database: process.env.DATABASE_NAME,
|
||||
});
|
||||
const pool = mysql.createPool({
|
||||
host: process.env.DATABASE_HOST,
|
||||
user: process.env.DATABASE_USER,
|
||||
password: process.env.DATABASE_PASSWORD,
|
||||
database: process.env.DATABASE_NAME,
|
||||
}).promise();
|
||||
|
||||
function createPool(host, user, password, db) {
|
||||
const newPool = mysql.createPool({
|
||||
host: host,
|
||||
user: user,
|
||||
password: password,
|
||||
database: db,
|
||||
}).promise();
|
||||
return newPool;
|
||||
}
|
||||
|
||||
export { connection, pool, createPool };
|
||||
19
api/modules/log.js
Normal file
19
api/modules/log.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import pino from 'pino';
|
||||
|
||||
const logger = pino();
|
||||
|
||||
export function log(x) {
|
||||
logger.info(x);
|
||||
}
|
||||
|
||||
export function debug(x) {
|
||||
logger.debug(x);
|
||||
}
|
||||
|
||||
export function warn(x) {
|
||||
logger.warn(x);
|
||||
}
|
||||
|
||||
export function error(x) {
|
||||
logger.error(x);
|
||||
}
|
||||
20
api/modules/random.js
Normal file
20
api/modules/random.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export function random(x, y) {
|
||||
return crypto.randomInt(x, y + 1);
|
||||
}
|
||||
|
||||
export function random2(min, max) {
|
||||
const range = max - min + 1;
|
||||
const byteLength = Math.ceil(Math.log2(range) / 8);
|
||||
let randomValue;
|
||||
|
||||
do {
|
||||
const randomBytes = crypto.randomBytes(byteLength);
|
||||
randomValue = parseInt(randomBytes.toString('hex'), 16);
|
||||
} while (randomValue >= range);
|
||||
|
||||
return randomValue + min;
|
||||
}
|
||||
|
||||
export function randomHEX(x) {
|
||||
return crypto.randomBytes(x).toString('hex');
|
||||
}
|
||||
54
api/modules/requestHandler.js
Normal file
54
api/modules/requestHandler.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import slowDown from 'express-slow-down';
|
||||
import http from 'http';
|
||||
import os from 'os';
|
||||
|
||||
const requestLimiter = rateLimit({
|
||||
windowMs: 60 * 1000,
|
||||
max: 5,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: 'Too many requests from this IP, please try again later',
|
||||
});
|
||||
|
||||
const speedLimiter = slowDown({
|
||||
windowMs: 60 * 1000,
|
||||
delayAfter: 5,
|
||||
delayMs: (hits) => hits * 100,
|
||||
});
|
||||
|
||||
function checkSystemLoad(req, res, next) {
|
||||
const load = os.loadavg()[0];
|
||||
const cores = os.cpus().length;
|
||||
const threshold = cores * 0.7;
|
||||
|
||||
if (load > threshold) {
|
||||
return respondWithStatus(res, 503, 'API Unavailable - System Overloaded!');
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
function respondWithStatus(res, statusCode, message) {
|
||||
const response = { status: statusCode, message: message };
|
||||
if (statusCode >= 400 && statusCode <= 599) {
|
||||
response.error = http.STATUS_CODES[statusCode];
|
||||
}
|
||||
return res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
function respondWithStatusJSON(res, statusCode, JSON) {
|
||||
const response = { status: statusCode, JSON };
|
||||
if (statusCode >= 400 && statusCode <= 599) {
|
||||
response.error = http.STATUS_CODES[statusCode];
|
||||
}
|
||||
return res.status(statusCode).json(response);
|
||||
}
|
||||
|
||||
export {
|
||||
requestLimiter,
|
||||
speedLimiter,
|
||||
checkSystemLoad,
|
||||
respondWithStatus,
|
||||
respondWithStatusJSON,
|
||||
};
|
||||
52
api/modules/token.js
Normal file
52
api/modules/token.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable no-undef */
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Level } from 'level';
|
||||
import { respondWithStatus } from './requestHandler';
|
||||
import { pool } from './database';
|
||||
|
||||
|
||||
// Set up LevelDB instance
|
||||
const db = new Level('./tokensDB');
|
||||
|
||||
// Generate a new JWT
|
||||
const generateToken = async (userId, password) => {
|
||||
const token = jwt.sign({ userId: userId, password: password }, process.env.JWT_SECRET, { expiresIn: '7d' });
|
||||
await db.put(token);
|
||||
return token;
|
||||
};
|
||||
|
||||
// Middleware to verify the JWT and set req.userId
|
||||
const verifyToken = async (req, res, next) => {
|
||||
const token = req.headers.authorization;
|
||||
if (!token) return await respondWithStatus(res, 401, 'No token provided');
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
req.userId = decoded.userId;
|
||||
|
||||
const [rows] = await pool.execute(
|
||||
'SELECT * FROM users WHERE id = ? LIMIT 1', [req.userId],
|
||||
);
|
||||
if (!rows.length) return await respondWithStatus(res, 404, 'User not found!');
|
||||
const passwordMatch = await Bun.password.verify(decoded.password, rows[0].password);
|
||||
if (!passwordMatch) return await respondWithStatus(res, 401, 'Token is invalid');
|
||||
|
||||
const now = Date.now().valueOf() / 1000;
|
||||
if (decoded.exp - now < 36000) {
|
||||
const newToken = generateToken(req.userId, decoded.password);
|
||||
res.cookie('token', newToken, {
|
||||
expires: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
});
|
||||
res.set('Authorization', newToken);
|
||||
}
|
||||
next();
|
||||
}
|
||||
catch (error) {
|
||||
return await respondWithStatus(res, 401, 'Invalid user');
|
||||
}
|
||||
};
|
||||
|
||||
export { generateToken, verifyToken };
|
||||
20
api/package.json
Normal file
20
api/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "nuitdelinfo2023-api",
|
||||
"module": "index.js",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"bun-types": "latest",
|
||||
"eslint": "^8.55.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"level": "^8.0.0",
|
||||
"morgan": "^1.10.0",
|
||||
"mysql2": "^3.6.5"
|
||||
}
|
||||
}
|
||||
40
api/routes/leaderboard.js
Normal file
40
api/routes/leaderboard.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/* eslint-disable no-undef */
|
||||
import express from 'express';
|
||||
import { pool } from '../modules/database.js';
|
||||
import { requestLimiter, respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', requestLimiter, async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT * FROM users');
|
||||
if (!rows.length) return await respondWithStatus(res, 404, 'There are no users');
|
||||
|
||||
return await respondWithStatusJSON(res, 200, {
|
||||
message: 'Successfully retrieved users',
|
||||
users: rows,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:username', requestLimiter, async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.execute('SELECT u.*, (SELECT COUNT(*) + 1 FROM users AS uu WHERE uu.score > u.score) AS rank FROM users AS u WHERE username = ? LIMIT 1', [req.params.username]);
|
||||
if (!rows.length) return await respondWithStatus(res, 404, 'There are no users');
|
||||
|
||||
return await respondWithStatusJSON(res, 200, {
|
||||
message: 'Successfully retrieved user',
|
||||
users: rows[0],
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
35
api/routes/test.js
Normal file
35
api/routes/test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import express from 'express';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received GET request' });
|
||||
|
||||
});
|
||||
|
||||
router.post('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received POST request' });
|
||||
|
||||
});
|
||||
|
||||
router.patch('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received PUT request' });
|
||||
|
||||
});
|
||||
|
||||
router.put('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received PUT request' });
|
||||
|
||||
});
|
||||
|
||||
router.delete('/', (req, res) => {
|
||||
|
||||
res.status(200).json({ code: 200, message:'Received DELETE request' });
|
||||
|
||||
});
|
||||
|
||||
export default router;
|
||||
79
api/routes/users.js
Normal file
79
api/routes/users.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/* eslint-disable no-undef */
|
||||
import express from 'express';
|
||||
import { pool } from '../modules/database.js';
|
||||
import { generateToken } from '../modules/token.js';
|
||||
import { requestLimiter, respondWithStatus, respondWithStatusJSON } from '../modules/requestHandler.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/register', requestLimiter, async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if ([ username, password ].every(Boolean)) {
|
||||
try {
|
||||
const [existingUsername] = await pool.execute('SELECT * FROM users WHERE username = ? LIMIT 1', [username]);
|
||||
if (existingUsername.length) {
|
||||
return await respondWithStatus(res, 400, 'Username is already taken');
|
||||
}
|
||||
|
||||
const hashedPassword = await Bun.password.hash(password);
|
||||
const [result] = await pool.execute(
|
||||
'INSERT INTO users (username, password) VALUES (?, ?)', [ username, hashedPassword ],
|
||||
);
|
||||
if (result.affectedRows === 0) {
|
||||
return await respondWithStatus(res, 500, 'Error storing user');
|
||||
}
|
||||
return await respondWithStatus(res, 200, 'Successfully registered');
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Missing fields');
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/login', requestLimiter, async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if ([username, password].every(Boolean)) {
|
||||
try {
|
||||
const [rows] = await pool.execute(
|
||||
'SELECT * FROM users WHERE username = ? LIMIT 1', [username],
|
||||
);
|
||||
if (!rows.length) {
|
||||
return await respondWithStatus(res, 404, 'Incorrect username or email');
|
||||
}
|
||||
const user = rows[0];
|
||||
const passwordMatch = await Bun.password.verify(password, user.password);
|
||||
if (!passwordMatch) return await respondWithStatus(res, 401, 'Incorrect password');
|
||||
|
||||
const token = await generateToken(user.id, password);
|
||||
res.cookie('token', token, {
|
||||
expires: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
});
|
||||
return await respondWithStatusJSON(res, 200, {
|
||||
message: 'Login successful',
|
||||
token: token,
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
return await respondWithStatus(res, 500, 'An error has occured');
|
||||
}
|
||||
}
|
||||
else {
|
||||
return await respondWithStatus(res, 400, 'Missing fields');
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user