diff options
| author | Andrew Lee <andrew@alee14.me> | 2025-03-24 15:42:10 -0400 |
|---|---|---|
| committer | Andrew Lee <andrew@alee14.me> | 2025-03-24 15:42:10 -0400 |
| commit | ad768e2b25b58d62a44aa2daeb1429a651d488e5 (patch) | |
| tree | cadfaee0b8998c4d0d13a2a03bf18cc55e495264 /bot/src/api | |
| parent | 0453bafa63ccd1057279a1be9286b3e7ebcb62d2 (diff) | |
| download | AleeBot-ad768e2b25b58d62a44aa2daeb1429a651d488e5.tar.gz AleeBot-ad768e2b25b58d62a44aa2daeb1429a651d488e5.tar.bz2 AleeBot-ad768e2b25b58d62a44aa2daeb1429a651d488e5.zip | |
Added JWT on API; Added back settings on Discord
Diffstat (limited to 'bot/src/api')
| -rw-r--r-- | bot/src/api/routes/auth.js | 73 | ||||
| -rw-r--r-- | bot/src/api/routes/quotes.js | 9 | ||||
| -rw-r--r-- | bot/src/api/routes/settings.js | 9 | ||||
| -rw-r--r-- | bot/src/api/server.js | 9 |
4 files changed, 89 insertions, 11 deletions
diff --git a/bot/src/api/routes/auth.js b/bot/src/api/routes/auth.js new file mode 100644 index 0000000..224a2d1 --- /dev/null +++ b/bot/src/api/routes/auth.js @@ -0,0 +1,73 @@ +import { Router } from 'express'; +import jwt from 'jsonwebtoken'; +import bcrypt from 'bcrypt'; +import dotenv from 'dotenv'; + +dotenv.config(); + +// Check if required environment variables are set +const requiredEnvVars = ['JWT_SECRET', 'AUTH_USERNAME', 'AUTH_PASSWORD_HASH']; +const missingVars = requiredEnvVars.filter(varName => !process.env[varName]); +if (missingVars.length > 0) { + console.error(`Missing required environment variables: ${missingVars.join(', ')}`); + console.error('For AUTH_PASSWORD_HASH, run bcrypt with the round of 10'); +} + +export function authRouter() { + const router = Router(); + + // Login endpoint + router.post('/login', async (req, res) => { + try { + const { username, password } = req.body; + + if (!username || !password) { + return res.status(400).json({ error: 'Username and password are required' }); + } + + // Check against environment variables + if (username !== process.env.API_USERNAME) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Verify password + const isPasswordValid = await bcrypt.compare(password, process.env.API_PASSWORD_HASH); + if (!isPasswordValid) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Generate JWT token + const token = jwt.sign( + { username: username }, + process.env.JWT_SECRET, + { expiresIn: '12h' } + ); + + res.json({ token }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} + +// Middleware to verify JWT token +export function verifyToken(req, res, next) { + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'No token provided' }); + } + + const token = authHeader.split(' ')[1]; + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); + } catch { + return res.status(403).json({ error: 'Invalid or expired token' }); + } +} diff --git a/bot/src/api/routes/quotes.js b/bot/src/api/routes/quotes.js index d39bb28..7f9f255 100644 --- a/bot/src/api/routes/quotes.js +++ b/bot/src/api/routes/quotes.js @@ -1,9 +1,10 @@ import { Router } from 'express'; import { pendingQuote, quote as newQuote } from '../../models/quote.js'; +import { verifyToken } from './auth.js'; export const quoteRouter = Router(); -quoteRouter.get('/quotes/pending', async (req, res) => { +quoteRouter.get('/quotes/pending', verifyToken, async (req, res) => { try { const quotes = await pendingQuote.findAll(); res.json(quotes); @@ -13,7 +14,7 @@ quoteRouter.get('/quotes/pending', async (req, res) => { } }); -quoteRouter.post('/quotes/add', async (req, res) => { +quoteRouter.post('/quotes/add', verifyToken, async (req, res) => { const { author, authorImage, quote, year, submitterID } = req.body; try { await newQuote.create({ @@ -30,7 +31,7 @@ quoteRouter.post('/quotes/add', async (req, res) => { } }); -quoteRouter.post('/quotes/approve', async (req, res) => { +quoteRouter.post('/quotes/approve', verifyToken, async (req, res) => { const { id } = req.body; try { const quote = await pendingQuote.findByPk(id); @@ -53,7 +54,7 @@ quoteRouter.post('/quotes/approve', async (req, res) => { } }); -quoteRouter.post('/quotes/reject', async (req, res) => { +quoteRouter.post('/quotes/reject', verifyToken, async (req, res) => { const { id } = req.body; try { const quote = await pendingQuote.findByPk(id); diff --git a/bot/src/api/routes/settings.js b/bot/src/api/routes/settings.js index ce28acd..bdef633 100644 --- a/bot/src/api/routes/settings.js +++ b/bot/src/api/routes/settings.js @@ -1,11 +1,12 @@ import { ChannelType } from 'discord.js'; import { Router } from 'express'; import { guildSettings } from '../../models/guild-settings.js'; +import { verifyToken } from './auth.js'; export function settingsRouter(client) { const router = Router(); - router.get('/settings/guild/:id', async (req, res) => { + router.get('/settings/guild/:id', verifyToken, async (req, res) => { try { const settings = await guildSettings.findOne({ where: { guildID: req.params.id } }); @@ -19,7 +20,6 @@ export function settingsRouter(client) { const channelInfo = { name: channel.name, id: channel.id, - position: channel.position, category: channel.parent ? channel.parent.name : 'No Category' }; @@ -42,9 +42,10 @@ export function settingsRouter(client) { } }); - router.post('/settings/guild', async (req, res) => { + router.post('/settings/guild/:id', verifyToken, async (req, res) => { try { - const { guildID, ...newSettings } = req.body; + const guildID = req.params.id; + const { ...newSettings } = req.body; const [updated] = await guildSettings.update(newSettings, { where: { guildID: guildID } }); if (updated) { const updatedSettings = await guildSettings.findOne({ where: { guildID: guildID } }); diff --git a/bot/src/api/server.js b/bot/src/api/server.js index 15211eb..9ad2026 100644 --- a/bot/src/api/server.js +++ b/bot/src/api/server.js @@ -6,6 +6,7 @@ import { readFileSync } from 'node:fs'; import { quoteRouter } from './routes/quotes.js'; import { settingsRouter } from './routes/settings.js'; +import { authRouter, verifyToken } from './routes/auth.js'; const app = express(); @@ -15,11 +16,13 @@ export const apiServer = (client) => { app.use('/api', quoteRouter); app.use('/api', settingsRouter(client)); + app.use('/api', authRouter()); app.get('/api/version', (req, res) => { const { version } = JSON.parse(readFileSync('./package.json', 'utf-8')); res.json({ - version: version + api_version: '1.0', + ab_version: version }); }); @@ -30,7 +33,7 @@ export const apiServer = (client) => { }); }); - app.get('/api/servers', (req, res) => { + app.get('/api/servers', verifyToken, (req, res) => { const guildsInfo = []; if (client.guilds.cache.size === 0) { @@ -52,7 +55,7 @@ export const apiServer = (client) => { }); - app.post('/api/leave', (req, res) => { + app.post('/api/leave', verifyToken, (req, res) => { const { id } = req.body; let guild = client.guilds.cache.get(id); |
