@bopen-io/messagebox-server 1.1.5

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 (69) hide show
  1. package/README.md +372 -0
  2. package/knexfile.d.ts +5 -0
  3. package/knexfile.ts +29 -0
  4. package/out/knexfile.js +25 -0
  5. package/out/knexfile.js.map +1 -0
  6. package/out/src/app.js +160 -0
  7. package/out/src/app.js.map +1 -0
  8. package/out/src/config/firebase.js +150 -0
  9. package/out/src/config/firebase.js.map +1 -0
  10. package/out/src/index.js +295 -0
  11. package/out/src/index.js.map +1 -0
  12. package/out/src/migrations/2022-12-28-001-initial-migration.js +28 -0
  13. package/out/src/migrations/2022-12-28-001-initial-migration.js.map +1 -0
  14. package/out/src/migrations/2023-01-17-messages-update.js +11 -0
  15. package/out/src/migrations/2023-01-17-messages-update.js.map +1 -0
  16. package/out/src/migrations/2024-03-05-001-messageID-upgrade.js +21 -0
  17. package/out/src/migrations/2024-03-05-001-messageID-upgrade.js.map +1 -0
  18. package/out/src/migrations/2025-01-31-001-notification-permissions.js +59 -0
  19. package/out/src/migrations/2025-01-31-001-notification-permissions.js.map +1 -0
  20. package/out/src/migrations/2025-01-31-002-device-registrations.js +24 -0
  21. package/out/src/migrations/2025-01-31-002-device-registrations.js.map +1 -0
  22. package/out/src/routes/__tests/acknowledgeMessage.test.js +129 -0
  23. package/out/src/routes/__tests/acknowledgeMessage.test.js.map +1 -0
  24. package/out/src/routes/__tests/listMessages.test.js +211 -0
  25. package/out/src/routes/__tests/listMessages.test.js.map +1 -0
  26. package/out/src/routes/__tests/sendMessage.test.js +307 -0
  27. package/out/src/routes/__tests/sendMessage.test.js.map +1 -0
  28. package/out/src/routes/acknowledgeMessage.js +144 -0
  29. package/out/src/routes/acknowledgeMessage.js.map +1 -0
  30. package/out/src/routes/index.js +17 -0
  31. package/out/src/routes/index.js.map +1 -0
  32. package/out/src/routes/listDevices.js +74 -0
  33. package/out/src/routes/listDevices.js.map +1 -0
  34. package/out/src/routes/listMessages.js +186 -0
  35. package/out/src/routes/listMessages.js.map +1 -0
  36. package/out/src/routes/permissions/getPermission.js +149 -0
  37. package/out/src/routes/permissions/getPermission.js.map +1 -0
  38. package/out/src/routes/permissions/getQuote.js +165 -0
  39. package/out/src/routes/permissions/getQuote.js.map +1 -0
  40. package/out/src/routes/permissions/index.js +12 -0
  41. package/out/src/routes/permissions/index.js.map +1 -0
  42. package/out/src/routes/permissions/listPermissions.js +190 -0
  43. package/out/src/routes/permissions/listPermissions.js.map +1 -0
  44. package/out/src/routes/permissions/setPermission.js +137 -0
  45. package/out/src/routes/permissions/setPermission.js.map +1 -0
  46. package/out/src/routes/registerDevice.js +150 -0
  47. package/out/src/routes/registerDevice.js.map +1 -0
  48. package/out/src/routes/sendMessage.js +451 -0
  49. package/out/src/routes/sendMessage.js.map +1 -0
  50. package/out/src/swagger.js +49 -0
  51. package/out/src/swagger.js.map +1 -0
  52. package/out/src/types/messagePermissions.js +2 -0
  53. package/out/src/types/messagePermissions.js.map +1 -0
  54. package/out/src/types/notifications.js +2 -0
  55. package/out/src/types/notifications.js.map +1 -0
  56. package/out/src/utils/logger.js +24 -0
  57. package/out/src/utils/logger.js.map +1 -0
  58. package/out/src/utils/messagePermissions.js +126 -0
  59. package/out/src/utils/messagePermissions.js.map +1 -0
  60. package/out/src/utils/sendFCMNotification.js +121 -0
  61. package/out/src/utils/sendFCMNotification.js.map +1 -0
  62. package/out/tsconfig.tsbuildinfo +1 -0
  63. package/package.json +88 -0
  64. package/src/migrations/2022-12-28-001-initial-migration.ts +31 -0
  65. package/src/migrations/2023-01-17-messages-update.ts +13 -0
  66. package/src/migrations/2024-03-05-001-messageID-upgrade.ts +25 -0
  67. package/src/migrations/2025-01-31-001-notification-permissions.ts +68 -0
  68. package/src/migrations/2025-01-31-002-device-registrations.ts +28 -0
  69. package/src/templates/documentation.ejs +62 -0
@@ -0,0 +1,165 @@
1
+ import { PublicKey } from '@bsv/sdk';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import { getRecipientFee, getServerDeliveryFee } from '../../utils/messagePermissions.js';
4
+ /**
5
+ * @swagger
6
+ * /permissions/quote:
7
+ * get:
8
+ * summary: Get message delivery quote(s)
9
+ * description: Get pricing information for sending messages to one or many recipients' message boxes
10
+ * tags:
11
+ * - Permissions
12
+ * parameters:
13
+ * - in: query
14
+ * name: recipient
15
+ * required: true
16
+ * schema:
17
+ * oneOf:
18
+ * - type: string
19
+ * - type: array
20
+ * items:
21
+ * type: string
22
+ * description: identityKey of the recipient, or multiple recipients by repeating the parameter (?recipient=A&recipient=B)
23
+ * - in: query
24
+ * name: messageBox
25
+ * required: true
26
+ * schema:
27
+ * type: string
28
+ * description: messageBox type
29
+ * responses:
30
+ * 200:
31
+ * description: Quote(s) retrieved successfully
32
+ * 400:
33
+ * description: Invalid request parameters
34
+ * 401:
35
+ * description: Authentication required
36
+ * 500:
37
+ * description: Internal server error
38
+ */
39
+ export default {
40
+ type: 'get',
41
+ path: '/permissions/quote',
42
+ func: async (req, res) => {
43
+ try {
44
+ Logger.log('[DEBUG] Processing message quote request');
45
+ console.log('[DEBUG] Processing message quote request');
46
+ // Validate authentication (the caller is the SENDER)
47
+ const sender = req.auth?.identityKey;
48
+ if (sender == null) {
49
+ Logger.log('[DEBUG] Authentication required for message quote');
50
+ return res.status(401).json({
51
+ status: 'error',
52
+ code: 'ERR_AUTHENTICATION_REQUIRED',
53
+ description: 'Authentication required.'
54
+ });
55
+ }
56
+ const { recipient, messageBox } = req.query;
57
+ // Required params
58
+ if (recipient == null || messageBox == null) {
59
+ Logger.log('[DEBUG] Missing required parameters for message quote');
60
+ return res.status(400).json({
61
+ status: 'error',
62
+ code: 'ERR_MISSING_PARAMETERS',
63
+ description: 'recipient and messageBox parameters are required.'
64
+ });
65
+ }
66
+ // Normalize recipients to array (preserve order)
67
+ const recipients = Array.isArray(recipient) ? recipient : [recipient];
68
+ if (recipients.length === 0) {
69
+ return res.status(400).json({
70
+ status: 'error',
71
+ code: 'ERR_MISSING_PARAMETERS',
72
+ description: 'At least one recipient is required.'
73
+ });
74
+ }
75
+ // Validate each recipient public key
76
+ const invalidIdx = [];
77
+ for (let i = 0; i < recipients.length; i++) {
78
+ try {
79
+ PublicKey.fromString(recipients[i]);
80
+ }
81
+ catch {
82
+ invalidIdx.push(i);
83
+ }
84
+ }
85
+ if (invalidIdx.length > 0) {
86
+ Logger.log('[DEBUG] Invalid recipient public key format in array');
87
+ return res.status(400).json({
88
+ status: 'error',
89
+ code: 'ERR_INVALID_PUBLIC_KEY',
90
+ description: `Invalid recipient public key at index(es): ${invalidIdx.join(', ')}.`
91
+ });
92
+ }
93
+ // Delivery fee for this messageBox (applies per message/recipient)
94
+ const perMessageDeliveryFee = await getServerDeliveryFee(messageBox);
95
+ // Single-recipient path → keep legacy response shape for compatibility
96
+ if (recipients.length === 1) {
97
+ const recipientKey = recipients[0];
98
+ const recipientFee = await getRecipientFee(recipientKey, sender, messageBox);
99
+ return res.status(200).json({
100
+ status: 'success',
101
+ description: 'Message delivery quote generated.',
102
+ quote: {
103
+ deliveryFee: perMessageDeliveryFee,
104
+ recipientFee
105
+ }
106
+ });
107
+ }
108
+ // Multi-recipient path → compute per-recipient, plus aggregates
109
+ const quotesByRecipient = [];
110
+ const blockedRecipients = [];
111
+ let totalRecipientFees = 0;
112
+ let totalDeliveryFees = 0;
113
+ // Helper to map fee -> status
114
+ const feeToStatus = (fee) => {
115
+ if (fee === -1)
116
+ return 'blocked';
117
+ if (fee === 0)
118
+ return 'always_allow';
119
+ return 'payment_required';
120
+ };
121
+ for (const r of recipients) {
122
+ const recipientFee = await getRecipientFee(r, sender, messageBox);
123
+ const status = feeToStatus(recipientFee);
124
+ quotesByRecipient.push({
125
+ recipient: r,
126
+ messageBox,
127
+ deliveryFee: perMessageDeliveryFee,
128
+ recipientFee,
129
+ status
130
+ });
131
+ // Aggregate: count deliveryFee per intended message, and recipientFee if not blocked
132
+ totalDeliveryFees += perMessageDeliveryFee;
133
+ if (recipientFee === -1) {
134
+ blockedRecipients.push(r);
135
+ }
136
+ else {
137
+ totalRecipientFees += recipientFee;
138
+ }
139
+ }
140
+ const totals = {
141
+ deliveryFees: totalDeliveryFees,
142
+ recipientFees: totalRecipientFees,
143
+ // If any are blocked, caller may want to handle those separately.
144
+ // We still provide a full monetary total for non-blocked recipients:
145
+ totalForPayableRecipients: totalDeliveryFees + totalRecipientFees
146
+ };
147
+ return res.status(200).json({
148
+ status: 'success',
149
+ description: `Message delivery quotes generated for ${recipients.length} recipients.`,
150
+ quotesByRecipient,
151
+ totals,
152
+ blockedRecipients
153
+ });
154
+ }
155
+ catch (error) {
156
+ Logger.error('[ERROR] Internal Server Error in message quote:', error);
157
+ return res.status(500).json({
158
+ status: 'error',
159
+ code: 'ERR_INTERNAL',
160
+ description: 'An internal error has occurred.'
161
+ });
162
+ }
163
+ }
164
+ };
165
+ //# sourceMappingURL=getQuote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getQuote.js","sourceRoot":"","sources":["../../../../src/routes/permissions/getQuote.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ // Export all permission-related routes
2
+ import setPermission from './setPermission.js';
3
+ import getPermission from './getPermission.js';
4
+ import getQuote from './getQuote.js';
5
+ import listPermissions from './listPermissions.js';
6
+ export const permissionRoutes = [
7
+ setPermission,
8
+ getPermission,
9
+ getQuote,
10
+ listPermissions
11
+ ];
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/routes/permissions/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,aAAa,MAAM,oBAAoB,CAAA;AAC9C,OAAO,aAAa,MAAM,oBAAoB,CAAA;AAC9C,OAAO,QAAQ,MAAM,eAAe,CAAA;AACpC,OAAO,eAAe,MAAM,sBAAsB,CAAA;AAElD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa;IACb,aAAa;IACb,QAAQ;IACR,eAAe;CAChB,CAAA"}
@@ -0,0 +1,190 @@
1
+ import knexConfig from '../../../knexfile.js';
2
+ import * as knexLib from 'knex';
3
+ import { Logger } from '../../utils/logger.js';
4
+ // Determine the environment (default to development)
5
+ const { NODE_ENV = 'development' } = process.env;
6
+ /**
7
+ * Knex instance connected based on environment (development, production, or staging).
8
+ */
9
+ const knex = knexLib.default?.(NODE_ENV === 'production' || NODE_ENV === 'staging'
10
+ ? knexConfig.production
11
+ : knexConfig.development) ?? knexLib(NODE_ENV === 'production' || NODE_ENV === 'staging'
12
+ ? knexConfig.production
13
+ : knexConfig.development);
14
+ /**
15
+ * @swagger
16
+ * /permissions/list:
17
+ * get:
18
+ * summary: List message permissions for the authenticated user
19
+ * description: Retrieve all permissions set by the authenticated user with optional filtering and pagination
20
+ * tags:
21
+ * - Permissions
22
+ * parameters:
23
+ * - in: query
24
+ * name: messageBox
25
+ * required: false
26
+ * schema:
27
+ * type: string
28
+ * description: Optional messageBox type filter (e.g., 'notifications', 'inbox')
29
+ * - in: query
30
+ * name: limit
31
+ * required: false
32
+ * schema:
33
+ * type: integer
34
+ * minimum: 1
35
+ * maximum: 1000
36
+ * default: 100
37
+ * description: Maximum number of permissions to return
38
+ * - in: query
39
+ * name: offset
40
+ * required: false
41
+ * schema:
42
+ * type: integer
43
+ * minimum: 0
44
+ * default: 0
45
+ * description: Number of permissions to skip for pagination
46
+ * - in: query
47
+ * name: createdAtOrder
48
+ * required: false
49
+ * schema:
50
+ * type: string
51
+ * enum: ['asc', 'desc']
52
+ * default: 'desc'
53
+ * description: Sort order for created_at date (asc=oldest first, desc=newest first)
54
+ * responses:
55
+ * 200:
56
+ * description: Permissions retrieved successfully
57
+ * content:
58
+ * application/json:
59
+ * schema:
60
+ * type: object
61
+ * properties:
62
+ * status:
63
+ * type: string
64
+ * example: 'success'
65
+ * permissions:
66
+ * type: array
67
+ * items:
68
+ * type: object
69
+ * properties:
70
+ * sender:
71
+ * type: string
72
+ * nullable: true
73
+ * description: Sender identity key (null for box-wide defaults)
74
+ * messageBox:
75
+ * type: string
76
+ * description: MessageBox type
77
+ * recipientFee:
78
+ * type: integer
79
+ * description: Fee setting (-1=block all, 0=always allow, >0=satoshi amount)
80
+ * created_at:
81
+ * type: string
82
+ * format: date-time
83
+ * updated_at:
84
+ * type: string
85
+ * format: date-time
86
+ * total_count:
87
+ * type: integer
88
+ * description: Total number of permissions (useful for pagination)
89
+ * 400:
90
+ * description: Invalid query parameters
91
+ * 401:
92
+ * description: Unauthorized - authentication required
93
+ * 500:
94
+ * description: Internal server error
95
+ */
96
+ export default {
97
+ type: 'get',
98
+ path: '/permissions/list',
99
+ /**
100
+ * List message permissions for the authenticated user
101
+ *
102
+ * @param {ListPermissionsRequest} req - Authenticated request with query parameters
103
+ * @param {Response} res - Express response object
104
+ * @returns {Promise<Response>} JSON response with permissions list
105
+ */
106
+ func: async (req, res) => {
107
+ Logger.log('[DEBUG] Processing /permissions/list request...');
108
+ try {
109
+ // Validate authentication
110
+ if (req.auth?.identityKey == null) {
111
+ return res.status(401).json({
112
+ status: 'error',
113
+ code: 'ERR_UNAUTHORIZED',
114
+ description: 'Authentication required'
115
+ });
116
+ }
117
+ // Parse and validate query parameters
118
+ const { messageBox, limit: limitStr, offset: offsetStr, createdAtOrder } = req.query;
119
+ const limit = limitStr != null ? parseInt(limitStr, 10) : 100;
120
+ const offset = offsetStr != null ? parseInt(offsetStr, 10) : 0;
121
+ const sortOrder = createdAtOrder === 'asc' ? 'asc' : 'desc'; // Default to 'desc'
122
+ // Validate pagination parameters
123
+ if (isNaN(limit) || limit < 1 || limit > 1000) {
124
+ return res.status(400).json({
125
+ status: 'error',
126
+ code: 'ERR_INVALID_LIMIT',
127
+ description: 'Limit must be a number between 1 and 1000'
128
+ });
129
+ }
130
+ if (isNaN(offset) || offset < 0) {
131
+ return res.status(400).json({
132
+ status: 'error',
133
+ code: 'ERR_INVALID_OFFSET',
134
+ description: 'Offset must be a non-negative number'
135
+ });
136
+ }
137
+ // Validate identity key format
138
+ const recipientKey = req.auth.identityKey;
139
+ Logger.log(`[DEBUG] Listing permissions for recipient: ${recipientKey}, messageBox: ${messageBox ?? 'all'}, limit: ${limit}, offset: ${offset}, createdAtOrder: ${sortOrder}`);
140
+ // Build base query
141
+ let query = knex('message_permissions')
142
+ .select([
143
+ 'sender',
144
+ 'message_box',
145
+ 'recipient_fee',
146
+ 'created_at',
147
+ 'updated_at'
148
+ ])
149
+ .where('recipient', recipientKey)
150
+ .orderBy([
151
+ { column: 'message_box', order: 'asc' },
152
+ { column: 'sender', order: 'asc', nulls: 'first' }, // Box-wide (null) first
153
+ { column: 'created_at', order: sortOrder }
154
+ ]);
155
+ // Apply messageBox filter if provided
156
+ if (messageBox != null) {
157
+ query = query.where('message_box', messageBox);
158
+ }
159
+ // Get total count for pagination info (before applying limit/offset)
160
+ const countQuery = query.clone().clearSelect().clearOrder().count('* as count');
161
+ const [{ count: totalCount }] = await countQuery;
162
+ const total = parseInt(String(totalCount), 10);
163
+ // Apply pagination
164
+ const permissions = await query
165
+ .limit(limit)
166
+ .offset(offset);
167
+ Logger.log(`[DEBUG] Found ${permissions.length} permissions (${total} total)`);
168
+ return res.status(200).json({
169
+ status: 'success',
170
+ permissions: permissions.map(p => ({
171
+ sender: p.sender, // null for box-wide defaults
172
+ messageBox: p.message_box,
173
+ recipientFee: p.recipient_fee,
174
+ createdAt: p.created_at,
175
+ updatedAt: p.updated_at
176
+ })),
177
+ totalCount: total
178
+ });
179
+ }
180
+ catch (error) {
181
+ Logger.error('[ERROR] Error listing permissions:', error);
182
+ return res.status(500).json({
183
+ status: 'error',
184
+ code: 'ERR_LIST_PERMISSIONS_FAILED',
185
+ description: 'Failed to list permissions'
186
+ });
187
+ }
188
+ }
189
+ };
190
+ //# sourceMappingURL=listPermissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listPermissions.js","sourceRoot":"","sources":["../../../../src/routes/permissions/listPermissions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { PublicKey } from '@bsv/sdk';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import { setMessagePermission } from '../../utils/messagePermissions.js';
4
+ /**
5
+ * @swagger
6
+ * /permissions/set:
7
+ * post:
8
+ * summary: Set message permission for a sender/box combination or box-wide default
9
+ * description: Set permission level for receiving messages. If sender is provided, sets permission for that specific sender. If sender is omitted, sets box-wide default for all senders.
10
+ * tags:
11
+ * - Permissions
12
+ * requestBody:
13
+ * required: true
14
+ * content:
15
+ * application/json:
16
+ * schema:
17
+ * type: object
18
+ * required:
19
+ * - messageBox
20
+ * - recipientFee
21
+ * properties:
22
+ * sender:
23
+ * type: string
24
+ * description: identityKey of the sender (optional - if omitted, sets box-wide default for all senders)
25
+ * messageBox:
26
+ * type: string
27
+ * description: messageBox type (e.g., 'notifications', 'inbox')
28
+ * recipientFee:
29
+ * type: integer
30
+ * description: Fee level (-1=blocked, 0=always allow, >0=satoshi amount required)
31
+ * responses:
32
+ * 200:
33
+ * description: Permission successfully set/updated
34
+ * 400:
35
+ * description: Invalid request data
36
+ * 401:
37
+ * description: Authentication required
38
+ * 500:
39
+ * description: Internal server error
40
+ */
41
+ export default {
42
+ type: 'post',
43
+ path: '/permissions/set',
44
+ func: async (req, res) => {
45
+ try {
46
+ Logger.log('[DEBUG] Processing set message permission request');
47
+ // Validate authentication
48
+ const recipient = req.auth?.identityKey;
49
+ if (recipient == null) {
50
+ Logger.log('[DEBUG] Authentication required for set permission');
51
+ return res.status(401).json({
52
+ status: 'error',
53
+ code: 'ERR_AUTHENTICATION_REQUIRED',
54
+ description: 'Authentication required.'
55
+ });
56
+ }
57
+ const { sender, messageBox, recipientFee } = req.body;
58
+ // Validate request body (sender is optional)
59
+ if (messageBox == null || typeof recipientFee !== 'number') {
60
+ Logger.log('[DEBUG] Invalid request body for set permission');
61
+ return res.status(400).json({
62
+ status: 'error',
63
+ code: 'ERR_INVALID_REQUEST',
64
+ description: 'messageBox (string) and recipientFee (number) are required. sender (string) is optional for box-wide settings.'
65
+ });
66
+ }
67
+ // Validate sender public key format only if provided
68
+ if (sender != null) {
69
+ try {
70
+ PublicKey.fromString(sender);
71
+ }
72
+ catch (error) {
73
+ Logger.log('[DEBUG] Invalid sender public key format');
74
+ return res.status(400).json({
75
+ status: 'error',
76
+ code: 'ERR_INVALID_PUBLIC_KEY',
77
+ description: 'Invalid sender public key format.'
78
+ });
79
+ }
80
+ }
81
+ // Validate recipientFee value
82
+ if (!Number.isInteger(recipientFee)) {
83
+ Logger.log('[DEBUG] Invalid recipientFee value - must be integer');
84
+ return res.status(400).json({
85
+ status: 'error',
86
+ code: 'ERR_INVALID_FEE_VALUE',
87
+ description: 'recipientFee must be an integer (-1, 0, or positive number).'
88
+ });
89
+ }
90
+ // Validate messageBox value
91
+ if (typeof messageBox !== 'string' || messageBox.trim() === '') {
92
+ Logger.log('[DEBUG] Invalid messageBox value');
93
+ return res.status(400).json({
94
+ status: 'error',
95
+ code: 'ERR_INVALID_MESSAGE_BOX',
96
+ description: 'messageBox must be a non-empty string.'
97
+ });
98
+ }
99
+ // Set the message permission (convert undefined sender to null for box-wide)
100
+ const success = await setMessagePermission(recipient, sender ?? null, messageBox, recipientFee);
101
+ if (success == null) {
102
+ return res.status(500).json({
103
+ status: 'error',
104
+ code: 'ERR_DATABASE_ERROR',
105
+ description: 'Failed to update message permission.'
106
+ });
107
+ }
108
+ const isBoxWide = sender == null;
109
+ Logger.log(`[DEBUG] Successfully updated message permission: ${sender ?? 'BOX-WIDE'} -> ${recipient} (${messageBox}), fee: ${recipientFee}`);
110
+ let description;
111
+ const senderText = isBoxWide ? 'all senders' : sender;
112
+ const actionText = isBoxWide ? 'Box-wide default for' : 'Messages from';
113
+ if (recipientFee === -1) {
114
+ description = `${actionText} ${senderText} to ${messageBox} ${isBoxWide ? 'is' : 'are'} now blocked.`;
115
+ }
116
+ else if (recipientFee === 0) {
117
+ description = `${actionText} ${senderText} to ${messageBox} ${isBoxWide ? 'is' : 'are'} now always allowed.`;
118
+ }
119
+ else {
120
+ description = `${actionText} ${senderText} to ${messageBox} now require${isBoxWide ? 's' : ''} ${recipientFee} satoshis.`;
121
+ }
122
+ return res.status(200).json({
123
+ status: 'success',
124
+ description
125
+ });
126
+ }
127
+ catch (error) {
128
+ Logger.error('[ERROR] Internal Server Error in set permission:', error);
129
+ return res.status(500).json({
130
+ status: 'error',
131
+ code: 'ERR_INTERNAL',
132
+ description: 'An internal error has occurred.'
133
+ });
134
+ }
135
+ }
136
+ };
137
+ //# sourceMappingURL=setPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setPermission.js","sourceRoot":"","sources":["../../../../src/routes/permissions/setPermission.ts"],"names":[],"mappings":"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"}