dgc-core 2.0.0 → 2.0.3
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/enums/user.d.ts +2 -2
- package/dist/enums/user.js +2 -2
- package/dist/giveaways/api-pagination.d.ts +6 -0
- package/dist/giveaways/api-pagination.js +25 -0
- package/dist/giveaways/domain.js +2 -2
- package/dist/giveaways/giveaway-repository.d.ts +4 -9
- package/dist/giveaways/giveaway-repository.js +29 -22
- package/dist/giveaways/index.d.ts +1 -0
- package/dist/giveaways/index.js +1 -0
- package/dist/types/api.d.ts +12 -0
- package/dist/types/api.js +2 -0
- package/dist/types/giveaway.d.ts +2 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/user.d.ts +2 -0
- package/package.json +1 -1
package/dist/enums/user.d.ts
CHANGED
package/dist/enums/user.js
CHANGED
|
@@ -6,6 +6,6 @@ var UserCollectorType;
|
|
|
6
6
|
UserCollectorType[UserCollectorType["Non-member"] = -1] = "Non-member";
|
|
7
7
|
UserCollectorType[UserCollectorType["Free"] = 2] = "Free";
|
|
8
8
|
UserCollectorType[UserCollectorType["Rookie"] = 1] = "Rookie";
|
|
9
|
-
UserCollectorType[UserCollectorType["Collector"] =
|
|
10
|
-
UserCollectorType[UserCollectorType["Elite"] =
|
|
9
|
+
UserCollectorType[UserCollectorType["Collector"] = 3] = "Collector";
|
|
10
|
+
UserCollectorType[UserCollectorType["Elite"] = 0] = "Elite";
|
|
11
11
|
})(UserCollectorType || (exports.UserCollectorType = UserCollectorType = {}));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseGiveawayApiPagination = parseGiveawayApiPagination;
|
|
4
|
+
const http_error_1 = require("../http-error");
|
|
5
|
+
/** Mobile giveaway pagination; private dashboard pagination keeps its own contract. */
|
|
6
|
+
function parseGiveawayApiPagination(params) {
|
|
7
|
+
const invalid = () => new http_error_1.HttpError('Invalid page or size parameter', 400);
|
|
8
|
+
const integer = (key, fallback) => {
|
|
9
|
+
const values = params.getAll(key);
|
|
10
|
+
if (!values.length)
|
|
11
|
+
return fallback;
|
|
12
|
+
if (values.length !== 1 || !/^\d+$/.test(values[0]))
|
|
13
|
+
throw invalid();
|
|
14
|
+
const value = Number(values[0]);
|
|
15
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
16
|
+
throw invalid();
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
19
|
+
const page = integer('page', 1);
|
|
20
|
+
const pageSize = integer('size', 20);
|
|
21
|
+
const skip = (page - 1) * pageSize;
|
|
22
|
+
if (pageSize > 50 || !Number.isSafeInteger(skip))
|
|
23
|
+
throw invalid();
|
|
24
|
+
return { page, pageSize, skip };
|
|
25
|
+
}
|
package/dist/giveaways/domain.js
CHANGED
|
@@ -177,14 +177,14 @@ function submitEntry(g, account, raw) {
|
|
|
177
177
|
new Date(g.startsAt) > now ||
|
|
178
178
|
new Date(g.closesAt) <= now)
|
|
179
179
|
throw new http_error_1.HttpError('This giveaway is not accepting entries', 409);
|
|
180
|
-
const country = input.country
|
|
180
|
+
const country = input.country;
|
|
181
181
|
if (g.countries.length && (!country || !g.countries.includes(country)))
|
|
182
182
|
throw new http_error_1.HttpError('This country is not eligible', 422);
|
|
183
183
|
const at = now.toISOString();
|
|
184
184
|
return {
|
|
185
185
|
giveawayId: g._id,
|
|
186
186
|
userId: account.userId,
|
|
187
|
-
|
|
187
|
+
name: account.name,
|
|
188
188
|
email: account.email,
|
|
189
189
|
username: account.username,
|
|
190
190
|
country,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GiveawayAction, GiveawayCategory, GiveawaySelectionMethod, GiveawayStatus, GiveawayWinnerStatus } from '../enums/giveaway';
|
|
2
2
|
import { UserCollectorType } from '../enums/user';
|
|
3
3
|
import type { ClientSession, Db, MongoClient } from 'mongodb';
|
|
4
|
-
import type { GiveawayPreset, GiveawayTicket, GiveawayClaim } from '../types';
|
|
4
|
+
import type { GiveawayPreset, GiveawayTicket, GiveawayClaim, PaginatedApiResponse } from '../types';
|
|
5
5
|
import type { UserProfile, AuditEvent, DashboardData, Entry, EntryRecord, Giveaway, GiveawayDetailData, GiveawaySummary, NotificationJob, NotificationRecord, OverviewData, PageResult, Winner, WinnerRecord } from './types';
|
|
6
6
|
type Stored<T extends {
|
|
7
7
|
_id: string;
|
|
@@ -26,7 +26,7 @@ export declare class GiveawayRepository {
|
|
|
26
26
|
private audit;
|
|
27
27
|
getGiveaway(id: string, session?: ClientSession): Promise<Giveaway>;
|
|
28
28
|
getPublicGiveaway(id: string): Promise<GiveawaySummary>;
|
|
29
|
-
listPublicGiveaways(params?: URLSearchParams): Promise<
|
|
29
|
+
listPublicGiveaways(params?: URLSearchParams): Promise<PaginatedApiResponse<GiveawaySummary>>;
|
|
30
30
|
listGiveaways(params?: URLSearchParams): Promise<PageResult<GiveawaySummary>>;
|
|
31
31
|
getDashboard(admin: string): Promise<DashboardData>;
|
|
32
32
|
getOverview(days?: number): Promise<OverviewData>;
|
|
@@ -53,12 +53,12 @@ export declare class GiveawayRepository {
|
|
|
53
53
|
_id: string;
|
|
54
54
|
email: string;
|
|
55
55
|
_collectorType: UserCollectorType | null;
|
|
56
|
+
name: string;
|
|
56
57
|
giveawayId: import("../types").GiveawayObjectId;
|
|
57
58
|
enteredAt: string;
|
|
58
59
|
username?: string | undefined;
|
|
59
60
|
rulesSnapshot: string;
|
|
60
61
|
userId: string;
|
|
61
|
-
fullName: string;
|
|
62
62
|
address?: import("../types").GiveawayDeliveryAddress | undefined;
|
|
63
63
|
country?: string | undefined;
|
|
64
64
|
rulesAcceptedAt: string;
|
|
@@ -233,12 +233,7 @@ export declare class GiveawayRepository {
|
|
|
233
233
|
_id: string;
|
|
234
234
|
}>;
|
|
235
235
|
getTicket(userId: string, entryId: string): Promise<GiveawayTicket>;
|
|
236
|
-
listTickets(userId: string, params: URLSearchParams): Promise<
|
|
237
|
-
data: GiveawayTicket[];
|
|
238
|
-
total: number;
|
|
239
|
-
page: number;
|
|
240
|
-
limit: number;
|
|
241
|
-
}>;
|
|
236
|
+
listTickets(userId: string, params: URLSearchParams): Promise<PaginatedApiResponse<GiveawayTicket>>;
|
|
242
237
|
getUserClaim(giveawayId: string, userId: string): Promise<GiveawayClaim>;
|
|
243
238
|
claimPrize(giveawayId: string, userId: string, raw: unknown): Promise<Omit<import("mongodb").WithId<Stored<Winner>>, "_id"> & {
|
|
244
239
|
_id: string;
|
|
@@ -8,6 +8,7 @@ const user_1 = require("../enums/user");
|
|
|
8
8
|
const node_crypto_1 = require("node:crypto");
|
|
9
9
|
const mongodb_1 = require("mongodb");
|
|
10
10
|
const entry_id_1 = require("./entry-id");
|
|
11
|
+
const api_pagination_1 = require("./api-pagination");
|
|
11
12
|
const domain_1 = require("./domain");
|
|
12
13
|
const schemas_1 = require("./schemas");
|
|
13
14
|
const projection = { entryVersion: 0 };
|
|
@@ -21,6 +22,11 @@ function serialize(record) {
|
|
|
21
22
|
return { ...record, _id: record._id.toHexString() };
|
|
22
23
|
}
|
|
23
24
|
const statuses = Object.values(giveaway_2.GiveawayStatus);
|
|
25
|
+
/** Normalize historical entry names at read boundaries without rewriting stored records. */
|
|
26
|
+
function normalizeEntry(record) {
|
|
27
|
+
const { fullName, ...fields } = record;
|
|
28
|
+
return { ...fields, name: record.name ?? fullName ?? '' };
|
|
29
|
+
}
|
|
24
30
|
function pagination(params) {
|
|
25
31
|
const integer = (key, fallback, max) => {
|
|
26
32
|
const value = Number(params.get(key));
|
|
@@ -179,9 +185,7 @@ class GiveawayRepository {
|
|
|
179
185
|
return { ...g, status, entryCount: await this.entries.countDocuments({ giveawayId: id }) };
|
|
180
186
|
}
|
|
181
187
|
async listPublicGiveaways(params = new URLSearchParams()) {
|
|
182
|
-
const { page,
|
|
183
|
-
const limit = Math.min(requestedLimit, 50);
|
|
184
|
-
const skip = (page - 1) * limit;
|
|
188
|
+
const { page, pageSize, skip } = (0, api_pagination_1.parseGiveawayApiPagination)(params);
|
|
185
189
|
const filter = {
|
|
186
190
|
$or: [
|
|
187
191
|
{
|
|
@@ -204,20 +208,21 @@ class GiveawayRepository {
|
|
|
204
208
|
{ $match: filter },
|
|
205
209
|
{ $sort: { createdAt: -1, _id: 1 } },
|
|
206
210
|
{ $skip: skip },
|
|
207
|
-
{ $limit:
|
|
211
|
+
{ $limit: pageSize },
|
|
208
212
|
...entryCountLookup
|
|
209
213
|
])
|
|
210
214
|
.toArray(),
|
|
211
215
|
this.giveaways.countDocuments(filter)
|
|
212
216
|
]);
|
|
213
217
|
return {
|
|
218
|
+
page,
|
|
219
|
+
pageSize,
|
|
220
|
+
count: rows.length,
|
|
221
|
+
totalCount: total,
|
|
214
222
|
data: rows.map(row => {
|
|
215
223
|
const g = row;
|
|
216
224
|
return { ...g, status: (0, domain_1.effectiveStatus)(g) };
|
|
217
|
-
})
|
|
218
|
-
total,
|
|
219
|
-
page,
|
|
220
|
-
limit
|
|
225
|
+
})
|
|
221
226
|
};
|
|
222
227
|
}
|
|
223
228
|
async listGiveaways(params = new URLSearchParams()) {
|
|
@@ -411,6 +416,7 @@ class GiveawayRepository {
|
|
|
411
416
|
const q = search(params);
|
|
412
417
|
if (q)
|
|
413
418
|
filter.$or = [
|
|
419
|
+
'name',
|
|
414
420
|
'fullName',
|
|
415
421
|
'_id',
|
|
416
422
|
'username',
|
|
@@ -470,7 +476,7 @@ class GiveawayRepository {
|
|
|
470
476
|
.aggregate([...pipeline, { $facet: { data, total: [{ $count: 'count' }] } }])
|
|
471
477
|
.next();
|
|
472
478
|
return {
|
|
473
|
-
data: result?.data || [],
|
|
479
|
+
data: (result?.data || []).map(normalizeEntry),
|
|
474
480
|
total: result?.total[0]?.count || 0,
|
|
475
481
|
page,
|
|
476
482
|
limit
|
|
@@ -542,6 +548,7 @@ class GiveawayRepository {
|
|
|
542
548
|
return {
|
|
543
549
|
data: data.map(w => ({
|
|
544
550
|
...w,
|
|
551
|
+
entry: normalizeEntry(w.entry),
|
|
545
552
|
giveaway: { ...w.giveaway, status: (0, domain_1.effectiveStatus)(w.giveaway) }
|
|
546
553
|
})),
|
|
547
554
|
total,
|
|
@@ -716,7 +723,7 @@ class GiveawayRepository {
|
|
|
716
723
|
})
|
|
717
724
|
.limit(1000)
|
|
718
725
|
.toArray();
|
|
719
|
-
const entries = rows;
|
|
726
|
+
const entries = rows.map(normalizeEntry);
|
|
720
727
|
const existing = await this.winners.findOne({ giveawayId: id, replacedAt: null }, { projection: { _id: 1 } });
|
|
721
728
|
(0, domain_1.confirmWinners)(g, entries, !!existing, 'preview', input);
|
|
722
729
|
return { entries, giveaway: { ...g, status: (0, domain_1.effectiveStatus)(g) } };
|
|
@@ -851,7 +858,7 @@ class GiveawayRepository {
|
|
|
851
858
|
const updated = { ...g, selection, revision: g.revision + 1 };
|
|
852
859
|
await this.updateGiveaway(updated, g.revision, session);
|
|
853
860
|
await this.audit(actor, 'Winner selected: ' + input.method, id, entry._id + (selection.reason ? ': ' + selection.reason : ''), session);
|
|
854
|
-
return { giveaway: updated, entry };
|
|
861
|
+
return { giveaway: updated, entry: normalizeEntry(entry) };
|
|
855
862
|
});
|
|
856
863
|
}
|
|
857
864
|
async resetSelection(actor, id, revision, reason) {
|
|
@@ -978,24 +985,23 @@ class GiveawayRepository {
|
|
|
978
985
|
};
|
|
979
986
|
}
|
|
980
987
|
async listTickets(userId, params) {
|
|
981
|
-
const { page,
|
|
982
|
-
const limit = Math.min(requestedLimit, 50);
|
|
983
|
-
const skip = (page - 1) * limit;
|
|
988
|
+
const { page, pageSize, skip } = (0, api_pagination_1.parseGiveawayApiPagination)(params);
|
|
984
989
|
const filter = { userId };
|
|
985
990
|
const [entries, total] = await Promise.all([
|
|
986
991
|
this.entries
|
|
987
992
|
.find(filter, { projection: { _id: 1 } })
|
|
988
993
|
.sort({ enteredAt: -1, _id: 1 })
|
|
989
994
|
.skip(skip)
|
|
990
|
-
.limit(
|
|
995
|
+
.limit(pageSize)
|
|
991
996
|
.toArray(),
|
|
992
997
|
this.entries.countDocuments(filter)
|
|
993
998
|
]);
|
|
994
999
|
return {
|
|
995
|
-
data: await Promise.all(entries.map(e => this.getTicket(userId, e._id))),
|
|
996
|
-
total,
|
|
997
1000
|
page,
|
|
998
|
-
|
|
1001
|
+
pageSize,
|
|
1002
|
+
count: entries.length,
|
|
1003
|
+
totalCount: total,
|
|
1004
|
+
data: await Promise.all(entries.map(e => this.getTicket(userId, e._id)))
|
|
999
1005
|
};
|
|
1000
1006
|
}
|
|
1001
1007
|
async getUserClaim(giveawayId, userId) {
|
|
@@ -1251,7 +1257,7 @@ class GiveawayRepository {
|
|
|
1251
1257
|
...marketingLookup
|
|
1252
1258
|
];
|
|
1253
1259
|
if (marketingOnly)
|
|
1254
|
-
pipeline.push({ $match: { marketingAllowed: true } }, { $project: { fullName: 1, email: 1 } });
|
|
1260
|
+
pipeline.push({ $match: { marketingAllowed: true } }, { $project: { name: 1, fullName: 1, email: 1 } });
|
|
1255
1261
|
const cursor = this.entries.aggregate(pipeline, {
|
|
1256
1262
|
batchSize: 100
|
|
1257
1263
|
});
|
|
@@ -1277,11 +1283,12 @@ class GiveawayRepository {
|
|
|
1277
1283
|
'Rules accepted'
|
|
1278
1284
|
]
|
|
1279
1285
|
]);
|
|
1280
|
-
for await (const
|
|
1286
|
+
for await (const record of cursor) {
|
|
1287
|
+
const e = normalizeEntry(record);
|
|
1281
1288
|
const row = marketingOnly
|
|
1282
|
-
? [e.
|
|
1289
|
+
? [e.name, e.email]
|
|
1283
1290
|
: [
|
|
1284
|
-
e.
|
|
1291
|
+
e.name,
|
|
1285
1292
|
e.email,
|
|
1286
1293
|
e.address?.line1 || '',
|
|
1287
1294
|
e.address?.line2 || '',
|
package/dist/giveaways/index.js
CHANGED
|
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
/** Server-only giveaway rules and persistence shared by the admin dashboard and cloud APIs. */
|
|
18
18
|
__exportStar(require("./domain"), exports);
|
|
19
|
+
__exportStar(require("./api-pagination"), exports);
|
|
19
20
|
__exportStar(require("./entry-id"), exports);
|
|
20
21
|
__exportStar(require("./giveaway-repository"), exports);
|
|
21
22
|
__exportStar(require("./schemas"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Successful non-paginated DGC API response. */
|
|
2
|
+
export type ApiResponse<T> = {
|
|
3
|
+
data: T;
|
|
4
|
+
};
|
|
5
|
+
/** Successful paginated DGC API response. count is the number of returned records. */
|
|
6
|
+
export type PaginatedApiResponse<T> = {
|
|
7
|
+
page: number;
|
|
8
|
+
pageSize: number;
|
|
9
|
+
count: number;
|
|
10
|
+
totalCount: number;
|
|
11
|
+
data: T[];
|
|
12
|
+
};
|
package/dist/types/giveaway.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export type GiveawayEntry = {
|
|
|
70
70
|
_id: GiveawayEntryId;
|
|
71
71
|
giveawayId: GiveawayObjectId;
|
|
72
72
|
userId: string;
|
|
73
|
-
|
|
73
|
+
name: string;
|
|
74
74
|
email: string;
|
|
75
75
|
/** Historical entry addresses are retained; new entries collect delivery only at claim. */
|
|
76
76
|
address?: GiveawayDeliveryAddress;
|
|
@@ -180,10 +180,9 @@ export type GiveawayOverviewData = {
|
|
|
180
180
|
liveGiveaways: GiveawaySummary[];
|
|
181
181
|
needsSelection: Giveaway[];
|
|
182
182
|
};
|
|
183
|
-
export type GiveawayUserProfile = Pick<FbUser, 'email' | '
|
|
183
|
+
export type GiveawayUserProfile = Pick<FbUser, 'email' | 'name' | '_collectorType'> & {
|
|
184
184
|
userId: string;
|
|
185
185
|
username?: string;
|
|
186
|
-
country?: string;
|
|
187
186
|
};
|
|
188
187
|
export type GiveawayPhysicalClaim = {
|
|
189
188
|
fullName: string;
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -21,6 +21,7 @@ __exportStar(require("../enums/card"), exports);
|
|
|
21
21
|
__exportStar(require("../enums/rc"), exports);
|
|
22
22
|
__exportStar(require("../enums/user"), exports);
|
|
23
23
|
__exportStar(require("./blockchain"), exports);
|
|
24
|
+
__exportStar(require("./api"), exports);
|
|
24
25
|
__exportStar(require("./card"), exports);
|
|
25
26
|
__exportStar(require("./embedding"), exports);
|
|
26
27
|
__exportStar(require("./env"), exports);
|
package/dist/types/user.d.ts
CHANGED