@odvi/create-dtt-framework 0.1.6 → 0.1.8

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 DTT Framework
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -2,25 +2,64 @@
2
2
 
3
3
  This is a DTT Framework application created with the CLI.
4
4
 
5
- ## Getting Started
5
+ ## Quick Start
6
6
 
7
- First, install dependencies:
7
+ ### Prerequisites
8
+
9
+ - **Node.js** 20 or higher
10
+ - **pnpm** 10 or higher
11
+ - **Supabase account** ([Sign up](https://supabase.com/))
12
+ - **Clerk account** ([Sign up](https://clerk.com/))
13
+ - **Snowflake account** (optional)
14
+
15
+ ### 1. Environment Setup
16
+
17
+ Copy the example environment file and fill in your credentials:
8
18
 
9
19
  ```bash
10
- pnpm install
20
+ cp .env.example .env
11
21
  ```
12
22
 
13
- Then, run the development server:
23
+ Edit `.env` and fill in the required keys for Clerk and Supabase. See [Environment Variables](docs/framework/environment-variables.md) for details.
24
+
25
+ ### 2. Database Setup
26
+
27
+ Push the database schema to your Supabase project:
28
+
29
+ ```bash
30
+ pnpm db:push
31
+ ```
32
+
33
+ This will create the necessary tables, including `health_check_tests` required for the health dashboard.
34
+
35
+ ### 3. Deploy Edge Functions
36
+
37
+ The health check system requires a Supabase Edge Function. Deploy it using the Supabase CLI:
38
+
39
+ ```bash
40
+ # Login to Supabase
41
+ npx supabase login
42
+
43
+ # Link your project (get your-project-ref from Supabase dashboard URL)
44
+ npx supabase link --project-ref your-project-ref
45
+
46
+ # Deploy the function
47
+ npx supabase functions deploy health-check
48
+ ```
49
+
50
+ ### 4. Start Development Server
14
51
 
15
52
  ```bash
16
53
  pnpm dev
17
54
  ```
18
55
 
19
- Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
56
+ Open [http://localhost:3000](http://localhost:3000) with your browser.
57
+
58
+ ### 5. Verify Health
20
59
 
21
- ## Learn More
60
+ Visit [http://localhost:3000/health](http://localhost:3000/health) to verify all services are connected and working.
22
61
 
23
- To learn more about DTT Framework, visit the [documentation](./docs/framework/01-overview.md).
62
+ ---
24
63
 
25
64
  ## Documentation
26
65
 
@@ -28,3 +67,114 @@ To learn more about DTT Framework, visit the [documentation](./docs/framework/01
28
67
  - [Tech Stack](./docs/framework/02-techstack.md)
29
68
  - [Environment Variables](./docs/framework/environment-variables.md)
30
69
  - [CLI Installation](./docs/framework/cli-installation.md)
70
+ - [Health Check System](./docs/framework/health-check-system.md)
71
+ - [Clerk Authentication](./docs/framework/clerk-authentication.md)
72
+ - [Supabase Integration](./docs/framework/supabase-integration.md)
73
+ - [Snowflake Integration](./docs/framework/snowflake-integration.md)
74
+
75
+ ## Key Features
76
+
77
+ ### 1. Authentication & User Management
78
+ - **Clerk Integration**: Complete authentication solution with sign-in, sign-up, and user management
79
+ - **Organization Support**: Multi-tenant architecture with organization memberships
80
+ - **Webhook Synchronization**: Automatic user data sync to local database
81
+ - **Protected Routes**: Middleware-based route protection
82
+
83
+ ### 2. Database Layer
84
+ - **PostgreSQL**: Robust relational database via Supabase
85
+ - **Drizzle ORM**: Type-safe database queries with excellent TypeScript support
86
+ - **Connection Pooling**: Optimized for Supabase Transaction mode
87
+ - **Schema Management**: Migration-based schema evolution
88
+
89
+ ### 3. Storage & Edge Computing
90
+ - **Supabase Storage**: File upload/download with signed URLs
91
+ - **Edge Functions**: Serverless compute with auth header passthrough
92
+ - **Bucket Management**: Organized file storage structure
93
+
94
+ ### 4. Data Warehouse
95
+ - **Snowflake Integration**: Direct connection to Snowflake data warehouse
96
+ - **Query Execution**: Execute SQL queries from the application
97
+ - **Warehouse Configuration**: Support for multiple warehouses and databases
98
+
99
+ ### 5. API Layer
100
+ - **Hono Framework**: Lightweight and fast API framework
101
+ - **Middleware Support**: Authentication, logging, CORS
102
+ - **Route Organization**: Modular route structure
103
+ - **Type Safety**: Full TypeScript support with inferred types
104
+
105
+ ### 6. State Management
106
+ - **TanStack Query**: Server state management with caching and synchronization
107
+ - **Zustand**: Lightweight client state management
108
+ - **React Hooks**: Custom hooks for common patterns
109
+
110
+ ### 7. Health Check System
111
+ - **Comprehensive Monitoring**: Health checks for all integrated services
112
+ - **Dashboard UI**: Visual health status display
113
+ - **Response Time Tracking**: Performance monitoring
114
+ - **Error Reporting**: Detailed error messages and status codes
115
+
116
+ ### 8. Developer Experience
117
+ - **TypeScript**: Full type safety across the stack
118
+ - **Tailwind CSS**: Utility-first styling
119
+ - **Shadcn/ui**: Beautiful, accessible UI components
120
+ - **Hot Reload**: Fast development with Next.js Turbo
121
+ - **Linting & Formatting**: ESLint and Prettier configured
122
+
123
+ ## Folder Structure
124
+
125
+ ```
126
+ dtt-framework/
127
+ ├── docs/ # Framework documentation
128
+ ├── public/ # Static assets
129
+ ├── src/ # Source code
130
+ │ ├── app/ # Next.js App Router
131
+ │ ├── components/ # React components
132
+ │ ├── features/ # Feature modules
133
+ │ ├── hooks/ # React hooks
134
+ │ ├── lib/ # Utility libraries and clients
135
+ │ ├── server/ # Server-side code (API, database)
136
+ │ ├── stores/ # Zustand state stores
137
+ │ ├── types/ # TypeScript types
138
+ │ ├── config/ # Configuration files
139
+ │ ├── env.js # Environment variables
140
+ │ ├── middleware.ts # Next.js middleware
141
+ │ └── styles/ # Styles
142
+ ├── .env.example # Environment variables template
143
+ ├── drizzle.config.ts # Drizzle ORM configuration
144
+ ├── next.config.js # Next.js configuration
145
+ ├── package.json # Dependencies and scripts
146
+ └── README.md # This file
147
+ ```
148
+
149
+ ## Development
150
+
151
+ ### Available Scripts
152
+
153
+ | Script | Description |
154
+ |--------|-------------|
155
+ | `pnpm dev` | Start development server with Turbo |
156
+ | `pnpm build` | Build for production |
157
+ | `pnpm start` | Start production server |
158
+ | `pnpm lint` | Run ESLint |
159
+ | `pnpm format:write` | Format code with Prettier |
160
+ | `pnpm typecheck` | Run TypeScript type checking |
161
+ | `pnpm db:generate` | Generate database migrations |
162
+ | `pnpm db:migrate` | Apply database migrations |
163
+ | `pnpm db:push` | Push schema changes to database |
164
+ | `pnpm db:studio` | Open Drizzle Studio |
165
+
166
+ ## Deployment
167
+
168
+ For deployment instructions, please refer to the [Deployment Guide](docs/framework/deployment/vercel.md).
169
+
170
+ ## Support
171
+
172
+ If you need help:
173
+
174
+ 1. **Check the documentation** in `docs/` folder
175
+ 2. **Check the health dashboard** at `/health`
176
+ 3. **Open an issue** on GitHub
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,18 @@
1
+ CREATE TABLE "users" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "email" varchar(255) NOT NULL,
4
+ "first_name" varchar(255),
5
+ "last_name" varchar(255),
6
+ "image_url" text,
7
+ "clerk_org_id" text,
8
+ "created_at" timestamp DEFAULT now() NOT NULL,
9
+ "updated_at" timestamp DEFAULT now() NOT NULL,
10
+ CONSTRAINT "users_email_unique" UNIQUE("email")
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE TABLE "health_check_tests" (
14
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
15
+ "test_key" text NOT NULL,
16
+ "test_value" text,
17
+ "created_at" timestamp DEFAULT now() NOT NULL
18
+ );
@@ -0,0 +1,129 @@
1
+ {
2
+ "id": "8ef2966f-b7b3-4fd4-adfc-84bfe2fdbf38",
3
+ "prevId": "00000000-0000-0000-0000-000000000000",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.users": {
8
+ "name": "users",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "text",
14
+ "primaryKey": true,
15
+ "notNull": true
16
+ },
17
+ "email": {
18
+ "name": "email",
19
+ "type": "varchar(255)",
20
+ "primaryKey": false,
21
+ "notNull": true
22
+ },
23
+ "first_name": {
24
+ "name": "first_name",
25
+ "type": "varchar(255)",
26
+ "primaryKey": false,
27
+ "notNull": false
28
+ },
29
+ "last_name": {
30
+ "name": "last_name",
31
+ "type": "varchar(255)",
32
+ "primaryKey": false,
33
+ "notNull": false
34
+ },
35
+ "image_url": {
36
+ "name": "image_url",
37
+ "type": "text",
38
+ "primaryKey": false,
39
+ "notNull": false
40
+ },
41
+ "clerk_org_id": {
42
+ "name": "clerk_org_id",
43
+ "type": "text",
44
+ "primaryKey": false,
45
+ "notNull": false
46
+ },
47
+ "created_at": {
48
+ "name": "created_at",
49
+ "type": "timestamp",
50
+ "primaryKey": false,
51
+ "notNull": true,
52
+ "default": "now()"
53
+ },
54
+ "updated_at": {
55
+ "name": "updated_at",
56
+ "type": "timestamp",
57
+ "primaryKey": false,
58
+ "notNull": true,
59
+ "default": "now()"
60
+ }
61
+ },
62
+ "indexes": {},
63
+ "foreignKeys": {},
64
+ "compositePrimaryKeys": {},
65
+ "uniqueConstraints": {
66
+ "users_email_unique": {
67
+ "name": "users_email_unique",
68
+ "nullsNotDistinct": false,
69
+ "columns": [
70
+ "email"
71
+ ]
72
+ }
73
+ },
74
+ "policies": {},
75
+ "checkConstraints": {},
76
+ "isRLSEnabled": false
77
+ },
78
+ "public.health_check_tests": {
79
+ "name": "health_check_tests",
80
+ "schema": "",
81
+ "columns": {
82
+ "id": {
83
+ "name": "id",
84
+ "type": "uuid",
85
+ "primaryKey": true,
86
+ "notNull": true,
87
+ "default": "gen_random_uuid()"
88
+ },
89
+ "test_key": {
90
+ "name": "test_key",
91
+ "type": "text",
92
+ "primaryKey": false,
93
+ "notNull": true
94
+ },
95
+ "test_value": {
96
+ "name": "test_value",
97
+ "type": "text",
98
+ "primaryKey": false,
99
+ "notNull": false
100
+ },
101
+ "created_at": {
102
+ "name": "created_at",
103
+ "type": "timestamp",
104
+ "primaryKey": false,
105
+ "notNull": true,
106
+ "default": "now()"
107
+ }
108
+ },
109
+ "indexes": {},
110
+ "foreignKeys": {},
111
+ "compositePrimaryKeys": {},
112
+ "uniqueConstraints": {},
113
+ "policies": {},
114
+ "checkConstraints": {},
115
+ "isRLSEnabled": false
116
+ }
117
+ },
118
+ "enums": {},
119
+ "schemas": {},
120
+ "sequences": {},
121
+ "roles": {},
122
+ "policies": {},
123
+ "views": {},
124
+ "_meta": {
125
+ "columns": {},
126
+ "schemas": {},
127
+ "tables": {}
128
+ }
129
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1767269294126,
9
+ "tag": "0000_awesome_argent",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,61 @@
1
+ import { FlatCompat } from "@eslint/eslintrc";
2
+ import tseslint from "typescript-eslint";
3
+ // @ts-ignore -- no types for this plugin
4
+ import drizzle from "eslint-plugin-drizzle";
5
+
6
+ const compat = new FlatCompat({
7
+ baseDirectory: import.meta.dirname,
8
+ });
9
+
10
+ export default tseslint.config(
11
+ {
12
+ ignores: [".next"],
13
+ },
14
+ ...compat.extends("next/core-web-vitals"),
15
+ {
16
+ files: ["**/*.ts", "**/*.tsx"],
17
+ plugins: {
18
+ drizzle,
19
+ },
20
+ extends: [
21
+ ...tseslint.configs.recommended,
22
+ ...tseslint.configs.recommendedTypeChecked,
23
+ ...tseslint.configs.stylisticTypeChecked,
24
+ ],
25
+ rules: {
26
+ "@typescript-eslint/array-type": "off",
27
+ "@typescript-eslint/consistent-type-definitions": "off",
28
+ "@typescript-eslint/consistent-type-imports": [
29
+ "warn",
30
+ { prefer: "type-imports", fixStyle: "inline-type-imports" },
31
+ ],
32
+ "@typescript-eslint/no-unused-vars": [
33
+ "warn",
34
+ { argsIgnorePattern: "^_" },
35
+ ],
36
+ "@typescript-eslint/require-await": "off",
37
+ "@typescript-eslint/no-misused-promises": [
38
+ "error",
39
+ { checksVoidReturn: { attributes: false } },
40
+ ],
41
+ "drizzle/enforce-delete-with-where": [
42
+ "error",
43
+ { drizzleObjectName: ["db", "ctx.db"] },
44
+ ],
45
+ "drizzle/enforce-update-with-where": [
46
+ "error",
47
+ { drizzleObjectName: ["db", "ctx.db"] },
48
+ ],
49
+ },
50
+ },
51
+ {
52
+ linterOptions: {
53
+ reportUnusedDisableDirectives: true,
54
+ },
55
+ languageOptions: {
56
+ parserOptions: {
57
+ projectService: true,
58
+ },
59
+ },
60
+ },
61
+ );
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # Use this script to start a docker container for a local development database
3
+
4
+ # TO RUN ON WINDOWS:
5
+ # 1. Install WSL (Windows Subsystem for Linux) - https://learn.microsoft.com/en-us/windows/wsl/install
6
+ # 2. Install Docker Desktop or Podman Deskop
7
+ # - Docker Desktop for Windows - https://docs.docker.com/docker-for-windows/install/
8
+ # - Podman Desktop - https://podman.io/getting-started/installation
9
+ # 3. Open WSL - `wsl`
10
+ # 4. Run this script - `./start-database.sh`
11
+
12
+ # On Linux and macOS you can run this script directly - `./start-database.sh`
13
+
14
+ # import env variables from .env
15
+ set -a
16
+ source .env
17
+
18
+ DB_PASSWORD=$(echo "$DATABASE_URL" | awk -F':' '{print $3}' | awk -F'@' '{print $1}')
19
+ DB_PORT=$(echo "$DATABASE_URL" | awk -F':' '{print $4}' | awk -F'\/' '{print $1}')
20
+ DB_NAME=$(echo "$DATABASE_URL" | awk -F'/' '{print $4}')
21
+ DB_CONTAINER_NAME="$DB_NAME-postgres"
22
+
23
+ if ! [ -x "$(command -v docker)" ] && ! [ -x "$(command -v podman)" ]; then
24
+ echo -e "Docker or Podman is not installed. Please install docker or podman and try again.\nDocker install guide: https://docs.docker.com/engine/install/\nPodman install guide: https://podman.io/getting-started/installation"
25
+ exit 1
26
+ fi
27
+
28
+ # determine which docker command to use
29
+ if [ -x "$(command -v docker)" ]; then
30
+ DOCKER_CMD="docker"
31
+ elif [ -x "$(command -v podman)" ]; then
32
+ DOCKER_CMD="podman"
33
+ fi
34
+
35
+ if ! $DOCKER_CMD info > /dev/null 2>&1; then
36
+ echo "$DOCKER_CMD daemon is not running. Please start $DOCKER_CMD and try again."
37
+ exit 1
38
+ fi
39
+
40
+ if command -v nc >/dev/null 2>&1; then
41
+ if nc -z localhost "$DB_PORT" 2>/dev/null; then
42
+ echo "Port $DB_PORT is already in use."
43
+ exit 1
44
+ fi
45
+ else
46
+ echo "Warning: Unable to check if port $DB_PORT is already in use (netcat not installed)"
47
+ read -p "Do you want to continue anyway? [y/N]: " -r REPLY
48
+ if ! [[ $REPLY =~ ^[Yy]$ ]]; then
49
+ echo "Aborting."
50
+ exit 1
51
+ fi
52
+ fi
53
+
54
+ if [ "$($DOCKER_CMD ps -q -f name=$DB_CONTAINER_NAME)" ]; then
55
+ echo "Database container '$DB_CONTAINER_NAME' already running"
56
+ exit 0
57
+ fi
58
+
59
+ if [ "$($DOCKER_CMD ps -q -a -f name=$DB_CONTAINER_NAME)" ]; then
60
+ $DOCKER_CMD start "$DB_CONTAINER_NAME"
61
+ echo "Existing database container '$DB_CONTAINER_NAME' started"
62
+ exit 0
63
+ fi
64
+
65
+ if [ "$DB_PASSWORD" = "password" ]; then
66
+ echo "You are using the default database password"
67
+ read -p "Should we generate a random password for you? [y/N]: " -r REPLY
68
+ if ! [[ $REPLY =~ ^[Yy]$ ]]; then
69
+ echo "Please change the default password in the .env file and try again"
70
+ exit 1
71
+ fi
72
+ # Generate a random URL-safe password
73
+ DB_PASSWORD=$(openssl rand -base64 12 | tr '+/' '-_')
74
+ if [[ "$(uname)" == "Darwin" ]]; then
75
+ # macOS requires an empty string to be passed with the `i` flag
76
+ sed -i '' "s#:password@#:$DB_PASSWORD@#" .env
77
+ else
78
+ sed -i "s#:password@#:$DB_PASSWORD@#" .env
79
+ fi
80
+ fi
81
+
82
+ $DOCKER_CMD run -d \
83
+ --name $DB_CONTAINER_NAME \
84
+ -e POSTGRES_USER="postgres" \
85
+ -e POSTGRES_PASSWORD="$DB_PASSWORD" \
86
+ -e POSTGRES_DB="$DB_NAME" \
87
+ -p "$DB_PORT":5432 \
88
+ docker.io/postgres && echo "Database container '$DB_CONTAINER_NAME' was successfully created"
@@ -0,0 +1 @@
1
+ v2.67.1
@@ -0,0 +1,84 @@
1
+ // Follow this setup guide to deploy the function:
2
+ // https://supabase.com/docs/guides/functions/deploy
3
+
4
+ import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
5
+
6
+ const corsHeaders = {
7
+ 'Access-Control-Allow-Origin': '*',
8
+ 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
9
+ }
10
+
11
+ serve(async (req) => {
12
+ if (req.method === 'OPTIONS') {
13
+ return new Response('ok', { headers: corsHeaders })
14
+ }
15
+
16
+ try {
17
+ const { message } = await req.json()
18
+ let authHeader = req.headers.get('Authorization')
19
+ const testAuthHeader = req.headers.get('x-test-auth')
20
+
21
+ // Handle ping check
22
+ if (message === 'ping') {
23
+ return new Response(
24
+ JSON.stringify({
25
+ message: 'pong',
26
+ timestamp: new Date().toISOString(),
27
+ }),
28
+ {
29
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
30
+ status: 200,
31
+ },
32
+ )
33
+ }
34
+
35
+ // Handle auth check
36
+ if (message === 'auth-check') {
37
+ // Prioritize test header if present (allows testing without bypassing Supabase Gateway auth)
38
+ if (testAuthHeader) {
39
+ authHeader = testAuthHeader
40
+ }
41
+
42
+ if (!authHeader) {
43
+ return new Response(
44
+ JSON.stringify({ error: 'No authorization header provided' }),
45
+ {
46
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
47
+ status: 401,
48
+ },
49
+ )
50
+ }
51
+
52
+ return new Response(
53
+ JSON.stringify({
54
+ message: 'Auth header received',
55
+ hasAuth: true,
56
+ // Don't log/return the full token in production for security
57
+ tokenPrefix: authHeader.substring(0, 10) + '...',
58
+ source: testAuthHeader ? 'x-test-auth' : 'Authorization'
59
+ }),
60
+ {
61
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
62
+ status: 200,
63
+ },
64
+ )
65
+ }
66
+
67
+ return new Response(
68
+ JSON.stringify({ error: 'Unknown message type' }),
69
+ {
70
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
71
+ status: 400,
72
+ },
73
+ )
74
+ } catch (error) {
75
+ return new Response(
76
+ JSON.stringify({ error: error.message }),
77
+ {
78
+ headers: { ...corsHeaders, 'Content-Type': 'application/json' },
79
+ status: 400,
80
+ },
81
+ )
82
+ }
83
+ })
84
+