@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,31 @@
1
+ import { Knex } from 'knex'
2
+
3
+ export async function up (knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('messageBox', (table) => {
5
+ table.increments('messageBoxId').primary()
6
+ table.timestamps(true, true)
7
+ table.string('type').notNullable() // What type of messages go in here?
8
+ table.string('identityKey').notNullable() // Who's message box is this?
9
+ table.unique(['type', 'identityKey'])
10
+ })
11
+
12
+ await knex.schema.createTable('messages', (table) => {
13
+ table.increments('messageId').primary()
14
+ table.timestamps(true, true)
15
+ table
16
+ .integer('messageBoxId')
17
+ .unsigned()
18
+ .references('messageBoxId')
19
+ .inTable('messageBox')
20
+ .onDelete('CASCADE') // All messages get deleted if the messageBox is deleted
21
+ table.string('sender').notNullable()
22
+ table.string('recipient').notNullable()
23
+ table.text('body', 'longtext').notNullable() // Contents of the message
24
+ table.boolean('acknowledged').defaultTo(false)
25
+ })
26
+ }
27
+
28
+ export async function down (knex: Knex): Promise<void> {
29
+ await knex.schema.dropTableIfExists('messages')
30
+ await knex.schema.dropTableIfExists('messageBox')
31
+ }
@@ -0,0 +1,13 @@
1
+ import { Knex } from 'knex'
2
+
3
+ export async function up (knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('messages', (table) => {
5
+ table.dropColumn('acknowledged')
6
+ })
7
+ }
8
+
9
+ export async function down (knex: Knex): Promise<void> {
10
+ await knex.schema.alterTable('messages', (table) => {
11
+ table.boolean('acknowledged').defaultTo(false)
12
+ })
13
+ }
@@ -0,0 +1,25 @@
1
+ import { Knex } from 'knex'
2
+
3
+ export async function up (knex: Knex): Promise<void> {
4
+ // Drop the existing 'messageId' column
5
+ await knex.schema.alterTable('messages', (table) => {
6
+ table.dropColumn('messageId')
7
+ })
8
+
9
+ // Add 'messageId' back as a unique string
10
+ await knex.schema.alterTable('messages', (table) => {
11
+ table.string('messageId').unique().notNullable()
12
+ })
13
+ }
14
+
15
+ export async function down (knex: Knex): Promise<void> {
16
+ // Drop the new 'messageId' column
17
+ await knex.schema.alterTable('messages', (table) => {
18
+ table.dropColumn('messageId')
19
+ })
20
+
21
+ // Restore 'messageId' as an auto-incrementing primary key
22
+ await knex.schema.alterTable('messages', (table) => {
23
+ table.increments('messageId').primary()
24
+ })
25
+ }
@@ -0,0 +1,68 @@
1
+ import { Knex } from 'knex'
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Create generalized message permissions table for all box/sender combinations
5
+ await knex.schema.createTable('message_permissions', (table) => {
6
+ table.increments('id').primary()
7
+ table.timestamp('created_at').defaultTo(knex.fn.now())
8
+ table.timestamp('updated_at').defaultTo(knex.fn.now())
9
+ table.string('recipient', 255).notNullable() // identityKey of who this permission belongs to
10
+ table.string('sender', 255).nullable() // identityKey of sender (NULL for box-wide defaults)
11
+ table.string('message_box', 255).notNullable() // messageBox type (e.g., 'notifications', 'inbox', etc.)
12
+ table.integer('recipient_fee').notNullable() // -1 = block all, 0 = always allow, >0 = satoshi amount required
13
+
14
+ table.unique(['recipient', 'sender', 'message_box'])
15
+
16
+ table.index('recipient')
17
+ table.index(['recipient', 'message_box'])
18
+ table.index('message_box')
19
+ table.index('sender')
20
+ })
21
+
22
+ // Create server fee configuration table
23
+ await knex.schema.createTable('server_fees', (table) => {
24
+ table.increments('id').primary()
25
+ table.timestamp('created_at').defaultTo(knex.fn.now())
26
+ table.timestamp('updated_at').defaultTo(knex.fn.now())
27
+ table.string('message_box').notNullable() // messageBox type
28
+ table.integer('delivery_fee').notNullable() // Server delivery fee in satoshis
29
+ table.unique(['message_box']) // Each box type can only have one server fee setting
30
+ })
31
+
32
+ // Insert default server fees for different message box types
33
+ const existingFees = await knex('server_fees').select('message_box')
34
+ const existingMessageBoxes = existingFees.map(fee => fee.message_box)
35
+
36
+ const defaultFees = [
37
+ {
38
+ message_box: 'notifications',
39
+ delivery_fee: 10, // Higher fee for FCM delivery
40
+ created_at: new Date(),
41
+ updated_at: new Date()
42
+ },
43
+ {
44
+ message_box: 'inbox',
45
+ delivery_fee: 0, // Free for regular inbox
46
+ created_at: new Date(),
47
+ updated_at: new Date()
48
+ },
49
+ {
50
+ message_box: 'payment_inbox',
51
+ delivery_fee: 0, // Free for payment inbox
52
+ created_at: new Date(),
53
+ updated_at: new Date()
54
+ }
55
+ ]
56
+
57
+ // Only insert fees for message boxes that don't already exist
58
+ const feesToInsert = defaultFees.filter(fee => !existingMessageBoxes.includes(fee.message_box))
59
+
60
+ if (feesToInsert.length > 0) {
61
+ await knex('server_fees').insert(feesToInsert)
62
+ }
63
+ }
64
+
65
+ export async function down(knex: Knex): Promise<void> {
66
+ await knex.schema.dropTableIfExists('server_fees')
67
+ await knex.schema.dropTableIfExists('message_permissions')
68
+ }
@@ -0,0 +1,28 @@
1
+ import { Knex } from 'knex'
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Create device registrations table for FCM push notifications
5
+ await knex.schema.createTable('device_registrations', (table) => {
6
+ table.increments('id').primary()
7
+ table.timestamp('created_at').defaultTo(knex.fn.now())
8
+ table.timestamp('updated_at').defaultTo(knex.fn.now())
9
+ table.string('identity_key', 255).notNullable() // User's identity key
10
+ table.string('fcm_token', 500).notNullable() // Firebase Cloud Messaging token (can be long)
11
+ table.string('device_id', 255).nullable() // Optional device identifier
12
+ table.string('platform', 50).nullable() // 'ios', 'android', 'web', etc.
13
+ table.timestamp('last_used').nullable() // When this token was last successfully used
14
+ table.boolean('active').defaultTo(true) // Whether this token is still active
15
+
16
+ // Unique constraint to prevent duplicate token registrations
17
+ table.unique(['fcm_token'])
18
+
19
+ // Index for efficient lookups by identity key
20
+ table.index('identity_key')
21
+ table.index(['identity_key', 'active'])
22
+ table.index('last_used')
23
+ })
24
+ }
25
+
26
+ export async function down(knex: Knex): Promise<void> {
27
+ await knex.schema.dropTableIfExists('device_registrations')
28
+ }
@@ -0,0 +1,62 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1, shrink-to-fit=no"
8
+ />
9
+ <title>MessageBox Server</title>
10
+ <style>
11
+ body {
12
+ font-family: Arial;
13
+ padding: 1em;
14
+ }
15
+ .main {
16
+ max-width: min(1440px, 100vw);
17
+ margin: auto;
18
+ word-wrap: break-word;
19
+ }
20
+ .method {
21
+ background-color: lime;
22
+ text-transform: uppercase;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <div class='main'>
28
+ <h1>PeerServ</h1>
29
+ <p>
30
+ Facilitates general secure peer-to-peer message exchanges between parties.
31
+ </p>
32
+ <p>Learn more on <a href='https://github.com/bitcoin-sv/message-box-server'>GitHub</a></p>
33
+ <h2>Endpoint Documentation</h2>
34
+ <% for (let i in routes) { %>
35
+ <% if (routes[i].hidden !== true) { %>
36
+ <h3>
37
+ <b class='method'><%= routes[i].type %></b> <%= routes[i].path %>
38
+ </h3>
39
+ <p>
40
+ <%= routes[i].summary %>
41
+ </p>
42
+ <% if (typeof routes[i].parameters === 'object') { %>
43
+ <h4>Parameters</h4>
44
+ <ul>
45
+ <% Object.entries(routes[i].parameters).forEach(([k, v]) => { %>
46
+ <% if (typeof v === 'object') { %>
47
+ <pre><%= JSON.stringify(routes[i].parameters, null, 2) %></pre>
48
+ <% } else { %>
49
+ <li><b><%= k %></b>: <%= v %></li>
50
+ <% } %>
51
+ <% }) %>
52
+ </ul>
53
+ <% } %>
54
+ <% if (typeof routes[i].exampleResponse === 'object') { %>
55
+ <h4>Example Response</h4>
56
+ <pre><%= JSON.stringify(routes[i].exampleResponse, null, 2) %></pre>
57
+ <% } %>
58
+ <% } %>
59
+ <% } %>
60
+ </div>
61
+ </body>
62
+ </html>