@nina-protocol/nina-db 0.0.105 → 0.0.107

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/dist/knexfile.js CHANGED
@@ -2,6 +2,10 @@
2
2
  * @type { Object.<string, import("knex").Knex.Config> }
3
3
  */
4
4
  import "dotenv/config.js";
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
5
9
  export default {
6
10
  development: {
7
11
  client: 'postgresql',
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export async function up(knex) {
6
+ await knex.schema.alterTable('releases', (table) => {
7
+ table.string('mint').nullable().alter();
8
+ });
9
+ }
10
+ /**
11
+ * @param { import("knex").Knex } knex
12
+ * @returns { Promise<void> }
13
+ */
14
+ export async function down(knex) {
15
+ await knex.schema.alterTable('releases', (table) => {
16
+ table.string('mint').notNullable().alter();
17
+ });
18
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export const up = function (knex) {
6
+ return knex.schema.table('releases', table => {
7
+ table.string('solanaAddress').nullable();
8
+ }).then(() => {
9
+ return knex('releases').whereNotNull('mint').update('solanaAddress', knex.ref('publicKey'));
10
+ });
11
+ };
12
+ /**
13
+ * @param { import("knex").Knex } knex
14
+ * @returns { Promise<void> }
15
+ */
16
+ export const down = function (knex) {
17
+ return knex.schema.table('releases', table => {
18
+ table.dropColumn('solanaAddress');
19
+ });
20
+ };
@@ -13,39 +13,39 @@ import promiseRetry from 'promise-retry';
13
13
  import { customAlphabet } from 'nanoid';
14
14
  import { getTokenMetadata } from '@solana/spl-token';
15
15
  const ensureHttps = (uri) => {
16
- if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
16
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
17
17
  return `https://${uri}`;
18
18
  }
19
19
  return uri;
20
20
  };
21
- const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
21
+ const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
22
22
  const randomStringGenerator = customAlphabet(alphabet, 12);
23
23
  export default class Release extends Model {
24
- static tableName = 'releases';
25
- static idColumn = 'id';
24
+ static tableName = "releases";
25
+ static idColumn = "id";
26
26
  static jsonSchema = {
27
- type: 'object',
28
- required: ['publicKey', 'mint', 'metadata', 'datetime', 'slug', 'price'],
27
+ type: "object",
28
+ required: ["metadata", "datetime", "slug", "price"],
29
29
  properties: {
30
- publicKey: { type: 'string' },
31
- mint: { type: 'string' },
32
- slug: { type: 'string' },
30
+ publicKey: { type: ["string"] },
31
+ mint: { type: "string" },
32
+ slug: { type: "string" },
33
33
  metadata: {
34
- type: 'object',
35
- required: ['name', 'symbol', 'description', 'image', 'properties',],
34
+ type: "object",
35
+ required: ["name", "symbol", "description", "image", "properties"],
36
36
  properties: {
37
- name: { type: 'string' },
38
- symbol: { type: 'string' },
39
- description: { type: 'string' },
37
+ name: { type: "string" },
38
+ symbol: { type: "string" },
39
+ description: { type: "string" },
40
40
  properties: {
41
- type: 'object',
41
+ type: "object",
42
42
  properties: {
43
- artist: { type: 'string' },
44
- title: { type: 'string' },
45
- date: { type: 'string' },
46
- files: { type: 'array' },
47
- category: { type: 'string' },
48
- creators: { type: 'array' },
43
+ artist: { type: "string" },
44
+ title: { type: "string" },
45
+ date: { type: "string" },
46
+ files: { type: "array" },
47
+ category: { type: "string" },
48
+ creators: { type: "array" }
49
49
  }
50
50
  }
51
51
  }
@@ -76,13 +76,13 @@ export default class Release extends Model {
76
76
  return result;
77
77
  }
78
78
  catch (error) {
79
- console.log('error fetching release account', error);
79
+ console.log("error fetching release account", error);
80
80
  retry(error);
81
81
  }
82
82
  }, {
83
83
  retries: 50,
84
84
  minTimeout: 500,
85
- maxTimeout: 1500,
85
+ maxTimeout: 1500
86
86
  });
87
87
  let metadataAccount;
88
88
  if (programId === process.env.NINA_PROGRAM_V2_ID) {
@@ -92,14 +92,16 @@ export default class Release extends Model {
92
92
  metadataAccount = (await metaplex.nfts().findAllByMintList({ mints: [releaseAccount.releaseMint] }, { commitment: 'confirmed' }))[0];
93
93
  }
94
94
  if (!metadataAccount) {
95
- throw new Error('No metadata account found for release - is not a complete release');
95
+ throw new Error("No metadata account found for release - is not a complete release");
96
96
  }
97
97
  let json;
98
98
  try {
99
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('www.', '').replace('arweave.net', 'gateway.irys.xyz')))).data;
99
+ json = (await axios.get(ensureHttps(metadataAccount.uri
100
+ .replace("www.", "")
101
+ .replace("arweave.net", "gateway.irys.xyz")))).data;
100
102
  }
101
103
  catch (error) {
102
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('gateway.irys.xyz', 'arweave.net')))).data;
104
+ json = (await axios.get(ensureHttps(metadataAccount.uri.replace("gateway.irys.xyz", "arweave.net")))).data;
103
105
  }
104
106
  const slug = await this.generateSlug(json);
105
107
  let publisher = await Account.findOrCreate(releaseAccount.authority.toBase58());
@@ -116,21 +118,27 @@ export default class Release extends Model {
116
118
  if (hubPublicKey) {
117
119
  const hub = await Hub.query().findOne({ publicKey: hubPublicKey });
118
120
  await release.$query().patch({ hubId: hub.id });
119
- await Hub.relatedQuery('releases').for(hub.id).patch({
120
- visible: true,
121
- }).where({ id: release.id });
121
+ await Hub.relatedQuery("releases")
122
+ .for(hub.id)
123
+ .patch({
124
+ visible: true
125
+ })
126
+ .where({ id: release.id });
122
127
  }
123
128
  return release;
124
129
  }
125
130
  catch (error) {
126
- console.log('error finding or creating release: ', error);
131
+ console.log("error finding or creating release: ", error);
127
132
  return null;
128
133
  }
129
134
  };
130
135
  static createRelease = async ({ publicKey, mint, metadata, datetime, publisherId, releaseAccount, programId }) => {
131
136
  const slug = await this.generateSlug(metadata);
132
- const price = releaseAccount.account?.price?.toNumber() || releaseAccount?.price?.toNumber() || 0;
133
- const paymentMint = releaseAccount.account?.paymentMint.toBase58() || releaseAccount?.paymentMint.toBase58();
137
+ const price = releaseAccount.account?.price?.toNumber() ||
138
+ releaseAccount?.price?.toNumber() ||
139
+ 0;
140
+ const paymentMint = releaseAccount.account?.paymentMint.toBase58() ||
141
+ releaseAccount?.paymentMint.toBase58();
134
142
  const release = await Release.query().insertGraph({
135
143
  publicKey,
136
144
  mint,
@@ -146,7 +154,11 @@ export default class Release extends Model {
146
154
  if (metadata.properties.tags) {
147
155
  for await (let tag of metadata.properties.tags) {
148
156
  const tagRecord = await Tag.findOrCreate(tag);
149
- await Release.relatedQuery('tags').for(release.id).relate(tagRecord.id).onConflict(['tagId', 'releaseId']).ignore();
157
+ await Release.relatedQuery("tags")
158
+ .for(release.id)
159
+ .relate(tagRecord.id)
160
+ .onConflict(["tagId", "releaseId"])
161
+ .ignore();
150
162
  }
151
163
  }
152
164
  if (programId === process.env.NINA_PROGRAM_ID) {
@@ -159,19 +171,27 @@ export default class Release extends Model {
159
171
  const royaltyRecipients = releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients;
160
172
  for await (let recipient of royaltyRecipients) {
161
173
  try {
162
- if (recipient.recipientAuthority.toBase58() !== "11111111111111111111111111111111") {
174
+ if (recipient.recipientAuthority.toBase58() !==
175
+ "11111111111111111111111111111111") {
163
176
  const recipientAccount = await Account.findOrCreate(recipient.recipientAuthority.toBase58());
164
- const revenueShares = (await recipientAccount.$relatedQuery('revenueShares')).map(revenueShare => revenueShare.id);
165
- if (!revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() > 0) {
166
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).relate(releaseRecord.id);
177
+ const revenueShares = (await recipientAccount.$relatedQuery("revenueShares")).map((revenueShare) => revenueShare.id);
178
+ if (!revenueShares.includes(releaseRecord.id) &&
179
+ recipient.percentShare.toNumber() > 0) {
180
+ await Account.relatedQuery("revenueShares")
181
+ .for(recipientAccount.id)
182
+ .relate(releaseRecord.id);
167
183
  }
168
- else if (revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() === 0) {
169
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).unrelate().where('id', releaseRecord.id);
184
+ else if (revenueShares.includes(releaseRecord.id) &&
185
+ recipient.percentShare.toNumber() === 0) {
186
+ await Account.relatedQuery("revenueShares")
187
+ .for(recipientAccount.id)
188
+ .unrelate()
189
+ .where("id", releaseRecord.id);
170
190
  }
171
191
  }
172
192
  }
173
193
  catch (error) {
174
- console.log('error processing royaltyRecipients: ', error);
194
+ console.log("error processing royaltyRecipients: ", error);
175
195
  }
176
196
  }
177
197
  };
@@ -181,14 +201,15 @@ export default class Release extends Model {
181
201
  string = string.substring(0, 200);
182
202
  }
183
203
  const slug = string
184
- .normalize('NFKD').replace(/[\u0300-\u036F]/g, '') // remove accents and convert to closest ascii equivalent
204
+ .normalize("NFKD")
205
+ .replace(/[\u0300-\u036F]/g, "") // remove accents and convert to closest ascii equivalent
185
206
  .toLowerCase() // convert to lowercase
186
- .replace('-', '') // remove hyphens
187
- .replace(/ +/g, ' ') // remove spaces
188
- .replace(/ /g, '-') // replace spaces with hyphens
189
- .replace(/[^a-zA-Z0-9-]/g, '-') // replace non-alphanumeric characters with hyphens
190
- .replace(/-+/g, '-') // replace multiple hyphens with single hyphen
191
- .replace(/-$/, ''); // remove trailing hyphens
207
+ .replace("-", "") // remove hyphens
208
+ .replace(/ +/g, " ") // remove spaces
209
+ .replace(/ /g, "-") // replace spaces with hyphens
210
+ .replace(/[^a-zA-Z0-9-]/g, "-") // replace non-alphanumeric characters with hyphens
211
+ .replace(/-+/g, "-") // replace multiple hyphens with single hyphen
212
+ .replace(/-$/, ""); // remove trailing hyphens
192
213
  const existingRelease = await Release.query().findOne({ slug });
193
214
  if (existingRelease) {
194
215
  return `${slug}-${randomStringGenerator()}`;
@@ -196,13 +217,15 @@ export default class Release extends Model {
196
217
  return slug;
197
218
  };
198
219
  format = async () => {
199
- const publisher = await this.$relatedQuery('publisher');
200
- const publishedThroughHub = await this.$relatedQuery('publishedThroughHub');
220
+ const publisher = await this.$relatedQuery("publisher");
221
+ const publishedThroughHub = await this.$relatedQuery("publishedThroughHub");
201
222
  if (publishedThroughHub) {
202
223
  this.publishedThroughHub = publishedThroughHub.publicKey;
203
224
  this.hub = publishedThroughHub;
204
225
  delete this.hub.id;
205
- const authority = await this.hub.$relatedQuery('authority').select('publicKey');
226
+ const authority = await this.hub
227
+ .$relatedQuery("authority")
228
+ .select("publicKey");
206
229
  this.hub.authority = authority.publicKey;
207
230
  delete this.hub.authorityId;
208
231
  }
@@ -212,93 +235,93 @@ export default class Release extends Model {
212
235
  delete this.publisherId;
213
236
  delete this.hubId;
214
237
  delete this.id;
215
- stripHtmlIfNeeded(this.metadata, 'description');
238
+ stripHtmlIfNeeded(this.metadata, "description");
216
239
  };
217
240
  static relationMappings = () => ({
218
241
  publishedThroughHub: {
219
242
  relation: Model.BelongsToOneRelation,
220
243
  modelClass: Hub,
221
244
  join: {
222
- from: 'releases.hubId',
223
- to: 'hubs.id',
224
- },
245
+ from: "releases.hubId",
246
+ to: "hubs.id"
247
+ }
225
248
  },
226
249
  publisher: {
227
250
  relation: Model.HasOneRelation,
228
251
  modelClass: Account,
229
252
  join: {
230
- from: 'releases.publisherId',
231
- to: 'accounts.id',
232
- },
253
+ from: "releases.publisherId",
254
+ to: "accounts.id"
255
+ }
233
256
  },
234
257
  collectors: {
235
258
  relation: Model.ManyToManyRelation,
236
259
  modelClass: Account,
237
260
  join: {
238
- from: 'releases.id',
261
+ from: "releases.id",
239
262
  through: {
240
- from: 'releases_collected.releaseId',
241
- to: 'releases_collected.accountId',
263
+ from: "releases_collected.releaseId",
264
+ to: "releases_collected.accountId"
242
265
  },
243
- to: 'accounts.id',
244
- },
266
+ to: "accounts.id"
267
+ }
245
268
  },
246
269
  exchanges: {
247
270
  relation: Model.HasManyRelation,
248
271
  modelClass: Exchange,
249
272
  join: {
250
- from: 'releases.id',
251
- to: 'exchanges.releaseId',
252
- },
273
+ from: "releases.id",
274
+ to: "exchanges.releaseId"
275
+ }
253
276
  },
254
277
  hubs: {
255
278
  relation: Model.ManyToManyRelation,
256
279
  modelClass: Hub,
257
280
  join: {
258
- from: 'releases.id',
281
+ from: "releases.id",
259
282
  through: {
260
- from: 'hubs_releases.releaseId',
261
- to: 'hubs_releases.hubId',
262
- extra: ['hubReleasePublicKey'],
283
+ from: "hubs_releases.releaseId",
284
+ to: "hubs_releases.hubId",
285
+ extra: ["hubReleasePublicKey"]
263
286
  },
264
- to: 'hubs.id',
265
- },
287
+ to: "hubs.id"
288
+ }
266
289
  },
267
290
  posts: {
268
291
  relation: Model.ManyToManyRelation,
269
292
  modelClass: Post,
270
293
  join: {
271
- from: 'releases.id',
294
+ from: "releases.id",
272
295
  through: {
273
- from: 'posts_releases.releaseId',
274
- to: 'posts_releases.postId',
296
+ from: "posts_releases.releaseId",
297
+ to: "posts_releases.postId"
275
298
  },
276
- to: 'posts.id',
277
- },
299
+ to: "posts.id"
300
+ }
278
301
  },
279
302
  revenueShareRecipients: {
280
303
  relation: Model.ManyToManyRelation,
281
304
  modelClass: Account,
282
305
  join: {
283
- from: 'releases.id',
306
+ from: "releases.id",
284
307
  through: {
285
- from: 'releases_revenue_share.releaseId',
286
- to: 'releases_revenue_share.accountId',
308
+ from: "releases_revenue_share.releaseId",
309
+ to: "releases_revenue_share.accountId"
287
310
  },
288
- to: 'accounts.id',
289
- },
311
+ to: "accounts.id"
312
+ }
290
313
  },
291
314
  tags: {
292
315
  relation: Model.ManyToManyRelation,
293
316
  modelClass: Tag,
294
317
  join: {
295
- from: 'releases.id',
318
+ from: "releases.id",
296
319
  through: {
297
- from: 'tags_releases.releaseId',
298
- to: 'tags_releases.tagId',
320
+ from: "tags_releases.releaseId",
321
+ to: "tags_releases.tagId"
299
322
  },
300
- to: 'tags.id',
301
- },
302
- },
323
+ to: "tags.id"
324
+ }
325
+ }
303
326
  });
304
327
  }
@@ -20,6 +20,7 @@ class Transaction extends Model {
20
20
  type: {
21
21
  type: 'string',
22
22
  enum: [
23
+ 'ReleaseInitAndPurchase',
23
24
  'ReleaseInitV2',
24
25
  'ReleaseUpdate',
25
26
  'ExchangeAccept',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nina-protocol/nina-db",
3
- "version": "0.0.105",
3
+ "version": "0.0.107",
4
4
  "description": "",
5
5
  "source": "src/index.js",
6
6
  "main": "dist/index.js",
package/src/knexfile.js CHANGED
@@ -2,6 +2,11 @@
2
2
  * @type { Object.<string, import("knex").Knex.Config> }
3
3
  */
4
4
  import "dotenv/config.js";
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
5
10
 
6
11
  export default {
7
12
  development: {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export async function up(knex) {
6
+ await knex.schema.alterTable('releases', (table) => {
7
+ table.string('mint').nullable().alter();
8
+ });
9
+ }
10
+
11
+ /**
12
+ * @param { import("knex").Knex } knex
13
+ * @returns { Promise<void> }
14
+ */
15
+ export async function down(knex) {
16
+ await knex.schema.alterTable('releases', (table) => {
17
+ table.string('mint').notNullable().alter();
18
+ });
19
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @param { import("knex").Knex } knex
3
+ * @returns { Promise<void> }
4
+ */
5
+ export const up = function(knex) {
6
+ return knex.schema.table('releases', table => {
7
+ table.string('solanaAddress').nullable();
8
+ }).then(() => {
9
+ return knex('releases').whereNotNull('mint').update('solanaAddress', knex.ref('publicKey'));
10
+ });
11
+ };
12
+
13
+ /**
14
+ * @param { import("knex").Knex } knex
15
+ * @returns { Promise<void> }
16
+ */
17
+ export const down = function(knex) {
18
+ return knex.schema.table('releases', table => {
19
+ table.dropColumn('solanaAddress');
20
+ });
21
+ };
@@ -14,41 +14,41 @@ import { customAlphabet } from 'nanoid';
14
14
  import { getTokenMetadata } from '@solana/spl-token';
15
15
 
16
16
  const ensureHttps = (uri) => {
17
- if (!uri.startsWith('http://') && !uri.startsWith('https://')) {
17
+ if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
18
18
  return `https://${uri}`;
19
19
  }
20
20
  return uri;
21
21
  };
22
22
 
23
- const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
23
+ const alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
24
24
  const randomStringGenerator = customAlphabet(alphabet, 12);
25
25
  export default class Release extends Model {
26
- static tableName = 'releases';
27
-
28
- static idColumn = 'id';
26
+ static tableName = "releases";
27
+
28
+ static idColumn = "id";
29
29
  static jsonSchema = {
30
- type: 'object',
31
- required: ['publicKey', 'mint', 'metadata', 'datetime', 'slug', 'price'],
30
+ type: "object",
31
+ required: ["metadata", "datetime", "slug", "price"],
32
32
  properties: {
33
- publicKey: { type: 'string' },
34
- mint: { type: 'string' },
35
- slug: { type: 'string' },
33
+ publicKey: { type: ["string"] },
34
+ mint: { type: "string" },
35
+ slug: { type: "string" },
36
36
  metadata: {
37
- type: 'object',
38
- required: ['name', 'symbol', 'description', 'image', 'properties',],
37
+ type: "object",
38
+ required: ["name", "symbol", "description", "image", "properties"],
39
39
  properties: {
40
- name: { type: 'string' },
41
- symbol: { type: 'string' },
42
- description: { type: 'string' },
40
+ name: { type: "string" },
41
+ symbol: { type: "string" },
42
+ description: { type: "string" },
43
43
  properties: {
44
- type: 'object',
44
+ type: "object",
45
45
  properties: {
46
- artist: { type: 'string' },
47
- title: { type: 'string' },
48
- date: { type: 'string' },
49
- files: { type: 'array' },
50
- category: { type: 'string' },
51
- creators: { type: 'array' },
46
+ artist: { type: "string" },
47
+ title: { type: "string" },
48
+ date: { type: "string" },
49
+ files: { type: "array" },
50
+ category: { type: "string" },
51
+ creators: { type: "array" }
52
52
  }
53
53
  }
54
54
  }
@@ -84,13 +84,14 @@ export default class Release extends Model {
84
84
  const result = await program.account[programModelName].fetch(new anchor.web3.PublicKey(publicKey), 'confirmed')
85
85
  return result
86
86
  } catch (error) {
87
- console.log('error fetching release account', error)
88
- retry(error)
87
+ console.log("error fetching release account", error);
88
+ retry(error);
89
89
  }
90
- }, {
90
+ },
91
+ {
91
92
  retries: 50,
92
93
  minTimeout: 500,
93
- maxTimeout: 1500,
94
+ maxTimeout: 1500
94
95
  }
95
96
  )
96
97
 
@@ -101,17 +102,35 @@ export default class Release extends Model {
101
102
  metadataAccount = (await metaplex.nfts().findAllByMintList({mints: [releaseAccount.releaseMint]}, { commitment: 'confirmed' }))[0];
102
103
  }
103
104
  if (!metadataAccount) {
104
- throw new Error('No metadata account found for release - is not a complete release')
105
+ throw new Error(
106
+ "No metadata account found for release - is not a complete release"
107
+ );
105
108
  }
106
- let json
109
+ let json;
107
110
  try {
108
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('www.','').replace('arweave.net', 'gateway.irys.xyz')))).data
111
+ json = (
112
+ await axios.get(
113
+ ensureHttps(
114
+ metadataAccount.uri
115
+ .replace("www.", "")
116
+ .replace("arweave.net", "gateway.irys.xyz")
117
+ )
118
+ )
119
+ ).data;
109
120
  } catch (error) {
110
- json = (await axios.get(ensureHttps(metadataAccount.uri.replace('gateway.irys.xyz', 'arweave.net')))).data
121
+ json = (
122
+ await axios.get(
123
+ ensureHttps(
124
+ metadataAccount.uri.replace("gateway.irys.xyz", "arweave.net")
125
+ )
126
+ )
127
+ ).data;
111
128
  }
112
-
129
+
113
130
  const slug = await this.generateSlug(json);
114
- let publisher = await Account.findOrCreate(releaseAccount.authority.toBase58());
131
+ let publisher = await Account.findOrCreate(
132
+ releaseAccount.authority.toBase58()
133
+ );
115
134
  release = await this.createRelease({
116
135
  publicKey,
117
136
  mint: programId === process.env.NINA_PROGRAM_V2_ID ? releaseAccount.mint.toBase58() : releaseAccount.releaseMint.toBase58(),
@@ -122,27 +141,34 @@ export default class Release extends Model {
122
141
  releaseAccount,
123
142
  programId,
124
143
  });
125
-
144
+
126
145
  if (hubPublicKey) {
127
-
128
- const hub = await Hub.query().findOne({ publicKey: hubPublicKey })
129
- await release.$query().patch({ hubId: hub.id })
130
- await Hub.relatedQuery('releases').for(hub.id).patch({
131
- visible: true,
132
- }).where( {id: release.id });
146
+ const hub = await Hub.query().findOne({ publicKey: hubPublicKey });
147
+ await release.$query().patch({ hubId: hub.id });
148
+ await Hub.relatedQuery("releases")
149
+ .for(hub.id)
150
+ .patch({
151
+ visible: true
152
+ })
153
+ .where({ id: release.id });
133
154
  }
134
-
135
- return release;
155
+
156
+ return release;
136
157
  } catch (error) {
137
- console.log('error finding or creating release: ', error)
158
+ console.log("error finding or creating release: ", error);
138
159
  return null;
139
160
  }
140
- }
161
+ };
141
162
 
142
163
  static createRelease = async ({publicKey, mint, metadata, datetime, publisherId, releaseAccount, programId}) => {
143
164
  const slug = await this.generateSlug(metadata);
144
- const price = releaseAccount.account?.price?.toNumber() || releaseAccount?.price?.toNumber() || 0;
145
- const paymentMint = releaseAccount.account?.paymentMint.toBase58() || releaseAccount?.paymentMint.toBase58();
165
+ const price =
166
+ releaseAccount.account?.price?.toNumber() ||
167
+ releaseAccount?.price?.toNumber() ||
168
+ 0;
169
+ const paymentMint =
170
+ releaseAccount.account?.paymentMint.toBase58() ||
171
+ releaseAccount?.paymentMint.toBase58();
146
172
  const release = await Release.query().insertGraph({
147
173
  publicKey,
148
174
  mint,
@@ -158,7 +184,11 @@ export default class Release extends Model {
158
184
  if (metadata.properties.tags) {
159
185
  for await (let tag of metadata.properties.tags) {
160
186
  const tagRecord = await Tag.findOrCreate(tag);
161
- await Release.relatedQuery('tags').for(release.id).relate(tagRecord.id).onConflict(['tagId', 'releaseId']).ignore();
187
+ await Release.relatedQuery("tags")
188
+ .for(release.id)
189
+ .relate(tagRecord.id)
190
+ .onConflict(["tagId", "releaseId"])
191
+ .ignore();
162
192
  }
163
193
  }
164
194
  if (programId === process.env.NINA_PROGRAM_ID) {
@@ -166,156 +196,177 @@ export default class Release extends Model {
166
196
  }
167
197
  tweetNewRelease(metadata, publisherId, slug);
168
198
  return release;
169
- }
199
+ };
170
200
 
171
201
  static processRevenueShares = async (releaseData, releaseRecord) => {
172
- const royaltyRecipients = releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients
202
+ const royaltyRecipients =
203
+ releaseData.account?.royaltyRecipients || releaseData.royaltyRecipients;
173
204
  for await (let recipient of royaltyRecipients) {
174
205
  try {
175
- if (recipient.recipientAuthority.toBase58() !== "11111111111111111111111111111111") {
176
- const recipientAccount = await Account.findOrCreate(recipient.recipientAuthority.toBase58());
177
- const revenueShares = (await recipientAccount.$relatedQuery('revenueShares')).map(revenueShare => revenueShare.id);
178
- if (!revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() > 0) {
179
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).relate(releaseRecord.id);
180
- } else if (revenueShares.includes(releaseRecord.id) && recipient.percentShare.toNumber() === 0) {
181
- await Account.relatedQuery('revenueShares').for(recipientAccount.id).unrelate().where('id', releaseRecord.id);
206
+ if (
207
+ recipient.recipientAuthority.toBase58() !==
208
+ "11111111111111111111111111111111"
209
+ ) {
210
+ const recipientAccount = await Account.findOrCreate(
211
+ recipient.recipientAuthority.toBase58()
212
+ );
213
+ const revenueShares = (
214
+ await recipientAccount.$relatedQuery("revenueShares")
215
+ ).map((revenueShare) => revenueShare.id);
216
+ if (
217
+ !revenueShares.includes(releaseRecord.id) &&
218
+ recipient.percentShare.toNumber() > 0
219
+ ) {
220
+ await Account.relatedQuery("revenueShares")
221
+ .for(recipientAccount.id)
222
+ .relate(releaseRecord.id);
223
+ } else if (
224
+ revenueShares.includes(releaseRecord.id) &&
225
+ recipient.percentShare.toNumber() === 0
226
+ ) {
227
+ await Account.relatedQuery("revenueShares")
228
+ .for(recipientAccount.id)
229
+ .unrelate()
230
+ .where("id", releaseRecord.id);
182
231
  }
183
232
  }
184
233
  } catch (error) {
185
- console.log('error processing royaltyRecipients: ', error)
234
+ console.log("error processing royaltyRecipients: ", error);
186
235
  }
187
236
  }
188
- }
237
+ };
189
238
 
190
239
  static generateSlug = async (metadata) => {
191
- let string = metadata.name
240
+ let string = metadata.name;
192
241
  if (string.length > 200) {
193
242
  string = string.substring(0, 200);
194
243
  }
195
244
  const slug = string
196
- .normalize('NFKD').replace(/[\u0300-\u036F]/g, '') // remove accents and convert to closest ascii equivalent
245
+ .normalize("NFKD")
246
+ .replace(/[\u0300-\u036F]/g, "") // remove accents and convert to closest ascii equivalent
197
247
  .toLowerCase() // convert to lowercase
198
- .replace('-', '') // remove hyphens
199
- .replace(/ +/g, ' ') // remove spaces
200
- .replace(/ /g, '-') // replace spaces with hyphens
201
- .replace(/[^a-zA-Z0-9-]/g, '-') // replace non-alphanumeric characters with hyphens
202
- .replace(/-+/g,'-') // replace multiple hyphens with single hyphen
203
- .replace(/-$/, '') // remove trailing hyphens
248
+ .replace("-", "") // remove hyphens
249
+ .replace(/ +/g, " ") // remove spaces
250
+ .replace(/ /g, "-") // replace spaces with hyphens
251
+ .replace(/[^a-zA-Z0-9-]/g, "-") // replace non-alphanumeric characters with hyphens
252
+ .replace(/-+/g, "-") // replace multiple hyphens with single hyphen
253
+ .replace(/-$/, ""); // remove trailing hyphens
204
254
 
205
255
  const existingRelease = await Release.query().findOne({ slug });
206
256
  if (existingRelease) {
207
257
  return `${slug}-${randomStringGenerator()}`;
208
258
  }
209
259
  return slug;
210
-
211
- }
260
+ };
212
261
 
213
262
  format = async () => {
214
- const publisher = await this.$relatedQuery('publisher');
215
- const publishedThroughHub = await this.$relatedQuery('publishedThroughHub');
263
+ const publisher = await this.$relatedQuery("publisher");
264
+ const publishedThroughHub = await this.$relatedQuery("publishedThroughHub");
216
265
  if (publishedThroughHub) {
217
266
  this.publishedThroughHub = publishedThroughHub.publicKey;
218
267
  this.hub = publishedThroughHub;
219
268
  delete this.hub.id;
220
- const authority = await this.hub.$relatedQuery('authority').select('publicKey');
269
+ const authority = await this.hub
270
+ .$relatedQuery("authority")
271
+ .select("publicKey");
221
272
  this.hub.authority = authority.publicKey;
222
273
  delete this.hub.authorityId;
223
274
  }
224
275
  await publisher.format();
225
276
  this.publisher = publisher.publicKey;
226
277
  this.publisherAccount = publisher;
227
- delete this.publisherId
228
- delete this.hubId
229
- delete this.id
278
+ delete this.publisherId;
279
+ delete this.hubId;
280
+ delete this.id;
230
281
 
231
- stripHtmlIfNeeded(this.metadata, 'description');
232
- }
282
+ stripHtmlIfNeeded(this.metadata, "description");
283
+ };
233
284
 
234
285
  static relationMappings = () => ({
235
286
  publishedThroughHub: {
236
287
  relation: Model.BelongsToOneRelation,
237
288
  modelClass: Hub,
238
289
  join: {
239
- from: 'releases.hubId',
240
- to: 'hubs.id',
241
- },
290
+ from: "releases.hubId",
291
+ to: "hubs.id"
292
+ }
242
293
  },
243
294
  publisher: {
244
295
  relation: Model.HasOneRelation,
245
296
  modelClass: Account,
246
297
  join: {
247
- from: 'releases.publisherId',
248
- to: 'accounts.id',
249
- },
298
+ from: "releases.publisherId",
299
+ to: "accounts.id"
300
+ }
250
301
  },
251
302
  collectors: {
252
303
  relation: Model.ManyToManyRelation,
253
304
  modelClass: Account,
254
305
  join: {
255
- from: 'releases.id',
306
+ from: "releases.id",
256
307
  through: {
257
- from: 'releases_collected.releaseId',
258
- to: 'releases_collected.accountId',
308
+ from: "releases_collected.releaseId",
309
+ to: "releases_collected.accountId"
259
310
  },
260
- to: 'accounts.id',
261
- },
311
+ to: "accounts.id"
312
+ }
262
313
  },
263
314
  exchanges: {
264
315
  relation: Model.HasManyRelation,
265
316
  modelClass: Exchange,
266
317
  join: {
267
- from: 'releases.id',
268
- to: 'exchanges.releaseId',
269
- },
318
+ from: "releases.id",
319
+ to: "exchanges.releaseId"
320
+ }
270
321
  },
271
322
  hubs: {
272
323
  relation: Model.ManyToManyRelation,
273
324
  modelClass: Hub,
274
325
  join: {
275
- from: 'releases.id',
276
- through : {
277
- from: 'hubs_releases.releaseId',
278
- to: 'hubs_releases.hubId',
279
- extra: ['hubReleasePublicKey'],
326
+ from: "releases.id",
327
+ through: {
328
+ from: "hubs_releases.releaseId",
329
+ to: "hubs_releases.hubId",
330
+ extra: ["hubReleasePublicKey"]
280
331
  },
281
- to: 'hubs.id',
282
- },
332
+ to: "hubs.id"
333
+ }
283
334
  },
284
335
  posts: {
285
336
  relation: Model.ManyToManyRelation,
286
337
  modelClass: Post,
287
338
  join: {
288
- from: 'releases.id',
289
- through : {
290
- from: 'posts_releases.releaseId',
291
- to: 'posts_releases.postId',
339
+ from: "releases.id",
340
+ through: {
341
+ from: "posts_releases.releaseId",
342
+ to: "posts_releases.postId"
292
343
  },
293
- to: 'posts.id',
294
- },
344
+ to: "posts.id"
345
+ }
295
346
  },
296
347
  revenueShareRecipients: {
297
348
  relation: Model.ManyToManyRelation,
298
349
  modelClass: Account,
299
350
  join: {
300
- from: 'releases.id',
351
+ from: "releases.id",
301
352
  through: {
302
- from: 'releases_revenue_share.releaseId',
303
- to: 'releases_revenue_share.accountId',
353
+ from: "releases_revenue_share.releaseId",
354
+ to: "releases_revenue_share.accountId"
304
355
  },
305
- to: 'accounts.id',
306
- },
356
+ to: "accounts.id"
357
+ }
307
358
  },
308
359
  tags: {
309
360
  relation: Model.ManyToManyRelation,
310
361
  modelClass: Tag,
311
362
  join: {
312
- from: 'releases.id',
363
+ from: "releases.id",
313
364
  through: {
314
- from: 'tags_releases.releaseId',
315
- to: 'tags_releases.tagId',
365
+ from: "tags_releases.releaseId",
366
+ to: "tags_releases.tagId"
316
367
  },
317
- to: 'tags.id',
318
- },
319
- },
320
- })
368
+ to: "tags.id"
369
+ }
370
+ }
371
+ });
321
372
  }
@@ -21,6 +21,7 @@ class Transaction extends Model {
21
21
  type: {
22
22
  type: 'string',
23
23
  enum: [
24
+ 'ReleaseInitAndPurchase',
24
25
  'ReleaseInitV2',
25
26
  'ReleaseUpdate',
26
27
  'ExchangeAccept',