fixxed color selector in the category settings and fixxed drag and drop
Build APK / build (push) Canceled after 2m0s
Build APK / build (push) Canceled after 2m0s
subtask
This commit is contained in:
Vendored
+101
-9
@@ -1,4 +1,7 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const asyncHandler_1 = require("../utils/asyncHandler");
|
||||
@@ -8,7 +11,47 @@ const drizzle_orm_1 = require("drizzle-orm");
|
||||
const auth_1 = require("../utils/auth");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const zod_1 = require("zod");
|
||||
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
||||
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
const router = (0, express_1.Router)();
|
||||
// Rate limiting for auth endpoints
|
||||
const authLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 10, // limit each IP to 10 requests per windowMs
|
||||
message: {
|
||||
error: {
|
||||
code: 'RATE_LIMITED',
|
||||
message: 'Too many attempts. Please try again later.',
|
||||
},
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
const loginLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // stricter limit for login
|
||||
message: {
|
||||
error: {
|
||||
code: 'RATE_LIMITED',
|
||||
message: 'Too many login attempts. Please try again later.',
|
||||
},
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
const passwordResetLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 60 * 60 * 1000, // 1 hour
|
||||
max: 3, // very strict for password reset
|
||||
message: {
|
||||
error: {
|
||||
code: 'RATE_LIMITED',
|
||||
message: 'Too many password reset requests. Please try again later.',
|
||||
},
|
||||
},
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
const usernameSchema = zod_1.z
|
||||
.string()
|
||||
.min(3)
|
||||
@@ -22,15 +65,24 @@ const loginSchema = zod_1.z.object({
|
||||
username: zod_1.z.string(),
|
||||
password: zod_1.z.string(),
|
||||
});
|
||||
// In production, use bcrypt or argon2 for password hashing
|
||||
function hashPassword(password) {
|
||||
// Simple hash for demo - replace with bcrypt in production
|
||||
return Buffer.from(password).toString('base64');
|
||||
const forgotPasswordSchema = zod_1.z.object({
|
||||
username: zod_1.z.string().min(1),
|
||||
});
|
||||
const resetPasswordSchema = zod_1.z.object({
|
||||
token: zod_1.z.string().min(1),
|
||||
password: zod_1.z.string().min(8),
|
||||
});
|
||||
const BCRYPT_ROUNDS = 12;
|
||||
async function hashPassword(password) {
|
||||
return bcryptjs_1.default.hash(password, BCRYPT_ROUNDS);
|
||||
}
|
||||
function verifyPassword(password, hash) {
|
||||
return hashPassword(password) === hash;
|
||||
async function verifyPassword(password, hash) {
|
||||
return bcryptjs_1.default.compare(password, hash);
|
||||
}
|
||||
router.post('/register', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
function generateResetToken() {
|
||||
return crypto_1.default.randomBytes(32).toString('hex');
|
||||
}
|
||||
router.post('/register', authLimiter, (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const data = registerSchema.parse(req.body);
|
||||
const existing = await db_1.db.select().from(schema_1.users).where((0, drizzle_orm_1.eq)(schema_1.users.username, data.username)).limit(1);
|
||||
if (existing.length > 0) {
|
||||
@@ -41,7 +93,7 @@ router.post('/register', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
await db_1.db.insert(schema_1.users).values({
|
||||
id: userId,
|
||||
username: data.username,
|
||||
passwordHash: hashPassword(data.password),
|
||||
passwordHash: await hashPassword(data.password),
|
||||
createdAt: now,
|
||||
});
|
||||
const token = (0, auth_1.generateToken)({ userId, username: data.username });
|
||||
@@ -50,7 +102,7 @@ router.post('/register', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
token,
|
||||
});
|
||||
}));
|
||||
router.post('/login', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
router.post('/login', loginLimiter, (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const data = loginSchema.parse(req.body);
|
||||
const user = await db_1.db.select().from(schema_1.users).where((0, drizzle_orm_1.eq)(schema_1.users.username, data.username)).limit(1);
|
||||
if (user.length === 0 || !verifyPassword(data.password, user[0].passwordHash)) {
|
||||
@@ -72,5 +124,45 @@ router.get('/me', (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
}
|
||||
res.json({ id: user[0].id, username: user[0].username });
|
||||
}));
|
||||
router.post('/forgot-password', passwordResetLimiter, (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const data = forgotPasswordSchema.parse(req.body);
|
||||
const user = await db_1.db.select().from(schema_1.users).where((0, drizzle_orm_1.eq)(schema_1.users.username, data.username)).limit(1);
|
||||
// Always return success to prevent username enumeration
|
||||
if (user.length === 0) {
|
||||
res.json({ success: true });
|
||||
return;
|
||||
}
|
||||
const resetToken = generateResetToken();
|
||||
const resetTokenExpiry = Date.now() + 3600000; // 1 hour
|
||||
await db_1.db
|
||||
.update(schema_1.users)
|
||||
.set({ resetToken, resetTokenExpiry })
|
||||
.where((0, drizzle_orm_1.eq)(schema_1.users.id, user[0].id));
|
||||
// In production, send email with reset link
|
||||
// For now, return token in response (dev only)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
res.json({ success: true, resetToken });
|
||||
}
|
||||
else {
|
||||
res.json({ success: true });
|
||||
}
|
||||
}));
|
||||
router.post('/reset-password', passwordResetLimiter, (0, asyncHandler_1.asyncHandler)(async (req, res) => {
|
||||
const data = resetPasswordSchema.parse(req.body);
|
||||
const user = await db_1.db
|
||||
.select()
|
||||
.from(schema_1.users)
|
||||
.where((0, drizzle_orm_1.eq)(schema_1.users.resetToken, data.token))
|
||||
.limit(1);
|
||||
if (user.length === 0 || !user[0].resetTokenExpiry || user[0].resetTokenExpiry < Date.now()) {
|
||||
throw new errorHandler_1.AppError('INVALID_TOKEN', 'Invalid or expired reset token', 400);
|
||||
}
|
||||
const newPasswordHash = await hashPassword(data.password);
|
||||
await db_1.db
|
||||
.update(schema_1.users)
|
||||
.set({ passwordHash: newPasswordHash, resetToken: null, resetTokenExpiry: null })
|
||||
.where((0, drizzle_orm_1.eq)(schema_1.users.id, user[0].id));
|
||||
res.json({ success: true });
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=auth.js.map
|
||||
Reference in New Issue
Block a user