diff --git a/.gitea/workflows/deploy-dev.yaml b/.gitea/workflows/deploy-dev.yaml index cbb62b4..05c02dc 100644 --- a/.gitea/workflows/deploy-dev.yaml +++ b/.gitea/workflows/deploy-dev.yaml @@ -19,9 +19,6 @@ jobs: with: node-version: 22 - - name: Clean install - run: npm clean-install - - name: Copy .env.example to .env run: cp functions/.env.example functions/.env diff --git a/firestore.indexes.json b/firestore.indexes.json index 2016f51..2ddb5ce 100644 --- a/firestore.indexes.json +++ b/firestore.indexes.json @@ -1,245 +1,4 @@ { - "indexes": [ - { - "collectionGroup": "day_pass_bookings", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "userId", - "order": "ASCENDING" - }, - { - "fieldPath": "createdAt", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "day_pass_entries", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "bookingId", - "order": "ASCENDING" - }, - { - "fieldPath": "entryDate", - "order": "ASCENDING" - } - ] - }, - { - "collectionGroup": "gyms", - "queryScope": "COLLECTION_GROUP", - "fields": [ - { - "fieldPath": "userId", - "order": "ASCENDING" - }, - { - "fieldPath": "createdAt", - "order": "ASCENDING" - } - ] - }, - { - "collectionGroup": "gyms", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "userId", - "order": "ASCENDING" - }, - { - "fieldPath": "name", - "order": "ASCENDING" - } - ] - }, - { - "collectionGroup": "memberships", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "gymId", - "order": "ASCENDING" - }, - { - "fieldPath": "createdAt", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "data.clientId", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "data.clientId", - "order": "ASCENDING" - }, - { - "fieldPath": "type", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "type", - "order": "ASCENDING" - }, - { - "fieldPath": "userId", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "data.ownerId", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "data.ownerId", - "order": "ASCENDING" - }, - { - "fieldPath": "type", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "data.trainerId", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "notifications", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "recipientId", - "order": "ASCENDING" - }, - { - "fieldPath": "timestamp", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "workout_logs", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "user_id", - "order": "ASCENDING" - }, - { - "fieldPath": "date", - "order": "DESCENDING" - } - ] - }, - { - "collectionGroup": "workout_logs", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "user_id", - "order": "ASCENDING" - }, - { - "fieldPath": "date", - "order": "ASCENDING" - } - ] - }, - { - "collectionGroup": "workout_logs", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "user_id", - "order": "ASCENDING" - }, - { - "fieldPath": "start_time", - "order": "ASCENDING" - }, - { - "fieldPath": "date", - "order": "ASCENDING" - } - ] - }, - { - "collectionGroup": "terms_and_conditions", - "queryScope": "COLLECTION", - "fields": [ - { - "fieldPath": "normalizedName", - "order": "ASCENDING" - }, - { - "fieldPath": "userUid", - "order": "ASCENDING" - } - ] - } - ], + "indexes": [], "fieldOverrides": [] } \ No newline at end of file diff --git a/functions/package-lock.json b/functions/package-lock.json index 1d81e0c..1ef4a3f 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -15,7 +15,7 @@ "cors": "^2.8.5", "date-fns": "^4.1.0", "firebase-admin": "^12.6.0", - "firebase-functions": "^6.0.1", + "firebase-functions": "^6.4.0", "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", @@ -4897,9 +4897,10 @@ } }, "node_modules/firebase-functions": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.2.tgz", - "integrity": "sha512-FC3A1/nhqt1ZzxRnj5HZLScQaozAcFSD/vSR8khqSoFNOfxuXgwJS6ZABTB7+v+iMD5z6Mmxw6OfqITUBuI7OQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.4.0.tgz", + "integrity": "sha512-Q/LGhJrmJEhT0dbV60J4hCkVSeOM6/r7xJS/ccmkXzTWMjo+UPAYX9zlQmGlEjotstZ0U9GtQSJSgbB2Z+TJDg==", + "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", "@types/express": "^4.17.21", diff --git a/functions/package.json b/functions/package.json index 334660d..099daf5 100644 --- a/functions/package.json +++ b/functions/package.json @@ -22,7 +22,7 @@ "cors": "^2.8.5", "date-fns": "^4.1.0", "firebase-admin": "^12.6.0", - "firebase-functions": "^6.0.1", + "firebase-functions": "^6.4.0", "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", diff --git a/functions/src/notifications/membershipStatusNotifications.ts b/functions/src/notifications/membershipStatusNotifications.ts index 2ed768f..85799cf 100644 --- a/functions/src/notifications/membershipStatusNotifications.ts +++ b/functions/src/notifications/membershipStatusNotifications.ts @@ -4,7 +4,7 @@ import * as admin from "firebase-admin"; const app = getAdmin(); const logger = getLogger(); -const kTrainerRole = 'Trainer'; +const kTrainerRole = "Trainer"; interface MembershipData { id?: string; @@ -53,6 +53,7 @@ export const checkExpiredMemberships = onSchedule( logger.info("Starting scheduled membership expiry check..."); try { + await updateDaysUntilExpiryForAllMemberships(); const expiredMemberships = await findExpiredMemberships(); const expiringMemberships = await findMembershipsExpiringIn10Days(); @@ -377,6 +378,107 @@ function calculateRenewalDateFromPayment( return renewalDate; } +async function updateDaysUntilExpiryForAllMemberships(): Promise { + try { + logger.info( + "Starting to update daysUntilExpiry for all active memberships..." + ); + + const snapshot = await app + .firestore() + .collection("memberships") + .where("status", "==", "ACTIVE") + .get(); + + const batchSize = 10; + const docs = snapshot.docs; + let updatedCount = 0; + + for (let i = 0; i < docs.length; i += batchSize) { + const batch = docs.slice(i, i + batchSize); + const batchResults = await Promise.allSettled( + batch.map(async (doc) => { + const data = doc.data() as MembershipData; + const daysUntilExpiry = await calculateDaysUntilExpiry(doc.id, data); + + if (daysUntilExpiry !== null) { + const updateData: any = { + daysUntilExpiry: daysUntilExpiry, + updatedAt: admin.firestore.FieldValue.serverTimestamp(), + }; + + await app + .firestore() + .collection("memberships") + .doc(doc.id) + .update(updateData); + + logger.info( + `Updated membership ${doc.id} with daysUntilExpiry: ${daysUntilExpiry}` + ); + return doc.id; + } + return null; + }) + ); + + batchResults.forEach((result) => { + if (result.status === "fulfilled" && result.value) { + updatedCount++; + } + }); + } + + logger.info(`Updated daysUntilExpiry for ${updatedCount} memberships`); + } catch (error) { + logger.error("Error updating daysUntilExpiry for memberships:", error); + throw error; + } +} + +async function calculateDaysUntilExpiry( + membershipId: string, + data: MembershipData +): Promise { + try { + if (!data.subscription || !data.subscription.frequency) { + logger.warn( + `Skipping expiry calculation for membership ${membershipId} with missing subscription data.` + ); + return null; + } + + const payments = await getPaymentsForMembership(membershipId); + if (payments.length === 0) { + logger.warn( + `No payments found for membership ${membershipId}, cannot determine expiry` + ); + return null; + } + + const latestPayment = payments[0]; + const startDate = latestPayment.dateTimestamp; + + const expiryDate = calculateExpiryDate( + startDate, + data.subscription.frequency + ); + + const now = new Date(); + + const timeDiff = expiryDate.getTime() - now.getTime(); + const daysUntilExpiry = Math.floor(timeDiff / (1000 * 3600 * 24)); + + return Math.max(0, daysUntilExpiry); + } catch (error) { + logger.error( + `Error calculating daysUntilExpiry for membership ${membershipId}:`, + error + ); + return null; + } +} + async function getTrainerAssignmentsForMembership( membershipId: string ): Promise { @@ -439,15 +541,33 @@ async function processExpiredMembership( membershipData: MembershipData ): Promise { try { - await app.firestore().collection("memberships").doc(membershipId).update({ - status: "EXPIRED", - updatedAt: admin.firestore.FieldValue.serverTimestamp(), - }); + const payments = await getPaymentsForMembership(membershipId); + if (payments.length > 0) { + const latestPayment = payments[0]; + const expiryDate = calculateExpiryDate( + latestPayment.dateTimestamp, + membershipData.subscription?.frequency || "monthly" + ); + + await app + .firestore() + .collection("memberships") + .doc(membershipId) + .update({ + expirationDate: admin.firestore.Timestamp.fromDate(expiryDate), + status: "EXPIRED", + updatedAt: admin.firestore.FieldValue.serverTimestamp(), + }); + } else { + await app.firestore().collection("memberships").doc(membershipId).update({ + status: "EXPIRED", + updatedAt: admin.firestore.FieldValue.serverTimestamp(), + }); + } logger.info(`Marked membership ${membershipId} as EXPIRED`); await sendPlanExpiredNotification(membershipId, membershipData); - await sendTrainerNotifications(membershipId, membershipData, "expired"); } catch (error) { logger.error(`Error processing membership ${membershipId}:`, error); @@ -461,8 +581,25 @@ async function processExpiringMembership( try { logger.info(`Processing expiring membership ${membershipId}`); - await sendPlanExpiringNotification(membershipId, membershipData); + const payments = await getPaymentsForMembership(membershipId); + if (payments.length > 0) { + const latestPayment = payments[0]; + const expiryDate = calculateExpiryDate( + latestPayment.dateTimestamp, + membershipData.subscription?.frequency || "monthly" + ); + await app + .firestore() + .collection("memberships") + .doc(membershipId) + .update({ + expirationDate: admin.firestore.Timestamp.fromDate(expiryDate), + updatedAt: admin.firestore.FieldValue.serverTimestamp(), + }); + } + + await sendPlanExpiringNotification(membershipId, membershipData); await sendTrainerNotifications(membershipId, membershipData, "expiring"); } catch (error) { logger.error( @@ -536,7 +673,7 @@ async function sendTrainerNotifications( if (notificationType === "expiring") { const now = new Date(); const timeDiff = expiryDate.getTime() - now.getTime(); - daysUntilExpiry = Math.ceil(timeDiff / (1000 * 3600 * 24)); + daysUntilExpiry = Math.floor(timeDiff / (1000 * 3600 * 24)); } } @@ -546,7 +683,6 @@ async function sendTrainerNotifications( try { const trainerName = await getTrainerName(assignment.trainerId); const trainerUserId = await getTrainerUserId(assignment.trainerId); - const notifType = notificationType === "expired" @@ -727,7 +863,7 @@ async function sendPlanExpiringNotification( const now = new Date(); const timeDiff = expiryDate.getTime() - now.getTime(); - daysUntilExpiry = Math.ceil(timeDiff / (1000 * 3600 * 24)); + daysUntilExpiry = Math.floor(timeDiff / (1000 * 3600 * 24)); } const existing = await app @@ -787,7 +923,6 @@ async function sendPlanExpiringNotification( ); } } - async function getClientName( membershipId: string, clientId: string diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index b0bc379..0000000 --- a/package-lock.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "name": "fitlien-services", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@types/busboy": "^1.5.4", - "@types/nodemailer": "^6.4.17", - "@types/pdfkit": "^0.13.9", - "busboy": "^1.6.0", - "date-fns": "^4.1.0", - "nodemailer": "^7.0.3", - "pdfkit": "^0.17.1" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/busboy": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz", - "integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/nodemailer": { - "version": "6.4.17", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", - "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/pdfkit": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.13.9.tgz", - "integrity": "sha512-RDG8Yb1zT7I01FfpwK7nMSA433XWpblMqSCtA5vJlSyavWZb303HUYPCel6JTiDDFqwGLvtAnYbH8N/e0Cb89g==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "dependencies": { - "base64-js": "^1.1.2" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", - "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/jpeg-exif": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", - "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==" - }, - "node_modules/linebreak": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", - "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", - "dependencies": { - "base64-js": "0.0.8", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/linebreak/node_modules/base64-js": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/nodemailer": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", - "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" - }, - "node_modules/pdfkit": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.1.tgz", - "integrity": "sha512-Kkf1I9no14O/uo593DYph5u3QwiMfby7JsBSErN1WqeyTgCBNJE3K4pXBn3TgkdKUIVu+buSl4bYUNC+8Up4xg==", - "dependencies": { - "crypto-js": "^4.2.0", - "fontkit": "^2.0.4", - "jpeg-exif": "^1.1.4", - "linebreak": "^1.1.0", - "png-js": "^1.0.0" - } - }, - "node_modules/png-js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", - "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" - }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" - }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index da0eb6c..0000000 --- a/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "@types/busboy": "^1.5.4", - "busboy": "^1.6.0", - "date-fns": "^4.1.0" - } -}