@imksh/create 1.0.28 → 1.0.30

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 (29) hide show
  1. package/package.json +1 -1
  2. package/templates/layers/node/javascript/package.json +2 -1
  3. package/templates/layers/node/javascript/src/controllers/push.controller.js +35 -14
  4. package/templates/layers/node/javascript/src/models/pushSubscription.model.js +5 -0
  5. package/templates/layers/node/javascript/src/utils/pushHelper.js +59 -12
  6. package/templates/layers/node/typescript/package-lock.json +3795 -0
  7. package/templates/layers/node/typescript/package.json +2 -1
  8. package/templates/layers/node/typescript/src/controllers/push.controller.ts +35 -14
  9. package/templates/layers/node/typescript/src/models/pushSubscription.model.ts +6 -0
  10. package/templates/layers/node/typescript/src/services/auth.service.ts +3 -3
  11. package/templates/layers/node/typescript/src/utils/pushHelper.ts +66 -12
  12. package/templates/layers/node-prisma/javascript/package.json +6 -3
  13. package/templates/layers/node-prisma/javascript/prisma/schema.prisma +20 -9
  14. package/templates/layers/node-prisma/javascript/prisma.config.js +12 -0
  15. package/templates/layers/node-prisma/javascript/src/config/{db.js → prisma.js} +9 -1
  16. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +58 -19
  17. package/templates/layers/node-prisma/javascript/src/utils/pushHelper.js +62 -14
  18. package/templates/layers/node-prisma/typescript/package-lock.json +4808 -0
  19. package/templates/layers/node-prisma/typescript/package.json +8 -4
  20. package/templates/layers/node-prisma/typescript/prisma/schema.prisma +20 -9
  21. package/templates/layers/node-prisma/typescript/prisma.config.ts +12 -0
  22. package/templates/layers/node-prisma/typescript/src/config/{db.ts → prisma.ts} +9 -1
  23. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -22
  24. package/templates/layers/node-prisma/typescript/src/index.ts +1 -1
  25. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +15 -5
  26. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +134 -128
  27. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +69 -14
  28. package/templates/layers/react-native/base/src/components/ui/BiometricLock.tsx +2 -1
  29. package/templates/layers/react-native/base/src/config/appConfig.ts +44 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imksh/create",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "A production-ready create for scaffolding modern applications.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,7 +24,8 @@
24
24
  "cloudinary": "^2.0.0",
25
25
  "nodemailer": "^6.9.7",
26
26
  "socket.io": "^4.7.2",
27
- "web-push": "^3.6.6"
27
+ "web-push": "^3.6.6",
28
+ "expo-server-sdk": "^3.10.0"
28
29
  },
29
30
  "devDependencies": {
30
31
  "nodemon": "^3.0.1"
@@ -6,26 +6,47 @@ export const subscribe = async (req, res, next) => {
6
6
  const subscription = req.body;
7
7
  const userId = req.user?._id || req.user?.id; // supports both mongoose and prisma IDs
8
8
 
9
- if (!subscription || !subscription.endpoint) {
9
+ if (!subscription) {
10
10
  return next({ status: 400, message: "Invalid subscription payload" });
11
11
  }
12
12
 
13
- const existingSub = await PushSubscription.findOne({ endpoint: subscription.endpoint });
13
+ const isExpo = !!subscription.token;
14
+ const isWeb = !!(subscription.endpoint && subscription.keys);
14
15
 
15
- if (existingSub) {
16
- if (userId) {
17
- existingSub.user = userId; await existingSub.save();
18
- }
19
- return res.status(200).json({ success: true, message: "Already subscribed." });
16
+ if (!isExpo && !isWeb) {
17
+ return next({ status: 400, message: "Missing token (EXPO) or endpoint/keys (WEB)" });
20
18
  }
21
19
 
22
-
23
- await PushSubscription.create({
24
- endpoint: subscription.endpoint,
25
- keys: subscription.keys,
26
- user: userId || undefined
27
- });
28
-
20
+ if (isExpo) {
21
+ const existingSub = await PushSubscription.findOne({ expoPushToken: subscription.token });
22
+ if (existingSub) {
23
+ if (userId && String(existingSub.user) !== String(userId)) {
24
+ existingSub.user = userId; await existingSub.save();
25
+ }
26
+ return res.status(200).json({ success: true, message: "Already subscribed." });
27
+ }
28
+
29
+ await PushSubscription.create({
30
+ type: "EXPO",
31
+ expoPushToken: subscription.token,
32
+ user: userId || undefined
33
+ });
34
+ } else {
35
+ const existingSub = await PushSubscription.findOne({ endpoint: subscription.endpoint });
36
+ if (existingSub) {
37
+ if (userId && String(existingSub.user) !== String(userId)) {
38
+ existingSub.user = userId; await existingSub.save();
39
+ }
40
+ return res.status(200).json({ success: true, message: "Already subscribed." });
41
+ }
42
+
43
+ await PushSubscription.create({
44
+ type: "WEB",
45
+ endpoint: subscription.endpoint,
46
+ keys: subscription.keys,
47
+ user: userId || undefined
48
+ });
49
+ }
29
50
 
30
51
  res.status(201).json({ success: true, message: "Subscription created." });
31
52
  } catch (error) {
@@ -3,6 +3,11 @@ import mongoose from "mongoose";
3
3
  // Handles both Web Push (endpoint + keys) and Expo Push (expoPushToken)
4
4
  const pushSubscriptionSchema = new mongoose.Schema(
5
5
  {
6
+ type: {
7
+ type: String,
8
+ enum: ["WEB", "EXPO"],
9
+ default: "WEB",
10
+ },
6
11
  // Web Push fields
7
12
  endpoint: {
8
13
  type: String,
@@ -1,20 +1,67 @@
1
1
  import webpush from "../config/webPush.js";
2
+ import { Expo } from "expo-server-sdk";
2
3
  import logger from "./logger.js";
3
4
  import PushSubscription from "../models/pushSubscription.model.js";
4
5
 
5
- export const sendPushToUser = async (userId, title, body, url = "/") => {
6
+ const expo = new Expo();
7
+
8
+ export const sendPushToUser = async (
9
+ userId,
10
+ title,
11
+ body,
12
+ url = "/",
13
+ ) => {
6
14
  try {
7
- const payload = JSON.stringify({ title, body, url });
8
- const subs = await PushSubscription.find({ user: userId });
9
- for (const sub of subs) {
10
- await webpush.sendNotification({ endpoint: sub.endpoint, keys: sub.keys }, payload).catch(err => {
11
- if (err.statusCode === 404 || err.statusCode === 410) {
12
- sub.deleteOne();
13
- } else {
14
- logger.error("Push Error", err);
15
- }
16
- });
17
- }
15
+ const subscriptions = await PushSubscription.find({ user: userId });
16
+
17
+ if (subscriptions.length === 0) return;
18
+
19
+ const webSubs = subscriptions.filter(s => s.type === "WEB");
20
+ const expoSubs = subscriptions.filter(s => s.type === "EXPO");
21
+
22
+ // Web Push
23
+ if (webSubs.length > 0) {
24
+ const payload = JSON.stringify({ title, body, url });
25
+ await Promise.allSettled(
26
+ webSubs.map(async (sub) => {
27
+ if (!sub.endpoint || !sub.keys) return;
28
+ const pushSub = { endpoint: sub.endpoint, keys: sub.keys };
29
+ try {
30
+ await webpush.sendNotification(pushSub, payload);
31
+ } catch (err) {
32
+ if (err.statusCode === 404 || err.statusCode === 410) {
33
+ await sub.deleteOne();
34
+ } else {
35
+ logger.error("Web Push Error", err);
36
+ }
37
+ }
38
+ })
39
+ );
40
+ }
41
+
42
+ // Expo Push
43
+ if (expoSubs.length > 0) {
44
+ const messages = [];
45
+ for (const sub of expoSubs) {
46
+ if (!sub.expoPushToken || !Expo.isExpoPushToken(sub.expoPushToken)) continue;
47
+ messages.push({
48
+ to: sub.expoPushToken,
49
+ sound: "default",
50
+ title,
51
+ body,
52
+ data: { url },
53
+ });
54
+ }
55
+
56
+ const chunks = expo.chunkPushNotifications(messages);
57
+ for (const chunk of chunks) {
58
+ try {
59
+ await expo.sendPushNotificationsAsync(chunk);
60
+ } catch (error) {
61
+ logger.error("Error sending push notification chunk", error);
62
+ }
63
+ }
64
+ }
18
65
  } catch (error) {
19
66
  logger.error("Error sending push notification to user", error);
20
67
  }