build: include production build artifacts in repo
This commit is contained in:
+133
@@ -0,0 +1,133 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
import { Body, Controller, Get, HttpCode, Module, Post, Req, Res } from "@nestjs/common";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
import { Throttle } from "@nestjs/throttler";
|
||||
import { changePasswordSchema, loginSchema, } from "@imen/shared";
|
||||
import { env, isProd } from "../config/env.js";
|
||||
import { AdminAuth, CurrentUser } from "../common/auth.js";
|
||||
import { ZodPipe } from "../common/zod.pipe.js";
|
||||
import { AuthService } from "./auth.service.js";
|
||||
const COOKIE = "imen_rt";
|
||||
const COOKIE_PATH = "/api/admin/auth";
|
||||
const ua = (req) => req.headers["user-agent"] ?? "";
|
||||
const readCookie = (req) => req.cookies?.[COOKIE];
|
||||
let AuthController = class AuthController {
|
||||
auth;
|
||||
constructor(auth) {
|
||||
this.auth = auth;
|
||||
}
|
||||
async login(body, req, res) {
|
||||
return this.respond(res, await this.auth.login(body, ua(req)));
|
||||
}
|
||||
async refresh(req, res) {
|
||||
try {
|
||||
return this.respond(res, await this.auth.refresh(readCookie(req), ua(req)));
|
||||
}
|
||||
catch (e) {
|
||||
res.clearCookie(COOKIE, { path: COOKIE_PATH });
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
async logout(req, res) {
|
||||
await this.auth.logout(readCookie(req));
|
||||
res.clearCookie(COOKIE, { path: COOKIE_PATH });
|
||||
}
|
||||
me(user) {
|
||||
return this.auth.me(user.id);
|
||||
}
|
||||
async changePassword(body, user) {
|
||||
await this.auth.changePassword(user.id, body);
|
||||
}
|
||||
respond(res, t) {
|
||||
res.cookie(COOKIE, t.refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: isProd && env.COOKIE_SECURE,
|
||||
sameSite: "strict",
|
||||
path: COOKIE_PATH,
|
||||
expires: t.refreshExpiresAt,
|
||||
});
|
||||
return { accessToken: t.accessToken, user: t.user };
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
Post("login"),
|
||||
HttpCode(200),
|
||||
Throttle({ default: { limit: 8, ttl: 60_000 } }),
|
||||
__param(0, Body(new ZodPipe(loginSchema))),
|
||||
__param(1, Req()),
|
||||
__param(2, Res({ passthrough: true })),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], AuthController.prototype, "login", null);
|
||||
__decorate([
|
||||
Post("refresh"),
|
||||
HttpCode(200),
|
||||
Throttle({ default: { limit: 30, ttl: 60_000 } }),
|
||||
__param(0, Req()),
|
||||
__param(1, Res({ passthrough: true })),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], AuthController.prototype, "refresh", null);
|
||||
__decorate([
|
||||
Post("logout"),
|
||||
HttpCode(204),
|
||||
__param(0, Req()),
|
||||
__param(1, Res({ passthrough: true })),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], AuthController.prototype, "logout", null);
|
||||
__decorate([
|
||||
Get("me"),
|
||||
AdminAuth(),
|
||||
__param(0, CurrentUser()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", void 0)
|
||||
], AuthController.prototype, "me", null);
|
||||
__decorate([
|
||||
Post("change-password"),
|
||||
HttpCode(204),
|
||||
AdminAuth(),
|
||||
__param(0, Body(new ZodPipe(changePasswordSchema))),
|
||||
__param(1, CurrentUser()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], AuthController.prototype, "changePassword", null);
|
||||
AuthController = __decorate([
|
||||
Controller("admin/auth"),
|
||||
__metadata("design:paramtypes", [AuthService])
|
||||
], AuthController);
|
||||
export { AuthController };
|
||||
let AuthModule = class AuthModule {
|
||||
};
|
||||
AuthModule = __decorate([
|
||||
Module({
|
||||
imports: [
|
||||
JwtModule.register({
|
||||
global: true,
|
||||
secret: env.JWT_SECRET,
|
||||
signOptions: { expiresIn: env.JWT_ACCESS_TTL },
|
||||
}),
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
exports: [AuthService],
|
||||
})
|
||||
], AuthModule);
|
||||
export { AuthModule };
|
||||
//# sourceMappingURL=auth.controller.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth.controller.js","sourceRoot":"","sources":["../../src/auth/auth.controller.ts"],"names":[],"mappings":"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"}
|
||||
Vendored
+127
@@ -0,0 +1,127 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
import { createHash, randomBytes } from "node:crypto";
|
||||
import { BadRequestException, Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { hash, verify } from "@node-rs/argon2";
|
||||
import { env } from "../config/env.js";
|
||||
import { AuditService } from "../common/common.module.js";
|
||||
import { PrismaService } from "../prisma/prisma.service.js";
|
||||
export const hashPassword = (password) => hash(password);
|
||||
const sha256 = (v) => createHash("sha256").update(v).digest("hex");
|
||||
export const toUserDto = (u) => ({
|
||||
id: u.id,
|
||||
phone: u.phone,
|
||||
name: u.name,
|
||||
role: u.role,
|
||||
isActive: u.isActive,
|
||||
lastLoginAt: u.lastLoginAt?.toISOString() ?? null,
|
||||
createdAt: u.createdAt.toISOString(),
|
||||
});
|
||||
const INVALID_LOGIN = "شماره موبایل یا رمز عبور اشتباه است";
|
||||
const DUMMY_HASH = hashPassword(randomBytes(16).toString("hex"));
|
||||
let AuthService = class AuthService {
|
||||
prisma;
|
||||
jwt;
|
||||
audit;
|
||||
constructor(prisma, jwt, audit) {
|
||||
this.prisma = prisma;
|
||||
this.jwt = jwt;
|
||||
this.audit = audit;
|
||||
}
|
||||
async login(input, userAgent) {
|
||||
const user = await this.prisma.adminUser.findUnique({ where: { phone: input.phone } });
|
||||
const ok = await verify(user?.passwordHash ?? (await DUMMY_HASH), input.password).catch(() => false);
|
||||
if (!user || !ok || !user.isActive)
|
||||
throw new UnauthorizedException(INVALID_LOGIN);
|
||||
const updated = await this.prisma.adminUser.update({
|
||||
where: { id: user.id },
|
||||
data: { lastLoginAt: new Date() },
|
||||
});
|
||||
this.audit.log(user.id, "login", "user", user.id, user.phone);
|
||||
return this.issue(updated, userAgent);
|
||||
}
|
||||
async refresh(token, userAgent) {
|
||||
if (!token)
|
||||
throw new UnauthorizedException("نشست شما منقضی شده است");
|
||||
const stored = await this.prisma.refreshToken.findUnique({
|
||||
where: { tokenHash: sha256(token) },
|
||||
include: { user: true },
|
||||
});
|
||||
if (!stored)
|
||||
throw new UnauthorizedException("نشست شما منقضی شده است");
|
||||
if (stored.revokedAt) {
|
||||
await this.revokeAll(stored.userId);
|
||||
throw new UnauthorizedException("نشست شما منقضی شده است");
|
||||
}
|
||||
if (stored.expiresAt < new Date() || !stored.user.isActive) {
|
||||
throw new UnauthorizedException("نشست شما منقضی شده است");
|
||||
}
|
||||
await this.prisma.refreshToken.update({
|
||||
where: { id: stored.id },
|
||||
data: { revokedAt: new Date() },
|
||||
});
|
||||
return this.issue(stored.user, userAgent);
|
||||
}
|
||||
async logout(token) {
|
||||
if (!token)
|
||||
return;
|
||||
await this.prisma.refreshToken.updateMany({
|
||||
where: { tokenHash: sha256(token), revokedAt: null },
|
||||
data: { revokedAt: new Date() },
|
||||
});
|
||||
}
|
||||
async changePassword(userId, input) {
|
||||
const user = await this.prisma.adminUser.findUniqueOrThrow({ where: { id: userId } });
|
||||
if (!(await verify(user.passwordHash, input.currentPassword).catch(() => false))) {
|
||||
throw new BadRequestException("رمز عبور فعلی اشتباه است");
|
||||
}
|
||||
await this.prisma.adminUser.update({
|
||||
where: { id: userId },
|
||||
data: { passwordHash: await hashPassword(input.newPassword) },
|
||||
});
|
||||
this.audit.log(userId, "change-password", "user", userId, user.phone);
|
||||
}
|
||||
async me(userId) {
|
||||
return toUserDto(await this.prisma.adminUser.findUniqueOrThrow({ where: { id: userId } }));
|
||||
}
|
||||
revokeAll(userId) {
|
||||
return this.prisma.refreshToken.updateMany({
|
||||
where: { userId, revokedAt: null },
|
||||
data: { revokedAt: new Date() },
|
||||
});
|
||||
}
|
||||
async issue(user, userAgent) {
|
||||
const payload = { sub: user.id, role: user.role };
|
||||
const accessToken = await this.jwt.signAsync(payload);
|
||||
const refreshToken = randomBytes(48).toString("base64url");
|
||||
const refreshExpiresAt = new Date(Date.now() + env.REFRESH_TTL_DAYS * 86_400_000);
|
||||
await this.prisma.refreshToken.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
tokenHash: sha256(refreshToken),
|
||||
expiresAt: refreshExpiresAt,
|
||||
userAgent: userAgent.slice(0, 300),
|
||||
},
|
||||
});
|
||||
void this.prisma.refreshToken
|
||||
.deleteMany({ where: { expiresAt: { lt: new Date(Date.now() - 86_400_000) } } })
|
||||
.catch(() => undefined);
|
||||
return { accessToken, refreshToken, refreshExpiresAt, user: toUserDto(user) };
|
||||
}
|
||||
};
|
||||
AuthService = __decorate([
|
||||
Injectable(),
|
||||
__metadata("design:paramtypes", [PrismaService,
|
||||
JwtService,
|
||||
AuditService])
|
||||
], AuthService);
|
||||
export { AuthService };
|
||||
//# sourceMappingURL=auth.service.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user