@m5kdev/backend 0.1.1 → 0.1.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/src/lib/posthog.js +7 -0
- package/dist/src/lib/sentry.js +9 -0
- package/dist/src/modules/access/access.repository.js +32 -0
- package/dist/src/modules/access/access.service.js +51 -0
- package/dist/src/modules/access/access.test.js +182 -0
- package/dist/src/modules/access/access.utils.js +20 -0
- package/dist/src/modules/ai/ai.db.js +39 -0
- package/dist/src/modules/ai/ai.prompt.js +30 -0
- package/dist/src/modules/ai/ai.repository.js +26 -0
- package/dist/src/modules/ai/ai.router.js +132 -0
- package/dist/src/modules/ai/ai.service.js +207 -0
- package/dist/src/modules/ai/ai.trpc.d.ts +5 -5
- package/dist/src/modules/ai/ai.trpc.js +20 -0
- package/dist/src/modules/ai/ideogram/ideogram.constants.js +167 -0
- package/dist/src/modules/ai/ideogram/ideogram.dto.js +49 -0
- package/dist/src/modules/ai/ideogram/ideogram.prompt.js +860 -0
- package/dist/src/modules/ai/ideogram/ideogram.repository.js +46 -0
- package/dist/src/modules/ai/ideogram/ideogram.service.js +11 -0
- package/dist/src/modules/auth/auth.db.js +215 -0
- package/dist/src/modules/auth/auth.dto.js +38 -0
- package/dist/src/modules/auth/auth.lib.d.ts +4 -4
- package/dist/src/modules/auth/auth.lib.js +284 -0
- package/dist/src/modules/auth/auth.middleware.js +52 -0
- package/dist/src/modules/auth/auth.repository.js +541 -0
- package/dist/src/modules/auth/auth.service.js +201 -0
- package/dist/src/modules/auth/auth.trpc.d.ts +18 -18
- package/dist/src/modules/auth/auth.trpc.js +157 -0
- package/dist/src/modules/auth/auth.utils.js +97 -0
- package/dist/src/modules/base/base.abstract.js +53 -0
- package/dist/src/modules/base/base.dto.js +112 -0
- package/dist/src/modules/base/base.grants.js +123 -0
- package/dist/src/modules/base/base.grants.test.js +668 -0
- package/dist/src/modules/base/base.repository.js +307 -0
- package/dist/src/modules/base/base.service.js +109 -0
- package/dist/src/modules/base/base.types.js +2 -0
- package/dist/src/modules/billing/billing.db.js +29 -0
- package/dist/src/modules/billing/billing.repository.js +235 -0
- package/dist/src/modules/billing/billing.router.js +56 -0
- package/dist/src/modules/billing/billing.service.js +147 -0
- package/dist/src/modules/billing/billing.trpc.d.ts +5 -5
- package/dist/src/modules/billing/billing.trpc.js +17 -0
- package/dist/src/modules/clay/clay.repository.js +26 -0
- package/dist/src/modules/clay/clay.service.js +24 -0
- package/dist/src/modules/connect/connect.db.js +30 -0
- package/dist/src/modules/connect/connect.dto.js +36 -0
- package/dist/src/modules/connect/connect.linkedin.js +53 -0
- package/dist/src/modules/connect/connect.oauth.js +198 -0
- package/dist/src/modules/connect/connect.repository.d.ts +7 -7
- package/dist/src/modules/connect/connect.repository.js +54 -0
- package/dist/src/modules/connect/connect.router.js +54 -0
- package/dist/src/modules/connect/connect.service.d.ts +14 -14
- package/dist/src/modules/connect/connect.service.js +114 -0
- package/dist/src/modules/connect/connect.trpc.d.ts +10 -10
- package/dist/src/modules/connect/connect.trpc.js +21 -0
- package/dist/src/modules/connect/connect.types.js +2 -0
- package/dist/src/modules/crypto/crypto.db.js +17 -0
- package/dist/src/modules/crypto/crypto.repository.js +10 -0
- package/dist/src/modules/crypto/crypto.service.js +52 -0
- package/dist/src/modules/email/email.service.js +107 -0
- package/dist/src/modules/file/file.repository.js +79 -0
- package/dist/src/modules/file/file.router.js +99 -0
- package/dist/src/modules/file/file.service.js +150 -0
- package/dist/src/modules/recurrence/recurrence.db.js +66 -0
- package/dist/src/modules/recurrence/recurrence.repository.js +39 -0
- package/dist/src/modules/recurrence/recurrence.service.js +70 -0
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +15 -15
- package/dist/src/modules/recurrence/recurrence.trpc.js +65 -0
- package/dist/src/modules/social/social.dto.js +18 -0
- package/dist/src/modules/social/social.linkedin.js +427 -0
- package/dist/src/modules/social/social.linkedin.test.js +235 -0
- package/dist/src/modules/social/social.service.js +76 -0
- package/dist/src/modules/social/social.types.js +2 -0
- package/dist/src/modules/tag/tag.db.js +42 -0
- package/dist/src/modules/tag/tag.dto.js +9 -0
- package/dist/src/modules/tag/tag.repository.js +154 -0
- package/dist/src/modules/tag/tag.service.js +31 -0
- package/dist/src/modules/tag/tag.trpc.d.ts +5 -5
- package/dist/src/modules/tag/tag.trpc.js +47 -0
- package/dist/src/modules/utils/applyPagination.js +16 -0
- package/dist/src/modules/utils/applySorting.js +18 -0
- package/dist/src/modules/utils/getConditionsFromFilters.js +200 -0
- package/dist/src/modules/video/video.service.js +84 -0
- package/dist/src/modules/webhook/webhook.constants.js +10 -0
- package/dist/src/modules/webhook/webhook.db.js +17 -0
- package/dist/src/modules/webhook/webhook.dto.js +7 -0
- package/dist/src/modules/webhook/webhook.repository.js +56 -0
- package/dist/src/modules/webhook/webhook.router.js +30 -0
- package/dist/src/modules/webhook/webhook.service.js +68 -0
- package/dist/src/modules/workflow/workflow.db.js +30 -0
- package/dist/src/modules/workflow/workflow.repository.js +105 -0
- package/dist/src/modules/workflow/workflow.service.js +37 -0
- package/dist/src/modules/workflow/workflow.trpc.d.ts +5 -5
- package/dist/src/modules/workflow/workflow.trpc.js +21 -0
- package/dist/src/modules/workflow/workflow.types.js +2 -0
- package/dist/src/modules/workflow/workflow.utils.js +173 -0
- package/dist/src/test/stubs/utils.js +5 -0
- package/dist/src/trpc/context.d.ts +5 -5
- package/dist/src/trpc/context.js +17 -0
- package/dist/src/trpc/index.js +6 -0
- package/dist/src/trpc/procedures.d.ts +56 -56
- package/dist/src/trpc/procedures.js +32 -0
- package/dist/src/trpc/utils.js +20 -0
- package/dist/src/types.d.ts +33 -33
- package/dist/src/types.js +13 -0
- package/dist/src/utils/errors.js +104 -0
- package/dist/src/utils/logger.js +11 -0
- package/dist/src/utils/posthog.js +31 -0
- package/dist/src/utils/types.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/tsconfig.json +2 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectRepository = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
|
+
const neverthrow_1 = require("neverthrow");
|
|
7
|
+
const base_repository_1 = require("#modules/base/base.repository");
|
|
8
|
+
const connect = tslib_1.__importStar(require("#modules/connect/connect.db"));
|
|
9
|
+
const schema = { ...connect };
|
|
10
|
+
class ConnectRepository extends base_repository_1.BaseTableRepository {
|
|
11
|
+
async list(data, tx) {
|
|
12
|
+
return this.throwableAsync(async () => {
|
|
13
|
+
const db = tx ?? this.orm;
|
|
14
|
+
const { ConditionBuilder } = this.helpers;
|
|
15
|
+
const conditions = new ConditionBuilder();
|
|
16
|
+
if (data.providers)
|
|
17
|
+
conditions.push((0, drizzle_orm_1.inArray)(this.schema.connect.provider, data.providers));
|
|
18
|
+
if (data.inactive)
|
|
19
|
+
conditions.push((0, drizzle_orm_1.isNull)(this.schema.connect.revokedAt));
|
|
20
|
+
conditions.push((0, drizzle_orm_1.eq)(this.schema.connect.userId, data.userId));
|
|
21
|
+
const rows = await db.select().from(this.schema.connect).where(conditions.join());
|
|
22
|
+
return (0, neverthrow_1.ok)(rows);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async upsert(data, tx) {
|
|
26
|
+
return this.throwableAsync(async () => {
|
|
27
|
+
const db = tx ?? this.orm;
|
|
28
|
+
const [existing] = await db
|
|
29
|
+
.select()
|
|
30
|
+
.from(this.schema.connect)
|
|
31
|
+
.where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(this.schema.connect.userId, data.userId), (0, drizzle_orm_1.eq)(this.schema.connect.provider, data.provider), (0, drizzle_orm_1.eq)(this.schema.connect.providerAccountId, data.providerAccountId)))
|
|
32
|
+
.limit(1);
|
|
33
|
+
if (existing) {
|
|
34
|
+
// Update existing
|
|
35
|
+
const [updated] = await db
|
|
36
|
+
.update(this.schema.connect)
|
|
37
|
+
.set({
|
|
38
|
+
...data,
|
|
39
|
+
updatedAt: new Date(),
|
|
40
|
+
lastRefreshedAt: new Date(),
|
|
41
|
+
})
|
|
42
|
+
.where((0, drizzle_orm_1.eq)(this.schema.connect.id, existing.id))
|
|
43
|
+
.returning();
|
|
44
|
+
return (0, neverthrow_1.ok)(updated);
|
|
45
|
+
}
|
|
46
|
+
// Create new
|
|
47
|
+
const [created] = await db.insert(this.schema.connect).values(data).returning();
|
|
48
|
+
if (!created)
|
|
49
|
+
return this.error("UNPROCESSABLE_CONTENT");
|
|
50
|
+
return (0, neverthrow_1.ok)(created);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.ConnectRepository = ConnectRepository;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConnectRouter = createConnectRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
function createConnectRouter(authMiddleware, connectService) {
|
|
6
|
+
const connectRouter = (0, express_1.Router)();
|
|
7
|
+
connectRouter.get("/:provider/start", authMiddleware, async (req, res) => {
|
|
8
|
+
const user = req.user;
|
|
9
|
+
const session = req.session;
|
|
10
|
+
if (!user || !session) {
|
|
11
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
12
|
+
}
|
|
13
|
+
const { provider } = req.params;
|
|
14
|
+
const { redirect } = req.query;
|
|
15
|
+
const result = await connectService.startAuth(user, session.id, provider);
|
|
16
|
+
if (result.isErr()) {
|
|
17
|
+
const errorUrl = redirect
|
|
18
|
+
? `${redirect}?error=${encodeURIComponent(result.error.message)}`
|
|
19
|
+
: `${process.env.VITE_APP_URL || process.env.VITE_SERVER_URL || "/"}?connect_error=${encodeURIComponent(result.error.message)}`;
|
|
20
|
+
return res.redirect(errorUrl);
|
|
21
|
+
}
|
|
22
|
+
return res.redirect(result.value.url);
|
|
23
|
+
});
|
|
24
|
+
connectRouter.get("/:provider/callback", authMiddleware, async (req, res) => {
|
|
25
|
+
const user = req.user;
|
|
26
|
+
const session = req.session;
|
|
27
|
+
if (!user || !session) {
|
|
28
|
+
return res.status(401).json({ message: "Unauthorized" });
|
|
29
|
+
}
|
|
30
|
+
const { provider } = req.params;
|
|
31
|
+
const { code, state, error, error_description } = req.query;
|
|
32
|
+
const { redirect } = req.query;
|
|
33
|
+
const successUrl = redirect && typeof redirect === "string"
|
|
34
|
+
? redirect
|
|
35
|
+
: `${process.env.VITE_APP_URL || process.env.VITE_SERVER_URL || "/"}?connect_success=true`;
|
|
36
|
+
// Handle OAuth errors from provider
|
|
37
|
+
if (error) {
|
|
38
|
+
const errorMessage = error_description || error || "OAuth error";
|
|
39
|
+
const errorUrl = `${successUrl}&error=${encodeURIComponent(String(errorMessage))}`;
|
|
40
|
+
return res.redirect(errorUrl);
|
|
41
|
+
}
|
|
42
|
+
if (!code || !state) {
|
|
43
|
+
const errorUrl = `${successUrl}&error=${encodeURIComponent("Missing code or state")}`;
|
|
44
|
+
return res.redirect(errorUrl);
|
|
45
|
+
}
|
|
46
|
+
const result = await connectService.handleCallback(user, session.id, provider, String(code), String(state));
|
|
47
|
+
if (result.isErr()) {
|
|
48
|
+
const errorUrl = `${successUrl}&error=${encodeURIComponent(result.error.message)}`;
|
|
49
|
+
return res.redirect(errorUrl);
|
|
50
|
+
}
|
|
51
|
+
return res.redirect(`${successUrl}&provider=${provider}`);
|
|
52
|
+
});
|
|
53
|
+
return connectRouter;
|
|
54
|
+
}
|
|
@@ -18,44 +18,44 @@ export declare class ConnectService extends BaseService<{
|
|
|
18
18
|
handleCallback(user: User, sessionId: string, providerId: string, code: string, state: string): Promise<import("#modules/base/base.dto").ServerResult<{
|
|
19
19
|
id: string;
|
|
20
20
|
userId: string;
|
|
21
|
+
updatedAt: Date | null;
|
|
22
|
+
createdAt: Date;
|
|
23
|
+
expiresAt: Date | null;
|
|
24
|
+
accessToken: string;
|
|
25
|
+
refreshToken: string | null;
|
|
26
|
+
scope: string | null;
|
|
21
27
|
provider: string;
|
|
28
|
+
parentId: string | null;
|
|
22
29
|
accountType: string;
|
|
23
30
|
providerAccountId: string;
|
|
24
31
|
handle: string | null;
|
|
25
32
|
displayName: string | null;
|
|
26
33
|
avatarUrl: string | null;
|
|
27
|
-
accessToken: string;
|
|
28
|
-
refreshToken: string | null;
|
|
29
34
|
tokenType: string | null;
|
|
30
|
-
scope: string | null;
|
|
31
|
-
expiresAt: Date | null;
|
|
32
|
-
parentId: string | null;
|
|
33
35
|
metadataJson: unknown;
|
|
34
36
|
revokedAt: Date | null;
|
|
35
37
|
lastRefreshedAt: Date | null;
|
|
36
|
-
createdAt: Date;
|
|
37
|
-
updatedAt: Date | null;
|
|
38
38
|
}>>;
|
|
39
39
|
refreshToken(connectionId: string): Promise<import("#modules/base/base.dto").ServerResult<{
|
|
40
40
|
id: string;
|
|
41
41
|
userId: string;
|
|
42
|
+
updatedAt: Date | null;
|
|
43
|
+
createdAt: Date;
|
|
44
|
+
expiresAt: Date | null;
|
|
45
|
+
accessToken: string;
|
|
46
|
+
refreshToken: string | null;
|
|
47
|
+
scope: string | null;
|
|
42
48
|
provider: string;
|
|
49
|
+
parentId: string | null;
|
|
43
50
|
accountType: string;
|
|
44
51
|
providerAccountId: string;
|
|
45
52
|
handle: string | null;
|
|
46
53
|
displayName: string | null;
|
|
47
54
|
avatarUrl: string | null;
|
|
48
|
-
accessToken: string;
|
|
49
|
-
refreshToken: string | null;
|
|
50
55
|
tokenType: string | null;
|
|
51
|
-
scope: string | null;
|
|
52
|
-
expiresAt: Date | null;
|
|
53
|
-
parentId: string | null;
|
|
54
56
|
metadataJson: unknown;
|
|
55
57
|
revokedAt: Date | null;
|
|
56
58
|
lastRefreshedAt: Date | null;
|
|
57
|
-
createdAt: Date;
|
|
58
|
-
updatedAt: Date | null;
|
|
59
59
|
}>>;
|
|
60
60
|
list(input: ConnectListInputSchema, { user }: {
|
|
61
61
|
user: User;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectService = void 0;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const base_service_1 = require("#modules/base/base.service");
|
|
6
|
+
const connect_oauth_1 = require("./connect.oauth");
|
|
7
|
+
class ConnectService extends base_service_1.BaseService {
|
|
8
|
+
providers = new Map();
|
|
9
|
+
constructor(repositories, providers) {
|
|
10
|
+
super(repositories);
|
|
11
|
+
this.providers = new Map(providers.map((provider) => [provider.id, provider]));
|
|
12
|
+
}
|
|
13
|
+
getProvider(id) {
|
|
14
|
+
return this.providers.get(id) || null;
|
|
15
|
+
}
|
|
16
|
+
async startAuth(_user, sessionId, providerId) {
|
|
17
|
+
return this.throwableAsync(async () => {
|
|
18
|
+
const provider = this.getProvider(providerId);
|
|
19
|
+
if (!provider) {
|
|
20
|
+
return this.error("BAD_REQUEST", `Unknown provider: ${providerId}`);
|
|
21
|
+
}
|
|
22
|
+
const state = await (0, connect_oauth_1.generateOAuthState)(sessionId, providerId);
|
|
23
|
+
const url = await (0, connect_oauth_1.buildAuthorizationUrl)(provider, state);
|
|
24
|
+
return (0, neverthrow_1.ok)({ url });
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async handleCallback(user, sessionId, providerId, code, state) {
|
|
28
|
+
return this.throwableAsync(async () => {
|
|
29
|
+
const provider = this.getProvider(providerId);
|
|
30
|
+
if (!provider) {
|
|
31
|
+
return this.error("BAD_REQUEST", `Unknown provider: ${providerId}`);
|
|
32
|
+
}
|
|
33
|
+
const oauthState = (0, connect_oauth_1.getOAuthState)(sessionId, providerId, state);
|
|
34
|
+
if (!oauthState) {
|
|
35
|
+
return this.error("BAD_REQUEST", "Invalid or expired state");
|
|
36
|
+
}
|
|
37
|
+
// Exchange code for tokens
|
|
38
|
+
// Note: redirectUri must match exactly what was used in authorization request
|
|
39
|
+
const tokens = await (0, connect_oauth_1.exchangeCodeForTokens)(provider, code, oauthState.codeVerifier, provider.redirectUri, state);
|
|
40
|
+
// Fetch profile
|
|
41
|
+
const profile = await provider.mapProfile(tokens.accessToken);
|
|
42
|
+
// Upsert connection
|
|
43
|
+
const connection = await this.repository.connect.upsert({
|
|
44
|
+
userId: user.id,
|
|
45
|
+
provider: providerId,
|
|
46
|
+
accountType: profile.accountType,
|
|
47
|
+
providerAccountId: profile.providerAccountId,
|
|
48
|
+
handle: profile.handle,
|
|
49
|
+
displayName: profile.displayName,
|
|
50
|
+
avatarUrl: profile.avatarUrl,
|
|
51
|
+
accessToken: tokens.accessToken,
|
|
52
|
+
refreshToken: tokens.refreshToken,
|
|
53
|
+
tokenType: tokens.tokenType || "bearer",
|
|
54
|
+
scope: tokens.scope,
|
|
55
|
+
expiresAt: tokens.expiresAt,
|
|
56
|
+
parentId: profile.parentId,
|
|
57
|
+
metadataJson: profile.metadata ? JSON.stringify(profile.metadata) : null,
|
|
58
|
+
});
|
|
59
|
+
if (connection.isErr()) {
|
|
60
|
+
return this.error("INTERNAL_SERVER_ERROR", "Failed to save connection", {
|
|
61
|
+
cause: connection.error,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return (0, neverthrow_1.ok)(connection.value);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async refreshToken(connectionId) {
|
|
68
|
+
return this.throwableAsync(async () => {
|
|
69
|
+
const connection = await this.repository.connect.findById(connectionId);
|
|
70
|
+
if (connection.isErr() || !connection.value) {
|
|
71
|
+
return this.error("NOT_FOUND", "Connection not found");
|
|
72
|
+
}
|
|
73
|
+
const conn = connection.value;
|
|
74
|
+
if (!conn.refreshToken) {
|
|
75
|
+
return this.error("BAD_REQUEST", "No refresh token available");
|
|
76
|
+
}
|
|
77
|
+
const provider = this.getProvider(conn.provider);
|
|
78
|
+
if (!provider) {
|
|
79
|
+
return this.error("BAD_REQUEST", `Unknown provider: ${conn.provider}`);
|
|
80
|
+
}
|
|
81
|
+
const tokens = await (0, connect_oauth_1.refreshAccessToken)(provider, conn.refreshToken);
|
|
82
|
+
const updateData = {
|
|
83
|
+
id: conn.id,
|
|
84
|
+
accessToken: tokens.accessToken,
|
|
85
|
+
refreshToken: tokens.refreshToken || null,
|
|
86
|
+
tokenType: tokens.tokenType || conn.tokenType || null,
|
|
87
|
+
scope: tokens.scope || conn.scope || null,
|
|
88
|
+
expiresAt: tokens.expiresAt || null,
|
|
89
|
+
lastRefreshedAt: new Date(),
|
|
90
|
+
};
|
|
91
|
+
const updated = await this.repository.connect.update(updateData);
|
|
92
|
+
if (updated.isErr()) {
|
|
93
|
+
return this.error("INTERNAL_SERVER_ERROR", "Failed to update tokens", {
|
|
94
|
+
cause: updated.error,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return (0, neverthrow_1.ok)(updated.value);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async list(input, { user }) {
|
|
101
|
+
return this.repository.connect.list({ userId: user.id, ...input });
|
|
102
|
+
}
|
|
103
|
+
async delete({ id }, { user }) {
|
|
104
|
+
const connection = await this.repository.connect.findById(id);
|
|
105
|
+
if (connection.isOk()) {
|
|
106
|
+
if (connection.value?.userId !== user.id) {
|
|
107
|
+
return this.error("FORBIDDEN", "Not your connection");
|
|
108
|
+
}
|
|
109
|
+
return this.repository.connect.deleteById(id);
|
|
110
|
+
}
|
|
111
|
+
return (0, neverthrow_1.err)(connection.error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.ConnectService = ConnectService;
|
|
@@ -4,9 +4,9 @@ export declare function createConnectTRPC(connectService: ConnectService): impor
|
|
|
4
4
|
session: {
|
|
5
5
|
id: string;
|
|
6
6
|
userId: string;
|
|
7
|
-
expiresAt: Date;
|
|
8
|
-
createdAt: Date;
|
|
9
7
|
updatedAt: Date;
|
|
8
|
+
createdAt: Date;
|
|
9
|
+
expiresAt: Date;
|
|
10
10
|
token: string;
|
|
11
11
|
ipAddress: string | null;
|
|
12
12
|
userAgent: string | null;
|
|
@@ -18,13 +18,12 @@ export declare function createConnectTRPC(connectService: ConnectService): impor
|
|
|
18
18
|
};
|
|
19
19
|
user: {
|
|
20
20
|
name: string;
|
|
21
|
-
image: string | null;
|
|
22
21
|
id: string;
|
|
23
|
-
createdAt: Date;
|
|
24
22
|
updatedAt: Date;
|
|
25
23
|
email: string;
|
|
26
|
-
metadata: Record<string, unknown>;
|
|
27
24
|
emailVerified: boolean;
|
|
25
|
+
image: string | null;
|
|
26
|
+
createdAt: Date;
|
|
28
27
|
role: string | null;
|
|
29
28
|
banned: boolean | null;
|
|
30
29
|
banReason: string | null;
|
|
@@ -34,6 +33,7 @@ export declare function createConnectTRPC(connectService: ConnectService): impor
|
|
|
34
33
|
paymentPlanTier: string | null;
|
|
35
34
|
paymentPlanExpiresAt: Date | null;
|
|
36
35
|
preferences: string | null;
|
|
36
|
+
metadata: Record<string, unknown>;
|
|
37
37
|
onboarding: number | null;
|
|
38
38
|
flags: string | null;
|
|
39
39
|
};
|
|
@@ -50,21 +50,21 @@ export declare function createConnectTRPC(connectService: ConnectService): impor
|
|
|
50
50
|
output: {
|
|
51
51
|
id: string;
|
|
52
52
|
userId: string;
|
|
53
|
+
createdAt: Date;
|
|
53
54
|
provider: string;
|
|
54
55
|
accountType: string;
|
|
55
56
|
providerAccountId: string;
|
|
56
|
-
|
|
57
|
+
updatedAt?: Date | null | undefined;
|
|
58
|
+
expiresAt?: Date | null | undefined;
|
|
59
|
+
scope?: string | null | undefined;
|
|
60
|
+
parentId?: string | null | undefined;
|
|
57
61
|
handle?: string | null | undefined;
|
|
58
62
|
displayName?: string | null | undefined;
|
|
59
63
|
avatarUrl?: string | null | undefined;
|
|
60
64
|
tokenType?: string | null | undefined;
|
|
61
|
-
scope?: string | null | undefined;
|
|
62
|
-
expiresAt?: Date | null | undefined;
|
|
63
|
-
parentId?: string | null | undefined;
|
|
64
65
|
metadataJson?: unknown;
|
|
65
66
|
revokedAt?: Date | null | undefined;
|
|
66
67
|
lastRefreshedAt?: Date | null | undefined;
|
|
67
|
-
updatedAt?: Date | null | undefined;
|
|
68
68
|
}[];
|
|
69
69
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
70
70
|
}>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConnectTRPC = createConnectTRPC;
|
|
4
|
+
const _trpc_1 = require("#trpc");
|
|
5
|
+
const connect_dto_1 = require("./connect.dto");
|
|
6
|
+
function createConnectTRPC(connectService) {
|
|
7
|
+
return (0, _trpc_1.router)({
|
|
8
|
+
list: _trpc_1.procedure
|
|
9
|
+
.input(connect_dto_1.connectListInputSchema)
|
|
10
|
+
.output(connect_dto_1.connectListOutputSchema)
|
|
11
|
+
.query(async ({ ctx, input }) => {
|
|
12
|
+
return (0, _trpc_1.handleTRPCResult)(await connectService.list(input, ctx));
|
|
13
|
+
}),
|
|
14
|
+
delete: _trpc_1.procedure
|
|
15
|
+
.input(connect_dto_1.connectDeleteInputSchema)
|
|
16
|
+
.output(connect_dto_1.connectDeleteOutputSchema)
|
|
17
|
+
.mutation(async ({ ctx, input }) => {
|
|
18
|
+
return (0, _trpc_1.handleTRPCResult)(await connectService.delete(input, ctx));
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cryptoPayments = void 0;
|
|
4
|
+
const sqlite_core_1 = require("drizzle-orm/sqlite-core");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
exports.cryptoPayments = (0, sqlite_core_1.sqliteTable)("crypto_payments", {
|
|
7
|
+
id: (0, sqlite_core_1.text)("id").primaryKey().$default(uuid_1.v4),
|
|
8
|
+
createdAt: (0, sqlite_core_1.integer)("created_at", { mode: "timestamp" })
|
|
9
|
+
.notNull()
|
|
10
|
+
.$default(() => new Date()),
|
|
11
|
+
updatedAt: (0, sqlite_core_1.integer)("updated_at", { mode: "timestamp" }),
|
|
12
|
+
address: (0, sqlite_core_1.text)("address").notNull(),
|
|
13
|
+
referenceId: (0, sqlite_core_1.text)("reference_id").notNull(),
|
|
14
|
+
status: (0, sqlite_core_1.text)("status").notNull().default("pending"),
|
|
15
|
+
derivationIndex: (0, sqlite_core_1.integer)("derivation_index").notNull(),
|
|
16
|
+
amountExpected: (0, sqlite_core_1.real)("amount_expected").notNull(),
|
|
17
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CryptoRepository = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const base_repository_1 = require("#modules/base/base.repository");
|
|
6
|
+
const crypto = tslib_1.__importStar(require("#modules/crypto/crypto.db"));
|
|
7
|
+
const schema = { ...crypto };
|
|
8
|
+
class CryptoRepository extends base_repository_1.BaseTableRepository {
|
|
9
|
+
}
|
|
10
|
+
exports.CryptoRepository = CryptoRepository;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CryptoService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const bip32_1 = tslib_1.__importDefault(require("bip32"));
|
|
6
|
+
const bip39 = tslib_1.__importStar(require("bip39"));
|
|
7
|
+
const bitcoin = tslib_1.__importStar(require("bitcoinjs-lib"));
|
|
8
|
+
const ecc = tslib_1.__importStar(require("tiny-secp256k1"));
|
|
9
|
+
const neverthrow_1 = require("neverthrow");
|
|
10
|
+
const base_service_1 = require("#modules/base/base.service");
|
|
11
|
+
const BITCOIN_NATIVE_SEGWIT_PATH = "m/84'/0'/0'/0";
|
|
12
|
+
const bip32 = (0, bip32_1.default)(ecc);
|
|
13
|
+
class CryptoService extends base_service_1.BaseService {
|
|
14
|
+
root = null;
|
|
15
|
+
getRoot() {
|
|
16
|
+
if (this.root)
|
|
17
|
+
return this.root;
|
|
18
|
+
const seed = process.env.BITCOIN_SEED;
|
|
19
|
+
if (!seed?.trim()) {
|
|
20
|
+
throw new Error("BITCOIN_SEED environment variable is not set");
|
|
21
|
+
}
|
|
22
|
+
if (!bip39.validateMnemonic(seed.trim())) {
|
|
23
|
+
throw new Error("BITCOIN_SEED is not a valid BIP39 mnemonic");
|
|
24
|
+
}
|
|
25
|
+
const seedBuffer = bip39.mnemonicToSeedSync(seed.trim());
|
|
26
|
+
this.root = bip32.fromSeed(seedBuffer);
|
|
27
|
+
return this.root;
|
|
28
|
+
}
|
|
29
|
+
createBitcoinAddress(derivationIndex) {
|
|
30
|
+
return this.throwable(() => {
|
|
31
|
+
if (derivationIndex < 0 || !Number.isInteger(derivationIndex)) {
|
|
32
|
+
return this.error("BAD_REQUEST", "derivationIndex must be a non-negative integer");
|
|
33
|
+
}
|
|
34
|
+
const root = this.getRoot();
|
|
35
|
+
const path = `${BITCOIN_NATIVE_SEGWIT_PATH}/${derivationIndex}`;
|
|
36
|
+
const child = root.derivePath(path);
|
|
37
|
+
if (!child.publicKey) {
|
|
38
|
+
return this.error("INTERNAL_SERVER_ERROR", "Failed to derive public key");
|
|
39
|
+
}
|
|
40
|
+
const payment = bitcoin.payments.p2wpkh({
|
|
41
|
+
pubkey: child.publicKey,
|
|
42
|
+
network: bitcoin.networks.bitcoin,
|
|
43
|
+
});
|
|
44
|
+
const address = payment.address;
|
|
45
|
+
if (!address) {
|
|
46
|
+
return this.error("INTERNAL_SERVER_ERROR", "Failed to generate address");
|
|
47
|
+
}
|
|
48
|
+
return (0, neverthrow_1.ok)(address);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.CryptoService = CryptoService;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmailService = void 0;
|
|
4
|
+
const neverthrow_1 = require("neverthrow");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const resend_1 = require("resend");
|
|
7
|
+
const base_service_1 = require("#modules/base/base.service");
|
|
8
|
+
class EmailService extends base_service_1.BaseService {
|
|
9
|
+
client;
|
|
10
|
+
brand;
|
|
11
|
+
noReplyFrom;
|
|
12
|
+
templates;
|
|
13
|
+
systemNotificationEmail;
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(undefined, undefined);
|
|
16
|
+
this.client = new resend_1.Resend(props.resendApiKey || process.env.RESEND_API_KEY);
|
|
17
|
+
this.client = {};
|
|
18
|
+
this.brand = props.brand;
|
|
19
|
+
this.noReplyFrom = props.noReplyFrom;
|
|
20
|
+
this.templates = props.templates;
|
|
21
|
+
this.systemNotificationEmail = props.systemNotificationEmail;
|
|
22
|
+
}
|
|
23
|
+
async sendTemplate(to, templateKey, templateProps, options) {
|
|
24
|
+
const template = this.templates[templateKey];
|
|
25
|
+
if (!template) {
|
|
26
|
+
return this.error("NOT_FOUND", `Email template not found: ${String(templateKey)}`);
|
|
27
|
+
}
|
|
28
|
+
const from = options?.from || this.noReplyFrom;
|
|
29
|
+
const subject = options?.subject || template.subject || String(templateKey);
|
|
30
|
+
const previewText = options?.previewText || template.previewText || subject;
|
|
31
|
+
const { error } = await this.client.emails.send({
|
|
32
|
+
to,
|
|
33
|
+
subject,
|
|
34
|
+
from,
|
|
35
|
+
react: (0, react_1.createElement)(template.react, { ...templateProps, previewText }),
|
|
36
|
+
});
|
|
37
|
+
if (error)
|
|
38
|
+
return this.error("INTERNAL_SERVER_ERROR", `Failed to send email: ${templateKey}`, {
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
return (0, neverthrow_1.ok)();
|
|
42
|
+
}
|
|
43
|
+
async sendBrandTemplate(to, templateKey, templateProps, options) {
|
|
44
|
+
return this.sendTemplate(to, templateKey, {
|
|
45
|
+
brand: this.brand,
|
|
46
|
+
...templateProps,
|
|
47
|
+
}, options);
|
|
48
|
+
}
|
|
49
|
+
async sendWaitlistConfirmation(email, overrideOptions) {
|
|
50
|
+
return this.sendTemplate(email, "waitlistConfirmation", {
|
|
51
|
+
email,
|
|
52
|
+
brand: this.brand,
|
|
53
|
+
}, overrideOptions);
|
|
54
|
+
}
|
|
55
|
+
async sendWaitlistInvite(email, code, overrideOptions) {
|
|
56
|
+
const url = `${process.env.VITE_APP_URL}/signup?code=${code}&email=${email}`;
|
|
57
|
+
return this.sendTemplate(email, "waitlistInvite", {
|
|
58
|
+
url,
|
|
59
|
+
brand: this.brand,
|
|
60
|
+
}, overrideOptions);
|
|
61
|
+
}
|
|
62
|
+
async sendWaitlistUserInvite(email, code, inviter, name, overrideOptions) {
|
|
63
|
+
const url = `${process.env.VITE_APP_URL}/signup?code=${code}&email=${email}`;
|
|
64
|
+
return this.sendTemplate(email, "waitlistUserInvite", {
|
|
65
|
+
url,
|
|
66
|
+
brand: this.brand,
|
|
67
|
+
inviter,
|
|
68
|
+
name,
|
|
69
|
+
}, {
|
|
70
|
+
previewText: `Create your ${this.brand.name} account today!`,
|
|
71
|
+
subject: `${inviter} has invited you to join ${this.brand.name}!`,
|
|
72
|
+
...overrideOptions,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async sendOrganizationInvite(email, organizationName, inviterName, role, url, overrideOptions) {
|
|
76
|
+
return this.sendTemplate(email, "organizationInvite", {
|
|
77
|
+
url,
|
|
78
|
+
brand: this.brand,
|
|
79
|
+
organizationName,
|
|
80
|
+
inviterName,
|
|
81
|
+
role,
|
|
82
|
+
}, {
|
|
83
|
+
previewText: `${inviterName} invited you to ${organizationName}`,
|
|
84
|
+
subject: `${inviterName} invited you to join ${organizationName}`,
|
|
85
|
+
...overrideOptions,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async sendVerification(email, url, overrideOptions) {
|
|
89
|
+
return this.sendTemplate(email, "verification", {
|
|
90
|
+
url,
|
|
91
|
+
brand: this.brand,
|
|
92
|
+
}, overrideOptions);
|
|
93
|
+
}
|
|
94
|
+
async sendResetPassword(email, url, overrideOptions) {
|
|
95
|
+
return this.sendTemplate(email, "passwordReset", {
|
|
96
|
+
url,
|
|
97
|
+
brand: this.brand,
|
|
98
|
+
}, overrideOptions);
|
|
99
|
+
}
|
|
100
|
+
async sendDeleteAccountVerification(email, url, overrideOptions) {
|
|
101
|
+
return this.sendTemplate(email, "accountDeletion", {
|
|
102
|
+
url,
|
|
103
|
+
brand: this.brand,
|
|
104
|
+
}, overrideOptions);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.EmailService = EmailService;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileRepository = void 0;
|
|
4
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
5
|
+
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
6
|
+
const neverthrow_1 = require("neverthrow");
|
|
7
|
+
const base_repository_1 = require("#modules/base/base.repository");
|
|
8
|
+
class FileRepository extends base_repository_1.BaseExternaRepository {
|
|
9
|
+
s3;
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
12
|
+
if (!process.env.AWS_REGION ||
|
|
13
|
+
!process.env.AWS_ACCESS_KEY_ID ||
|
|
14
|
+
!process.env.AWS_SECRET_ACCESS_KEY) {
|
|
15
|
+
throw new Error("Missing AWS environment variables");
|
|
16
|
+
}
|
|
17
|
+
this.s3 = new client_s3_1.S3Client({
|
|
18
|
+
region: process.env.AWS_REGION,
|
|
19
|
+
credentials: {
|
|
20
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
21
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
22
|
+
},
|
|
23
|
+
...(process.env.AWS_S3_ENDPOINT ? { endpoint: process.env.AWS_S3_ENDPOINT } : {}),
|
|
24
|
+
forcePathStyle: !!process.env.AWS_S3_ENDPOINT, // Path style is often required for non-AWS S3 providers
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async getS3UploadUrl(filename, filetype, expiresIn = 60 * 5) {
|
|
28
|
+
return this.throwableAsync(async () => {
|
|
29
|
+
const command = new client_s3_1.PutObjectCommand({
|
|
30
|
+
Bucket: process.env.AWS_S3_BUCKET,
|
|
31
|
+
Key: filename,
|
|
32
|
+
ContentType: filetype,
|
|
33
|
+
});
|
|
34
|
+
const url = await (0, s3_request_presigner_1.getSignedUrl)(this.s3, command, { expiresIn });
|
|
35
|
+
return (0, neverthrow_1.ok)(url);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async getS3DownloadUrl(path, expiresIn = 60 * 5) {
|
|
39
|
+
return this.throwableAsync(async () => {
|
|
40
|
+
const command = new client_s3_1.GetObjectCommand({
|
|
41
|
+
Bucket: process.env.AWS_S3_BUCKET,
|
|
42
|
+
Key: path,
|
|
43
|
+
});
|
|
44
|
+
const url = await (0, s3_request_presigner_1.getSignedUrl)(this.s3, command, { expiresIn });
|
|
45
|
+
return (0, neverthrow_1.ok)(url);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async getS3Object(path) {
|
|
49
|
+
return this.throwableAsync(async () => {
|
|
50
|
+
const command = new client_s3_1.GetObjectCommand({
|
|
51
|
+
Bucket: process.env.AWS_S3_BUCKET,
|
|
52
|
+
Key: path,
|
|
53
|
+
});
|
|
54
|
+
const data = await this.s3.send(command);
|
|
55
|
+
return (0, neverthrow_1.ok)(data);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async getS3ObjectT(path) {
|
|
59
|
+
return this.throwableAsync(async () => {
|
|
60
|
+
const command = new client_s3_1.GetObjectCommand({
|
|
61
|
+
Bucket: process.env.AWS_S3_BUCKET,
|
|
62
|
+
Key: path,
|
|
63
|
+
});
|
|
64
|
+
const data = await this.s3.send(command);
|
|
65
|
+
return (0, neverthrow_1.ok)(data);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async deleteS3Object(path) {
|
|
69
|
+
return this.throwableAsync(async () => {
|
|
70
|
+
const command = new client_s3_1.DeleteObjectCommand({
|
|
71
|
+
Bucket: process.env.AWS_S3_BUCKET,
|
|
72
|
+
Key: path,
|
|
73
|
+
});
|
|
74
|
+
await this.s3.send(command);
|
|
75
|
+
return (0, neverthrow_1.ok)(undefined);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.FileRepository = FileRepository;
|