@fplpool/fpl-models 1.0.0

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/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@fplpool/fpl-models",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "keywords": [],
9
+ "author": "Akamelu Sopuruchukwu",
10
+ "license": "ISC",
11
+ "description": "Shared application models",
12
+ "dependencies": {
13
+ "dotenv": "^17.3.1",
14
+ "mongoose": "^9.3.0"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^25.5.0",
21
+ "ts-node": "^10.9.2",
22
+ "typescript": "^5.9.3"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/austin230196/fpl-models.git"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/austin230196/fpl-models/issues"
30
+ },
31
+ "homepage": "https://github.com/austin230196/fpl-models#readme"
32
+ }
@@ -0,0 +1,38 @@
1
+ import {config} from "dotenv";
2
+
3
+
4
+ config();
5
+
6
+
7
+ export default {
8
+ db: {
9
+ mongo: {
10
+ uri: process.env.MONGO_URI || ``,
11
+ host: process.env.MONGO_HOST || 'localhost',
12
+ port: process.env.MONGO_PORT || 27017,
13
+ database: process.env.MONGO_DATABASE || 'fpl',
14
+ }
15
+ },
16
+ models: {
17
+ user: 'User',
18
+ userSettings: 'UserSettings',
19
+ session: 'Session',
20
+ wallet: 'Wallet',
21
+ card: 'Card',
22
+ transaction: 'Transaction',
23
+ notification: 'Notification',
24
+ history: 'History',
25
+ pool: 'Pool',
26
+ poolGameweekDeadline: 'PoolGameweekDeadline',
27
+ poolMember: 'PoolMember',
28
+ poolWallet: 'PoolWallet',
29
+ poolGameweek: 'PoolGameweek',
30
+ poolGameweekWinner: 'PoolGameweekWinner',
31
+ poolInvite: 'PoolInvite',
32
+ poolTransaction: 'PoolTransaction',
33
+ poolHistory: 'PoolHistory',
34
+ job: 'Job',
35
+ invoice: 'Invoice',
36
+ config: 'Config',
37
+ },
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export * from "./models/user.model";
2
+ export * from "./models/user-settings.model";
3
+ export * from "./models/session.model";
4
+ export * from "./models/wallet.model";
5
+ export * from "./models/card.model";
6
+ export * from "./models/transaction.model";
7
+ export * from "./models/pool.model";
8
+ export * from "./models/pool-gameweek-deadline.model";
9
+ export * from "./models/pool-member.model";
10
+ export * from "./models/pool-wallet.model";
11
+ export * from "./models/pool-gameweek.model";
12
+ export * from "./models/pool-gameweek-winner.model";
13
+ export * from "./models/pool-invite.model";
14
+ export * from "./models/pool-transaction.model";
15
+ export * from "./models/job.model";
16
+ export * from "./models/invoice.model";
17
+ export * from "./models/config.model";
18
+
19
+ export * from "./repositories/generic.repository";
@@ -0,0 +1,76 @@
1
+ import { Schema, model, Types, Document } from "mongoose";
2
+ import config from "../config";
3
+
4
+
5
+
6
+ export interface ICard extends Document {
7
+ _id: Types.ObjectId;
8
+ paymentMethodId: string;
9
+ last4: string;
10
+ brand: string;
11
+ expiryMonth: number;
12
+ expiryYear: number;
13
+ holderName: string;
14
+ isDefault: boolean;
15
+ addedAt: Date;
16
+ updatedAt: Date;
17
+ }
18
+
19
+
20
+
21
+ const cardSchema = new Schema<ICard>({
22
+ paymentMethodId: {
23
+ type: String,
24
+ required: true,
25
+ unique: true,
26
+ trim: true
27
+ },
28
+ last4: {
29
+ type: String,
30
+ required: true
31
+ },
32
+ brand: {
33
+ type: String,
34
+ required: true
35
+ },
36
+ expiryMonth: {
37
+ type: Number,
38
+ required: true,
39
+ min: 1,
40
+ max: 12
41
+ },
42
+ expiryYear: {
43
+ type: Number,
44
+ required: true
45
+ },
46
+ holderName: {
47
+ type: String,
48
+ required: true
49
+ },
50
+ isDefault: {
51
+ type: Boolean,
52
+ default: false
53
+ },
54
+ }, {
55
+ timestamps: {
56
+ createdAt: 'addedAt',
57
+ updatedAt: true
58
+ },
59
+ toJSON: {virtuals: true},
60
+ toObject: {virtuals: true}
61
+ })
62
+
63
+
64
+ cardSchema.virtual('expired').get(function(){
65
+ const currentDate = new Date();
66
+ const currentYear = currentDate.getFullYear();
67
+ const currentMonth = currentDate.getMonth() + 1;
68
+
69
+ if (this.expiryYear < currentYear) return true;
70
+ if (this.expiryYear === currentYear && this.expiryMonth < currentMonth) return true;
71
+ return false;
72
+ })
73
+
74
+ const Card = model<ICard>(config.models.card, cardSchema);
75
+
76
+ export default Card;
@@ -0,0 +1,52 @@
1
+ import { Document, model, Schema, Types } from "mongoose";
2
+
3
+ export interface IConfig extends Document {
4
+ _id: Types.ObjectId;
5
+ name: string,
6
+ version: string,
7
+ gameweek: {
8
+ active: boolean,
9
+ current: number,
10
+ next: number | null,
11
+ activeTable: null | Record<string, number>
12
+ }
13
+ }
14
+
15
+
16
+ const configSchema = new Schema<IConfig>({
17
+ name: {
18
+ type: String,
19
+ required: true
20
+ },
21
+ version: {
22
+ type: String,
23
+ required: true
24
+ },
25
+ gameweek: {
26
+ active: {
27
+ type: Boolean,
28
+ required: true,
29
+ default: false
30
+ },
31
+ current: {
32
+ type: Number,
33
+ required: true,
34
+ },
35
+ next: {
36
+ type: Number,
37
+ required: true,
38
+ default: null
39
+ },
40
+ activeTable: {
41
+ type: Object,
42
+ required: false,
43
+ default: null
44
+ }
45
+ }
46
+ })
47
+
48
+
49
+ const Config = model<IConfig>("Config", configSchema);
50
+
51
+
52
+ export default Config;
@@ -0,0 +1,100 @@
1
+ import {Document, Schema, Types, model} from "mongoose";
2
+
3
+ import config from "../config";
4
+ import sperseId from "sperse-id";
5
+
6
+
7
+
8
+
9
+
10
+ export enum InvoiceStatus {
11
+ Pending = "pending",
12
+ Completed = "completed",
13
+ Failed = "failed",
14
+ }
15
+
16
+ export interface IInvoice extends Document {
17
+ _id: Types.ObjectId;
18
+ uid: string;
19
+ wallet: Types.ObjectId;
20
+ status: InvoiceStatus;
21
+ receipt?: string;
22
+ createdAt: Date;
23
+ updatedAt: Date;
24
+ description?: string;
25
+ amount: string;
26
+ metadata: {
27
+ paymentMethodType?: InvoicePaymentMethodType,
28
+ cardId?: Types.ObjectId;
29
+ currency: string;
30
+ };
31
+ }
32
+
33
+ export enum InvoicePaymentMethodType {
34
+ Card = "card",
35
+ BankTransfer = "bank_transfer",
36
+ // Crypto = "crypto",
37
+ }
38
+
39
+
40
+
41
+ const invoiceSchema = new Schema<IInvoice>({
42
+ uid: {
43
+ type: String,
44
+ required: true,
45
+ unique: true,
46
+ default: () => sperseId(12, {numbers: true, capitalLetters: false, smallLetters: true})
47
+ },
48
+ wallet: {
49
+ type: Schema.Types.ObjectId,
50
+ ref: config.models.wallet,
51
+ required: true
52
+ },
53
+ receipt: {
54
+ type: String,
55
+ required: false,
56
+ default: null
57
+ },
58
+ status: {
59
+ type: String,
60
+ required: [true, 'Status is required'],
61
+ enum: InvoiceStatus,
62
+ default: InvoiceStatus.Pending
63
+ },
64
+ description: {
65
+ type: String,
66
+ required: false,
67
+ default: null
68
+ },
69
+ amount: {
70
+ type: String,
71
+ required: true,
72
+ default: "0.00"
73
+ },
74
+ metadata: {
75
+ paymentMethodType: {
76
+ type: String,
77
+ enum: InvoicePaymentMethodType,
78
+ default: InvoicePaymentMethodType.Card
79
+ },
80
+ cardId: {
81
+ type: String,
82
+ required: false,
83
+ default: null
84
+ },
85
+ currency: {
86
+ type: String,
87
+ required: true,
88
+ default: "USD"
89
+ }
90
+ }
91
+ }, {
92
+ timestamps: true,
93
+ toJSON: {virtuals: true},
94
+ toObject: {virtuals: true}
95
+ })
96
+
97
+
98
+ const Invoice = model<IInvoice>(config.models.invoice, invoiceSchema)
99
+
100
+ export default Invoice;
@@ -0,0 +1,62 @@
1
+ import {Document, Schema, Types, model} from "mongoose";
2
+
3
+ import config from "../config";
4
+
5
+
6
+
7
+ export enum JobStatus {
8
+ Pending = "pending",
9
+ Completed = "completed",
10
+ Failed = "failed",
11
+ }
12
+
13
+ export interface IJob extends Document {
14
+ _id: Types.ObjectId;
15
+ queueName: string;
16
+ payload: Record<string, any>;
17
+ status: JobStatus;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ lastAttemptedAt: Date | null;
21
+ finalizedAt: Date | null;
22
+ errorLogs: Array<any> | null;
23
+ }
24
+
25
+
26
+ const jobSchema = new Schema<IJob>({
27
+ queueName: {
28
+ type: String,
29
+ required: [true, 'Queue name is required'],
30
+ },
31
+ payload: {
32
+ type: Object
33
+ },
34
+ errorLogs: {
35
+ type: Array,
36
+ default: null
37
+ },
38
+ status: {
39
+ type: String,
40
+ required: [true, 'Status is required'],
41
+ enum: JobStatus,
42
+ default: JobStatus.Pending
43
+ },
44
+ lastAttemptedAt: {
45
+ type: Date,
46
+ default: null,
47
+ },
48
+ finalizedAt: {
49
+ type: Date,
50
+ default: null,
51
+ },
52
+ }, {
53
+ timestamps: true,
54
+ toJSON: {virtuals: true},
55
+ toObject: {virtuals: true}
56
+ })
57
+
58
+
59
+
60
+ const Job = model<IJob>(config.models.job, jobSchema)
61
+
62
+ export default Job;
@@ -0,0 +1,46 @@
1
+ import { Document, Schema, Types, model } from "mongoose";
2
+ import config from "../config";
3
+ import { IPool } from "./pool.model";
4
+
5
+
6
+ export enum PoolGameweekDeadlineType {
7
+ Personal = 'personal',
8
+ Members = 'members'
9
+ }
10
+
11
+ export interface IPoolGameweekDeadline extends Document {
12
+ _id: Types.ObjectId;
13
+ pool: IPool;
14
+ type: PoolGameweekDeadlineType;
15
+ msBeforeDeadline: number;
16
+ }
17
+
18
+
19
+ const poolGameweekDeadlineSchema = new Schema<IPoolGameweekDeadline>({
20
+ pool: {
21
+ type: Schema.Types.ObjectId,
22
+ ref: config.models.pool,
23
+ required: true
24
+ },
25
+ type: {
26
+ type: String,
27
+ enum: PoolGameweekDeadlineType,
28
+ default: PoolGameweekDeadlineType.Members
29
+ },
30
+ msBeforeDeadline: {
31
+ type: Number,
32
+ required: true,
33
+ default: 1000 * 60 * 30 //30 minutes before deadline
34
+ }
35
+ },
36
+ {
37
+ timestamps: true,
38
+ toJSON: {virtuals: true},
39
+ toObject: {virtuals: true}
40
+ }
41
+ );
42
+
43
+
44
+ const PoolGameweekDeadline = model<IPoolGameweekDeadline>(config.models.poolGameweekDeadline, poolGameweekDeadlineSchema);
45
+
46
+ export default PoolGameweekDeadline;
@@ -0,0 +1,55 @@
1
+ import { Schema, model, Types, Document } from "mongoose";
2
+ import config from "../config";
3
+
4
+
5
+
6
+
7
+ export enum PoolGameweekWinnerStatus {
8
+ Pending = "pending",
9
+ Paid = "paid",
10
+ Failed = "failed"
11
+ }
12
+
13
+ export interface IPoolGameweekWinner extends Document {
14
+ _id: Types.ObjectId;
15
+ gameweek: Types.ObjectId;
16
+ winner: Types.ObjectId;
17
+ score: number;
18
+ status: PoolGameweekWinnerStatus;
19
+ createdAt: Date;
20
+ updatedAt: Date;
21
+ }
22
+
23
+
24
+ const poolGameweekWinnerSchema = new Schema<IPoolGameweekWinner>({
25
+ gameweek: {
26
+ type: Schema.Types.ObjectId,
27
+ ref: config.models.poolGameweek,
28
+ required: true
29
+ },
30
+ winner: {
31
+ type: Schema.Types.ObjectId,
32
+ ref: config.models.poolMember,
33
+ required: true
34
+ },
35
+ score: {
36
+ type: Number,
37
+ required: true,
38
+ default: 0
39
+ },
40
+ status: {
41
+ type: String,
42
+ enum: PoolGameweekWinnerStatus,
43
+ default: PoolGameweekWinnerStatus.Pending
44
+ },
45
+ }, {
46
+ timestamps: true,
47
+ toJSON: {virtuals: true},
48
+ toObject: {virtuals: true}
49
+ })
50
+
51
+
52
+
53
+ const PoolGameweekWinner = model<IPoolGameweekWinner>(config.models.poolGameweekWinner, poolGameweekWinnerSchema);
54
+
55
+ export default PoolGameweekWinner;
@@ -0,0 +1,114 @@
1
+ import { Schema, model, Types, Document } from "mongoose";
2
+ import config from "../config";
3
+ import { poolAmountPattern } from "./pool-transaction.model";
4
+ import AdvancedError from "../errors/advanced.error";
5
+ import StatusCode from "../enums/status-code.enum";
6
+
7
+
8
+
9
+
10
+ export interface IPoolGameweek extends Document {
11
+ _id: Types.ObjectId;
12
+ pool: Types.ObjectId;
13
+ gameweek: number;
14
+ active: boolean;
15
+ activePlayers: {member: Types.ObjectId, score: number}[];
16
+ winner: Types.ObjectId | null;
17
+ balance: string;
18
+ startedAt: Date | null;
19
+ participationRateBps: number;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ hasPaid: (memberId: Types.ObjectId) => Promise<boolean>;
23
+ }
24
+
25
+
26
+ const poolGameweekSchema = new Schema<IPoolGameweek>({
27
+ pool: {
28
+ type: Schema.Types.ObjectId,
29
+ ref: config.models.pool,
30
+ required: true
31
+ },
32
+ gameweek: {
33
+ type: Number,
34
+ required: true
35
+ },
36
+ activePlayers: [{
37
+ member: {
38
+ type: Schema.Types.ObjectId,
39
+ ref: config.models.poolMember,
40
+ },
41
+ score: {
42
+ type: Number,
43
+ required: true,
44
+ default: 0
45
+ }
46
+ }],
47
+ active: {
48
+ type: Boolean,
49
+ default: false
50
+ },
51
+ startedAt: {
52
+ type: Date,
53
+ required: false,
54
+ default: null
55
+ },
56
+ participationRateBps: {
57
+ type: Number,
58
+ required: false,
59
+ default: 0
60
+ },
61
+ balance: {
62
+ type: String,
63
+ required: false,
64
+ default: "0.00",
65
+ match: poolAmountPattern,
66
+ set: function(value: string) {
67
+ // If value already looks like a float (has a .), ensure two decimals
68
+ if (typeof value === "string") {
69
+ if (poolAmountPattern.test(value)) {
70
+ // If it has a dot but is not two decimals, pad right
71
+ if (value.indexOf('.') !== -1) {
72
+ let [whole, decimals] = value.split('.');
73
+ if (decimals === undefined) return whole + ".00";
74
+ if (decimals.length === 1) return whole + "." + decimals + "0";
75
+ if (decimals.length === 2) return value;
76
+ if (decimals.length > 2) return whole + "." + decimals.slice(0,2);
77
+ }
78
+ // No decimal: add .00
79
+ return value + ".00";
80
+ }
81
+ // If it's an integer, add .00
82
+ if (/^\d+$/.test(value)) {
83
+ return value + ".00";
84
+ }
85
+ }
86
+ return value;
87
+ }
88
+ },
89
+ winner: {
90
+ type: Schema.Types.ObjectId,
91
+ ref: config.models.poolGameweekWinner,
92
+ required: false,
93
+ default: null
94
+ }
95
+ }, {
96
+ timestamps: true,
97
+ toJSON: {virtuals: true},
98
+ toObject: {virtuals: true}
99
+ })
100
+
101
+
102
+ poolGameweekSchema.methods.hasPaid = async function(memberId: Types.ObjectId): Promise<boolean> {
103
+ try{
104
+ return await this.activePlayers.some(({member}: {member: Types.ObjectId, score: number}) => member.toString() === memberId.toString());
105
+ }catch(e: any){
106
+ throw new AdvancedError(e.message, e.statusCode || StatusCode.INTERNAL_SERVER_ERROR)
107
+ }
108
+ }
109
+
110
+
111
+
112
+ const PoolGameweek = model<IPoolGameweek>(config.models.poolGameweek, poolGameweekSchema);
113
+
114
+ export default PoolGameweek;
@@ -0,0 +1,65 @@
1
+ import { Schema, model, Types, Document } from "mongoose";
2
+ import config from "../config";
3
+
4
+
5
+ export interface IPoolInvite extends Document {
6
+ _id: Types.ObjectId;
7
+ pool: Types.ObjectId;
8
+ email: string;
9
+ status: PoolInviteStatus;
10
+ invitedAt: Date;
11
+ expiresAt: Date;
12
+ updatedAt: Date;
13
+ hasExpired: () => Promise<boolean>;
14
+ }
15
+
16
+ export enum PoolInviteStatus {
17
+ Pending = "pending",
18
+ Accepted = "accepted",
19
+ Declined = "declined",
20
+ Expired = "expired",
21
+ }
22
+
23
+
24
+ const poolInviteSchema = new Schema<IPoolInvite>({
25
+ pool: {
26
+ type: Schema.Types.ObjectId,
27
+ ref: config.models.pool,
28
+ required: true
29
+ },
30
+ email: {
31
+ type: String,
32
+ required: true
33
+ },
34
+ status: {
35
+ type: String,
36
+ required: true,
37
+ enum: PoolInviteStatus,
38
+ default: PoolInviteStatus.Pending
39
+ },
40
+ expiresAt: {
41
+ type: Date,
42
+ required: true,
43
+ default: new Date(Date.now() + (1000 * 60 * 60 * 24 * 7)) //7 days
44
+ }
45
+ }, {
46
+ timestamps: {
47
+ createdAt: 'invitedAt',
48
+ updatedAt: true
49
+ },
50
+ toJSON: {virtuals: true},
51
+ toObject: {virtuals: true}
52
+ })
53
+
54
+ poolInviteSchema.methods.hasExpired = async function(): Promise<boolean> {
55
+ if(Date.now() >= this.expiresAt.getTime() && this.status.toString() !== PoolInviteStatus.Expired.toString()){
56
+ this.status = PoolInviteStatus.Expired;
57
+ await this.save();
58
+ }
59
+ return this.status.toString() === PoolInviteStatus.Expired.toString();
60
+ }
61
+
62
+
63
+ const PoolInvite = model<IPoolInvite>(config.models.poolInvite, poolInviteSchema);
64
+
65
+ export default PoolInvite;