@m5kdev/backend 0.1.4 → 0.2.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/dist/src/lib/posthog.d.ts +0 -1
- package/dist/src/lib/sentry.d.ts +0 -1
- package/dist/src/modules/access/access.repository.d.ts +0 -1
- package/dist/src/modules/access/access.service.d.ts +0 -1
- package/dist/src/modules/access/access.test.d.ts +0 -1
- package/dist/src/modules/access/access.utils.d.ts +0 -1
- package/dist/src/modules/ai/ai.db.d.ts +0 -1
- package/dist/src/modules/ai/ai.prompt.d.ts +0 -1
- package/dist/src/modules/ai/ai.repository.d.ts +0 -1
- package/dist/src/modules/ai/ai.router.d.ts +0 -1
- package/dist/src/modules/ai/ai.service.d.ts +0 -1
- package/dist/src/modules/ai/ai.trpc.d.ts +5 -42
- package/dist/src/modules/ai/ai.trpc.js +5 -5
- package/dist/src/modules/ai/ideogram/ideogram.constants.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.dto.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.prompt.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.repository.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.service.d.ts +0 -1
- package/dist/src/modules/auth/auth.db.d.ts +0 -1
- package/dist/src/modules/auth/auth.dto.d.ts +7 -8
- package/dist/src/modules/auth/auth.lib.d.ts +8 -9
- package/dist/src/modules/auth/auth.middleware.d.ts +0 -1
- package/dist/src/modules/auth/auth.repository.d.ts +0 -1
- package/dist/src/modules/auth/auth.service.d.ts +0 -1
- package/dist/src/modules/auth/auth.trpc.d.ts +52 -89
- package/dist/src/modules/auth/auth.trpc.js +55 -53
- package/dist/src/modules/auth/auth.utils.d.ts +0 -1
- package/dist/src/modules/base/base.abstract.d.ts +0 -1
- package/dist/src/modules/base/base.dto.d.ts +2 -3
- package/dist/src/modules/base/base.grants.d.ts +0 -1
- package/dist/src/modules/base/base.grants.test.d.ts +0 -1
- package/dist/src/modules/base/base.repository.d.ts +0 -1
- package/dist/src/modules/base/base.service.d.ts +2 -4
- package/dist/src/modules/base/base.types.d.ts +0 -1
- package/dist/src/modules/billing/billing.db.d.ts +0 -1
- package/dist/src/modules/billing/billing.repository.d.ts +0 -1
- package/dist/src/modules/billing/billing.router.d.ts +0 -1
- package/dist/src/modules/billing/billing.service.d.ts +0 -1
- package/dist/src/modules/billing/billing.trpc.d.ts +6 -43
- package/dist/src/modules/billing/billing.trpc.js +7 -7
- package/dist/src/modules/clay/clay.repository.d.ts +0 -1
- package/dist/src/modules/clay/clay.service.d.ts +0 -1
- package/dist/src/modules/connect/connect.db.d.ts +0 -1
- package/dist/src/modules/connect/connect.dto.d.ts +8 -9
- package/dist/src/modules/connect/connect.linkedin.d.ts +0 -1
- package/dist/src/modules/connect/connect.oauth.d.ts +0 -1
- package/dist/src/modules/connect/connect.repository.d.ts +3 -4
- package/dist/src/modules/connect/connect.router.d.ts +0 -1
- package/dist/src/modules/connect/connect.service.d.ts +6 -7
- package/dist/src/modules/connect/connect.trpc.d.ts +9 -46
- package/dist/src/modules/connect/connect.trpc.js +7 -7
- package/dist/src/modules/connect/connect.types.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.db.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.repository.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.service.d.ts +0 -1
- package/dist/src/modules/email/email.service.d.ts +0 -1
- package/dist/src/modules/file/file.repository.d.ts +0 -1
- package/dist/src/modules/file/file.router.d.ts +0 -1
- package/dist/src/modules/file/file.service.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.db.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.repository.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.service.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +11 -48
- package/dist/src/modules/recurrence/recurrence.trpc.js +17 -17
- package/dist/src/modules/social/social.dto.d.ts +0 -1
- package/dist/src/modules/social/social.linkedin.d.ts +0 -1
- package/dist/src/modules/social/social.linkedin.test.d.ts +0 -1
- package/dist/src/modules/social/social.service.d.ts +0 -1
- package/dist/src/modules/social/social.types.d.ts +0 -1
- package/dist/src/modules/tag/tag.db.d.ts +0 -1
- package/dist/src/modules/tag/tag.dto.d.ts +0 -1
- package/dist/src/modules/tag/tag.repository.d.ts +0 -1
- package/dist/src/modules/tag/tag.service.d.ts +0 -1
- package/dist/src/modules/tag/tag.trpc.d.ts +10 -47
- package/dist/src/modules/tag/tag.trpc.js +15 -15
- package/dist/src/modules/utils/applyPagination.d.ts +0 -1
- package/dist/src/modules/utils/applySorting.d.ts +0 -1
- package/dist/src/modules/utils/getConditionsFromFilters.d.ts +0 -1
- package/dist/src/modules/video/video.service.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.constants.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.db.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.dto.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.repository.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.router.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.service.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.db.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.repository.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.service.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.trpc.d.ts +6 -43
- package/dist/src/modules/workflow/workflow.trpc.js +7 -7
- package/dist/src/modules/workflow/workflow.types.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.utils.d.ts +0 -1
- package/dist/src/test/stubs/utils.d.ts +0 -1
- package/dist/src/trpc/context.d.ts +4 -5
- package/dist/src/trpc/index.d.ts +0 -1
- package/dist/src/trpc/procedures.d.ts +24 -25
- package/dist/src/trpc/utils.d.ts +0 -1
- package/dist/src/types.d.ts +61 -209
- package/dist/src/types.js +4 -5
- package/dist/src/utils/errors.d.ts +0 -1
- package/dist/src/utils/logger.d.ts +0 -1
- package/dist/src/utils/posthog.d.ts +0 -1
- package/dist/src/utils/trpc.d.ts +58 -0
- package/dist/src/utils/trpc.js +63 -0
- package/dist/src/utils/types.d.ts +0 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -8
- package/.cursor/rules/backend.mdc +0 -70
- package/.turbo/turbo-build.log +0 -5
- package/.turbo/turbo-check-types.log +0 -5
- package/.turbo/turbo-lint$colon$fix.log +0 -255
- package/CHANGELOG.md +0 -37
- package/dist/src/lib/posthog.d.ts.map +0 -1
- package/dist/src/lib/sentry.d.ts.map +0 -1
- package/dist/src/modules/access/access.repository.d.ts.map +0 -1
- package/dist/src/modules/access/access.service.d.ts.map +0 -1
- package/dist/src/modules/access/access.test.d.ts.map +0 -1
- package/dist/src/modules/access/access.utils.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.db.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.prompt.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.repository.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.router.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.service.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.trpc.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.constants.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.dto.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.prompt.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.repository.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.service.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.db.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.dto.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.lib.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.middleware.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.repository.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.service.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.trpc.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.utils.d.ts.map +0 -1
- package/dist/src/modules/base/base.abstract.d.ts.map +0 -1
- package/dist/src/modules/base/base.dto.d.ts.map +0 -1
- package/dist/src/modules/base/base.grants.d.ts.map +0 -1
- package/dist/src/modules/base/base.grants.test.d.ts.map +0 -1
- package/dist/src/modules/base/base.repository.d.ts.map +0 -1
- package/dist/src/modules/base/base.service.d.ts.map +0 -1
- package/dist/src/modules/base/base.types.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.db.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.repository.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.router.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.service.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.trpc.d.ts.map +0 -1
- package/dist/src/modules/clay/clay.repository.d.ts.map +0 -1
- package/dist/src/modules/clay/clay.service.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.db.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.dto.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.linkedin.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.oauth.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.repository.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.router.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.service.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.trpc.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.types.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.db.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.repository.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.service.d.ts.map +0 -1
- package/dist/src/modules/email/email.service.d.ts.map +0 -1
- package/dist/src/modules/file/file.repository.d.ts.map +0 -1
- package/dist/src/modules/file/file.router.d.ts.map +0 -1
- package/dist/src/modules/file/file.service.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.db.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.repository.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.service.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts.map +0 -1
- package/dist/src/modules/social/social.dto.d.ts.map +0 -1
- package/dist/src/modules/social/social.linkedin.d.ts.map +0 -1
- package/dist/src/modules/social/social.linkedin.test.d.ts.map +0 -1
- package/dist/src/modules/social/social.service.d.ts.map +0 -1
- package/dist/src/modules/social/social.types.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.db.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.dto.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.repository.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.service.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.trpc.d.ts.map +0 -1
- package/dist/src/modules/utils/applyPagination.d.ts.map +0 -1
- package/dist/src/modules/utils/applySorting.d.ts.map +0 -1
- package/dist/src/modules/utils/getConditionsFromFilters.d.ts.map +0 -1
- package/dist/src/modules/video/video.service.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.constants.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.db.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.dto.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.repository.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.router.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.service.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.db.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.repository.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.service.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.trpc.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.types.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.utils.d.ts.map +0 -1
- package/dist/src/test/stubs/utils.d.ts.map +0 -1
- package/dist/src/trpc/context.d.ts.map +0 -1
- package/dist/src/trpc/index.d.ts.map +0 -1
- package/dist/src/trpc/procedures.d.ts.map +0 -1
- package/dist/src/trpc/utils.d.ts.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/utils/errors.d.ts.map +0 -1
- package/dist/src/utils/logger.d.ts.map +0 -1
- package/dist/src/utils/posthog.d.ts.map +0 -1
- package/dist/src/utils/types.d.ts.map +0 -1
- package/jest.config.ts +0 -19
- package/src/lib/posthog.ts +0 -5
- package/src/lib/sentry.ts +0 -8
- package/src/modules/access/access.repository.ts +0 -36
- package/src/modules/access/access.service.ts +0 -81
- package/src/modules/access/access.test.ts +0 -216
- package/src/modules/access/access.utils.ts +0 -46
- package/src/modules/ai/ai.db.ts +0 -38
- package/src/modules/ai/ai.prompt.ts +0 -47
- package/src/modules/ai/ai.repository.ts +0 -53
- package/src/modules/ai/ai.router.ts +0 -148
- package/src/modules/ai/ai.service.ts +0 -310
- package/src/modules/ai/ai.trpc.ts +0 -22
- package/src/modules/ai/ideogram/ideogram.constants.ts +0 -170
- package/src/modules/ai/ideogram/ideogram.dto.ts +0 -64
- package/src/modules/ai/ideogram/ideogram.prompt.ts +0 -858
- package/src/modules/ai/ideogram/ideogram.repository.ts +0 -39
- package/src/modules/ai/ideogram/ideogram.service.ts +0 -14
- package/src/modules/auth/auth.db.ts +0 -224
- package/src/modules/auth/auth.dto.ts +0 -47
- package/src/modules/auth/auth.lib.ts +0 -349
- package/src/modules/auth/auth.middleware.ts +0 -62
- package/src/modules/auth/auth.repository.ts +0 -672
- package/src/modules/auth/auth.service.ts +0 -261
- package/src/modules/auth/auth.trpc.ts +0 -208
- package/src/modules/auth/auth.utils.ts +0 -117
- package/src/modules/base/base.abstract.ts +0 -62
- package/src/modules/base/base.dto.ts +0 -206
- package/src/modules/base/base.grants.test.ts +0 -861
- package/src/modules/base/base.grants.ts +0 -199
- package/src/modules/base/base.repository.ts +0 -433
- package/src/modules/base/base.service.ts +0 -154
- package/src/modules/base/base.types.ts +0 -7
- package/src/modules/billing/billing.db.ts +0 -27
- package/src/modules/billing/billing.repository.ts +0 -328
- package/src/modules/billing/billing.router.ts +0 -77
- package/src/modules/billing/billing.service.ts +0 -177
- package/src/modules/billing/billing.trpc.ts +0 -17
- package/src/modules/clay/clay.repository.ts +0 -29
- package/src/modules/clay/clay.service.ts +0 -61
- package/src/modules/connect/connect.db.ts +0 -32
- package/src/modules/connect/connect.dto.ts +0 -44
- package/src/modules/connect/connect.linkedin.ts +0 -70
- package/src/modules/connect/connect.oauth.ts +0 -288
- package/src/modules/connect/connect.repository.ts +0 -65
- package/src/modules/connect/connect.router.ts +0 -76
- package/src/modules/connect/connect.service.ts +0 -171
- package/src/modules/connect/connect.trpc.ts +0 -26
- package/src/modules/connect/connect.types.ts +0 -27
- package/src/modules/crypto/crypto.db.ts +0 -15
- package/src/modules/crypto/crypto.repository.ts +0 -13
- package/src/modules/crypto/crypto.service.ts +0 -57
- package/src/modules/email/email.service.ts +0 -222
- package/src/modules/file/file.repository.ts +0 -95
- package/src/modules/file/file.router.ts +0 -108
- package/src/modules/file/file.service.ts +0 -186
- package/src/modules/recurrence/recurrence.db.ts +0 -79
- package/src/modules/recurrence/recurrence.repository.ts +0 -70
- package/src/modules/recurrence/recurrence.service.ts +0 -105
- package/src/modules/recurrence/recurrence.trpc.ts +0 -82
- package/src/modules/social/social.dto.ts +0 -22
- package/src/modules/social/social.linkedin.test.ts +0 -277
- package/src/modules/social/social.linkedin.ts +0 -593
- package/src/modules/social/social.service.ts +0 -112
- package/src/modules/social/social.types.ts +0 -43
- package/src/modules/tag/tag.db.ts +0 -41
- package/src/modules/tag/tag.dto.ts +0 -18
- package/src/modules/tag/tag.repository.ts +0 -222
- package/src/modules/tag/tag.service.ts +0 -48
- package/src/modules/tag/tag.trpc.ts +0 -62
- package/src/modules/uploads/0581796b-8845-420d-bd95-cd7de79f6d37.webm +0 -0
- package/src/modules/uploads/33b1e649-6727-4bd0-94d0-a0b363646865.webm +0 -0
- package/src/modules/uploads/49a8c4c0-54d7-4c94-bef4-c93c029f9ed0.webm +0 -0
- package/src/modules/uploads/50e31e38-a2f0-47ca-8b7d-2d7fcad9267d.webm +0 -0
- package/src/modules/uploads/72ac8cf9-c3a7-4cd8-8a78-6d8e137a4c7e.webm +0 -0
- package/src/modules/uploads/75293649-d966-46cd-a675-67518958ae9c.png +0 -0
- package/src/modules/uploads/88b7b867-ce15-4891-bf73-81305a7de1f7.wav +0 -0
- package/src/modules/uploads/a5d6fee8-6a59-42c6-9d4a-ac8a3c5e7245.webm +0 -0
- package/src/modules/uploads/c13a9785-ca5a-4983-af30-b338ed76d370.webm +0 -0
- package/src/modules/uploads/caa1a5a7-71ba-4381-902d-7e2cafdf6dcb.webm +0 -0
- package/src/modules/uploads/cbeb0b81-374d-445b-914b-40ace7c8e031.webm +0 -0
- package/src/modules/uploads/d626aa82-b10f-493f-aee7-87bfb3361dfc.webm +0 -0
- package/src/modules/uploads/d7de4c16-de0c-495d-9612-e72260a6ecca.png +0 -0
- package/src/modules/uploads/e532e38a-6421-400e-8a5f-8e7bc8ce411b.wav +0 -0
- package/src/modules/uploads/e86ec867-6adf-4c51-84e0-00b0836625e8.webm +0 -0
- package/src/modules/utils/applyPagination.ts +0 -13
- package/src/modules/utils/applySorting.ts +0 -21
- package/src/modules/utils/getConditionsFromFilters.ts +0 -216
- package/src/modules/video/video.service.ts +0 -89
- package/src/modules/webhook/webhook.constants.ts +0 -9
- package/src/modules/webhook/webhook.db.ts +0 -15
- package/src/modules/webhook/webhook.dto.ts +0 -9
- package/src/modules/webhook/webhook.repository.ts +0 -68
- package/src/modules/webhook/webhook.router.ts +0 -29
- package/src/modules/webhook/webhook.service.ts +0 -78
- package/src/modules/workflow/workflow.db.ts +0 -29
- package/src/modules/workflow/workflow.repository.ts +0 -171
- package/src/modules/workflow/workflow.service.ts +0 -56
- package/src/modules/workflow/workflow.trpc.ts +0 -26
- package/src/modules/workflow/workflow.types.ts +0 -30
- package/src/modules/workflow/workflow.utils.ts +0 -259
- package/src/test/stubs/utils.ts +0 -2
- package/src/trpc/context.ts +0 -21
- package/src/trpc/index.ts +0 -3
- package/src/trpc/procedures.ts +0 -43
- package/src/trpc/utils.ts +0 -20
- package/src/types.ts +0 -22
- package/src/utils/errors.ts +0 -148
- package/src/utils/logger.ts +0 -8
- package/src/utils/posthog.ts +0 -43
- package/src/utils/types.ts +0 -5
- package/tsconfig.json +0 -21
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { err, ok } from "neverthrow";
|
|
2
|
-
import type Stripe from "stripe";
|
|
3
|
-
import type { User } from "#modules/auth/auth.lib";
|
|
4
|
-
import type { ServerResult, ServerResultAsync } from "#modules/base/base.dto";
|
|
5
|
-
import { BaseService } from "#modules/base/base.service";
|
|
6
|
-
import type { BillingRepository } from "#modules/billing/billing.repository";
|
|
7
|
-
|
|
8
|
-
import { posthogCapture } from "#utils/posthog";
|
|
9
|
-
|
|
10
|
-
const allowedEvents: Stripe.Event.Type[] = [
|
|
11
|
-
"checkout.session.completed",
|
|
12
|
-
"customer.subscription.created",
|
|
13
|
-
"customer.subscription.updated",
|
|
14
|
-
"customer.subscription.deleted",
|
|
15
|
-
"customer.subscription.paused",
|
|
16
|
-
"customer.subscription.resumed",
|
|
17
|
-
"customer.subscription.pending_update_applied",
|
|
18
|
-
"customer.subscription.pending_update_expired",
|
|
19
|
-
"customer.subscription.trial_will_end",
|
|
20
|
-
"invoice.paid",
|
|
21
|
-
"invoice.payment_failed",
|
|
22
|
-
"invoice.payment_action_required",
|
|
23
|
-
"invoice.upcoming",
|
|
24
|
-
"invoice.marked_uncollectible",
|
|
25
|
-
"invoice.payment_succeeded",
|
|
26
|
-
"payment_intent.succeeded",
|
|
27
|
-
"payment_intent.payment_failed",
|
|
28
|
-
"payment_intent.canceled",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
export class BillingService extends BaseService<{ billing: BillingRepository }, never> {
|
|
32
|
-
async createUserCustomer({
|
|
33
|
-
user,
|
|
34
|
-
}: {
|
|
35
|
-
user: { id: string; email: string; name?: string };
|
|
36
|
-
}): ServerResultAsync<Stripe.Customer> {
|
|
37
|
-
let stripeCustomer: Stripe.Customer | null = null;
|
|
38
|
-
const existingCustomer = await this.repository.billing.getCustomerByEmail(user.email);
|
|
39
|
-
if (existingCustomer.isErr()) return err(existingCustomer.error);
|
|
40
|
-
stripeCustomer = existingCustomer.value;
|
|
41
|
-
if (!stripeCustomer) {
|
|
42
|
-
const newCustomer = await this.repository.billing.createCustomer({
|
|
43
|
-
email: user.email,
|
|
44
|
-
name: user.name,
|
|
45
|
-
userId: user.id,
|
|
46
|
-
});
|
|
47
|
-
if (newCustomer.isErr()) return err(newCustomer.error);
|
|
48
|
-
stripeCustomer = newCustomer.value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!stripeCustomer)
|
|
52
|
-
return this.error("INTERNAL_SERVER_ERROR", "Failed to create or get stripe customer");
|
|
53
|
-
const updatedUser = await this.repository.billing.updateUserCustomerId({
|
|
54
|
-
userId: user.id,
|
|
55
|
-
customerId: stripeCustomer.id,
|
|
56
|
-
});
|
|
57
|
-
if (updatedUser.isErr()) return err(updatedUser.error);
|
|
58
|
-
return ok(stripeCustomer);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async createUserHook({
|
|
62
|
-
user,
|
|
63
|
-
}: {
|
|
64
|
-
user: { id: string; email: string; name?: string };
|
|
65
|
-
}): ServerResultAsync<boolean> {
|
|
66
|
-
const stripeCustomer = await this.createUserCustomer({ user });
|
|
67
|
-
if (stripeCustomer.isErr()) return err(stripeCustomer.error);
|
|
68
|
-
|
|
69
|
-
if (this.repository.billing.hasTrial()) {
|
|
70
|
-
const existingSubscription = await this.repository.billing.getLatestSubscription(user.id);
|
|
71
|
-
if (existingSubscription.isErr()) return err(existingSubscription.error);
|
|
72
|
-
if (!existingSubscription.value) {
|
|
73
|
-
const subscription = await this.repository.billing.createTrialSubscription(
|
|
74
|
-
stripeCustomer.value.id
|
|
75
|
-
);
|
|
76
|
-
if (subscription.isErr()) return err(subscription.error);
|
|
77
|
-
}
|
|
78
|
-
const syncResult = await this.syncStripeData(stripeCustomer.value.id);
|
|
79
|
-
if (syncResult.isErr()) return err(syncResult.error);
|
|
80
|
-
if (syncResult.value === false)
|
|
81
|
-
return this.error("INTERNAL_SERVER_ERROR", "Sync did not create new subscription");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return ok(true);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async getActiveSubscription({ user }: { user: User }) {
|
|
88
|
-
return this.repository.billing.getActiveSubscription(user.id);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async listInvoices({ user }: { user: User }): ServerResultAsync<Stripe.Invoice[]> {
|
|
92
|
-
if (!user.stripeCustomerId) return this.error("NOT_FOUND", "User has no stripe customer id");
|
|
93
|
-
return this.repository.billing.listInvoices(user.stripeCustomerId);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async createCheckoutSession(
|
|
97
|
-
{ priceId }: { priceId: string },
|
|
98
|
-
{ user }: { user: User }
|
|
99
|
-
): ServerResultAsync<Stripe.Checkout.Session> {
|
|
100
|
-
let stripeCustomerId = user.stripeCustomerId;
|
|
101
|
-
if (!stripeCustomerId) {
|
|
102
|
-
const stripeCustomer = await this.createUserCustomer({ user });
|
|
103
|
-
if (stripeCustomer.isErr()) return err(stripeCustomer.error);
|
|
104
|
-
stripeCustomerId = stripeCustomer.value.id;
|
|
105
|
-
}
|
|
106
|
-
return this.repository.billing.createCheckoutSession({
|
|
107
|
-
customerId: stripeCustomerId,
|
|
108
|
-
priceId,
|
|
109
|
-
userId: user.id,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async createBillingPortalSession({
|
|
114
|
-
user,
|
|
115
|
-
}: {
|
|
116
|
-
user: User;
|
|
117
|
-
}): ServerResultAsync<Stripe.BillingPortal.Session> {
|
|
118
|
-
let stripeCustomerId = user.stripeCustomerId;
|
|
119
|
-
if (!stripeCustomerId) {
|
|
120
|
-
const stripeCustomer = await this.createUserCustomer({ user });
|
|
121
|
-
if (stripeCustomer.isErr()) return err(stripeCustomer.error);
|
|
122
|
-
stripeCustomerId = stripeCustomer.value.id;
|
|
123
|
-
}
|
|
124
|
-
return this.repository.billing.createBillingPortalSession(stripeCustomerId);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
constructEvent(body: Buffer | string, signature: string): ServerResult<Stripe.Event> {
|
|
128
|
-
if (!process.env.STRIPE_WEBHOOK_SECRET)
|
|
129
|
-
return this.error("INTERNAL_SERVER_ERROR", "Stripe webhook secret is not set");
|
|
130
|
-
return this.repository.billing.constructEvent(
|
|
131
|
-
body,
|
|
132
|
-
signature,
|
|
133
|
-
process.env.STRIPE_WEBHOOK_SECRET!
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async syncStripeData(customerId: string, eventType?: string): ServerResultAsync<boolean> {
|
|
138
|
-
const user = await this.repository.billing.getUserByCustomerId(customerId);
|
|
139
|
-
if (user.isErr()) return err(user.error);
|
|
140
|
-
if (!user.value) return this.error("NOT_FOUND", "User not found");
|
|
141
|
-
|
|
142
|
-
if (eventType) {
|
|
143
|
-
posthogCapture({
|
|
144
|
-
distinctId: user.value.id,
|
|
145
|
-
event: `stripe.${eventType}`,
|
|
146
|
-
properties: {
|
|
147
|
-
customerId,
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return this.repository.billing.syncStripeData({ customerId, userId: user.value.id });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async processEvent(event: Stripe.Event): ServerResultAsync<boolean> {
|
|
155
|
-
return this.throwableAsync(async () => {
|
|
156
|
-
// Skip processing if the event isn't one I'm tracking (list of all events below)
|
|
157
|
-
if (!allowedEvents.includes(event.type)) return ok(false);
|
|
158
|
-
|
|
159
|
-
// All the events I track have a customerId
|
|
160
|
-
const { customer: customerId } = event?.data?.object as {
|
|
161
|
-
customer: string; // Sadly TypeScript does not know this
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// This helps make it typesafe and also lets me know if my assumption is wrong
|
|
165
|
-
if (typeof customerId !== "string") {
|
|
166
|
-
return this.error(
|
|
167
|
-
"INTERNAL_SERVER_ERROR",
|
|
168
|
-
`[STRIPE HOOK] Unexpected event structure: customer ID is not a string. Event type: ${event.type}`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const result = await this.syncStripeData(customerId, event.type);
|
|
173
|
-
if (result.isErr()) return err(result.error);
|
|
174
|
-
return ok(true);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { billingSchema } from "@m5kdev/commons/modules/billing/billing.schema";
|
|
2
|
-
import type { BillingService } from "#modules/billing/billing.service";
|
|
3
|
-
import { handleTRPCResult, procedure, router } from "#trpc";
|
|
4
|
-
|
|
5
|
-
export function createBillingTRPC(billingService: BillingService) {
|
|
6
|
-
return router({
|
|
7
|
-
getActiveSubscription: procedure
|
|
8
|
-
.output(billingSchema.nullable())
|
|
9
|
-
.query(async ({ ctx: { user } }) => {
|
|
10
|
-
return handleTRPCResult(await billingService.getActiveSubscription({ user }));
|
|
11
|
-
}),
|
|
12
|
-
|
|
13
|
-
listInvoices: procedure.query(async ({ ctx: { user } }) => {
|
|
14
|
-
return handleTRPCResult(await billingService.listInvoices({ user }));
|
|
15
|
-
}),
|
|
16
|
-
});
|
|
17
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { ok } from "neverthrow";
|
|
2
|
-
import type { ServerResultAsync } from "#modules/base/base.dto";
|
|
3
|
-
import { BaseExternaRepository } from "#modules/base/base.repository";
|
|
4
|
-
|
|
5
|
-
const { CLAY_WEBHOOK_AUTH_TOKEN } = process.env;
|
|
6
|
-
|
|
7
|
-
export class ClayRepository extends BaseExternaRepository {
|
|
8
|
-
async sendToWebhook(
|
|
9
|
-
webhookUrl: string,
|
|
10
|
-
row: Record<string, unknown>,
|
|
11
|
-
callbackUrl: string
|
|
12
|
-
): ServerResultAsync<void> {
|
|
13
|
-
return this.throwableAsync(async () => {
|
|
14
|
-
const response = await fetch(webhookUrl, {
|
|
15
|
-
method: "POST",
|
|
16
|
-
headers: {
|
|
17
|
-
"Content-Type": "application/json",
|
|
18
|
-
...(CLAY_WEBHOOK_AUTH_TOKEN ? { "x-clay-webhook-auth": CLAY_WEBHOOK_AUTH_TOKEN } : {}),
|
|
19
|
-
},
|
|
20
|
-
body: JSON.stringify({ ...row, callback: callbackUrl }),
|
|
21
|
-
});
|
|
22
|
-
if (!response.ok)
|
|
23
|
-
return this.error("BAD_REQUEST", `HTTP error! status: ${response.status}`, {
|
|
24
|
-
cause: response,
|
|
25
|
-
});
|
|
26
|
-
return ok();
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { z } from "zod";
|
|
2
|
-
import type { ServerResultAsync } from "#modules/base/base.dto";
|
|
3
|
-
import { BaseService } from "#modules/base/base.service";
|
|
4
|
-
import type { WebhookService } from "#modules/webhook/webhook.service";
|
|
5
|
-
import type { ClayRepository } from "./clay.repository";
|
|
6
|
-
|
|
7
|
-
type ClayTable = {
|
|
8
|
-
name?: string;
|
|
9
|
-
tableId?: string;
|
|
10
|
-
viewId?: string;
|
|
11
|
-
webhookUrl: string;
|
|
12
|
-
schema?: z.ZodAny;
|
|
13
|
-
timeoutInSeconds?: number;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export class ClayService<K extends string> extends BaseService<
|
|
17
|
-
{ clay: ClayRepository },
|
|
18
|
-
{ webhook: WebhookService }
|
|
19
|
-
> {
|
|
20
|
-
private tables: Record<K, ClayTable>;
|
|
21
|
-
constructor(
|
|
22
|
-
repositories: { clay: ClayRepository },
|
|
23
|
-
services: { webhook: WebhookService },
|
|
24
|
-
tables: Record<K, ClayTable>
|
|
25
|
-
) {
|
|
26
|
-
super(repositories, services);
|
|
27
|
-
this.tables = tables;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async waitForResponse<T>(
|
|
31
|
-
webhookUrl: string,
|
|
32
|
-
row: Record<string, unknown>,
|
|
33
|
-
timeoutInSeconds?: number
|
|
34
|
-
): ServerResultAsync<T> {
|
|
35
|
-
return await this.service.webhook.waitForRequest<T>((url) => {
|
|
36
|
-
return this.repository.clay.sendToWebhook(webhookUrl, row, url);
|
|
37
|
-
}, timeoutInSeconds);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async sendToTable(
|
|
41
|
-
table: K,
|
|
42
|
-
row: Record<string, unknown>,
|
|
43
|
-
timeoutInSeconds?: number
|
|
44
|
-
): ServerResultAsync<
|
|
45
|
-
z.infer<
|
|
46
|
-
(typeof this.tables)[K]["schema"] extends z.ZodAny
|
|
47
|
-
? z.infer<(typeof this.tables)[K]["schema"]>
|
|
48
|
-
: unknown
|
|
49
|
-
>
|
|
50
|
-
> {
|
|
51
|
-
const tableData = this.tables[table];
|
|
52
|
-
if (!tableData) return this.error("NOT_FOUND", `Table ${table} not found`);
|
|
53
|
-
const response = await this.waitForResponse<z.infer<typeof tableData.schema>>(
|
|
54
|
-
tableData.webhookUrl,
|
|
55
|
-
row,
|
|
56
|
-
tableData.timeoutInSeconds || timeoutInSeconds
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
return response;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
2
|
-
import { v4 as uuidv4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
export const connect = sqliteTable("connect", {
|
|
5
|
-
id: text("id").primaryKey().$default(uuidv4),
|
|
6
|
-
userId: text("user_id").notNull(), // FK -> users.id
|
|
7
|
-
|
|
8
|
-
provider: text("provider").notNull(), // e.g. "linkedin"
|
|
9
|
-
accountType: text("account_type").notNull(), // "user" | "page" | "org" | "channel"
|
|
10
|
-
providerAccountId: text("provider_account_id").notNull(), // e.g. LinkedIn URN, FB Page ID, IG business acct ID, X user ID
|
|
11
|
-
handle: text("handle"), // @name or page slug
|
|
12
|
-
displayName: text("display_name"),
|
|
13
|
-
avatarUrl: text("avatar_url"),
|
|
14
|
-
|
|
15
|
-
// OAuth credentials (ENCRYPTED)
|
|
16
|
-
accessToken: text("access_token").notNull(),
|
|
17
|
-
refreshToken: text("refresh_token"), // may be null if provider doesn’t issue refresh tokens
|
|
18
|
-
tokenType: text("token_type"), // e.g. "bearer"
|
|
19
|
-
scope: text("scope"), // space- or comma-separated list, for auditing
|
|
20
|
-
expiresAt: integer("expires_at", { mode: "timestamp" }), // epoch seconds
|
|
21
|
-
|
|
22
|
-
// Provider-specific glue
|
|
23
|
-
parentId: text("parent_id"), // e.g. FB Page’s connected IG business account, or org URN
|
|
24
|
-
metadataJson: text("metadata_json", { mode: "json" }), // JSON string for extras (region, perms, etc.)
|
|
25
|
-
|
|
26
|
-
revokedAt: integer("revoked_at", { mode: "timestamp" }),
|
|
27
|
-
lastRefreshedAt: integer("last_refreshed_at", { mode: "timestamp" }),
|
|
28
|
-
createdAt: integer("created_at", { mode: "timestamp" })
|
|
29
|
-
.notNull()
|
|
30
|
-
.$default(() => new Date()),
|
|
31
|
-
updatedAt: integer("updated_at", { mode: "timestamp" }),
|
|
32
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export const connectSelectSchema = z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
userId: z.string(),
|
|
6
|
-
provider: z.string(),
|
|
7
|
-
accountType: z.string(),
|
|
8
|
-
providerAccountId: z.string(),
|
|
9
|
-
handle: z.string().nullish(),
|
|
10
|
-
displayName: z.string().nullish(),
|
|
11
|
-
avatarUrl: z.string().nullish(),
|
|
12
|
-
accessToken: z.string(),
|
|
13
|
-
refreshToken: z.string().nullish(),
|
|
14
|
-
tokenType: z.string().nullish(),
|
|
15
|
-
scope: z.string().nullish(),
|
|
16
|
-
expiresAt: z.date().nullish(),
|
|
17
|
-
parentId: z.string().nullish(),
|
|
18
|
-
metadataJson: z.unknown().nullish(),
|
|
19
|
-
revokedAt: z.date().nullish(),
|
|
20
|
-
lastRefreshedAt: z.date().nullish(),
|
|
21
|
-
createdAt: z.date(),
|
|
22
|
-
updatedAt: z.date().nullish(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export type ConnectSelectSchema = z.infer<typeof connectSelectSchema>;
|
|
26
|
-
|
|
27
|
-
export const connectSelectOutputSchema = connectSelectSchema.omit({
|
|
28
|
-
accessToken: true,
|
|
29
|
-
refreshToken: true,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
export const connectListInputSchema = z.object({
|
|
33
|
-
providers: z.array(z.string()).optional(),
|
|
34
|
-
inactive: z.boolean().optional(),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export type ConnectListInputSchema = z.infer<typeof connectListInputSchema>;
|
|
38
|
-
|
|
39
|
-
export const connectListOutputSchema = z.array(connectSelectOutputSchema);
|
|
40
|
-
|
|
41
|
-
export const connectDeleteInputSchema = z.object({ id: z.string() });
|
|
42
|
-
export const connectDeleteOutputSchema = z.object({ id: z.string() });
|
|
43
|
-
export type ConnectDeleteInputSchema = z.infer<typeof connectDeleteInputSchema>;
|
|
44
|
-
export type ConnectDeleteOutputSchema = z.infer<typeof connectDeleteOutputSchema>;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { ConnectProfile, ConnectProvider } from "./connect.types";
|
|
2
|
-
|
|
3
|
-
interface LinkedInUserInfoResponse {
|
|
4
|
-
sub: string; // Subject identifier (user ID)
|
|
5
|
-
name?: string; // Full name
|
|
6
|
-
given_name?: string; // First name
|
|
7
|
-
family_name?: string; // Last name
|
|
8
|
-
picture?: string; // Profile picture URL
|
|
9
|
-
locale?: string;
|
|
10
|
-
email?: string;
|
|
11
|
-
email_verified?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function createLinkedInProvider(): ConnectProvider {
|
|
15
|
-
const clientId = process.env.LINKEDIN_CLIENT_ID;
|
|
16
|
-
const clientSecret = process.env.LINKEDIN_CLIENT_SECRET;
|
|
17
|
-
const baseUrl = process.env.VITE_SERVER_URL;
|
|
18
|
-
|
|
19
|
-
if (!clientId || !clientSecret || !baseUrl) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
"Missing required LinkedIn OAuth environment variables: LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET, VITE_SERVER_URL"
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
id: "linkedin",
|
|
27
|
-
clientId,
|
|
28
|
-
clientSecret,
|
|
29
|
-
redirectUri: `${baseUrl}/connect/linkedin/callback`,
|
|
30
|
-
// LinkedIn OpenID Connect scopes
|
|
31
|
-
scopes: ["openid", "profile", "w_member_social"],
|
|
32
|
-
// LinkedIn doesn't support PKCE - disable it
|
|
33
|
-
supportsPKCE: false,
|
|
34
|
-
// LinkedIn OpenID Connect endpoints
|
|
35
|
-
issuerConfig: {
|
|
36
|
-
issuer: "https://www.linkedin.com",
|
|
37
|
-
authorization_endpoint: "https://www.linkedin.com/oauth/v2/authorization",
|
|
38
|
-
token_endpoint: "https://www.linkedin.com/oauth/v2/accessToken",
|
|
39
|
-
userinfo_endpoint: "https://api.linkedin.com/v2/userinfo",
|
|
40
|
-
},
|
|
41
|
-
async mapProfile(accessToken: string): Promise<ConnectProfile> {
|
|
42
|
-
// Use OpenID Connect userinfo endpoint
|
|
43
|
-
const response = await fetch("https://api.linkedin.com/v2/userinfo", {
|
|
44
|
-
headers: {
|
|
45
|
-
Authorization: `Bearer ${accessToken}`,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
if (!response.ok) {
|
|
50
|
-
throw new Error(`LinkedIn API error: ${response.status} ${response.statusText}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const data = (await response.json()) as LinkedInUserInfoResponse;
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
providerAccountId: data.sub,
|
|
57
|
-
displayName: data.name,
|
|
58
|
-
avatarUrl: data.picture,
|
|
59
|
-
accountType: "user",
|
|
60
|
-
metadata: {
|
|
61
|
-
givenName: data.given_name,
|
|
62
|
-
familyName: data.family_name,
|
|
63
|
-
locale: data.locale,
|
|
64
|
-
email: data.email,
|
|
65
|
-
emailVerified: data.email_verified,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
}
|