@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.
- package/README.md +372 -0
- package/knexfile.d.ts +5 -0
- package/knexfile.ts +29 -0
- package/out/knexfile.js +25 -0
- package/out/knexfile.js.map +1 -0
- package/out/src/app.js +160 -0
- package/out/src/app.js.map +1 -0
- package/out/src/config/firebase.js +150 -0
- package/out/src/config/firebase.js.map +1 -0
- package/out/src/index.js +295 -0
- package/out/src/index.js.map +1 -0
- package/out/src/migrations/2022-12-28-001-initial-migration.js +28 -0
- package/out/src/migrations/2022-12-28-001-initial-migration.js.map +1 -0
- package/out/src/migrations/2023-01-17-messages-update.js +11 -0
- package/out/src/migrations/2023-01-17-messages-update.js.map +1 -0
- package/out/src/migrations/2024-03-05-001-messageID-upgrade.js +21 -0
- package/out/src/migrations/2024-03-05-001-messageID-upgrade.js.map +1 -0
- package/out/src/migrations/2025-01-31-001-notification-permissions.js +59 -0
- package/out/src/migrations/2025-01-31-001-notification-permissions.js.map +1 -0
- package/out/src/migrations/2025-01-31-002-device-registrations.js +24 -0
- package/out/src/migrations/2025-01-31-002-device-registrations.js.map +1 -0
- package/out/src/routes/__tests/acknowledgeMessage.test.js +129 -0
- package/out/src/routes/__tests/acknowledgeMessage.test.js.map +1 -0
- package/out/src/routes/__tests/listMessages.test.js +211 -0
- package/out/src/routes/__tests/listMessages.test.js.map +1 -0
- package/out/src/routes/__tests/sendMessage.test.js +307 -0
- package/out/src/routes/__tests/sendMessage.test.js.map +1 -0
- package/out/src/routes/acknowledgeMessage.js +144 -0
- package/out/src/routes/acknowledgeMessage.js.map +1 -0
- package/out/src/routes/index.js +17 -0
- package/out/src/routes/index.js.map +1 -0
- package/out/src/routes/listDevices.js +74 -0
- package/out/src/routes/listDevices.js.map +1 -0
- package/out/src/routes/listMessages.js +186 -0
- package/out/src/routes/listMessages.js.map +1 -0
- package/out/src/routes/permissions/getPermission.js +149 -0
- package/out/src/routes/permissions/getPermission.js.map +1 -0
- package/out/src/routes/permissions/getQuote.js +165 -0
- package/out/src/routes/permissions/getQuote.js.map +1 -0
- package/out/src/routes/permissions/index.js +12 -0
- package/out/src/routes/permissions/index.js.map +1 -0
- package/out/src/routes/permissions/listPermissions.js +190 -0
- package/out/src/routes/permissions/listPermissions.js.map +1 -0
- package/out/src/routes/permissions/setPermission.js +137 -0
- package/out/src/routes/permissions/setPermission.js.map +1 -0
- package/out/src/routes/registerDevice.js +150 -0
- package/out/src/routes/registerDevice.js.map +1 -0
- package/out/src/routes/sendMessage.js +451 -0
- package/out/src/routes/sendMessage.js.map +1 -0
- package/out/src/swagger.js +49 -0
- package/out/src/swagger.js.map +1 -0
- package/out/src/types/messagePermissions.js +2 -0
- package/out/src/types/messagePermissions.js.map +1 -0
- package/out/src/types/notifications.js +2 -0
- package/out/src/types/notifications.js.map +1 -0
- package/out/src/utils/logger.js +24 -0
- package/out/src/utils/logger.js.map +1 -0
- package/out/src/utils/messagePermissions.js +126 -0
- package/out/src/utils/messagePermissions.js.map +1 -0
- package/out/src/utils/sendFCMNotification.js +121 -0
- package/out/src/utils/sendFCMNotification.js.map +1 -0
- package/out/tsconfig.tsbuildinfo +1 -0
- package/package.json +88 -0
- package/src/migrations/2022-12-28-001-initial-migration.ts +31 -0
- package/src/migrations/2023-01-17-messages-update.ts +13 -0
- package/src/migrations/2024-03-05-001-messageID-upgrade.ts +25 -0
- package/src/migrations/2025-01-31-001-notification-permissions.ts +68 -0
- package/src/migrations/2025-01-31-002-device-registrations.ts +28 -0
- 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>
|