fiberx-backend-toolkit 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/constants.d.ts +18 -0
- package/dist/config/constants.js +21 -1
- package/dist/storage/drivers/base_storage_driver.d.ts +11 -0
- package/dist/storage/drivers/base_storage_driver.js +5 -0
- package/dist/storage/drivers/gcs_storage_driver.d.ts +21 -0
- package/dist/storage/drivers/gcs_storage_driver.js +62 -0
- package/dist/storage/drivers/local_storage_driver.d.ts +22 -0
- package/dist/storage/drivers/local_storage_driver.js +60 -0
- package/dist/storage/main.d.ts +5 -0
- package/dist/storage/main.js +14 -0
- package/dist/storage/processors/file_upload_processor.d.ts +16 -0
- package/dist/storage/processors/file_upload_processor.js +91 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/main.js +1 -0
- package/dist/types/storage_type.d.ts +47 -0
- package/dist/types/storage_type.js +3 -0
- package/dist/validators/file_validator_util.d.ts +12 -0
- package/dist/validators/file_validator_util.js +86 -0
- package/package.json +2 -1
|
@@ -71,3 +71,21 @@ export declare const CONTENT_LANG_KEY = "CONTENT_LANG";
|
|
|
71
71
|
export declare const DEFAULT_CONTENT_URL = "http://localhost:4000/content/{{type}}_content/{{lang}}.json";
|
|
72
72
|
export declare const DEFAULT_CONTENT_LANG = "en-GB";
|
|
73
73
|
export declare const DEFAULT_CONTENT_CACHE_TTL: number;
|
|
74
|
+
export declare const FILE_UPLOAD_LIMITS: {
|
|
75
|
+
IMAGE_MAX_SIZE: number;
|
|
76
|
+
VIDEO_MAX_SIZE: number;
|
|
77
|
+
DOCUMENT_MAX_SIZE: number;
|
|
78
|
+
};
|
|
79
|
+
export declare const FILE_UPLOAD_ENV_KEYS: {
|
|
80
|
+
IMAGE_MAX_SIZE: string;
|
|
81
|
+
VIDEO_MAX_SIZE: string;
|
|
82
|
+
DOCUMENT_MAX_SIZE: string;
|
|
83
|
+
IMAGE_MIME_TYPES: string;
|
|
84
|
+
VIDEO_MIME_TYPES: string;
|
|
85
|
+
DOCUMENT_MIME_TYPES: string;
|
|
86
|
+
};
|
|
87
|
+
export declare const FILE_UPLOAD_ALLOWED_MIME_TYPES: {
|
|
88
|
+
image: string[];
|
|
89
|
+
video: string[];
|
|
90
|
+
document: string[];
|
|
91
|
+
};
|
package/dist/config/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_CONTENT_CACHE_TTL = exports.DEFAULT_CONTENT_LANG = exports.DEFAULT_CONTENT_URL = exports.CONTENT_LANG_KEY = exports.CONTENT_URL_KEY = exports.CONTENT_CACHE_TTL_KEY = exports.EMAIL_ENQUEUE_UTIL_FILE_NAME = exports.EMAIL_ENQUEUE_TYPES_FILE_NAME = exports.DEFAULT_MAILER_CACHE_TTL = exports.DEFAULT_MAILER_CACHE_KEY = exports.DEFAULT_RBAC_CACHE_TTL = exports.DEFAULT_RBAC_CACHE_KEY = exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.HEADERS_KEY_NAME = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.EMAIL_PREVIEW_DIR = exports.EMAIL_ENQUEUE_DIR = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
|
|
6
|
+
exports.FILE_UPLOAD_ALLOWED_MIME_TYPES = exports.FILE_UPLOAD_ENV_KEYS = exports.FILE_UPLOAD_LIMITS = exports.DEFAULT_CONTENT_CACHE_TTL = exports.DEFAULT_CONTENT_LANG = exports.DEFAULT_CONTENT_URL = exports.CONTENT_LANG_KEY = exports.CONTENT_URL_KEY = exports.CONTENT_CACHE_TTL_KEY = exports.EMAIL_ENQUEUE_UTIL_FILE_NAME = exports.EMAIL_ENQUEUE_TYPES_FILE_NAME = exports.DEFAULT_MAILER_CACHE_TTL = exports.DEFAULT_MAILER_CACHE_KEY = exports.DEFAULT_RBAC_CACHE_TTL = exports.DEFAULT_RBAC_CACHE_KEY = exports.DEFAULT_TOTP_WINDOW = exports.DEFAULT_TOTP_DIGITS = exports.DEFAULT_TOTP_STEP = exports.ALPHABET_CORPUS = exports.CHARACTER_CORPUS = exports.REQUEST_RATE_LIMITTER_OPTIONS = exports.CORS_MAX_AGE_IN_MICRO_SECONDS = exports.CORS_MAX_AGE_IN_SECONDS = exports.CORS_ALLOWED_HEADERS = exports.HEADERS_KEY_NAME = exports.CORS_ALLOWED_METHODS = exports.DEVICE_ID_HEADERS_NAME = exports.DEVICE_ID_COOKIE_NAME = exports.DEVICE_ID_COOKIE_MAX_AGE = exports.REQUEST_ID_HEADERS_NAME = exports.REQUEST_ID_COOKIE_NAME = exports.REQUEST_ID_COOKIE_MAX_AGE = exports.SEQUELIZE_SEEDER_META_TABLE_NAME = exports.SEQUELIZE_META_TABLE_NAME = exports.EMAIL_PREVIEW_DIR = exports.EMAIL_ENQUEUE_DIR = exports.SEEDERS_DIR = exports.MIGRATIONS_DIR = exports.MODELS_DIR = exports.SCHEMA_SNAPSHOTS_DIR = exports.SCHEMAS_DIR = exports.ENV_VAR_DIR = exports.LOG_DIR = exports.BASE_DIR = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
exports.BASE_DIR = process.cwd();
|
|
9
9
|
exports.LOG_DIR = path_1.default.join(exports.BASE_DIR, "logs");
|
|
@@ -120,3 +120,23 @@ exports.CONTENT_LANG_KEY = "CONTENT_LANG";
|
|
|
120
120
|
exports.DEFAULT_CONTENT_URL = "http://localhost:4000/content/{{type}}_content/{{lang}}.json";
|
|
121
121
|
exports.DEFAULT_CONTENT_LANG = "en-GB";
|
|
122
122
|
exports.DEFAULT_CONTENT_CACHE_TTL = (1000 * 60 * 60 * 24); // 24 hours
|
|
123
|
+
// src/config/constants.ts
|
|
124
|
+
// src/config/constants.ts
|
|
125
|
+
exports.FILE_UPLOAD_LIMITS = {
|
|
126
|
+
IMAGE_MAX_SIZE: 5 * 1024 * 1024,
|
|
127
|
+
VIDEO_MAX_SIZE: 50 * 1024 * 1024,
|
|
128
|
+
DOCUMENT_MAX_SIZE: 10 * 1024 * 1024
|
|
129
|
+
};
|
|
130
|
+
exports.FILE_UPLOAD_ENV_KEYS = {
|
|
131
|
+
IMAGE_MAX_SIZE: "FILE_UPLOAD_IMAGE_MAX_SIZE",
|
|
132
|
+
VIDEO_MAX_SIZE: "FILE_UPLOAD_VIDEO_MAX_SIZE",
|
|
133
|
+
DOCUMENT_MAX_SIZE: "FILE_UPLOAD_DOCUMENT_MAX_SIZE",
|
|
134
|
+
IMAGE_MIME_TYPES: "FILE_UPLOAD_IMAGE_MIME_TYPES",
|
|
135
|
+
VIDEO_MIME_TYPES: "FILE_UPLOAD_VIDEO_MIME_TYPES",
|
|
136
|
+
DOCUMENT_MIME_TYPES: "FILE_UPLOAD_DOCUMENT_MIME_TYPES"
|
|
137
|
+
};
|
|
138
|
+
exports.FILE_UPLOAD_ALLOWED_MIME_TYPES = {
|
|
139
|
+
image: ["image/png", "image/jpeg", "image/webp"],
|
|
140
|
+
video: ["video/mp4", "video/webm"],
|
|
141
|
+
document: ["application/pdf", "application/msword"]
|
|
142
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StorageDriver, StorageFile, StorageInitConfig } from "../../types/storage_type";
|
|
2
|
+
declare abstract class BaseStorageDriver implements StorageDriver {
|
|
3
|
+
abstract initialize(config: StorageInitConfig): Promise<void>;
|
|
4
|
+
abstract upload(...args: any[]): Promise<StorageFile>;
|
|
5
|
+
abstract get(key: string): Promise<StorageFile | null>;
|
|
6
|
+
abstract exists(key: string): Promise<boolean>;
|
|
7
|
+
abstract delete(key: string): Promise<void>;
|
|
8
|
+
abstract list(prefix?: string): Promise<StorageFile[]>;
|
|
9
|
+
abstract getPublicUrl(key: string): string;
|
|
10
|
+
}
|
|
11
|
+
export default BaseStorageDriver;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import BaseStorageDriver from "./base_storage_driver";
|
|
2
|
+
import { StorageInitConfig } from "../../types/storage_type";
|
|
3
|
+
declare class GCSStorageDriver extends BaseStorageDriver {
|
|
4
|
+
private bucket;
|
|
5
|
+
initialize(config: StorageInitConfig): Promise<void>;
|
|
6
|
+
upload(key: string, file: Buffer, mime_type: string, is_public?: boolean): Promise<{
|
|
7
|
+
key: string;
|
|
8
|
+
url: string;
|
|
9
|
+
mime_type: string;
|
|
10
|
+
size: number;
|
|
11
|
+
}>;
|
|
12
|
+
getPublicUrl(key: string): string;
|
|
13
|
+
exists(key: string): Promise<boolean>;
|
|
14
|
+
delete(key: string): Promise<void>;
|
|
15
|
+
get(key: string): Promise<{
|
|
16
|
+
key: string;
|
|
17
|
+
url: string;
|
|
18
|
+
} | null>;
|
|
19
|
+
list(prefix?: string): Promise<any>;
|
|
20
|
+
}
|
|
21
|
+
export default GCSStorageDriver;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const storage_1 = require("@google-cloud/storage");
|
|
7
|
+
const base_storage_driver_1 = __importDefault(require("./base_storage_driver"));
|
|
8
|
+
class GCSStorageDriver extends base_storage_driver_1.default {
|
|
9
|
+
bucket;
|
|
10
|
+
async initialize(config) {
|
|
11
|
+
if (config.type !== "gcs") {
|
|
12
|
+
throw new Error("Invalid config for GCS");
|
|
13
|
+
}
|
|
14
|
+
const storage = new storage_1.Storage({
|
|
15
|
+
projectId: config.project_id,
|
|
16
|
+
credentials: config.credentials
|
|
17
|
+
});
|
|
18
|
+
this.bucket = storage.bucket(config.bucket_name);
|
|
19
|
+
}
|
|
20
|
+
async upload(key, file, mime_type, is_public = true) {
|
|
21
|
+
const file_ref = this.bucket.file(key);
|
|
22
|
+
await file_ref.save(file, {
|
|
23
|
+
contentType: mime_type
|
|
24
|
+
});
|
|
25
|
+
if (is_public) {
|
|
26
|
+
await file_ref.makePublic();
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
key,
|
|
30
|
+
url: this.getPublicUrl(key),
|
|
31
|
+
mime_type,
|
|
32
|
+
size: file.length
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
getPublicUrl(key) {
|
|
36
|
+
return `https://storage.googleapis.com/${this.bucket.name}/${key}`;
|
|
37
|
+
}
|
|
38
|
+
async exists(key) {
|
|
39
|
+
const [exists] = await this.bucket.file(key).exists();
|
|
40
|
+
return exists;
|
|
41
|
+
}
|
|
42
|
+
async delete(key) {
|
|
43
|
+
await this.bucket.file(key).delete();
|
|
44
|
+
}
|
|
45
|
+
async get(key) {
|
|
46
|
+
const exists = await this.exists(key);
|
|
47
|
+
if (!exists)
|
|
48
|
+
return null;
|
|
49
|
+
return {
|
|
50
|
+
key,
|
|
51
|
+
url: this.getPublicUrl(key)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async list(prefix = "") {
|
|
55
|
+
const [files] = await this.bucket.getFiles({ prefix });
|
|
56
|
+
return files.map((f) => ({
|
|
57
|
+
key: f.name,
|
|
58
|
+
url: this.getPublicUrl(f.name)
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.default = GCSStorageDriver;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import BaseStorageDriver from "./base_storage_driver";
|
|
2
|
+
import { StorageInitConfig } from "../../types/storage_type";
|
|
3
|
+
declare class LocalStorageDriver extends BaseStorageDriver {
|
|
4
|
+
private base_dir;
|
|
5
|
+
private public_base_url;
|
|
6
|
+
initialize(config: StorageInitConfig): Promise<void>;
|
|
7
|
+
upload(key: string, file: Buffer, mime_type: string): Promise<{
|
|
8
|
+
key: string;
|
|
9
|
+
url: string;
|
|
10
|
+
mime_type: string;
|
|
11
|
+
size: number;
|
|
12
|
+
}>;
|
|
13
|
+
exists(key: string): Promise<boolean>;
|
|
14
|
+
getPublicUrl(key: string): string;
|
|
15
|
+
get(key: string): Promise<{
|
|
16
|
+
key: string;
|
|
17
|
+
url: string;
|
|
18
|
+
} | null>;
|
|
19
|
+
delete(key: string): Promise<void>;
|
|
20
|
+
list(prefix?: string): Promise<never[]>;
|
|
21
|
+
}
|
|
22
|
+
export default LocalStorageDriver;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const base_storage_driver_1 = __importDefault(require("./base_storage_driver"));
|
|
9
|
+
class LocalStorageDriver extends base_storage_driver_1.default {
|
|
10
|
+
base_dir;
|
|
11
|
+
public_base_url;
|
|
12
|
+
async initialize(config) {
|
|
13
|
+
if (config.type !== "local") {
|
|
14
|
+
throw new Error("Invalid config for LocalStorageDriver");
|
|
15
|
+
}
|
|
16
|
+
this.base_dir = config.base_dir;
|
|
17
|
+
this.public_base_url = config.public_base_url || "";
|
|
18
|
+
await promises_1.default.mkdir(this.base_dir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
async upload(key, file, mime_type) {
|
|
21
|
+
const full_path = path_1.default.join(this.base_dir, key);
|
|
22
|
+
await promises_1.default.mkdir(path_1.default.dirname(full_path), { recursive: true });
|
|
23
|
+
await promises_1.default.writeFile(full_path, file);
|
|
24
|
+
return {
|
|
25
|
+
key,
|
|
26
|
+
url: this.getPublicUrl(key),
|
|
27
|
+
mime_type,
|
|
28
|
+
size: file.length
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async exists(key) {
|
|
32
|
+
try {
|
|
33
|
+
await promises_1.default.access(path_1.default.join(this.base_dir, key));
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getPublicUrl(key) {
|
|
41
|
+
return `${this.public_base_url}/${key}`;
|
|
42
|
+
}
|
|
43
|
+
async get(key) {
|
|
44
|
+
const exists = await this.exists(key);
|
|
45
|
+
if (!exists)
|
|
46
|
+
return null;
|
|
47
|
+
return {
|
|
48
|
+
key,
|
|
49
|
+
url: this.getPublicUrl(key)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async delete(key) {
|
|
53
|
+
await promises_1.default.unlink(path_1.default.join(this.base_dir, key));
|
|
54
|
+
}
|
|
55
|
+
async list(prefix = "") {
|
|
56
|
+
// optional recursive implementation
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.default = LocalStorageDriver;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import BaseStorageDriver from "./drivers/base_storage_driver";
|
|
2
|
+
import GCSStorageDriver from "./drivers/gcs_storage_driver";
|
|
3
|
+
import LocalStorageDriver from "./drivers/local_storage_driver";
|
|
4
|
+
import FileUploadProcessor from "./processors/file_upload_processor";
|
|
5
|
+
export { BaseStorageDriver, GCSStorageDriver, LocalStorageDriver, FileUploadProcessor, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FileUploadProcessor = exports.LocalStorageDriver = exports.GCSStorageDriver = exports.BaseStorageDriver = void 0;
|
|
7
|
+
const base_storage_driver_1 = __importDefault(require("./drivers/base_storage_driver"));
|
|
8
|
+
exports.BaseStorageDriver = base_storage_driver_1.default;
|
|
9
|
+
const gcs_storage_driver_1 = __importDefault(require("./drivers/gcs_storage_driver"));
|
|
10
|
+
exports.GCSStorageDriver = gcs_storage_driver_1.default;
|
|
11
|
+
const local_storage_driver_1 = __importDefault(require("./drivers/local_storage_driver"));
|
|
12
|
+
exports.LocalStorageDriver = local_storage_driver_1.default;
|
|
13
|
+
const file_upload_processor_1 = __importDefault(require("./processors/file_upload_processor"));
|
|
14
|
+
exports.FileUploadProcessor = file_upload_processor_1.default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FileUploadInput, FileUploadResult, StorageDriver } from "../../types/storage_type";
|
|
2
|
+
declare class FileUploadProcessor {
|
|
3
|
+
name: string;
|
|
4
|
+
private static instance;
|
|
5
|
+
private readonly driver;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly env;
|
|
8
|
+
private readonly validator;
|
|
9
|
+
private constructor();
|
|
10
|
+
static initialize(driver: StorageDriver): FileUploadProcessor;
|
|
11
|
+
static getInstance(): FileUploadProcessor;
|
|
12
|
+
private getExtension;
|
|
13
|
+
private generateKey;
|
|
14
|
+
upload(input: FileUploadInput): Promise<FileUploadResult>;
|
|
15
|
+
}
|
|
16
|
+
export default FileUploadProcessor;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const logger_util_1 = __importDefault(require("../../utils/logger_util"));
|
|
7
|
+
const env_manager_util_1 = __importDefault(require("../../utils/env_manager_util"));
|
|
8
|
+
const uuid_1 = require("uuid");
|
|
9
|
+
const file_validator_util_1 = __importDefault(require("../../validators/file_validator_util"));
|
|
10
|
+
class FileUploadProcessor {
|
|
11
|
+
name = "file_upload_processor";
|
|
12
|
+
static instance = null;
|
|
13
|
+
driver;
|
|
14
|
+
logger = new logger_util_1.default(this.name);
|
|
15
|
+
env = env_manager_util_1.default.getInstance();
|
|
16
|
+
validator = new file_validator_util_1.default();
|
|
17
|
+
constructor(driver) {
|
|
18
|
+
this.driver = driver;
|
|
19
|
+
}
|
|
20
|
+
static initialize(driver) {
|
|
21
|
+
if (this.instance) {
|
|
22
|
+
throw new Error("Already initialized");
|
|
23
|
+
}
|
|
24
|
+
this.instance = new FileUploadProcessor(driver);
|
|
25
|
+
return this.instance;
|
|
26
|
+
}
|
|
27
|
+
static getInstance() {
|
|
28
|
+
if (!this.instance) {
|
|
29
|
+
throw new Error("Not initialized");
|
|
30
|
+
}
|
|
31
|
+
return this.instance;
|
|
32
|
+
}
|
|
33
|
+
// ==============================
|
|
34
|
+
// EXTENSION HELPER
|
|
35
|
+
// ==============================
|
|
36
|
+
getExtension(file_name) {
|
|
37
|
+
const ext = file_name.split(".").pop();
|
|
38
|
+
return ext ? ext : "bin";
|
|
39
|
+
}
|
|
40
|
+
// ==============================
|
|
41
|
+
// KEY GENERATION
|
|
42
|
+
// ==============================
|
|
43
|
+
generateKey(category, ext, folder) {
|
|
44
|
+
return `${folder || "uploads"}/${category}/${(0, uuid_1.v4)()}.${ext}`;
|
|
45
|
+
}
|
|
46
|
+
// ==============================
|
|
47
|
+
// MAIN METHOD
|
|
48
|
+
// ==============================
|
|
49
|
+
async upload(input) {
|
|
50
|
+
try {
|
|
51
|
+
const category = this.validator.getCategory(input.mime_type);
|
|
52
|
+
// ✅ validate AFTER category detection
|
|
53
|
+
const { v_state, v_msg } = this.validator.validate(input, category);
|
|
54
|
+
if (!v_state) {
|
|
55
|
+
return { status: false, msg: v_msg };
|
|
56
|
+
}
|
|
57
|
+
const ext = this.getExtension(input.original_name);
|
|
58
|
+
const key = this.generateKey(category, ext, input.folder);
|
|
59
|
+
const stored = await this.driver.upload(key, input.file, input.mime_type, input.is_public ?? true);
|
|
60
|
+
if (!stored) {
|
|
61
|
+
return { status: false, msg: "failed_to_store_file" };
|
|
62
|
+
}
|
|
63
|
+
const result = {
|
|
64
|
+
key,
|
|
65
|
+
original_name: input.original_name,
|
|
66
|
+
mime_type: input.mime_type,
|
|
67
|
+
category,
|
|
68
|
+
size: input.size,
|
|
69
|
+
url: stored.url
|
|
70
|
+
};
|
|
71
|
+
this.logger.info("File uploaded successfully", {
|
|
72
|
+
key,
|
|
73
|
+
category,
|
|
74
|
+
size: input.size
|
|
75
|
+
});
|
|
76
|
+
return { status: true, msg: "file_uploaded_successfully", data: result };
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.logger.error("Upload failed", {
|
|
80
|
+
error,
|
|
81
|
+
input_meta: {
|
|
82
|
+
name: input.original_name,
|
|
83
|
+
size: input.size,
|
|
84
|
+
mime: input.mime_type
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return { status: false, msg: "file_upload_error_occured" };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.default = FileUploadProcessor;
|
package/dist/types/main.d.ts
CHANGED
package/dist/types/main.js
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type StorageInitConfig = {
|
|
2
|
+
type: "gcs";
|
|
3
|
+
bucket_name: string;
|
|
4
|
+
project_id: string;
|
|
5
|
+
credentials: Record<string, any>;
|
|
6
|
+
} | {
|
|
7
|
+
type: "local";
|
|
8
|
+
base_dir: string;
|
|
9
|
+
public_base_url?: string;
|
|
10
|
+
};
|
|
11
|
+
export interface StorageFile {
|
|
12
|
+
key: string;
|
|
13
|
+
url: string;
|
|
14
|
+
size?: number;
|
|
15
|
+
mime_type?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface StorageDriver {
|
|
18
|
+
initialize(config: StorageInitConfig): Promise<void>;
|
|
19
|
+
upload(key: string, file: Buffer, mime_type: string, is_public?: boolean): Promise<StorageFile | null>;
|
|
20
|
+
get(key: string): Promise<StorageFile | null>;
|
|
21
|
+
exists(key: string): Promise<boolean>;
|
|
22
|
+
delete(key: string): Promise<void>;
|
|
23
|
+
list(prefix?: string): Promise<StorageFile[]>;
|
|
24
|
+
getPublicUrl(key: string): string;
|
|
25
|
+
}
|
|
26
|
+
export type FileCategory = "image" | "video" | "document";
|
|
27
|
+
export interface FileUploadInput {
|
|
28
|
+
file: Buffer;
|
|
29
|
+
original_name: string;
|
|
30
|
+
mime_type: string;
|
|
31
|
+
size: number;
|
|
32
|
+
folder?: string;
|
|
33
|
+
is_public?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface FileUploadDataPayloadInterface {
|
|
36
|
+
key: string;
|
|
37
|
+
original_name: string;
|
|
38
|
+
mime_type: string;
|
|
39
|
+
category: FileCategory;
|
|
40
|
+
size: number;
|
|
41
|
+
url: string;
|
|
42
|
+
}
|
|
43
|
+
export interface FileUploadResult {
|
|
44
|
+
status: boolean;
|
|
45
|
+
msg: string;
|
|
46
|
+
data?: FileUploadDataPayloadInterface;
|
|
47
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FileCategory, FileUploadInput } from "../types/storage_type";
|
|
2
|
+
import { QueryValidationResultInterface } from "../types/validator_type";
|
|
3
|
+
declare class FileValidatorUtil {
|
|
4
|
+
name: string;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
private readonly env;
|
|
7
|
+
getCategory(mime: string): FileCategory;
|
|
8
|
+
private getMaxSize;
|
|
9
|
+
private getAllowedMimeTypes;
|
|
10
|
+
validate(input: FileUploadInput, category: FileCategory): QueryValidationResultInterface<null>;
|
|
11
|
+
}
|
|
12
|
+
export default FileValidatorUtil;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/storage/utils/file_validator_util.ts
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const logger_util_1 = __importDefault(require("../utils/logger_util"));
|
|
8
|
+
const env_manager_util_1 = __importDefault(require("../utils/env_manager_util"));
|
|
9
|
+
const constants_1 = require("../config/constants");
|
|
10
|
+
class FileValidatorUtil {
|
|
11
|
+
name = "file_validator_util";
|
|
12
|
+
logger = new logger_util_1.default(this.name);
|
|
13
|
+
env = env_manager_util_1.default.getInstance();
|
|
14
|
+
// ==============================
|
|
15
|
+
// CATEGORY
|
|
16
|
+
// ==============================
|
|
17
|
+
getCategory(mime) {
|
|
18
|
+
if (mime.startsWith("image/"))
|
|
19
|
+
return "image";
|
|
20
|
+
if (mime.startsWith("video/"))
|
|
21
|
+
return "video";
|
|
22
|
+
return "document";
|
|
23
|
+
}
|
|
24
|
+
// ==============================
|
|
25
|
+
// MAX SIZE
|
|
26
|
+
// ==============================
|
|
27
|
+
getMaxSize(category) {
|
|
28
|
+
switch (category) {
|
|
29
|
+
case "image":
|
|
30
|
+
return Number(this.env.getEnvVar(constants_1.FILE_UPLOAD_ENV_KEYS.IMAGE_MAX_SIZE, constants_1.FILE_UPLOAD_LIMITS.IMAGE_MAX_SIZE));
|
|
31
|
+
case "video":
|
|
32
|
+
return Number(this.env.getEnvVar(constants_1.FILE_UPLOAD_ENV_KEYS.VIDEO_MAX_SIZE, constants_1.FILE_UPLOAD_LIMITS.VIDEO_MAX_SIZE));
|
|
33
|
+
case "document":
|
|
34
|
+
default:
|
|
35
|
+
return Number(this.env.getEnvVar(constants_1.FILE_UPLOAD_ENV_KEYS.DOCUMENT_MAX_SIZE, constants_1.FILE_UPLOAD_LIMITS.DOCUMENT_MAX_SIZE));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ==============================
|
|
39
|
+
// MIME TYPES
|
|
40
|
+
// ==============================
|
|
41
|
+
getAllowedMimeTypes(category) {
|
|
42
|
+
const env_value = this.env.getEnvVar(constants_1.FILE_UPLOAD_ENV_KEYS[`${category.toUpperCase()}_MIME_TYPES`]);
|
|
43
|
+
if (env_value) {
|
|
44
|
+
// allow both CSV string or array
|
|
45
|
+
if (Array.isArray(env_value))
|
|
46
|
+
return env_value;
|
|
47
|
+
return String(env_value)
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((m) => m.trim());
|
|
50
|
+
}
|
|
51
|
+
return constants_1.FILE_UPLOAD_ALLOWED_MIME_TYPES[category];
|
|
52
|
+
}
|
|
53
|
+
// ==============================
|
|
54
|
+
// MAIN VALIDATION
|
|
55
|
+
// ==============================
|
|
56
|
+
validate(input, category) {
|
|
57
|
+
if (!input.mime_type) {
|
|
58
|
+
return { v_state: false, v_msg: "missing_mime_type" };
|
|
59
|
+
}
|
|
60
|
+
if (!input.original_name) {
|
|
61
|
+
return { v_state: false, v_msg: "missing_original_file_name" };
|
|
62
|
+
}
|
|
63
|
+
// 🔹 size validation
|
|
64
|
+
const max_size = this.getMaxSize(category);
|
|
65
|
+
if (input.size > max_size) {
|
|
66
|
+
this.logger.error("File size exceeds limit", {
|
|
67
|
+
size: input.size,
|
|
68
|
+
max_size,
|
|
69
|
+
category
|
|
70
|
+
});
|
|
71
|
+
return { v_state: false, v_msg: "file_size_to_large" };
|
|
72
|
+
}
|
|
73
|
+
// 🔹 mime validation
|
|
74
|
+
const allowed = this.getAllowedMimeTypes(category);
|
|
75
|
+
if (!allowed.includes(input.mime_type)) {
|
|
76
|
+
this.logger.error("Invalid mime type", {
|
|
77
|
+
mime: input.mime_type,
|
|
78
|
+
allowed,
|
|
79
|
+
category
|
|
80
|
+
});
|
|
81
|
+
return { v_state: false, v_msg: "invalid_file_mime_type" };
|
|
82
|
+
}
|
|
83
|
+
return { v_state: true, v_msg: "file_input_valid" };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.default = FileValidatorUtil;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fiberx-backend-toolkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "A TypeScript backend toolkit providing shared domain logic, infrastructure helpers, and utilities for FiberX server-side applications and services.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"author": "David Matt-Ojo",
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"@google-cloud/storage": "^7.19.0",
|
|
26
27
|
"@types/nodemailer": "^7.0.11",
|
|
27
28
|
"axios": "^1.11.0",
|
|
28
29
|
"bcrypt": "^6.0.0",
|