fix some for api download pic

This commit is contained in:
Montech
2026-09-19 15:44:00 +03:30
parent 7227e9c61b
commit 79226c9f13
247 changed files with 409 additions and 199 deletions
+102 -2
View File
@@ -92400,6 +92400,53 @@ var require_admin_misc_module = __commonJS({
}
});
// dist/modules/admin-uploads/upload-path.js
var require_upload_path = __commonJS({
"dist/modules/admin-uploads/upload-path.js"(exports2) {
"use strict";
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.getUploadDir = getUploadDir;
exports2.mimeForUpload = mimeForUpload;
exports2.resolveStoredUpload = resolveStoredUpload;
var node_fs_1 = require("node:fs");
var node_path_12 = require("node:path");
function getUploadDir() {
return (0, node_path_12.resolve)(process.env.UPLOAD_DIR ?? (0, node_path_12.join)(process.cwd(), "storage", "uploads"));
}
var UPLOAD_MIME = {
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
".avif": "image/avif",
".svg": "image/svg+xml",
".pdf": "application/pdf"
};
function mimeForUpload(filePath) {
const dot = filePath.lastIndexOf(".");
const ext = dot >= 0 ? filePath.slice(dot).toLowerCase() : "";
return UPLOAD_MIME[ext];
}
function resolveStoredUpload(rawName) {
const safe = (0, node_path_12.basename)(rawName);
if (!safe || safe === "." || safe === "..")
return null;
const dir = getUploadDir();
const candidates = [(0, node_path_12.join)(dir, safe), (0, node_path_12.join)(dir, "uploads", safe)];
for (const candidate of candidates) {
const resolved = (0, node_path_12.resolve)(candidate);
const rel = (0, node_path_12.relative)(dir, resolved);
if (!rel || rel.startsWith("..") || (0, node_path_12.isAbsolute)(rel))
continue;
if ((0, node_fs_1.existsSync)(resolved) && (0, node_fs_1.statSync)(resolved).isFile())
return resolved;
}
return null;
}
}
});
// dist/modules/admin-uploads/admin-uploads.service.js
var require_admin_uploads_service = __commonJS({
"dist/modules/admin-uploads/admin-uploads.service.js"(exports2) {
@@ -92421,13 +92468,14 @@ var require_admin_uploads_service = __commonJS({
var promises_1 = require("node:fs/promises");
var node_path_12 = require("node:path");
var prisma_service_1 = require_prisma_service();
var upload_path_1 = require_upload_path();
var AdminUploadsService = class AdminUploadsService {
static {
AdminUploadsService_1 = this;
}
prisma;
/** Matches legacy's flat public/storage directory. */
dir = process.env.UPLOAD_DIR ?? (0, node_path_12.join)(process.cwd(), "storage", "uploads");
dir = (0, upload_path_1.getUploadDir)();
/** Mirrors legacy's `type` field: 1 = image, 2 = any other document. */
static IMAGE_MIME = /^image\/(jpeg|png|gif|webp|svg\+xml)$/;
static ALLOWED = /^(image\/(jpeg|png|gif|webp|svg\+xml)|application\/pdf)$/;
@@ -92542,6 +92590,57 @@ var require_admin_uploads_controller = __commonJS({
}
});
// dist/modules/admin-uploads/public-uploads.controller.js
var require_public_uploads_controller = __commonJS({
"dist/modules/admin-uploads/public-uploads.controller.js"(exports2) {
"use strict";
var __decorate4 = exports2 && exports2.__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 __metadata3 = exports2 && exports2.__metadata || function(k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param3 = exports2 && exports2.__param || function(paramIndex, decorator) {
return function(target, key) {
decorator(target, key, paramIndex);
};
};
Object.defineProperty(exports2, "__esModule", { value: true });
exports2.PublicUploadsController = void 0;
var common_12 = require_common();
var node_fs_1 = require("node:fs");
var guards_1 = require_guards2();
var upload_path_1 = require_upload_path();
var PublicUploadsController = class PublicUploadsController {
stream(filename) {
const file2 = (0, upload_path_1.resolveStoredUpload)(filename);
if (!file2)
throw new common_12.NotFoundException("\u0641\u0627\u06CC\u0644 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
return new common_12.StreamableFile((0, node_fs_1.createReadStream)(file2), {
type: (0, upload_path_1.mimeForUpload)(file2) ?? "application/octet-stream",
disposition: "inline"
});
}
};
exports2.PublicUploadsController = PublicUploadsController;
__decorate4([
(0, common_12.Get)(":filename"),
(0, common_12.Header)("Cache-Control", "public, max-age=86400"),
__param3(0, (0, common_12.Param)("filename")),
__metadata3("design:type", Function),
__metadata3("design:paramtypes", [String]),
__metadata3("design:returntype", common_12.StreamableFile)
], PublicUploadsController.prototype, "stream", null);
exports2.PublicUploadsController = PublicUploadsController = __decorate4([
(0, guards_1.Public)(),
(0, common_12.Controller)("uploads")
], PublicUploadsController);
}
});
// dist/modules/admin-uploads/admin-uploads.module.js
var require_admin_uploads_module = __commonJS({
"dist/modules/admin-uploads/admin-uploads.module.js"(exports2) {
@@ -92557,6 +92656,7 @@ var require_admin_uploads_module = __commonJS({
var common_12 = require_common();
var prisma_module_1 = require_prisma_module();
var admin_uploads_controller_1 = require_admin_uploads_controller();
var public_uploads_controller_1 = require_public_uploads_controller();
var admin_uploads_service_1 = require_admin_uploads_service();
var AdminUploadsModule = class AdminUploadsModule {
};
@@ -92564,7 +92664,7 @@ var require_admin_uploads_module = __commonJS({
exports2.AdminUploadsModule = AdminUploadsModule = __decorate4([
(0, common_12.Module)({
imports: [prisma_module_1.PrismaModule],
controllers: [admin_uploads_controller_1.AdminUploadsController],
controllers: [admin_uploads_controller_1.AdminUploadsController, public_uploads_controller_1.PublicUploadsController],
providers: [admin_uploads_service_1.AdminUploadsService],
exports: [admin_uploads_service_1.AdminUploadsService]
})
+3 -4
View File
@@ -634,7 +634,7 @@ const config = {
"value": "prisma-client-js"
},
"output": {
"value": "E:\\gold bank\\gold-banking-monorepo\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client",
"value": "D:\\APP\\gold-bank\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client",
"fromEnvVar": null
},
"config": {
@@ -652,11 +652,10 @@ const config = {
}
],
"previewFeatures": [],
"sourceFilePath": "E:\\gold bank\\gold-banking-monorepo\\apps\\api\\prisma\\schema.prisma"
"sourceFilePath": "D:\\APP\\gold-bank\\apps\\api\\prisma\\schema.prisma"
},
"relativeEnvPaths": {
"rootEnvPath": null,
"schemaEnvPath": "../../../../../../apps/api/.env"
"rootEnvPath": null
},
"relativePath": "../../../../../../apps/api/prisma",
"clientVersion": "5.22.0",
+3 -4
View File
@@ -635,7 +635,7 @@ const config = {
"value": "prisma-client-js"
},
"output": {
"value": "E:\\gold bank\\gold-banking-monorepo\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client",
"value": "D:\\APP\\gold-bank\\node_modules\\.pnpm\\@prisma+client@5.22.0_prisma@5.22.0\\node_modules\\@prisma\\client",
"fromEnvVar": null
},
"config": {
@@ -653,11 +653,10 @@ const config = {
}
],
"previewFeatures": [],
"sourceFilePath": "E:\\gold bank\\gold-banking-monorepo\\apps\\api\\prisma\\schema.prisma"
"sourceFilePath": "D:\\APP\\gold-bank\\apps\\api\\prisma\\schema.prisma"
},
"relativeEnvPaths": {
"rootEnvPath": null,
"schemaEnvPath": "../../../../../../apps/api/.env"
"rootEnvPath": null
},
"relativePath": "../../../../../../apps/api/prisma",
"clientVersion": "5.22.0",
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from '../../prisma/prisma.module';
import { AdminUploadsController } from './admin-uploads.controller';
import { PublicUploadsController } from './public-uploads.controller';
import { AdminUploadsService } from './admin-uploads.service';
@Module({
imports: [PrismaModule],
controllers: [AdminUploadsController],
controllers: [AdminUploadsController, PublicUploadsController],
providers: [AdminUploadsService],
exports: [AdminUploadsService],
})
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from 'node:crypto';
import { mkdir, writeFile } from 'node:fs/promises';
import { extname, join } from 'node:path';
import { PrismaService } from '../../prisma/prisma.service';
import { getUploadDir } from './upload-path';
/**
* File uploads — ports legacy `POST file/create`
@@ -22,7 +23,7 @@ import { PrismaService } from '../../prisma/prisma.service';
@Injectable()
export class AdminUploadsService {
/** Matches legacy's flat public/storage directory. */
private readonly dir = process.env.UPLOAD_DIR ?? join(process.cwd(), 'storage', 'uploads');
private readonly dir = getUploadDir();
/** Mirrors legacy's `type` field: 1 = image, 2 = any other document. */
private static readonly IMAGE_MIME = /^image\/(jpeg|png|gif|webp|svg\+xml)$/;
@@ -0,0 +1,28 @@
import { Controller, Get, Header, NotFoundException, Param, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'node:fs';
import { Public } from '../auth/guards';
import { mimeForUpload, resolveStoredUpload } from './upload-path';
/**
* Public file serving at GET /api/uploads/:filename.
*
* ProductImage.url is stored as `/uploads/<file>`, which nginx proxies. Shoppers
* and operators also hit `/api/uploads/<file>` (user-app axios base is `/api`,
* and the API Dokploy domain sits behind the same prefix). useStaticAssets only
* mounts `/uploads/`, so those requests 404'd with Nest's "Cannot GET /api/uploads/...".
*/
@Public()
@Controller('uploads')
export class PublicUploadsController {
@Get(':filename')
@Header('Cache-Control', 'public, max-age=86400')
stream(@Param('filename') filename: string): StreamableFile {
const file = resolveStoredUpload(filename);
if (!file) throw new NotFoundException('فایل یافت نشد');
return new StreamableFile(createReadStream(file), {
type: mimeForUpload(file) ?? 'application/octet-stream',
disposition: 'inline',
});
}
}
@@ -0,0 +1,48 @@
import { existsSync, statSync } from 'node:fs';
import { basename, isAbsolute, join, relative, resolve } from 'node:path';
/**
* Directory files are written to and served from.
*
* Default matches docker-compose's `api_uploads:/app/storage/uploads` volume.
* A nested leftover (`storage/uploads/uploads/`) is also checked when serving,
* because copying a folder named `uploads` into the volume creates that extra
* directory and the previous static root then 404'd every file.
*/
export function getUploadDir(): string {
return resolve(process.env.UPLOAD_DIR ?? join(process.cwd(), 'storage', 'uploads'));
}
const UPLOAD_MIME: Record<string, string> = {
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.webp': 'image/webp',
'.avif': 'image/avif',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
};
export function mimeForUpload(filePath: string): string | undefined {
const dot = filePath.lastIndexOf('.');
const ext = dot >= 0 ? filePath.slice(dot).toLowerCase() : '';
return UPLOAD_MIME[ext];
}
/** Resolve a public filename to an on-disk file, or null if it is missing/unsafe. */
export function resolveStoredUpload(rawName: string): string | null {
const safe = basename(rawName);
if (!safe || safe === '.' || safe === '..') return null;
const dir = getUploadDir();
const candidates = [join(dir, safe), join(dir, 'uploads', safe)];
for (const candidate of candidates) {
const resolved = resolve(candidate);
const rel = relative(dir, resolved);
if (!rel || rel.startsWith('..') || isAbsolute(rel)) continue;
if (existsSync(resolved) && statSync(resolved).isFile()) return resolved;
}
return null;
}