@codaijs/keel 0.2.3 → 0.2.4
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/__tests__/sail-installer.test.js +25 -25
- package/dist/sail-installer.js +174 -174
- package/dist/scaffold.js +68 -68
- package/package.json +58 -58
- package/sails/_template/addon.json +20 -20
- package/sails/_template/install.ts +402 -402
- package/sails/admin-dashboard/README.md +117 -117
- package/sails/admin-dashboard/addon.json +28 -28
- package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -34
- package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -243
- package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -40
- package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -240
- package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -149
- package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -173
- package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -203
- package/sails/admin-dashboard/install.ts +305 -305
- package/sails/analytics/README.md +178 -178
- package/sails/analytics/addon.json +27 -27
- package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -58
- package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -64
- package/sails/analytics/files/frontend/lib/analytics.ts +103 -103
- package/sails/analytics/install.ts +297 -297
- package/sails/file-uploads/addon.json +30 -30
- package/sails/file-uploads/files/backend/routes/files.ts +198 -198
- package/sails/file-uploads/files/backend/schema/files.ts +36 -36
- package/sails/file-uploads/files/backend/services/file-storage.ts +128 -128
- package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -248
- package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -147
- package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -106
- package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -118
- package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -37
- package/sails/file-uploads/install.ts +466 -466
- package/sails/gdpr/README.md +174 -174
- package/sails/gdpr/addon.json +27 -27
- package/sails/gdpr/files/backend/routes/gdpr.ts +140 -140
- package/sails/gdpr/files/backend/services/gdpr.ts +293 -293
- package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -97
- package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -192
- package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -75
- package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -186
- package/sails/gdpr/install.ts +756 -756
- package/sails/google-oauth/README.md +121 -121
- package/sails/google-oauth/addon.json +22 -22
- package/sails/google-oauth/files/GoogleButton.tsx +50 -50
- package/sails/google-oauth/install.ts +252 -252
- package/sails/i18n/README.md +193 -193
- package/sails/i18n/addon.json +30 -30
- package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -108
- package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -31
- package/sails/i18n/files/frontend/lib/i18n.ts +32 -32
- package/sails/i18n/files/frontend/locales/de/common.json +44 -44
- package/sails/i18n/files/frontend/locales/en/common.json +44 -44
- package/sails/i18n/install.ts +407 -407
- package/sails/push-notifications/README.md +163 -163
- package/sails/push-notifications/addon.json +31 -31
- package/sails/push-notifications/files/backend/routes/notifications.ts +153 -153
- package/sails/push-notifications/files/backend/schema/notifications.ts +31 -31
- package/sails/push-notifications/files/backend/services/notifications.ts +117 -117
- package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -12
- package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -154
- package/sails/push-notifications/install.ts +384 -384
- package/sails/r2-storage/addon.json +29 -29
- package/sails/r2-storage/files/backend/services/storage.ts +71 -71
- package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -167
- package/sails/r2-storage/install.ts +412 -412
- package/sails/rate-limiting/addon.json +20 -20
- package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -104
- package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -137
- package/sails/rate-limiting/install.ts +300 -300
- package/sails/registry.json +107 -107
- package/sails/stripe/README.md +214 -214
- package/sails/stripe/addon.json +24 -24
- package/sails/stripe/files/backend/routes/stripe.ts +154 -154
- package/sails/stripe/files/backend/schema/stripe.ts +74 -74
- package/sails/stripe/files/backend/services/stripe.ts +224 -224
- package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -135
- package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -86
- package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -116
- package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -226
- package/sails/stripe/install.ts +378 -378
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* S3-compatible file storage service.
|
|
3
|
-
*
|
|
4
|
-
* Works with Cloudflare R2, AWS S3, MinIO, and any other S3-compatible
|
|
5
|
-
* provider. Configured entirely via environment variables.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
S3Client,
|
|
10
|
-
PutObjectCommand,
|
|
11
|
-
GetObjectCommand,
|
|
12
|
-
DeleteObjectCommand,
|
|
13
|
-
ListObjectsV2Command,
|
|
14
|
-
} from "@aws-sdk/client-s3";
|
|
15
|
-
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
16
|
-
import { env } from "../env.js";
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Client
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
const s3Client = new S3Client({
|
|
23
|
-
region: env.S3_REGION,
|
|
24
|
-
endpoint: env.S3_ENDPOINT,
|
|
25
|
-
credentials: {
|
|
26
|
-
accessKeyId: env.S3_ACCESS_KEY_ID,
|
|
27
|
-
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
|
|
28
|
-
},
|
|
29
|
-
// Required for some S3-compatible providers (R2, MinIO)
|
|
30
|
-
forcePathStyle: true,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Constants
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
const UPLOAD_URL_EXPIRY = 60 * 10; // 10 minutes
|
|
38
|
-
const DOWNLOAD_URL_EXPIRY = 60 * 60; // 1 hour
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// Helpers
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Sanitise a file name for use as an S3 key segment.
|
|
46
|
-
* Removes path separators and other problematic characters.
|
|
47
|
-
*/
|
|
48
|
-
function sanitizeFileName(name: string): string {
|
|
49
|
-
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
// Public API
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Generate a presigned PUT URL for uploading a file.
|
|
58
|
-
*
|
|
59
|
-
* @returns The presigned upload URL and the object key that will be stored.
|
|
60
|
-
*/
|
|
61
|
-
export async function generateUploadUrl(
|
|
62
|
-
userId: string,
|
|
63
|
-
fileName: string,
|
|
64
|
-
contentType: string,
|
|
65
|
-
maxSizeBytes?: number,
|
|
66
|
-
): Promise<{ uploadUrl: string; key: string }> {
|
|
67
|
-
const sanitized = sanitizeFileName(fileName);
|
|
68
|
-
const key = `${userId}/${Date.now()}-${sanitized}`;
|
|
69
|
-
|
|
70
|
-
const command = new PutObjectCommand({
|
|
71
|
-
Bucket: env.S3_BUCKET_NAME,
|
|
72
|
-
Key: key,
|
|
73
|
-
ContentType: contentType,
|
|
74
|
-
...(maxSizeBytes ? { ContentLength: maxSizeBytes } : {}),
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const uploadUrl = await getSignedUrl(s3Client, command, {
|
|
78
|
-
expiresIn: UPLOAD_URL_EXPIRY,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return { uploadUrl, key };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Generate a presigned GET URL for downloading / viewing a file.
|
|
86
|
-
*/
|
|
87
|
-
export async function generateDownloadUrl(key: string): Promise<string> {
|
|
88
|
-
const command = new GetObjectCommand({
|
|
89
|
-
Bucket: env.S3_BUCKET_NAME,
|
|
90
|
-
Key: key,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return getSignedUrl(s3Client, command, {
|
|
94
|
-
expiresIn: DOWNLOAD_URL_EXPIRY,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Delete an object from the bucket.
|
|
100
|
-
*/
|
|
101
|
-
export async function deleteFile(key: string): Promise<void> {
|
|
102
|
-
const command = new DeleteObjectCommand({
|
|
103
|
-
Bucket: env.S3_BUCKET_NAME,
|
|
104
|
-
Key: key,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
await s3Client.send(command);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* List objects under a prefix (e.g., a user's directory).
|
|
112
|
-
*/
|
|
113
|
-
export async function listFiles(
|
|
114
|
-
prefix: string,
|
|
115
|
-
): Promise<{ key: string; size: number; lastModified: Date | undefined }[]> {
|
|
116
|
-
const command = new ListObjectsV2Command({
|
|
117
|
-
Bucket: env.S3_BUCKET_NAME,
|
|
118
|
-
Prefix: prefix,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const response = await s3Client.send(command);
|
|
122
|
-
|
|
123
|
-
return (response.Contents ?? []).map((obj) => ({
|
|
124
|
-
key: obj.Key!,
|
|
125
|
-
size: obj.Size ?? 0,
|
|
126
|
-
lastModified: obj.LastModified,
|
|
127
|
-
}));
|
|
128
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* S3-compatible file storage service.
|
|
3
|
+
*
|
|
4
|
+
* Works with Cloudflare R2, AWS S3, MinIO, and any other S3-compatible
|
|
5
|
+
* provider. Configured entirely via environment variables.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
S3Client,
|
|
10
|
+
PutObjectCommand,
|
|
11
|
+
GetObjectCommand,
|
|
12
|
+
DeleteObjectCommand,
|
|
13
|
+
ListObjectsV2Command,
|
|
14
|
+
} from "@aws-sdk/client-s3";
|
|
15
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
16
|
+
import { env } from "../env.js";
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Client
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const s3Client = new S3Client({
|
|
23
|
+
region: env.S3_REGION,
|
|
24
|
+
endpoint: env.S3_ENDPOINT,
|
|
25
|
+
credentials: {
|
|
26
|
+
accessKeyId: env.S3_ACCESS_KEY_ID,
|
|
27
|
+
secretAccessKey: env.S3_SECRET_ACCESS_KEY,
|
|
28
|
+
},
|
|
29
|
+
// Required for some S3-compatible providers (R2, MinIO)
|
|
30
|
+
forcePathStyle: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Constants
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
const UPLOAD_URL_EXPIRY = 60 * 10; // 10 minutes
|
|
38
|
+
const DOWNLOAD_URL_EXPIRY = 60 * 60; // 1 hour
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Helpers
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sanitise a file name for use as an S3 key segment.
|
|
46
|
+
* Removes path separators and other problematic characters.
|
|
47
|
+
*/
|
|
48
|
+
function sanitizeFileName(name: string): string {
|
|
49
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Public API
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate a presigned PUT URL for uploading a file.
|
|
58
|
+
*
|
|
59
|
+
* @returns The presigned upload URL and the object key that will be stored.
|
|
60
|
+
*/
|
|
61
|
+
export async function generateUploadUrl(
|
|
62
|
+
userId: string,
|
|
63
|
+
fileName: string,
|
|
64
|
+
contentType: string,
|
|
65
|
+
maxSizeBytes?: number,
|
|
66
|
+
): Promise<{ uploadUrl: string; key: string }> {
|
|
67
|
+
const sanitized = sanitizeFileName(fileName);
|
|
68
|
+
const key = `${userId}/${Date.now()}-${sanitized}`;
|
|
69
|
+
|
|
70
|
+
const command = new PutObjectCommand({
|
|
71
|
+
Bucket: env.S3_BUCKET_NAME,
|
|
72
|
+
Key: key,
|
|
73
|
+
ContentType: contentType,
|
|
74
|
+
...(maxSizeBytes ? { ContentLength: maxSizeBytes } : {}),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const uploadUrl = await getSignedUrl(s3Client, command, {
|
|
78
|
+
expiresIn: UPLOAD_URL_EXPIRY,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return { uploadUrl, key };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate a presigned GET URL for downloading / viewing a file.
|
|
86
|
+
*/
|
|
87
|
+
export async function generateDownloadUrl(key: string): Promise<string> {
|
|
88
|
+
const command = new GetObjectCommand({
|
|
89
|
+
Bucket: env.S3_BUCKET_NAME,
|
|
90
|
+
Key: key,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return getSignedUrl(s3Client, command, {
|
|
94
|
+
expiresIn: DOWNLOAD_URL_EXPIRY,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Delete an object from the bucket.
|
|
100
|
+
*/
|
|
101
|
+
export async function deleteFile(key: string): Promise<void> {
|
|
102
|
+
const command = new DeleteObjectCommand({
|
|
103
|
+
Bucket: env.S3_BUCKET_NAME,
|
|
104
|
+
Key: key,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await s3Client.send(command);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* List objects under a prefix (e.g., a user's directory).
|
|
112
|
+
*/
|
|
113
|
+
export async function listFiles(
|
|
114
|
+
prefix: string,
|
|
115
|
+
): Promise<{ key: string; size: number; lastModified: Date | undefined }[]> {
|
|
116
|
+
const command = new ListObjectsV2Command({
|
|
117
|
+
Bucket: env.S3_BUCKET_NAME,
|
|
118
|
+
Prefix: prefix,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const response = await s3Client.send(command);
|
|
122
|
+
|
|
123
|
+
return (response.Contents ?? []).map((obj) => ({
|
|
124
|
+
key: obj.Key!,
|
|
125
|
+
size: obj.Size ?? 0,
|
|
126
|
+
lastModified: obj.LastModified,
|
|
127
|
+
}));
|
|
128
|
+
}
|