Initial commit

This commit is contained in:
Michilis
2025-12-14 23:08:45 -03:00
commit 1e1753dff3
58 changed files with 18294 additions and 0 deletions

195
backend/src/routes/auth.js Normal file
View File

@@ -0,0 +1,195 @@
import { Router } from 'express';
import { authService } from '../services/auth.js';
import { authenticate } from '../middleware/auth.js';
import { validateBody, signupSchema, loginSchema, nostrLoginSchema } from '../utils/validation.js';
import { prisma } from '../config/database.js';
import { randomBytes } from 'crypto';
const router = Router();
// Signup
router.post('/signup', validateBody(signupSchema), async (req, res, next) => {
try {
const result = await authService.signup(req.body);
// Set refresh token as httpOnly cookie
res.cookie('refresh_token', result.refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
res.status(201).json({
user: result.user,
accessToken: result.accessToken,
expiresIn: result.expiresIn,
});
} catch (error) {
next(error);
}
});
// Login
router.post('/login', validateBody(loginSchema), async (req, res, next) => {
try {
const result = await authService.login(req.body);
res.cookie('refresh_token', result.refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({
user: result.user,
accessToken: result.accessToken,
expiresIn: result.expiresIn,
});
} catch (error) {
next(error);
}
});
// Refresh token
router.post('/refresh', async (req, res, next) => {
try {
const refreshToken = req.cookies?.refresh_token || req.body.refreshToken;
if (!refreshToken) {
return res.status(401).json({ error: 'Refresh token required' });
}
const result = await authService.refreshTokens(refreshToken);
res.json({
user: result.user,
accessToken: result.accessToken,
expiresIn: result.expiresIn,
});
} catch (error) {
next(error);
}
});
// Logout
router.post('/logout', async (req, res, next) => {
try {
const refreshToken = req.cookies?.refresh_token;
await authService.logout(refreshToken);
res.clearCookie('refresh_token');
res.json({ success: true });
} catch (error) {
next(error);
}
});
// Get current user
router.get('/me', authenticate, async (req, res) => {
res.json({ user: req.user });
});
// Update profile
router.patch('/me', authenticate, async (req, res, next) => {
try {
const { displayName, lightningAddress, avatarUrl } = req.body;
const updated = await prisma.user.update({
where: { id: req.user.id },
data: {
displayName,
lightningAddress,
avatarUrl,
},
select: {
id: true,
email: true,
displayName: true,
avatarUrl: true,
role: true,
lightningAddress: true,
createdAt: true,
},
});
res.json({ user: updated });
} catch (error) {
next(error);
}
});
// Nostr challenge
router.post('/nostr/challenge', async (req, res) => {
const challenge = randomBytes(32).toString('hex');
// Store challenge temporarily (in production, use Redis with TTL)
// For now, we'll include it in the response and verify the signature
res.json({
challenge,
message: `Sign this message to login to LNPaywall: ${challenge}`,
});
});
// Nostr verify
router.post('/nostr/verify', validateBody(nostrLoginSchema), async (req, res, next) => {
try {
const result = await authService.nostrLogin(req.body);
res.cookie('refresh_token', result.refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({
user: result.user,
accessToken: result.accessToken,
expiresIn: result.expiresIn,
});
} catch (error) {
next(error);
}
});
// OAuth start (redirect to provider)
router.get('/oauth/:provider/start', (req, res) => {
const { provider } = req.params;
const { redirect } = req.query;
// Store redirect URL in session/cookie for after OAuth callback
if (redirect) {
res.cookie('oauth_redirect', redirect, {
httpOnly: true,
maxAge: 10 * 60 * 1000, // 10 minutes
});
}
// In production, implement full OAuth flow
// For now, return instructions
res.json({
message: `OAuth ${provider} not fully implemented. Use email/password or Nostr login.`,
supported: ['google', 'github'],
});
});
// OAuth callback
router.get('/oauth/:provider/callback', async (req, res, next) => {
try {
const { provider } = req.params;
const { code } = req.query;
// In production, exchange code for tokens and get user info
res.json({
message: `OAuth ${provider} callback received`,
code: code ? 'received' : 'missing',
});
} catch (error) {
next(error);
}
});
export default router;