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.
Files changed (58) hide show
  1. package/README.md +123 -344
  2. package/copy-dts.js +59 -0
  3. package/dist/auth/index.js +7 -2
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/auth/index.mjs +7 -2
  6. package/dist/auth/index.mjs.map +1 -1
  7. package/dist/config/index.js +6 -1
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/config/index.mjs +6 -1
  10. package/dist/config/index.mjs.map +1 -1
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +232 -41
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +231 -42
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/logger/index.js +6 -1
  17. package/dist/logger/index.js.map +1 -1
  18. package/dist/logger/index.mjs +6 -1
  19. package/dist/logger/index.mjs.map +1 -1
  20. package/dist/notifications/index.js +6 -1
  21. package/dist/notifications/index.js.map +1 -1
  22. package/dist/notifications/index.mjs +6 -1
  23. package/dist/notifications/index.mjs.map +1 -1
  24. package/dist/queue/index.js +6 -1
  25. package/dist/queue/index.js.map +1 -1
  26. package/dist/queue/index.mjs +6 -1
  27. package/dist/queue/index.mjs.map +1 -1
  28. package/dist/rate-limit/index.js +7 -1
  29. package/dist/rate-limit/index.js.map +1 -1
  30. package/dist/rate-limit/index.mjs +7 -1
  31. package/dist/rate-limit/index.mjs.map +1 -1
  32. package/dist/response/index.js +51 -40
  33. package/dist/response/index.js.map +1 -1
  34. package/dist/response/index.mjs +51 -40
  35. package/dist/response/index.mjs.map +1 -1
  36. package/dist/upload/index.d.ts +57 -0
  37. package/dist/upload/index.js +344 -0
  38. package/dist/upload/index.js.map +1 -0
  39. package/dist/upload/index.mjs +334 -0
  40. package/dist/upload/index.mjs.map +1 -0
  41. package/jest-output.json +72 -0
  42. package/jest.config.js +19 -0
  43. package/package.json +20 -8
  44. package/saas-banner.svg +239 -0
  45. package/src/auth/jwt.ts +1 -1
  46. package/src/config/index.ts +5 -0
  47. package/src/index.ts +2 -0
  48. package/src/rate-limit/express.ts +1 -0
  49. package/src/response/index.ts +49 -40
  50. package/src/upload/index.ts +268 -0
  51. package/tests/auth.test.ts +134 -0
  52. package/tests/config.test.ts +36 -0
  53. package/tests/logger.test.ts +47 -0
  54. package/tests/notifications.test.ts +19 -0
  55. package/tests/rate-limit.test.ts +50 -0
  56. package/tests/upload.test.ts +33 -0
  57. package/tsconfig.test.json +14 -0
  58. 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 express = require('express');
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
- express.Response.prototype.success = function(data, message, statusCode = 200) {
1117
- return ResponseHelper.success(this, data, message, statusCode);
1118
- };
1119
- express.Response.prototype.created = function(data, message) {
1120
- return ResponseHelper.created(this, data, message);
1121
- };
1122
- express.Response.prototype.updated = function(data, message) {
1123
- return ResponseHelper.updated(this, data, message);
1124
- };
1125
- express.Response.prototype.deleted = function(message) {
1126
- return ResponseHelper.deleted(this, message);
1127
- };
1128
- express.Response.prototype.error = function(error, statusCode = 400, code, details) {
1129
- return ResponseHelper.error(this, error, statusCode, code, details);
1130
- };
1131
- express.Response.prototype.badRequest = function(error, code) {
1132
- return ResponseHelper.badRequest(this, error, code);
1133
- };
1134
- express.Response.prototype.unauthorized = function(error, code) {
1135
- return ResponseHelper.unauthorized(this, error, code);
1136
- };
1137
- express.Response.prototype.forbidden = function(error, code) {
1138
- return ResponseHelper.forbidden(this, error, code);
1139
- };
1140
- express.Response.prototype.notFound = function(error, code) {
1141
- return ResponseHelper.notFound(this, error, code);
1142
- };
1143
- express.Response.prototype.conflict = function(error, code) {
1144
- return ResponseHelper.conflict(this, error, code);
1145
- };
1146
- express.Response.prototype.validationError = function(error, details) {
1147
- return ResponseHelper.validationError(this, error, details);
1148
- };
1149
- express.Response.prototype.internalError = function(error) {
1150
- return ResponseHelper.internalError(this, error);
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
- express.Response.prototype.paginated = function(data, page, limit, total) {
1153
- return ResponseHelper.paginated(this, data, page, limit, total);
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