@m5kdev/backend 0.1.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.
Files changed (113) hide show
  1. package/LICENSE +621 -0
  2. package/README.md +22 -0
  3. package/package.json +205 -0
  4. package/src/lib/posthog.ts +5 -0
  5. package/src/lib/sentry.ts +8 -0
  6. package/src/modules/access/access.repository.ts +36 -0
  7. package/src/modules/access/access.service.ts +81 -0
  8. package/src/modules/access/access.test.ts +216 -0
  9. package/src/modules/access/access.utils.ts +46 -0
  10. package/src/modules/ai/ai.db.ts +38 -0
  11. package/src/modules/ai/ai.prompt.ts +47 -0
  12. package/src/modules/ai/ai.repository.ts +53 -0
  13. package/src/modules/ai/ai.router.ts +148 -0
  14. package/src/modules/ai/ai.service.ts +310 -0
  15. package/src/modules/ai/ai.trpc.ts +22 -0
  16. package/src/modules/ai/ideogram/ideogram.constants.ts +170 -0
  17. package/src/modules/ai/ideogram/ideogram.dto.ts +64 -0
  18. package/src/modules/ai/ideogram/ideogram.prompt.ts +858 -0
  19. package/src/modules/ai/ideogram/ideogram.repository.ts +39 -0
  20. package/src/modules/ai/ideogram/ideogram.service.ts +14 -0
  21. package/src/modules/auth/auth.db.ts +224 -0
  22. package/src/modules/auth/auth.dto.ts +47 -0
  23. package/src/modules/auth/auth.lib.ts +349 -0
  24. package/src/modules/auth/auth.middleware.ts +62 -0
  25. package/src/modules/auth/auth.repository.ts +672 -0
  26. package/src/modules/auth/auth.service.ts +261 -0
  27. package/src/modules/auth/auth.trpc.ts +208 -0
  28. package/src/modules/auth/auth.utils.ts +117 -0
  29. package/src/modules/base/base.abstract.ts +62 -0
  30. package/src/modules/base/base.dto.ts +206 -0
  31. package/src/modules/base/base.grants.test.ts +861 -0
  32. package/src/modules/base/base.grants.ts +199 -0
  33. package/src/modules/base/base.repository.ts +433 -0
  34. package/src/modules/base/base.service.ts +154 -0
  35. package/src/modules/base/base.types.ts +7 -0
  36. package/src/modules/billing/billing.db.ts +27 -0
  37. package/src/modules/billing/billing.repository.ts +328 -0
  38. package/src/modules/billing/billing.router.ts +77 -0
  39. package/src/modules/billing/billing.service.ts +177 -0
  40. package/src/modules/billing/billing.trpc.ts +17 -0
  41. package/src/modules/clay/clay.repository.ts +29 -0
  42. package/src/modules/clay/clay.service.ts +61 -0
  43. package/src/modules/connect/connect.db.ts +32 -0
  44. package/src/modules/connect/connect.dto.ts +44 -0
  45. package/src/modules/connect/connect.linkedin.ts +70 -0
  46. package/src/modules/connect/connect.oauth.ts +288 -0
  47. package/src/modules/connect/connect.repository.ts +65 -0
  48. package/src/modules/connect/connect.router.ts +76 -0
  49. package/src/modules/connect/connect.service.ts +171 -0
  50. package/src/modules/connect/connect.trpc.ts +26 -0
  51. package/src/modules/connect/connect.types.ts +27 -0
  52. package/src/modules/crypto/crypto.db.ts +15 -0
  53. package/src/modules/crypto/crypto.repository.ts +13 -0
  54. package/src/modules/crypto/crypto.service.ts +57 -0
  55. package/src/modules/email/email.service.ts +222 -0
  56. package/src/modules/file/file.repository.ts +95 -0
  57. package/src/modules/file/file.router.ts +108 -0
  58. package/src/modules/file/file.service.ts +186 -0
  59. package/src/modules/recurrence/recurrence.db.ts +79 -0
  60. package/src/modules/recurrence/recurrence.repository.ts +70 -0
  61. package/src/modules/recurrence/recurrence.service.ts +105 -0
  62. package/src/modules/recurrence/recurrence.trpc.ts +82 -0
  63. package/src/modules/social/social.dto.ts +22 -0
  64. package/src/modules/social/social.linkedin.test.ts +277 -0
  65. package/src/modules/social/social.linkedin.ts +593 -0
  66. package/src/modules/social/social.service.ts +112 -0
  67. package/src/modules/social/social.types.ts +43 -0
  68. package/src/modules/tag/tag.db.ts +41 -0
  69. package/src/modules/tag/tag.dto.ts +18 -0
  70. package/src/modules/tag/tag.repository.ts +222 -0
  71. package/src/modules/tag/tag.service.ts +48 -0
  72. package/src/modules/tag/tag.trpc.ts +62 -0
  73. package/src/modules/uploads/0581796b-8845-420d-bd95-cd7de79f6d37.webm +0 -0
  74. package/src/modules/uploads/33b1e649-6727-4bd0-94d0-a0b363646865.webm +0 -0
  75. package/src/modules/uploads/49a8c4c0-54d7-4c94-bef4-c93c029f9ed0.webm +0 -0
  76. package/src/modules/uploads/50e31e38-a2f0-47ca-8b7d-2d7fcad9267d.webm +0 -0
  77. package/src/modules/uploads/72ac8cf9-c3a7-4cd8-8a78-6d8e137a4c7e.webm +0 -0
  78. package/src/modules/uploads/75293649-d966-46cd-a675-67518958ae9c.png +0 -0
  79. package/src/modules/uploads/88b7b867-ce15-4891-bf73-81305a7de1f7.wav +0 -0
  80. package/src/modules/uploads/a5d6fee8-6a59-42c6-9d4a-ac8a3c5e7245.webm +0 -0
  81. package/src/modules/uploads/c13a9785-ca5a-4983-af30-b338ed76d370.webm +0 -0
  82. package/src/modules/uploads/caa1a5a7-71ba-4381-902d-7e2cafdf6dcb.webm +0 -0
  83. package/src/modules/uploads/cbeb0b81-374d-445b-914b-40ace7c8e031.webm +0 -0
  84. package/src/modules/uploads/d626aa82-b10f-493f-aee7-87bfb3361dfc.webm +0 -0
  85. package/src/modules/uploads/d7de4c16-de0c-495d-9612-e72260a6ecca.png +0 -0
  86. package/src/modules/uploads/e532e38a-6421-400e-8a5f-8e7bc8ce411b.wav +0 -0
  87. package/src/modules/uploads/e86ec867-6adf-4c51-84e0-00b0836625e8.webm +0 -0
  88. package/src/modules/utils/applyPagination.ts +13 -0
  89. package/src/modules/utils/applySorting.ts +21 -0
  90. package/src/modules/utils/getConditionsFromFilters.ts +216 -0
  91. package/src/modules/video/video.service.ts +89 -0
  92. package/src/modules/webhook/webhook.constants.ts +9 -0
  93. package/src/modules/webhook/webhook.db.ts +15 -0
  94. package/src/modules/webhook/webhook.dto.ts +9 -0
  95. package/src/modules/webhook/webhook.repository.ts +68 -0
  96. package/src/modules/webhook/webhook.router.ts +29 -0
  97. package/src/modules/webhook/webhook.service.ts +78 -0
  98. package/src/modules/workflow/workflow.db.ts +29 -0
  99. package/src/modules/workflow/workflow.repository.ts +171 -0
  100. package/src/modules/workflow/workflow.service.ts +56 -0
  101. package/src/modules/workflow/workflow.trpc.ts +26 -0
  102. package/src/modules/workflow/workflow.types.ts +30 -0
  103. package/src/modules/workflow/workflow.utils.ts +259 -0
  104. package/src/test/stubs/utils.ts +2 -0
  105. package/src/trpc/context.ts +21 -0
  106. package/src/trpc/index.ts +3 -0
  107. package/src/trpc/procedures.ts +43 -0
  108. package/src/trpc/utils.ts +20 -0
  109. package/src/types.ts +22 -0
  110. package/src/utils/errors.ts +148 -0
  111. package/src/utils/logger.ts +8 -0
  112. package/src/utils/posthog.ts +43 -0
  113. package/src/utils/types.ts +5 -0
@@ -0,0 +1,148 @@
1
+ import type { captureException } from "@sentry/node";
2
+ import { type TRPC_ERROR_CODE_KEY, TRPCError } from "@trpc/server";
3
+ import { getHTTPStatusCodeFromError } from "@trpc/server/http";
4
+ import { camel } from "radashi";
5
+ import type { ServerErrorLayer } from "#modules/base/base.types";
6
+ import { logger } from "./logger";
7
+
8
+ export type { ServerErrorLayer };
9
+ export class ServerError extends Error {
10
+ readonly code: TRPC_ERROR_CODE_KEY;
11
+ readonly layer: ServerErrorLayer;
12
+ readonly layerName: string;
13
+ readonly clientMessage?: string;
14
+ readonly context?: Record<string, unknown>;
15
+ readonly boundaryStack?: string; // where we wrapped it
16
+
17
+ constructor({
18
+ code,
19
+ layer,
20
+ layerName,
21
+ message,
22
+ clientMessage,
23
+ cause,
24
+ context,
25
+ captureBoundary = true,
26
+ }: {
27
+ code: TRPC_ERROR_CODE_KEY;
28
+ layer?: ServerErrorLayer;
29
+ layerName?: string;
30
+ message?: string;
31
+ clientMessage?: string;
32
+ cause?: unknown;
33
+ context?: Record<string, unknown>;
34
+ captureBoundary?: boolean;
35
+ }) {
36
+ // keep native cause chain when the cause is an Error
37
+ super(message ?? `server.error.${layer}.${camel(code)}`, {
38
+ cause: cause instanceof Error ? cause : undefined,
39
+ });
40
+
41
+ this.code = code;
42
+ this.layer = layer ?? "unknown";
43
+ this.layerName = layerName ?? "UnknownLayer";
44
+ this.clientMessage = clientMessage ?? `server.error.${layer}.${camel(code)}`;
45
+ this.context = context;
46
+ if (captureBoundary) this.boundaryStack = new Error().stack;
47
+
48
+ Error.captureStackTrace?.(this, ServerError);
49
+ Object.setPrototypeOf(this, new.target.prototype);
50
+ }
51
+
52
+ is5xxError(): boolean {
53
+ const statusCode = this.getHTTPStatusCode();
54
+ return statusCode >= 500 && statusCode < 600;
55
+ }
56
+
57
+ getHTTPStatusCode(): number {
58
+ return getHTTPStatusCodeFromError(this.toTRPC());
59
+ }
60
+
61
+ toTRPC(): TRPCError {
62
+ return new TRPCError({
63
+ code: this.code,
64
+ message: this.message,
65
+ cause: this.cause,
66
+ });
67
+ }
68
+
69
+ toJSON() {
70
+ return {
71
+ code: this.code,
72
+ layer: this.layer,
73
+ layerName: this.layerName,
74
+ message: this.message,
75
+ context: this.context,
76
+ stack: process.env.NODE_ENV !== "production" ? this.stack : undefined,
77
+ boundaryStack: process.env.NODE_ENV !== "production" ? this.boundaryStack : undefined,
78
+ // Shallow representation of cause to avoid cycles
79
+ cause:
80
+ this.cause instanceof Error
81
+ ? { name: this.cause.name, message: this.cause.message, stack: this.cause.stack }
82
+ : this.cause,
83
+ };
84
+ }
85
+
86
+ static fromUnknown(
87
+ code: TRPC_ERROR_CODE_KEY,
88
+ cause: unknown,
89
+ opts?: { layer?: ServerErrorLayer; layerName?: string; context?: Record<string, unknown> }
90
+ ) {
91
+ const msg = cause instanceof Error ? cause.message : undefined;
92
+ return new ServerError({
93
+ code,
94
+ layer: opts?.layer,
95
+ layerName: opts?.layerName,
96
+ message: msg,
97
+ cause,
98
+ context: opts?.context,
99
+ captureBoundary: true,
100
+ });
101
+ }
102
+ }
103
+
104
+ export type ErrorReporter = {
105
+ captureException: (
106
+ err: Parameters<typeof captureException>[0],
107
+ hint?: Parameters<typeof captureException>[1]
108
+ ) => string;
109
+ };
110
+ declare global {
111
+ // eslint-disable-next-line no-var
112
+ var m5ErrorReporter: ErrorReporter | undefined;
113
+ }
114
+
115
+ export function getErrorReporter(): ErrorReporter | undefined {
116
+ return globalThis.m5ErrorReporter;
117
+ }
118
+
119
+ export function setErrorReporter(reporter: ErrorReporter) {
120
+ globalThis.m5ErrorReporter = reporter;
121
+ }
122
+
123
+ export function reportError(
124
+ err: ServerError | Error | unknown,
125
+ hint?: Parameters<typeof captureException>[1]
126
+ ): string | undefined {
127
+ let eventHint = hint;
128
+ const reporter = getErrorReporter();
129
+ if (!reporter) {
130
+ logger.error("[reportError] No error reporter set!");
131
+ return;
132
+ }
133
+ if (err instanceof ServerError) {
134
+ // Merge - don't clobber caller-provided hint
135
+ eventHint = {
136
+ ...hint,
137
+ captureContext: {
138
+ ...(hint as any)?.captureContext,
139
+ extra: {
140
+ ...((hint as any)?.captureContext?.extra ?? {}),
141
+ layer: err.layer,
142
+ layerName: err.layerName,
143
+ },
144
+ },
145
+ };
146
+ }
147
+ return reporter.captureException(err, eventHint);
148
+ }
@@ -0,0 +1,8 @@
1
+ import pino from "pino";
2
+
3
+ export const logger = pino({
4
+ transport: {
5
+ target: "pino-pretty",
6
+ },
7
+ level: "debug",
8
+ });
@@ -0,0 +1,43 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import type { PostHog } from "posthog-node";
3
+
4
+ declare global {
5
+ // eslint-disable-next-line no-var
6
+ var m5Posthog: PostHog | undefined;
7
+ }
8
+
9
+ export function getPosthog(): PostHog | undefined {
10
+ return globalThis.m5Posthog;
11
+ }
12
+
13
+ export function setPosthog(posthog: PostHog) {
14
+ globalThis.m5Posthog = posthog;
15
+ }
16
+
17
+ type PosthogRequestState = {
18
+ disableCapture: boolean;
19
+ };
20
+
21
+ const posthogRequestState = new AsyncLocalStorage<PosthogRequestState>();
22
+
23
+ export function runWithPosthogRequestState<T>(state: PosthogRequestState, callback: () => T): T {
24
+ return posthogRequestState.run(state, callback);
25
+ }
26
+
27
+ export function isPosthogCaptureDisabled(): boolean {
28
+ return posthogRequestState.getStore()?.disableCapture ?? false;
29
+ }
30
+
31
+ export function posthogCapture(
32
+ event: Parameters<PostHog["capture"]>[0]
33
+ ): ReturnType<PostHog["capture"]> | undefined {
34
+ if (isPosthogCaptureDisabled()) {
35
+ return undefined;
36
+ }
37
+
38
+ const posthog = getPosthog();
39
+ if (!posthog) {
40
+ return undefined;
41
+ }
42
+ return posthog.capture(event);
43
+ }
@@ -0,0 +1,5 @@
1
+ import type { Result } from "neverthrow";
2
+ import type { ServerError } from "./errors";
3
+
4
+ export type ServerResult<T> = Result<T, ServerError>;
5
+ export type ServerResultAsync<T> = Promise<ServerResult<T>>;