From 7db993fb0e1e3cbfad4d061db90aacd7b2ef427e Mon Sep 17 00:00:00 2001 From: Ryahn Date: Mon, 27 Jan 2025 17:57:51 -0500 Subject: [PATCH] Update typos and added scripts --- scripts/delete_user.js | 27 ++++++++ scripts/generate_password.js | 36 +++++++++++ scripts/make_user.js | 27 ++++++++ ...20250127223039_add_cascade_to_tempemail.js | 17 +++++ src/db/models/User.js | 15 +++++ src/db/seeds/admin_user.js | 2 + src/routes/auth.js | 62 ++++++++++++++++--- src/routes/email.js | 47 ++++++++++++-- src/routes/index.js | 8 +++ 9 files changed, 230 insertions(+), 11 deletions(-) create mode 100644 scripts/delete_user.js create mode 100644 scripts/generate_password.js create mode 100644 scripts/make_user.js create mode 100644 src/db/migrations/20250127223039_add_cascade_to_tempemail.js create mode 100644 src/routes/index.js diff --git a/scripts/delete_user.js b/scripts/delete_user.js new file mode 100644 index 0000000..2f61561 --- /dev/null +++ b/scripts/delete_user.js @@ -0,0 +1,27 @@ +const User = require('../src/db/models/User'); +const { Model } = require('objection'); +const Knex = require('knex'); +const knexConfig = require('../src/config/database'); +const knex = Knex(knexConfig.development); +Model.knex(knex); + +const email = process.argv[2]; +const id = process.argv[3]; + +if (!email && !id) { + console.error('Usage: node delete_user.js || '); + process.exit(1); +} + +async function main() { + let user; + if (id) { + user = await User.query().where('id', id).delete(); + } else { + user = await User.query().where('email', email).delete(); + } + console.log(`User deleted`); + process.exit(0); +} + +main(); \ No newline at end of file diff --git a/scripts/generate_password.js b/scripts/generate_password.js new file mode 100644 index 0000000..8f6bb46 --- /dev/null +++ b/scripts/generate_password.js @@ -0,0 +1,36 @@ +const bcrypt = require('bcrypt'); +const User = require('../src/db/models/User'); +const { Model } = require('objection'); +const Knex = require('knex'); +const knexConfig = require('../src/config/database'); +const knex = Knex(knexConfig.development); +Model.knex(knex); + +const password = process.argv[2]; +const email = process.argv[3]; + +if (!password && !email) { + console.error('Usage: node generate_password.js [email]'); + process.exit(1); +} + +async function main() { + let user; + if (email) { + user = await User.query().where('email', email).first(); + if (!user) { + console.error('User not found'); + process.exit(1); + } + + user.password = bcrypt.hashSync(password, 10); + await user.$query().patch(); + console.log(`Password updated for user ${user.email}`); + process.exit(0); + } else { + console.log(bcrypt.hashSync(password, 10)); + process.exit(0); + } +} + +main(); \ No newline at end of file diff --git a/scripts/make_user.js b/scripts/make_user.js new file mode 100644 index 0000000..c98c70d --- /dev/null +++ b/scripts/make_user.js @@ -0,0 +1,27 @@ +const User = require('../src/db/models/User'); +const { Model } = require('objection'); +const Knex = require('knex'); +const knexConfig = require('../src/config/database'); +const knex = Knex(knexConfig.development); +Model.knex(knex); + +const email = process.argv[2]; +const password = process.argv[3]; +const isAdmin = process.argv[4] === 'true'; + +if (!email || !password) { + console.error('Usage: node make_user.js [isAdmin]'); + process.exit(1); +} + +async function main() { + const user = await User.query().insert({ + email: email, + password: password, + is_admin: isAdmin + }); + console.log(`User created: ${user.email}`); + process.exit(0); +} + +main(); \ No newline at end of file diff --git a/src/db/migrations/20250127223039_add_cascade_to_tempemail.js b/src/db/migrations/20250127223039_add_cascade_to_tempemail.js new file mode 100644 index 0000000..23da186 --- /dev/null +++ b/src/db/migrations/20250127223039_add_cascade_to_tempemail.js @@ -0,0 +1,17 @@ +exports.up = function(knex) { + return knex.schema.alterTable('temp_emails', table => { + table.dropForeign('user_id'); + + table.foreign('user_id') + .references('users.id') + .onDelete('CASCADE'); + }); + }; + + exports.down = function(knex) { + return knex.schema.alterTable('temp_emails', table => { + table.dropForeign('user_id'); + table.foreign('user_id') + .references('users.id'); + }); + }; \ No newline at end of file diff --git a/src/db/models/User.js b/src/db/models/User.js index 6fdd12b..f7cf9c8 100644 --- a/src/db/models/User.js +++ b/src/db/models/User.js @@ -6,6 +6,21 @@ class User extends BaseModel { return 'users'; } + static get relationMappings() { + const TempEmail = require('./TempEmail'); + return { + tempEmails: { + relation: BaseModel.HasManyRelation, + modelClass: TempEmail, + join: { + from: 'users.id', + to: 'temp_emails.user_id' + }, + onDelete: 'CASCADE' + } + }; + } + async $beforeInsert() { await super.$beforeInsert(); this.password = await bcrypt.hash(this.password, 10); diff --git a/src/db/seeds/admin_user.js b/src/db/seeds/admin_user.js index 37a81e8..40ee2f0 100644 --- a/src/db/seeds/admin_user.js +++ b/src/db/seeds/admin_user.js @@ -9,6 +9,8 @@ const config = require('../../config/haraka'); */ exports.seed = async function(knex) { await knex('users').del(); + await knex('temp_emails').del(); + await knex('messages').del(); const hashedPassword = await bcrypt.hash('admin', 10); const [userId] = await knex('users').insert([ diff --git a/src/routes/auth.js b/src/routes/auth.js index d84742f..1091b54 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -1,9 +1,32 @@ const express = require('express'); const router = express.Router(); const jwt = require('jsonwebtoken'); -const { User } = require('../db/models/User'); +const User = require('../db/models/User'); const config = require('../config/haraka'); + +/** + * @swagger + * /auth/login: + * post: + * summary: Login and get API key + * description: Login with email and password to get an API key + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * description: User's email + * example: user@example.com + * password: + * type: string + * description: User's password + * example: password123 + */ router.post('/login', async (req, res) => { try { const { email, password } = req.body; @@ -13,16 +36,41 @@ router.post('/login', async (req, res) => { return res.status(401).json({ error: 'Invalid credentials' }); } - const token = jwt.sign( - { id: user.id, email: user.email, is_admin: user.is_admin }, - config.auth.jwtSecret, - { expiresIn: config.auth.tokenExpiry } - ); + const generateToken = () => { + return jwt.sign( + { id: user.id, email: user.email, is_admin: user.is_admin }, + config.auth.jwtSecret, + { expiresIn: config.auth.tokenExpiry } + ); + }; - await User.query().where('id', user.id).update({ api_key: token }); + let currentToken = await User.query() + .select('api_key') + .where('id', user.id) + .first(); + + let token; + + if (currentToken && currentToken.api_key) { + try { + jwt.verify(currentToken.api_key, config.auth.jwtSecret); + token = currentToken.api_key; + } catch (tokenError) { + token = generateToken(); + await User.query() + .where('id', user.id) + .update({ api_key: token }); + } + } else { + token = generateToken(); + await User.query() + .where('id', user.id) + .update({ api_key: token }); + } res.json({ token }); } catch (error) { + console.error('Login error:', error); res.status(500).json({ error: error.message }); } }); diff --git a/src/routes/email.js b/src/routes/email.js index 651780d..f1726d6 100644 --- a/src/routes/email.js +++ b/src/routes/email.js @@ -33,7 +33,24 @@ function generateUniqueName() { return `${formattedName}${randomNumber}`; } -router.post('/', async (req, res) => { +/** + * @swagger + * /email/generate: + * post: + * summary: Generate a temporary email + * description: Generate a temporary email with a random domain + * requestBody: + * required: false + * content: + * application/json: + * schema: + * type: object + * properties: + * name: + * type: string + * description: Name for the temporary email + */ +router.post('/generate', async (req, res) => { try { const randomDomain = await Domain.query() .where('active', true) @@ -59,14 +76,36 @@ router.post('/', async (req, res) => { } }); -router.get('/', async (req, res) => { +/** + * @swagger + * /email/list: + * get: + * summary: List all temporary emails + * description: Get a list of all temporary emails for the authenticated user + */ +router.get('/list', async (req, res) => { const tempEmails = await TempEmail.query().where('user_id', req.user.id); res.json(tempEmails); }); -router.delete('/:id', async (req, res) => { +/** + * @swagger + * /email/delete/{id}: + * delete: + * summary: Delete a temporary email + * description: Delete a temporary email by its ID + * parameters: + * - in: path + * name: id + * required: true + * description: ID of the temporary email to delete + */ +router.delete('/delete/:id', async (req, res) => { const tempEmail = await TempEmail.query().where('id', req.params.id).andWhere('user_id', req.user.id).delete(); - res.json(tempEmail); + if (!tempEmail) { + res.status(404).json({ status: 'error', message: 'Temp email not found' }); + } + res.json({ status: 'success', message: 'Temp email deleted' }); }); module.exports = router; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..431782f --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,8 @@ +const express = require('express'); +const router = express.Router(); + +router.get('/', async (req, res) => { + res.json({ message: 'Hello World' }); +}); + +module.exports = router; \ No newline at end of file