- Updated all component headers and documentation
- Changed navbar and footer branding
- Updated homepage hero badge
- Modified page title in index.html
- Simplified footer text to 'Built with ❤️'
- Consistent V2 capitalization across all references
229 lines
7.7 KiB
JavaScript
229 lines
7.7 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.AuthService = void 0;
|
|
const argon2_1 = __importDefault(require("argon2"));
|
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
const zod_1 = require("zod");
|
|
const prisma_1 = require("../lib/prisma");
|
|
const logger_1 = require("../lib/logger");
|
|
const loginSchema = zod_1.z.object({
|
|
email: zod_1.z.string().email(),
|
|
password: zod_1.z.string().min(8),
|
|
});
|
|
const registerSchema = zod_1.z.object({
|
|
email: zod_1.z.string().email(),
|
|
name: zod_1.z.string().min(2),
|
|
password: zod_1.z.string().min(8),
|
|
organizationName: zod_1.z.string().min(2).optional(),
|
|
});
|
|
class AuthService {
|
|
JWT_SECRET = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
|
|
JWT_EXPIRES_IN = '7d';
|
|
async hashPassword(password) {
|
|
try {
|
|
return await argon2_1.default.hash(password, {
|
|
type: argon2_1.default.argon2id,
|
|
memoryCost: 2 ** 16,
|
|
timeCost: 3,
|
|
parallelism: 1,
|
|
});
|
|
}
|
|
catch (error) {
|
|
logger_1.logger.error('Password hashing failed:', error);
|
|
throw new Error('Password hashing failed');
|
|
}
|
|
}
|
|
async verifyPassword(hash, password) {
|
|
try {
|
|
return await argon2_1.default.verify(hash, password);
|
|
}
|
|
catch (error) {
|
|
logger_1.logger.error('Password verification failed:', error);
|
|
return false;
|
|
}
|
|
}
|
|
generateToken(userId, email) {
|
|
return jsonwebtoken_1.default.sign({
|
|
userId,
|
|
email,
|
|
iat: Math.floor(Date.now() / 1000),
|
|
}, this.JWT_SECRET, { expiresIn: this.JWT_EXPIRES_IN });
|
|
}
|
|
verifyToken(token) {
|
|
try {
|
|
const decoded = jsonwebtoken_1.default.verify(token, this.JWT_SECRET);
|
|
return decoded;
|
|
}
|
|
catch (error) {
|
|
logger_1.logger.error('Token verification failed:', error);
|
|
throw new Error('Invalid token');
|
|
}
|
|
}
|
|
async login(data) {
|
|
const { email, password } = loginSchema.parse(data);
|
|
logger_1.logger.info(`Login attempt for email: ${email}`);
|
|
const user = await prisma_1.prisma.user.findUnique({
|
|
where: { email },
|
|
include: {
|
|
memberships: {
|
|
include: {
|
|
organization: {
|
|
select: {
|
|
name: true,
|
|
plan: true,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (!user) {
|
|
logger_1.logger.warn(`Login failed: User not found for email ${email}`);
|
|
throw new Error('Invalid credentials');
|
|
}
|
|
const isValidPassword = await this.verifyPassword(user.passwordHash, password);
|
|
if (!isValidPassword) {
|
|
logger_1.logger.warn(`Login failed: Invalid password for email ${email}`);
|
|
throw new Error('Invalid credentials');
|
|
}
|
|
await prisma_1.prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { lastLoginAt: new Date() }
|
|
});
|
|
const token = this.generateToken(user.id, user.email);
|
|
const authUser = {
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
memberships: user.memberships.map(m => ({
|
|
orgId: m.orgId,
|
|
role: m.role,
|
|
organization: {
|
|
name: m.organization.name,
|
|
plan: m.organization.plan,
|
|
}
|
|
}))
|
|
};
|
|
logger_1.logger.info(`Login successful for user: ${user.email}`);
|
|
return { user: authUser, token };
|
|
}
|
|
async register(data) {
|
|
const { email, name, password, organizationName } = registerSchema.parse(data);
|
|
logger_1.logger.info(`Registration attempt for email: ${email}`);
|
|
const existingUser = await prisma_1.prisma.user.findUnique({
|
|
where: { email }
|
|
});
|
|
if (existingUser) {
|
|
logger_1.logger.warn(`Registration failed: User already exists for email ${email}`);
|
|
throw new Error('User already exists');
|
|
}
|
|
const passwordHash = await this.hashPassword(password);
|
|
const result = await prisma_1.prisma.$transaction(async (tx) => {
|
|
const user = await tx.user.create({
|
|
data: {
|
|
email,
|
|
name,
|
|
passwordHash,
|
|
}
|
|
});
|
|
const organization = await tx.organization.create({
|
|
data: {
|
|
name: organizationName || `${name}'s Organization`,
|
|
plan: 'free',
|
|
}
|
|
});
|
|
await tx.orgMembership.create({
|
|
data: {
|
|
userId: user.id,
|
|
orgId: organization.id,
|
|
role: 'OWNER',
|
|
}
|
|
});
|
|
await tx.project.create({
|
|
data: {
|
|
name: 'Default Project',
|
|
orgId: organization.id,
|
|
settingsJson: {
|
|
description: 'Default project for redirect tracking',
|
|
defaultMethod: 'GET',
|
|
},
|
|
}
|
|
});
|
|
return { user, organization };
|
|
});
|
|
logger_1.logger.info(`Registration successful for user: ${email}`);
|
|
return {
|
|
id: result.user.id,
|
|
email: result.user.email,
|
|
name: result.user.name,
|
|
memberships: [{
|
|
orgId: result.organization.id,
|
|
role: 'OWNER',
|
|
organization: {
|
|
name: result.organization.name,
|
|
plan: result.organization.plan,
|
|
}
|
|
}]
|
|
};
|
|
}
|
|
async getUserById(userId) {
|
|
const user = await prisma_1.prisma.user.findUnique({
|
|
where: { id: userId },
|
|
include: {
|
|
memberships: {
|
|
include: {
|
|
organization: {
|
|
select: {
|
|
name: true,
|
|
plan: true,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (!user) {
|
|
return null;
|
|
}
|
|
return {
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
memberships: user.memberships.map(m => ({
|
|
orgId: m.orgId,
|
|
role: m.role,
|
|
organization: {
|
|
name: m.organization.name,
|
|
plan: m.organization.plan,
|
|
}
|
|
}))
|
|
};
|
|
}
|
|
async hasOrgAccess(userId, orgId) {
|
|
const membership = await prisma_1.prisma.orgMembership.findUnique({
|
|
where: {
|
|
orgId_userId: {
|
|
orgId,
|
|
userId,
|
|
}
|
|
}
|
|
});
|
|
return !!membership;
|
|
}
|
|
async getUserRole(userId, orgId) {
|
|
const membership = await prisma_1.prisma.orgMembership.findUnique({
|
|
where: {
|
|
orgId_userId: {
|
|
orgId,
|
|
userId,
|
|
}
|
|
}
|
|
});
|
|
return membership?.role || null;
|
|
}
|
|
}
|
|
exports.AuthService = AuthService;
|
|
//# sourceMappingURL=auth.service.js.map
|