@odvi/create-dtt-framework 0.1.8 → 0.1.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@odvi/create-dtt-framework",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "CLI tool to scaffold new projects with DTT Framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,106 +1,117 @@
1
- # Since the ".env" file is gitignored, you can use the ".env.example" file to
2
- # build a new ".env" file when you clone the repo. Keep this file up-to-date
3
- # when you add new variables to `.env`.
4
-
5
- # This file will be committed to version control, so make sure not to have any
6
- # secrets in it. If you are cloning this repo, create a copy of this file named
7
- # ".env" and populate it with your secrets.
8
-
9
- # When adding additional environment variables, the schema in "/src/env.js"
10
- # should be updated accordingly.
11
-
12
- # ========================================
13
- # App Configuration
14
- # ========================================
15
- # The base URL of your application. Used for redirects, webhooks, and callbacks.
16
- # In development: http://localhost:3000
17
- # In production: https://your-domain.com
18
- NEXT_PUBLIC_APP_URL=http://localhost:3000
19
-
20
- # ========================================
21
- # Clerk Authentication
22
- # ========================================
23
- # Clerk publishable key - used on the client side for authentication
24
- # Get this from your Clerk Dashboard > API Keys > Publishable Key
25
- NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
26
-
27
- # Clerk secret key - used on the server side for authentication
28
- # Get this from your Clerk Dashboard > API Keys > Secret Key
29
- CLERK_SECRET_KEY=sk_test_xxx
30
-
31
- # Clerk webhook secret - used to verify webhook events from Clerk
32
- # Get this from your Clerk Dashboard > Webhooks > Add Endpoint > Signing Secret
33
- CLERK_WEBHOOK_SECRET=whsec_xxx
34
-
35
- # URL path for the sign-in page
36
- NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
37
-
38
- # URL path for the sign-up page
39
- NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
40
-
41
- # Where to redirect users after successful sign-in
42
- NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/health
43
-
44
- # Where to redirect users after successful sign-up
45
- NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/health
46
-
47
- # ========================================
48
- # Supabase
49
- # ========================================
50
- # Supabase project URL - found in your Supabase project settings
51
- NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
52
-
53
- # Supabase anonymous/public key - safe to expose on client side
54
- # Found in your Supabase project settings under API keys
55
- NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJxxx
56
-
57
- # Supabase service role key - full admin access, keep secret!
58
- # Found in your Supabase project settings under API keys
59
- SUPABASE_SERVICE_ROLE_KEY=eyJxxx
60
-
61
- # PostgreSQL connection string for Drizzle ORM
62
- # Use Supabase's Transaction mode pooler for better performance
63
- # Format: postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
64
- DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
65
-
66
- # ========================================
67
- # Snowflake Data Warehouse
68
- # ========================================
69
- # Snowflake account identifier (e.g., ef19411.ap-southeast-1)
70
- SNOWFLAKE_ACCOUNT=ef19411.ap-southeast-1
71
-
72
- # Snowflake warehouse to use
73
- SNOWFLAKE_WAREHOUSE=COMPUTE_WH
74
-
75
- # Snowflake role
76
- SNOWFLAKE_ROLE=ACCOUNTADMIN
77
-
78
- # Authentication method (SNOWFLAKE_JWT for Key Pair Auth)
79
- SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT
80
-
81
- # Snowflake username
82
- SNOWFLAKE_USERNAME=APP_USER_WITH_KEY_AUTH
83
-
84
- # Private Key for Authentication (PEM format)
85
- SNOWFLAKE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
86
- ...
87
- -----END PRIVATE KEY-----"
88
-
89
- # Passphrase for the Private Key (if encrypted)
90
- SNOWFLAKE_PRIVATE_KEY_PASSPHRASE="<password>"
91
-
92
- # Enable logging (optional)
93
- SNOWFLAKE_LOGGING=true
94
-
95
- # ========================================
96
- # NextBank API (Placeholder)
97
- # ========================================
98
- # Base URL for the NextBank API endpoint
99
- NEXTBANK_API=https://api.nextbank.com
100
-
101
- # Username for NextBank Basic Authentication
102
- NEXTBANK_API_USERNAME=user
103
-
104
- # Password for NextBank Basic Authentication
105
- NEXTBANK_API_PASSWORD=pass
106
-
1
+ # Since the ".env" file is gitignored, you can use the ".env.example" file to
2
+ # build a new ".env" file when you clone the repo. Keep this file up-to-date
3
+ # when you add new variables to `.env`.
4
+
5
+ # This file will be committed to version control, so make sure not to have any
6
+ # secrets in it. If you are cloning this repo, create a copy of this file named
7
+ # ".env" and populate it with your secrets.
8
+
9
+ # When adding additional environment variables, the schema in "/src/env.js"
10
+ # should be updated accordingly.
11
+
12
+ # ========================================
13
+ # App Configuration
14
+ # ========================================
15
+ # The base URL of your application. Used for redirects, webhooks, and callbacks.
16
+ # In development: http://localhost:3000
17
+ # In production: https://your-domain.com
18
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
19
+
20
+ # ========================================
21
+ # Clerk Authentication
22
+ # ========================================
23
+ # Clerk publishable key - used on the client side for authentication
24
+ # Get this from your Clerk Dashboard > API Keys > Publishable Key
25
+ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
26
+
27
+ # Clerk secret key - used on the server side for authentication
28
+ # Get this from your Clerk Dashboard > API Keys > Secret Key
29
+ CLERK_SECRET_KEY=sk_test_xxx
30
+
31
+ # Clerk webhook secret - used to verify webhook events from Clerk
32
+ # Get this from your Clerk Dashboard > Webhooks > Add Endpoint > Signing Secret
33
+ CLERK_WEBHOOK_SECRET=whsec_xxx
34
+
35
+ # URL path for the sign-in page
36
+ NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
37
+
38
+ # URL path for the sign-up page
39
+ NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
40
+
41
+ # Where to redirect users after successful sign-in
42
+ NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/health
43
+
44
+ # Where to redirect users after successful sign-up
45
+ NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/health
46
+
47
+ # ========================================
48
+ # Supabase
49
+ # ========================================
50
+ # Supabase project URL - found in your Supabase project settings
51
+ NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
52
+
53
+ # Supabase anonymous/public key - safe to expose on client side
54
+ # Found in your Supabase project settings under API keys
55
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJxxx
56
+
57
+ # Supabase service role key - full admin access, keep secret!
58
+ # Found in your Supabase project settings under API keys
59
+ SUPABASE_SERVICE_ROLE_KEY=eyJxxx
60
+
61
+ # PostgreSQL connection string for Drizzle ORM
62
+ # Use Supabase's Transaction mode pooler for better performance
63
+ # Format: postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
64
+ DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
65
+
66
+ # ========================================
67
+ # Snowflake Data Warehouse
68
+ # ========================================
69
+ # Snowflake account identifier (e.g., ef19411.ap-southeast-1)
70
+ SNOWFLAKE_ACCOUNT=ef19411.ap-southeast-1
71
+
72
+ # Snowflake warehouse to use
73
+ SNOWFLAKE_WAREHOUSE=COMPUTE_WH
74
+
75
+ # Snowflake role
76
+ SNOWFLAKE_ROLE=ACCOUNTADMIN
77
+
78
+ # Authentication method (SNOWFLAKE_JWT for Key Pair Auth)
79
+ SNOWFLAKE_AUTHENTICATOR=SNOWFLAKE_JWT
80
+
81
+ # Snowflake username
82
+ SNOWFLAKE_USERNAME=APP_USER_WITH_KEY_AUTH
83
+
84
+ # Private Key for Authentication (PEM format)
85
+ SNOWFLAKE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
86
+ ...
87
+ -----END PRIVATE KEY-----"
88
+
89
+ # Passphrase for the Private Key (if encrypted)
90
+ SNOWFLAKE_PRIVATE_KEY_PASSPHRASE="<password>"
91
+
92
+ # Enable logging (optional)
93
+ SNOWFLAKE_LOGGING=true
94
+
95
+ # ========================================
96
+ # AWS S3 (Optional)
97
+ # ========================================
98
+ # AWS Access Key ID for S3 access
99
+ AWS_ACCESS_KEY_ID=AKIA...
100
+
101
+ # AWS Secret Access Key for S3 access
102
+ AWS_ACCESS_SECRET_KEY=wJalr...
103
+
104
+ # AWS Region where your S3 buckets are located (e.g., us-east-1)
105
+ AWS_REGION=us-east-1
106
+
107
+ # ========================================
108
+ # NextBank API (Placeholder)
109
+ # ========================================
110
+ # Base URL for the NextBank API endpoint
111
+ NEXTBANK_API=https://api.nextbank.com
112
+
113
+ # Username for NextBank Basic Authentication
114
+ NEXTBANK_API_USERNAME=user
115
+
116
+ # Password for NextBank Basic Authentication
117
+ NEXTBANK_API_PASSWORD=pass
@@ -97,6 +97,19 @@ SNOWFLAKE_SCHEMA=PUBLIC
97
97
  # Snowflake role name
98
98
  SNOWFLAKE_ROLE=ANALYST
99
99
 
100
+ # ============================================
101
+ # AWS S3 (Optional)
102
+ # ============================================
103
+
104
+ # AWS Access Key ID
105
+ AWS_ACCESS_KEY_ID=AKIA...
106
+
107
+ # AWS Secret Access Key
108
+ AWS_ACCESS_SECRET_KEY=wJalr...
109
+
110
+ # AWS Region (e.g., us-east-1, ap-southeast-1)
111
+ AWS_REGION=us-east-1
112
+
100
113
  # ============================================
101
114
  # NextBank (Placeholder - Optional)
102
115
  # ============================================
@@ -418,6 +431,45 @@ SNOWFLAKE_ROLE=ANALYST
418
431
 
419
432
  ---
420
433
 
434
+ ### AWS S3 Variables (Optional)
435
+
436
+ #### AWS_ACCESS_KEY_ID
437
+
438
+ **Purpose:** AWS Access Key ID for S3 access
439
+
440
+ **Where to find:** AWS IAM Console → Users → Security credentials
441
+
442
+ **Example:**
443
+ ```bash
444
+ AWS_ACCESS_KEY_ID=AKIA...
445
+ ```
446
+
447
+ #### AWS_ACCESS_SECRET_KEY
448
+
449
+ **Purpose:** AWS Secret Access Key for S3 access
450
+
451
+ **Where to find:** AWS IAM Console → Users → Security credentials (only visible when created)
452
+
453
+ **Example:**
454
+ ```bash
455
+ AWS_ACCESS_SECRET_KEY=wJalr...
456
+ ```
457
+
458
+ **Notes:**
459
+ - Never commit to version control
460
+ - Ensure the user has appropriate S3 permissions (e.g., `AmazonS3ReadOnlyAccess` or custom policy)
461
+
462
+ #### AWS_REGION
463
+
464
+ **Purpose:** AWS Region where your S3 buckets are located
465
+
466
+ **Example:**
467
+ ```bash
468
+ AWS_REGION=us-east-1
469
+ ```
470
+
471
+ ---
472
+
421
473
  ### NextBank Variables (Placeholder)
422
474
 
423
475
  **Note:** NextBank is a placeholder integration. These variables are optional and not currently used.
@@ -30,6 +30,11 @@ const envSchema = z.object({
30
30
  NEXTBANK_API: z.string().url().optional(),
31
31
  NEXTBANK_API_USERNAME: z.string().optional(),
32
32
  NEXTBANK_API_PASSWORD: z.string().optional(),
33
+
34
+ // AWS S3 (optional)
35
+ AWS_ACCESS_KEY_ID: z.string().optional(),
36
+ AWS_ACCESS_SECRET_KEY: z.string().optional(),
37
+ AWS_REGION: z.string().optional(),
33
38
  })
34
39
 
35
40
  export const env = envSchema.parse(process.env)
@@ -52,6 +52,13 @@ export const SERVICES = [
52
52
  { name: 'Ping API', endpoint: '/nextbank/ping' },
53
53
  ],
54
54
  },
55
+ {
56
+ name: 'AWS S3',
57
+ icon: 'cloud',
58
+ checks: [
59
+ { name: 'Connect', endpoint: '/aws-s3/connect' },
60
+ ],
61
+ },
55
62
  {
56
63
  name: 'Framework',
57
64
  icon: 'code',
@@ -0,0 +1,59 @@
1
+ import { Hono } from 'hono'
2
+ import { S3Client, ListBucketsCommand } from '@aws-sdk/client-s3'
3
+ import { env } from '@/config/env'
4
+
5
+ export const awsS3HealthRoutes = new Hono()
6
+
7
+ awsS3HealthRoutes.get('/connect', async (c) => {
8
+ const start = performance.now()
9
+
10
+ try {
11
+ // Check if credentials are configured
12
+ if (!env.AWS_ACCESS_KEY_ID || !env.AWS_ACCESS_SECRET_KEY || !env.AWS_REGION) {
13
+ return c.json({
14
+ status: 'warning',
15
+ responseTimeMs: Math.round(performance.now() - start),
16
+ message: 'AWS S3 credentials not configured - skipping connection check',
17
+ data: {
18
+ skipped: true,
19
+ reason: 'Missing AWS_ACCESS_KEY_ID, AWS_ACCESS_SECRET_KEY, or AWS_REGION',
20
+ },
21
+ })
22
+ }
23
+
24
+ const s3Client = new S3Client({
25
+ region: env.AWS_REGION,
26
+ credentials: {
27
+ accessKeyId: env.AWS_ACCESS_KEY_ID,
28
+ secretAccessKey: env.AWS_ACCESS_SECRET_KEY,
29
+ },
30
+ })
31
+
32
+ // Try to list buckets to verify connectivity and credentials
33
+ const command = new ListBucketsCommand({})
34
+ const response = await s3Client.send(command)
35
+
36
+ return c.json({
37
+ status: 'healthy',
38
+ responseTimeMs: Math.round(performance.now() - start),
39
+ message: 'Successfully connected to AWS S3',
40
+ data: {
41
+ region: env.AWS_REGION,
42
+ bucketCount: response.Buckets?.length ?? 0,
43
+ },
44
+ })
45
+ } catch (error) {
46
+ return c.json(
47
+ {
48
+ status: 'error',
49
+ responseTimeMs: Math.round(performance.now() - start),
50
+ error: error instanceof Error ? error.message : 'Failed to connect to AWS S3',
51
+ data: {
52
+ details: error instanceof Error ? error.stack : undefined,
53
+ },
54
+ },
55
+ 500
56
+ )
57
+ }
58
+ })
59
+
@@ -6,6 +6,7 @@ import { edgeFunctionsHealthRoutes } from './edge-functions'
6
6
  import { snowflakeHealthRoutes } from './snowflake'
7
7
  import { nextbankHealthRoutes } from './nextbank'
8
8
  import { frameworkHealthRoutes } from './framework'
9
+ import { awsS3HealthRoutes } from './aws-s3'
9
10
 
10
11
  export const healthRoutes = new Hono()
11
12
 
@@ -16,6 +17,7 @@ healthRoutes.route('/edge', edgeFunctionsHealthRoutes)
16
17
  healthRoutes.route('/snowflake', snowflakeHealthRoutes)
17
18
  healthRoutes.route('/nextbank', nextbankHealthRoutes)
18
19
  healthRoutes.route('/framework', frameworkHealthRoutes)
20
+ healthRoutes.route('/aws-s3', awsS3HealthRoutes)
19
21
 
20
22
  healthRoutes.get('/all', async (c) => {
21
23
  const start = performance.now()
@@ -38,6 +40,7 @@ healthRoutes.get('/all', async (c) => {
38
40
  { name: 'Snowflake Query', url: `${baseUrl}/api/health/snowflake/query` },
39
41
  { name: 'NextBank Ping', url: `${baseUrl}/api/health/nextbank/ping` },
40
42
  { name: 'NextBank Auth', url: `${baseUrl}/api/health/nextbank/auth` },
43
+ { name: 'AWS S3 Connect', url: `${baseUrl}/api/health/aws-s3/connect` },
41
44
  ]
42
45
 
43
46
  // Run all checks in parallel
@@ -46,7 +49,7 @@ healthRoutes.get('/all', async (c) => {
46
49
  const checkStart = performance.now()
47
50
  try {
48
51
  const response = await fetch(check.url, {
49
- method: check.method || 'GET',
52
+ method: check.method ?? 'GET',
50
53
  headers: {
51
54
  'Content-Type': 'application/json',
52
55
  // Forward auth header if present
@@ -60,7 +63,7 @@ healthRoutes.get('/all', async (c) => {
60
63
 
61
64
  return {
62
65
  name: check.name,
63
- status: response.ok ? data.status || 'healthy' : 'error',
66
+ status: response.ok ? data.status ?? 'healthy' : 'error',
64
67
  responseTimeMs: Math.round(performance.now() - checkStart),
65
68
  httpStatus: response.status,
66
69
  data,
@@ -85,12 +88,13 @@ healthRoutes.get('/all', async (c) => {
85
88
  edge: results.filter((r) => r.status === 'fulfilled' && r.value.name.includes('Edge')),
86
89
  snowflake: results.filter((r) => r.status === 'fulfilled' && r.value.name.includes('Snowflake')),
87
90
  nextbank: results.filter((r) => r.status === 'fulfilled' && r.value.name.includes('NextBank')),
91
+ awsS3: results.filter((r) => r.status === 'fulfilled' && r.value.name.includes('AWS S3')),
88
92
  }
89
93
 
90
94
  // Determine overall status
91
95
  const allResults = results.map((r) => (r.status === 'fulfilled' ? r.value : null))
92
96
  const hasErrors = allResults.some((r) => r && (r.status === 'error' || r.httpStatus >= 400))
93
- const allHealthy = allResults.every((r) => r && r.status === 'healthy')
97
+ const allHealthy = allResults.every((r) => r?.status === 'healthy')
94
98
 
95
99
  return c.json({
96
100
  status: hasErrors ? 'unhealthy' : allHealthy ? 'healthy' : 'partial',