saas-backend-kit 1.0.0 → 1.0.2
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/README.md +123 -344
- package/copy-dts.js +59 -0
- package/dist/auth/index.js +7 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +7 -2
- package/dist/auth/index.mjs.map +1 -1
- package/dist/config/index.js +6 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/index.mjs +6 -1
- package/dist/config/index.mjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +232 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +231 -42
- package/dist/index.mjs.map +1 -1
- package/dist/logger/index.js +6 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/logger/index.mjs +6 -1
- package/dist/logger/index.mjs.map +1 -1
- package/dist/notifications/index.js +6 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/index.mjs +6 -1
- package/dist/notifications/index.mjs.map +1 -1
- package/dist/queue/index.js +6 -1
- package/dist/queue/index.js.map +1 -1
- package/dist/queue/index.mjs +6 -1
- package/dist/queue/index.mjs.map +1 -1
- package/dist/rate-limit/index.js +7 -1
- package/dist/rate-limit/index.js.map +1 -1
- package/dist/rate-limit/index.mjs +7 -1
- package/dist/rate-limit/index.mjs.map +1 -1
- package/dist/response/index.js +51 -40
- package/dist/response/index.js.map +1 -1
- package/dist/response/index.mjs +51 -40
- package/dist/response/index.mjs.map +1 -1
- package/dist/upload/index.d.ts +57 -0
- package/dist/upload/index.js +344 -0
- package/dist/upload/index.js.map +1 -0
- package/dist/upload/index.mjs +334 -0
- package/dist/upload/index.mjs.map +1 -0
- package/jest-output.json +72 -0
- package/jest.config.js +19 -0
- package/package.json +20 -8
- package/saas-banner.svg +239 -0
- package/src/auth/jwt.ts +1 -1
- package/src/config/index.ts +5 -0
- package/src/index.ts +2 -0
- package/src/rate-limit/express.ts +1 -0
- package/src/response/index.ts +49 -40
- package/src/upload/index.ts +268 -0
- package/tests/auth.test.ts +134 -0
- package/tests/config.test.ts +36 -0
- package/tests/logger.test.ts +47 -0
- package/tests/notifications.test.ts +19 -0
- package/tests/rate-limit.test.ts +50 -0
- package/tests/upload.test.ts +33 -0
- package/tsconfig.test.json +14 -0
- package/tsup.config.ts +2 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,8 @@ var bullmq = require('bullmq');
|
|
|
6
6
|
var jwt = require('jsonwebtoken');
|
|
7
7
|
var bcrypt = require('bcryptjs');
|
|
8
8
|
var nodemailer = require('nodemailer');
|
|
9
|
-
var
|
|
9
|
+
var clientS3 = require('@aws-sdk/client-s3');
|
|
10
|
+
var s3RequestPresigner = require('@aws-sdk/s3-request-presigner');
|
|
10
11
|
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
@@ -68,7 +69,12 @@ var init_config = __esm({
|
|
|
68
69
|
SLACK_WEBHOOK_URL: zod.z.string().optional(),
|
|
69
70
|
RATE_LIMIT_WINDOW: zod.z.string().default("1m"),
|
|
70
71
|
RATE_LIMIT_LIMIT: zod.z.string().default("100"),
|
|
71
|
-
LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info")
|
|
72
|
+
LOG_LEVEL: zod.z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
|
73
|
+
AWS_REGION: zod.z.string().default("us-east-1"),
|
|
74
|
+
AWS_ACCESS_KEY_ID: zod.z.string().optional(),
|
|
75
|
+
AWS_SECRET_ACCESS_KEY: zod.z.string().optional(),
|
|
76
|
+
AWS_S3_BUCKET: zod.z.string().optional(),
|
|
77
|
+
AWS_ENDPOINT: zod.z.string().optional()
|
|
72
78
|
});
|
|
73
79
|
ConfigManager = class {
|
|
74
80
|
config = null;
|
|
@@ -440,6 +446,7 @@ var init_express = __esm({
|
|
|
440
446
|
cleanupInterval;
|
|
441
447
|
constructor() {
|
|
442
448
|
this.cleanupInterval = setInterval(() => this.cleanup(), 6e4);
|
|
449
|
+
this.cleanupInterval.unref();
|
|
443
450
|
}
|
|
444
451
|
cleanup() {
|
|
445
452
|
const now = Date.now();
|
|
@@ -570,7 +577,7 @@ var JWTService = class {
|
|
|
570
577
|
return jwt__default.default.verify(token, this.refreshSecret);
|
|
571
578
|
}
|
|
572
579
|
refreshTokens(refreshToken) {
|
|
573
|
-
const payload = this.verifyRefreshToken(refreshToken);
|
|
580
|
+
const { iat, exp, nbf, ...payload } = this.verifyRefreshToken(refreshToken);
|
|
574
581
|
return this.generateTokenPair(payload);
|
|
575
582
|
}
|
|
576
583
|
};
|
|
@@ -1047,6 +1054,8 @@ init_express();
|
|
|
1047
1054
|
|
|
1048
1055
|
// src/index.ts
|
|
1049
1056
|
init_config();
|
|
1057
|
+
|
|
1058
|
+
// src/response/index.ts
|
|
1050
1059
|
var ResponseHelper = class {
|
|
1051
1060
|
static success(res, data, message, statusCode = 200) {
|
|
1052
1061
|
const response2 = {
|
|
@@ -1113,46 +1122,226 @@ var ResponseHelper = class {
|
|
|
1113
1122
|
return res.status(204).send();
|
|
1114
1123
|
}
|
|
1115
1124
|
};
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
};
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
};
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
};
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
};
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
};
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
};
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
};
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
};
|
|
1149
|
-
|
|
1150
|
-
|
|
1125
|
+
try {
|
|
1126
|
+
const proto = __require("express").response;
|
|
1127
|
+
if (proto) {
|
|
1128
|
+
proto.success = function(data, message, statusCode = 200) {
|
|
1129
|
+
return ResponseHelper.success(this, data, message, statusCode);
|
|
1130
|
+
};
|
|
1131
|
+
proto.created = function(data, message) {
|
|
1132
|
+
return ResponseHelper.created(this, data, message);
|
|
1133
|
+
};
|
|
1134
|
+
proto.updated = function(data, message) {
|
|
1135
|
+
return ResponseHelper.updated(this, data, message);
|
|
1136
|
+
};
|
|
1137
|
+
proto.deleted = function(message) {
|
|
1138
|
+
return ResponseHelper.deleted(this, message);
|
|
1139
|
+
};
|
|
1140
|
+
proto.error = function(error, statusCode = 400, code, details) {
|
|
1141
|
+
return ResponseHelper.error(this, error, statusCode, code, details);
|
|
1142
|
+
};
|
|
1143
|
+
proto.badRequest = function(error, code) {
|
|
1144
|
+
return ResponseHelper.badRequest(this, error, code);
|
|
1145
|
+
};
|
|
1146
|
+
proto.unauthorized = function(error, code) {
|
|
1147
|
+
return ResponseHelper.unauthorized(this, error, code);
|
|
1148
|
+
};
|
|
1149
|
+
proto.forbidden = function(error, code) {
|
|
1150
|
+
return ResponseHelper.forbidden(this, error, code);
|
|
1151
|
+
};
|
|
1152
|
+
proto.notFound = function(error, code) {
|
|
1153
|
+
return ResponseHelper.notFound(this, error, code);
|
|
1154
|
+
};
|
|
1155
|
+
proto.conflict = function(error, code) {
|
|
1156
|
+
return ResponseHelper.conflict(this, error, code);
|
|
1157
|
+
};
|
|
1158
|
+
proto.validationError = function(error, details) {
|
|
1159
|
+
return ResponseHelper.validationError(this, error, details);
|
|
1160
|
+
};
|
|
1161
|
+
proto.internalError = function(error) {
|
|
1162
|
+
return ResponseHelper.internalError(this, error);
|
|
1163
|
+
};
|
|
1164
|
+
proto.paginated = function(data, page, limit, total) {
|
|
1165
|
+
return ResponseHelper.paginated(this, data, page, limit, total);
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
} catch {
|
|
1169
|
+
}
|
|
1170
|
+
var response = ResponseHelper;
|
|
1171
|
+
|
|
1172
|
+
// src/upload/index.ts
|
|
1173
|
+
init_config();
|
|
1174
|
+
init_logger();
|
|
1175
|
+
var S3Service = class {
|
|
1176
|
+
client = null;
|
|
1177
|
+
bucket;
|
|
1178
|
+
initialized = false;
|
|
1179
|
+
constructor() {
|
|
1180
|
+
this.bucket = "";
|
|
1181
|
+
}
|
|
1182
|
+
initialize(config2) {
|
|
1183
|
+
this.client = new clientS3.S3Client({
|
|
1184
|
+
region: config2.region || "us-east-1",
|
|
1185
|
+
credentials: config2.accessKeyId && config2.secretAccessKey ? {
|
|
1186
|
+
accessKeyId: config2.accessKeyId,
|
|
1187
|
+
secretAccessKey: config2.secretAccessKey
|
|
1188
|
+
} : void 0,
|
|
1189
|
+
endpoint: config2.endpoint,
|
|
1190
|
+
forcePathStyle: config2.forcePathStyle || false
|
|
1191
|
+
});
|
|
1192
|
+
this.bucket = config2.bucket;
|
|
1193
|
+
this.initialized = true;
|
|
1194
|
+
exports.logger.info("S3 service initialized", { bucket: this.bucket });
|
|
1195
|
+
}
|
|
1196
|
+
isInitialized() {
|
|
1197
|
+
return this.initialized;
|
|
1198
|
+
}
|
|
1199
|
+
ensureInitialized() {
|
|
1200
|
+
if (!this.initialized) {
|
|
1201
|
+
const region = exports.config.get("AWS_REGION") || "us-east-1";
|
|
1202
|
+
const bucket = exports.config.get("AWS_S3_BUCKET") || "";
|
|
1203
|
+
this.initialize({
|
|
1204
|
+
region,
|
|
1205
|
+
accessKeyId: exports.config.get("AWS_ACCESS_KEY_ID"),
|
|
1206
|
+
secretAccessKey: exports.config.get("AWS_SECRET_ACCESS_KEY"),
|
|
1207
|
+
bucket,
|
|
1208
|
+
endpoint: exports.config.get("AWS_ENDPOINT")
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
async upload(file, options = {}) {
|
|
1213
|
+
this.ensureInitialized();
|
|
1214
|
+
const key = options.key || this.generateKey();
|
|
1215
|
+
const contentType = options.contentType || this.guessContentType(key);
|
|
1216
|
+
const command = new clientS3.PutObjectCommand({
|
|
1217
|
+
Bucket: this.bucket,
|
|
1218
|
+
Key: key,
|
|
1219
|
+
Body: file,
|
|
1220
|
+
ContentType: contentType,
|
|
1221
|
+
Metadata: options.metadata
|
|
1222
|
+
});
|
|
1223
|
+
await this.client.send(command);
|
|
1224
|
+
const url = await this.getSignedUrl(key, { expiresIn: options.expiresIn || 3600 });
|
|
1225
|
+
exports.logger.info("File uploaded to S3", { key, bucket: this.bucket, contentType });
|
|
1226
|
+
return {
|
|
1227
|
+
key,
|
|
1228
|
+
url,
|
|
1229
|
+
bucket: this.bucket,
|
|
1230
|
+
contentType
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
async uploadImage(file, filename, options = {}) {
|
|
1234
|
+
const key = options.key || `images/${Date.now()}-${filename}`;
|
|
1235
|
+
return this.upload(file, {
|
|
1236
|
+
...options,
|
|
1237
|
+
key,
|
|
1238
|
+
contentType: options.contentType || this.getImageContentType(filename)
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
async uploadVideo(file, filename, options = {}) {
|
|
1242
|
+
const key = options.key || `videos/${Date.now()}-${filename}`;
|
|
1243
|
+
return this.upload(file, {
|
|
1244
|
+
...options,
|
|
1245
|
+
key,
|
|
1246
|
+
contentType: options.contentType || this.getVideoContentType(filename)
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
async delete(key) {
|
|
1250
|
+
this.ensureInitialized();
|
|
1251
|
+
const command = new clientS3.DeleteObjectCommand({
|
|
1252
|
+
Bucket: this.bucket,
|
|
1253
|
+
Key: key
|
|
1254
|
+
});
|
|
1255
|
+
await this.client.send(command);
|
|
1256
|
+
exports.logger.info("File deleted from S3", { key, bucket: this.bucket });
|
|
1257
|
+
}
|
|
1258
|
+
async getSignedUrl(key, options = {}) {
|
|
1259
|
+
this.ensureInitialized();
|
|
1260
|
+
const command = new clientS3.GetObjectCommand({
|
|
1261
|
+
Bucket: this.bucket,
|
|
1262
|
+
Key: key
|
|
1263
|
+
});
|
|
1264
|
+
return s3RequestPresigner.getSignedUrl(this.client, command, {
|
|
1265
|
+
expiresIn: options.expiresIn || 3600
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
async getPublicUrl(key) {
|
|
1269
|
+
return `https://${this.bucket}.s3.${exports.config.get("AWS_REGION") || "us-east-1"}.amazonaws.com/${key}`;
|
|
1270
|
+
}
|
|
1271
|
+
async listFiles(prefix, maxKeys = 1e3) {
|
|
1272
|
+
this.ensureInitialized();
|
|
1273
|
+
const command = new clientS3.ListObjectsV2Command({
|
|
1274
|
+
Bucket: this.bucket,
|
|
1275
|
+
Prefix: prefix,
|
|
1276
|
+
MaxKeys: maxKeys
|
|
1277
|
+
});
|
|
1278
|
+
const response2 = await this.client.send(command);
|
|
1279
|
+
return (response2.Contents || []).map((item) => ({
|
|
1280
|
+
key: item.Key || "",
|
|
1281
|
+
lastModified: item.LastModified,
|
|
1282
|
+
size: item.Size
|
|
1283
|
+
}));
|
|
1284
|
+
}
|
|
1285
|
+
generateKey() {
|
|
1286
|
+
const timestamp = Date.now();
|
|
1287
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
1288
|
+
return `uploads/${timestamp}-${random}`;
|
|
1289
|
+
}
|
|
1290
|
+
guessContentType(key) {
|
|
1291
|
+
const ext = key.split(".").pop()?.toLowerCase();
|
|
1292
|
+
const contentTypes = {
|
|
1293
|
+
jpg: "image/jpeg",
|
|
1294
|
+
jpeg: "image/jpeg",
|
|
1295
|
+
png: "image/png",
|
|
1296
|
+
gif: "image/gif",
|
|
1297
|
+
webp: "image/webp",
|
|
1298
|
+
svg: "image/svg+xml",
|
|
1299
|
+
mp4: "video/mp4",
|
|
1300
|
+
webm: "video/webm",
|
|
1301
|
+
mov: "video/quicktime",
|
|
1302
|
+
avi: "video/x-msvideo",
|
|
1303
|
+
pdf: "application/pdf",
|
|
1304
|
+
json: "application/json",
|
|
1305
|
+
txt: "text/plain"
|
|
1306
|
+
};
|
|
1307
|
+
return contentTypes[ext || ""] || "application/octet-stream";
|
|
1308
|
+
}
|
|
1309
|
+
getImageContentType(filename) {
|
|
1310
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
1311
|
+
const imageTypes = {
|
|
1312
|
+
jpg: "image/jpeg",
|
|
1313
|
+
jpeg: "image/jpeg",
|
|
1314
|
+
png: "image/png",
|
|
1315
|
+
gif: "image/gif",
|
|
1316
|
+
webp: "image/webp",
|
|
1317
|
+
svg: "image/svg+xml"
|
|
1318
|
+
};
|
|
1319
|
+
return imageTypes[ext || ""] || "image/jpeg";
|
|
1320
|
+
}
|
|
1321
|
+
getVideoContentType(filename) {
|
|
1322
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
1323
|
+
const videoTypes = {
|
|
1324
|
+
mp4: "video/mp4",
|
|
1325
|
+
webm: "video/webm",
|
|
1326
|
+
mov: "video/quicktime",
|
|
1327
|
+
avi: "video/x-msvideo",
|
|
1328
|
+
mkv: "video/x-matroska",
|
|
1329
|
+
ogv: "video/ogg"
|
|
1330
|
+
};
|
|
1331
|
+
return videoTypes[ext || ""] || "video/mp4";
|
|
1332
|
+
}
|
|
1151
1333
|
};
|
|
1152
|
-
|
|
1153
|
-
|
|
1334
|
+
var s3Service = new S3Service();
|
|
1335
|
+
var upload = {
|
|
1336
|
+
initialize: (config2) => s3Service.initialize(config2),
|
|
1337
|
+
file: (file, options) => s3Service.upload(file, options),
|
|
1338
|
+
image: (file, filename, options) => s3Service.uploadImage(file, filename, options),
|
|
1339
|
+
video: (file, filename, options) => s3Service.uploadVideo(file, filename, options),
|
|
1340
|
+
delete: (key) => s3Service.delete(key),
|
|
1341
|
+
getSignedUrl: (key, options) => s3Service.getSignedUrl(key, options),
|
|
1342
|
+
getPublicUrl: (key) => s3Service.getPublicUrl(key),
|
|
1343
|
+
listFiles: (prefix, maxKeys) => s3Service.listFiles(prefix, maxKeys)
|
|
1154
1344
|
};
|
|
1155
|
-
var response = ResponseHelper;
|
|
1156
1345
|
|
|
1157
1346
|
// src/plugin.ts
|
|
1158
1347
|
init_logger();
|
|
@@ -1299,5 +1488,7 @@ exports.notification = notification;
|
|
|
1299
1488
|
exports.notify = notify;
|
|
1300
1489
|
exports.rateLimit = rateLimit;
|
|
1301
1490
|
exports.response = response;
|
|
1491
|
+
exports.s3Service = s3Service;
|
|
1492
|
+
exports.upload = upload;
|
|
1302
1493
|
//# sourceMappingURL=index.js.map
|
|
1303
1494
|
//# sourceMappingURL=index.js.map
|