feat: implement full backend API for Pantree Phase 1 MVP
This commit is contained in:
103
src/routes/auth.js
Normal file
103
src/routes/auth.js
Normal file
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const authService = require('../services/authService');
|
||||
const { authenticate } = require('../middleware/authenticate');
|
||||
const { validate } = require('../utils/validate');
|
||||
const {
|
||||
signupSchema,
|
||||
signinSchema,
|
||||
googleAuthSchema,
|
||||
passwordResetRequestSchema,
|
||||
passwordResetConfirmSchema,
|
||||
restoreAccountSchema,
|
||||
} = require('../utils/validation');
|
||||
|
||||
// POST /v1/auth/signup
|
||||
router.post('/signup', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(signupSchema, req.body);
|
||||
const result = await authService.signup(data);
|
||||
return res.status(201).json(result);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /v1/auth/signin
|
||||
router.post('/signin', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(signinSchema, req.body);
|
||||
const result = await authService.signin(data);
|
||||
return res.status(200).json(result);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /v1/auth/google
|
||||
router.post('/google', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(googleAuthSchema, req.body);
|
||||
const result = await authService.googleAuth(data);
|
||||
const status = result.is_new_user ? 201 : 200;
|
||||
// Strip internal flag from response
|
||||
const { is_new_user, ...response } = result; // eslint-disable-line no-unused-vars
|
||||
return res.status(status).json(response);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /v1/auth/password-reset — request reset email
|
||||
router.post('/password-reset', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(passwordResetRequestSchema, req.body);
|
||||
await authService.requestPasswordReset(data);
|
||||
// Always 200 — never reveal whether email exists
|
||||
return res.status(200).json({
|
||||
message: 'If an account exists with this email, a reset link has been sent.',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /v1/auth/password-reset — complete reset with token
|
||||
router.put('/password-reset', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(passwordResetConfirmSchema, req.body);
|
||||
await authService.confirmPasswordReset(data);
|
||||
return res.status(200).json({
|
||||
message: 'Password updated successfully.',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /v1/auth/account — soft-delete authenticated user's account
|
||||
router.delete('/account', authenticate, async (req, res, next) => {
|
||||
try {
|
||||
await authService.deleteAccount(req.user.sub);
|
||||
return res.status(204).send();
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /v1/auth/restore-account — restore soft-deleted account
|
||||
router.post('/restore-account', async (req, res, next) => {
|
||||
try {
|
||||
const data = validate(restoreAccountSchema, req.body);
|
||||
const result = await authService.restoreAccount(data);
|
||||
return res.status(200).json(result);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user