├── .env.example ├── .gitattributes ├── .gitignore ├── .npmrc ├── .nvmrc ├── LICENSE ├── README.md ├── apps ├── apiservice │ ├── eslint.config.js │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── turbo.json │ └── wrangler.toml ├── astro │ ├── .gitignore │ ├── README.md │ ├── astro.config.mjs │ ├── components.json │ ├── eslint.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── favicon.svg │ │ └── logos │ │ │ ├── astro.svg │ │ │ ├── clerk.svg │ │ │ ├── cloudflare.svg │ │ │ ├── d1.svg │ │ │ ├── expo.svg │ │ │ ├── r2.svg │ │ │ ├── trpc.svg │ │ │ ├── turborepo.svg │ │ │ └── workers.svg │ ├── src │ │ ├── components │ │ │ ├── react │ │ │ │ ├── footer.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── hero.tsx │ │ │ │ ├── primary-features.tsx │ │ │ │ └── secondary-features.tsx │ │ │ └── ui │ │ │ │ ├── button.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ └── input.tsx │ │ ├── env.d.ts │ │ ├── layouts │ │ │ └── Layout.astro │ │ ├── lib │ │ │ └── utils.ts │ │ ├── pages │ │ │ └── index.astro │ │ └── styles │ │ │ └── globals.css │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── turbo.json │ └── wrangler.toml ├── expo │ ├── .env.example │ ├── .npmrc │ ├── README.md │ ├── app.json │ ├── app │ │ ├── (auth) │ │ │ ├── _layout.tsx │ │ │ ├── sign-in.tsx │ │ │ └── sign-up.tsx │ │ ├── (tabs) │ │ │ ├── _layout.tsx │ │ │ ├── account.tsx │ │ │ ├── add.tsx │ │ │ └── lore.tsx │ │ ├── _layout.tsx │ │ └── index.tsx │ ├── assets │ │ ├── fonts │ │ │ └── SpaceMono-Regular.ttf │ │ ├── images │ │ │ ├── adaptive-icon.png │ │ │ ├── favicon.png │ │ │ ├── icon.png │ │ │ ├── partial-react-logo.png │ │ │ ├── placeholder.png │ │ │ ├── react-logo.png │ │ │ ├── react-logo@2x.png │ │ │ ├── react-logo@3x.png │ │ │ ├── splash-icon.png │ │ │ └── splash.png │ │ └── styles │ │ │ ├── 3d.png │ │ │ ├── childrens.png │ │ │ ├── classical.png │ │ │ └── ethereal.png │ ├── babel.config.ts │ ├── eslint.config.mjs │ ├── package.json │ ├── tsconfig.json │ ├── turbo.json │ └── utils │ │ ├── TRPCProvider.tsx │ │ ├── api.tsx │ │ └── base-url.tsx └── workflows │ ├── .editorconfig │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── eslint.config.js │ ├── package.json │ ├── src │ ├── index.ts │ └── workflows │ │ ├── daily-recap.ts │ │ └── get-users-for-recap.ts │ ├── tsconfig.json │ ├── turbo.json │ ├── worker-configuration.d.ts │ └── wrangler.toml ├── package.json ├── packages ├── db │ ├── drizzle.config.ts │ ├── eslint.config.js │ ├── migrations │ │ ├── 0000_youthful_proudstar.sql │ │ ├── 0001_swift_puck.sql │ │ ├── 0002_sharp_leech.sql │ │ ├── 0003_swift_sunfire.sql │ │ ├── 0004_thin_thunderbolt.sql │ │ ├── 0005_chunky_rocket_raccoon.sql │ │ ├── 0006_pretty_morbius.sql │ │ ├── 0007_chief_quentin_quire.sql │ │ └── meta │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ ├── 0004_snapshot.json │ │ │ ├── 0005_snapshot.json │ │ │ ├── 0006_snapshot.json │ │ │ ├── 0007_snapshot.json │ │ │ └── _journal.json │ ├── package.json │ ├── src │ │ ├── client.ts │ │ ├── index.ts │ │ └── schema.ts │ └── tsconfig.json └── trpc │ ├── eslint.config.js │ ├── package.json │ ├── src │ ├── index.ts │ ├── root.ts │ ├── router │ │ ├── moments.ts │ │ ├── post.ts │ │ ├── recaps.ts │ │ └── user.ts │ ├── trpc.ts │ └── types.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tooling ├── eslint │ ├── base.js │ ├── nextjs.js │ ├── package.json │ ├── react.js │ ├── tsconfig.json │ └── types.d.ts ├── github │ ├── package.json │ └── setup │ │ └── action.yml ├── prettier │ ├── index.js │ ├── package.json │ └── tsconfig.json ├── tailwind │ ├── base.ts │ ├── eslint.config.js │ ├── native.ts │ ├── package.json │ ├── tsconfig.json │ └── web.ts └── typescript │ ├── base.json │ ├── internal-package.json │ └── package.json ├── turbo.json └── turbo └── generators ├── config.ts └── templates ├── eslint.config.js.hbs ├── package.json.hbs └── tsconfig.json.hbs /.env.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_ACCOUNT_ID= 2 | CLOUDFLARE_DATABASE_ID= 3 | CLOUDFLARE_D1_TOKEN= -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | next-env.d.ts 15 | 16 | # nitro 17 | .nitro/ 18 | .output/ 19 | 20 | # expo 21 | .expo/ 22 | expo-env.d.ts 23 | apps/expo/.gitignore 24 | apps/expo/ios 25 | apps/expo/android 26 | 27 | # production 28 | build 29 | 30 | # misc 31 | .DS_Store 32 | *.pem 33 | 34 | # debug 35 | npm-debug.log* 36 | yarn-debug.log* 37 | yarn-error.log* 38 | .pnpm-debug.log* 39 | 40 | # local env files 41 | .env 42 | .env*.local 43 | 44 | # vercel 45 | .vercel 46 | 47 | # typescript 48 | dist/ 49 | .cache 50 | 51 | # turbo 52 | .turbo 53 | /apps/expo-app/android/app/src/debug/AndroidManifest.xml 54 | /apps/expo-app/android/app/src/main/AndroidManifest.xml 55 | /apps/expo-app/android/app/build.gradle 56 | /apps/expo-app/android/build.gradle 57 | /apps/expo-app/android/app/src/main/res/values/colors.xml 58 | /apps/expo-app/android/app/src/main/res/values-night/colors.xml 59 | /apps/expo-app/android/app/debug.keystore 60 | /apps/expo-app/android/gradle.properties 61 | /apps/expo-app/android/gradle/wrapper/gradle-wrapper.jar 62 | /apps/expo-app/android/gradle/wrapper/gradle-wrapper.properties 63 | /apps/expo-app/android/gradlew 64 | /apps/expo-app/android/gradlew.bat 65 | /apps/expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp 66 | /apps/expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp 67 | /apps/expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp 68 | /apps/expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp 69 | /apps/expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp 70 | /apps/expo-app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml 71 | /apps/expo-app/android/app/src/main/res/drawable/ic_launcher_background.xml 72 | /apps/expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp 73 | /apps/expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp 74 | /apps/expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp 75 | /apps/expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp 76 | /apps/expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp 77 | /apps/expo-app/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp 78 | /apps/expo-app/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp 79 | /apps/expo-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp 80 | /apps/expo-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp 81 | /apps/expo-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp 82 | /apps/expo-app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml 83 | /apps/expo-app/android/app/src/main/java/com/anonymous/expoapp/MainActivity.kt 84 | /apps/expo-app/android/app/src/main/java/com/anonymous/expoapp/MainApplication.kt 85 | /apps/expo-app/android/app/proguard-rules.pro 86 | /apps/expo-app/android/app/src/main/res/drawable/rn_edit_text_material.xml 87 | /apps/expo-app/android/settings.gradle 88 | /apps/expo-app/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png 89 | /apps/expo-app/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png 90 | /apps/expo-app/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png 91 | /apps/expo-app/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png 92 | /apps/expo-app/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png 93 | /apps/expo-app/android/app/src/main/res/values/strings.xml 94 | /apps/expo-app/android/app/src/main/res/values/styles.xml 95 | /apps/apiservice/.wrangler/ 96 | /packages/db/.wrangler/ 97 | /apps/eventsservice/.wrangler/ 98 | .dev.vars 99 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | link-workspace-packages=true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.16 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Brenden Padilla 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cloudflare Native and Web Starter Kit 2 | 3 | A starter template that demonstrates how to build AI-powered mobile and web applications using Cloudflare workers and wrangler CLI. This example app creates AI-generated stories about a user's day and generates accompanying images - all powered by Cloudflare workers. You can use this as a foundation to build any type of AI-powered application you want. 4 | 5 | ## 🚀 Features 6 | 7 | - 📱 **Expo Mobile App**: Cross-platform mobile application 8 | - 🌐 **Astro Landing Page**: Fast, modern web presence 9 | - 🔒 **Clerk Authentication**: Secure user management 10 | - 🔄 **tRPC API**: Type-safe API communication 11 | - 🤖 **Workers AI**: Edge AI processing 12 | - 📦 **R2 Storage**: Image and asset storage 13 | - 💾 **D1 Database**: Edge SQLite database with Drizzle ORM 14 | - 🏗️ **Cloudflare Workers**: Serverless compute 15 | - 🔄 **Workflows**: Durable AI task processing 16 | 17 | ## 📦 Project Structure 18 | 19 | ``` 20 | . 21 | ├── apps/ 22 | │ ├── apiservice/ # Cloudflare Worker API 23 | │ ├── expo/ # Mobile application 24 | │ ├── astro/ # Landing page 25 | │ └── workflows/ # Cloudflare Workers AI processing 26 | ├── packages/ 27 | │ ├── db/ # Database schema and utilities 28 | │ └── trpc/ # tRPC router definitions 29 | └── tooling/ # Shared development tools 30 | ``` 31 | 32 | ## 🛠️ Prerequisites 33 | 34 | - Node.js >= 20.16.0 35 | - pnpm >= 9.6.0 36 | - Cloudflare account 37 | - Wrangler CLI (`npm install -g wrangler`) 38 | - Clerk account 39 | 40 | ## 🚀 Getting Started 41 | 42 | 1. **Clone the repository** 43 | 44 | ```bash 45 | git clone 46 | cd cloudflare-turbo 47 | ``` 48 | 49 | 2. **Install dependencies** 50 | 51 | ```bash 52 | pnpm install 53 | ``` 54 | 55 | 3. **Configure Cloudflare Resources** 56 | 57 | First, login to Cloudflare CLI: 58 | ```bash 59 | wrangler login 60 | ``` 61 | 62 | Create a D1 Database: 63 | ```bash 64 | wrangler d1 create your-database-name 65 | ``` 66 | 67 | Create an R2 Bucket: 68 | ```bash 69 | wrangler r2 bucket create your-bucket-name 70 | ``` 71 | 72 | 4. **Configure wrangler.toml Files** 73 | 74 | You'll need to update the wrangler.toml files in both `apps/apiservice` and `apps/workflows` with your specific configuration: 75 | 76 | Example `wrangler.toml` structure: 77 | ```toml 78 | name = "your-app-name" 79 | main = "src/index.ts" 80 | 81 | [[d1_databases]] 82 | binding = "DB" 83 | database_name = "your-database-name" 84 | database_id = "your-database-id" 85 | 86 | [[r2_buckets]] 87 | binding = "BUCKET" 88 | bucket_name = "your-bucket-name" 89 | ``` 90 | 91 | 5. **Set up Cloudflare API Token** 92 | 93 | Create a Cloudflare API token with D1 read and write permissions: 94 | 95 | 1. Go to the [Cloudflare Dashboard](https://dash.cloudflare.com/profile/api-tokens) 96 | 2. Click "Create Token" 97 | 3. Use the "Create Custom Token" option 98 | 4. Grant the following permissions: 99 | - Account > D1 > Edit 100 | 5. Copy the generated token 101 | 102 | Create a `.env` file in the root of the project with the following variables: 103 | ```bash 104 | CLOUDFLARE_ACCOUNT_ID="your-account-id" 105 | CLOUDFLARE_DATABASE_ID="your-d1-database-id" 106 | CLOUDFLARE_D1_TOKEN="your-api-token" 107 | ``` 108 | 109 | You can find your Account ID in the Cloudflare Dashboard URL or overview page. 110 | The Database ID was provided when you created your D1 database. 111 | 112 | 6. **Initial Deployment** 113 | 114 | Before running the app locally, you need to deploy the API service and workflows: 115 | 116 | ```bash 117 | # Deploy API service 118 | cd apps/apiservice 119 | pnpm run deploy 120 | 121 | # Deploy workflows 122 | cd ../workflows 123 | pnpm run deploy 124 | ``` 125 | 126 | 7. **Environment Setup** 127 | 128 | Create `.env` files based on the provided examples and update with your credentials: 129 | ```bash 130 | cp apps/expo/.env.example apps/expo/.env 131 | ``` 132 | 133 | 8. **Start Development** 134 | 135 | ```bash 136 | pnpm dev 137 | ``` 138 | -------------------------------------------------------------------------------- /apps/apiservice/eslint.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from "@acme/eslint-config/base"; 2 | 3 | /** @type {import('typescript-eslint').Config} */ 4 | export default [ 5 | { 6 | ignores: ["dist/**"], 7 | }, 8 | ...baseConfig 9 | ]; 10 | -------------------------------------------------------------------------------- /apps/apiservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/apiservice", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "wrangler dev --ip 0.0.0.0 --remote", 8 | "build": "tsc", 9 | "deploy": "wrangler publish", 10 | "clean": "git clean -xdf .cache .turbo node_modules .wrangler" 11 | }, 12 | "dependencies": { 13 | "@acme/db": "workspace:*", 14 | "@acme/trpc": "workspace:*", 15 | "typescript": "catalog:", 16 | "wrangler": "3.93.0" 17 | }, 18 | "devDependencies": { 19 | "@acme/eslint-config": "workspace:*", 20 | "@acme/tsconfig": "workspace:*", 21 | "@types/node": "^20.16.11" 22 | } 23 | } -------------------------------------------------------------------------------- /apps/apiservice/src/index.ts: -------------------------------------------------------------------------------- 1 | // This file is the entrypoint for this Cloudflare worker, which contains our API service. 2 | // It uses the TRPC framework to handle requests and responses in a Type safe manner. 3 | // It also has access to our Cloudflare D1 database, R2 bucket, and Clerk for authentication 4 | // for securely storing and retrieving data. 5 | 6 | // We use the getDB function from the db package to create the DrizzleDB dynamically, and pass 7 | //it to the tRPC context. 8 | 9 | // We set up the Clerk client in our tRPC context, so we need to pass the publishable key and 10 | // secret key set in our wrangler.toml. Production apps should use wrangler secrets to keep 11 | //your secrets out of your codebase. 12 | 13 | // Additionally, in our package.json dev script, we set the ip flag to 0.0.0.0 to allow the API 14 | //service to be accessed from Expo Go for local development 15 | 16 | import { appRouter, createContext } from '@acme/trpc'; 17 | import type { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch'; 18 | import { fetchRequestHandler } from '@trpc/server/adapters/fetch'; 19 | import type { D1Database } from '@cloudflare/workers-types'; 20 | import { getDB } from "@acme/db"; 21 | import type { R2Bucket } from '@cloudflare/workers-types'; 22 | 23 | interface Env { 24 | CLERK_PUBLISHABLE_KEY: string; 25 | CLERK_SECRET_KEY: string; 26 | DB: D1Database; 27 | IMAGES_BUCKET: R2Bucket; 28 | } 29 | 30 | export default { 31 | async fetch(request: Request, env: Env): Promise { 32 | const db = getDB(env); 33 | const corsHeaders = { 34 | 'Access-Control-Allow-Origin': '*', 35 | 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', 36 | 'Access-Control-Allow-Headers': 'Content-Type', 37 | }; 38 | 39 | // Handle OPTIONS preflight requests 40 | if (request.method === 'OPTIONS') { 41 | return new Response(null, { 42 | headers: corsHeaders 43 | }); 44 | } 45 | 46 | // Your existing TRPC handler 47 | return fetchRequestHandler({ 48 | endpoint: '/trpc', 49 | req: request, 50 | router: appRouter, 51 | createContext: (opts: FetchCreateContextFnOptions) => createContext({ 52 | ...opts, 53 | clerkPublicKey: env.CLERK_PUBLISHABLE_KEY, 54 | clerkSecretKey: env.CLERK_SECRET_KEY, 55 | db, 56 | imagesBucket: env.IMAGES_BUCKET 57 | }), 58 | }); 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /apps/apiservice/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@acme/tsconfig/base.json", 3 | "exclude": ["node_modules"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/apiservice/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "dev": { 6 | "persistent": true, 7 | "interactive": true 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /apps/apiservice/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "apiservice" 2 | compatibility_date = "2024-09-23" 3 | compatibility_flags = ["nodejs_compat"] 4 | 5 | main = "src/index.ts" 6 | 7 | [build] 8 | command = "pnpm run build" 9 | 10 | [[r2_buckets]] 11 | bucket_name = "" 12 | preview_bucket_name = "" 13 | binding = "" 14 | 15 | [[d1_databases]] 16 | binding = "DB" 17 | database_name = "" 18 | database_id = "" 19 | 20 | 21 | # You can use wrangler to set secrets and environments to keep your secrets out of your codebase. 22 | [vars] 23 | WRANGLER_BUILD_CONDITIONS="" 24 | WRANGLER_BUILD_PLATFORM="node" 25 | CLERK_PUBLISHABLE_KEY = "" 26 | CLERK_SECRET_KEY = "" 27 | 28 | [dev] 29 | port = 8787 30 | 31 | 32 | -------------------------------------------------------------------------------- /apps/astro/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | # generated types 4 | .astro/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # logs 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | .wrangler/ 16 | 17 | # environment variables 18 | .env 19 | .env.production 20 | 21 | # macOS-specific files 22 | .DS_Store 23 | 24 | # jetbrains setting folder 25 | .idea/ 26 | -------------------------------------------------------------------------------- /apps/astro/README.md: -------------------------------------------------------------------------------- 1 | # Astro Starter Kit: Minimal 2 | 3 | ```sh 4 | npm create astro@latest -- --template minimal 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal) 8 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal) 9 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json) 10 | 11 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 12 | 13 | ## 🚀 Project Structure 14 | 15 | Inside of your Astro project, you'll see the following folders and files: 16 | 17 | ```text 18 | / 19 | ├── public/ 20 | ├── src/ 21 | │ └── pages/ 22 | │ └── index.astro 23 | └── package.json 24 | ``` 25 | 26 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. 27 | 28 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. 29 | 30 | Any static assets, like images, can be placed in the `public/` directory. 31 | 32 | ## 🧞 Commands 33 | 34 | All commands are run from the root of the project, from a terminal: 35 | 36 | | Command | Action | 37 | | :------------------------ | :----------------------------------------------- | 38 | | `npm install` | Installs dependencies | 39 | | `npm run dev` | Starts local dev server at `localhost:4321` | 40 | | `npm run build` | Build your production site to `./dist/` | 41 | | `npm run preview` | Preview your build locally, before deploying | 42 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 43 | | `npm run astro -- --help` | Get help using the Astro CLI | 44 | 45 | ## 👀 Want to learn more? 46 | 47 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). 48 | -------------------------------------------------------------------------------- /apps/astro/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | 3 | import react from '@astrojs/react'; 4 | 5 | import tailwind from '@astrojs/tailwind'; 6 | 7 | import cloudflare from '@astrojs/cloudflare'; 8 | 9 | // https://astro.build/config 10 | export default defineConfig({ 11 | output: 'server', 12 | adapter: cloudflare({ 13 | mode: 'directory' 14 | }), 15 | integrations: [react(), tailwind({applyBaseStyles: false,})] 16 | }); -------------------------------------------------------------------------------- /apps/astro/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/styles/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /apps/astro/eslint.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig, { restrictEnvAccess } from "@acme/eslint-config/base"; 2 | import nextjsConfig from "@acme/eslint-config/nextjs"; 3 | import reactConfig from "@acme/eslint-config/react"; 4 | 5 | /** @type {import('typescript-eslint').Config} */ 6 | export default [ 7 | { 8 | ignores: [".astro/**"], 9 | }, 10 | ...baseConfig, 11 | ...reactConfig, 12 | ...nextjsConfig, 13 | ...restrictEnvAccess, 14 | ]; 15 | -------------------------------------------------------------------------------- /apps/astro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acme/astro", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro check && astro build", 9 | "preview": "astro preview", 10 | "astro": "astro", 11 | "clean": "git clean -xdf .cache .next .turbo node_modules", 12 | "deploy": "wrangler pages deploy dist", 13 | "deploy:prod": "wrangler pages deploy dist --env production" 14 | }, 15 | "dependencies": { 16 | "@astrojs/check": "^0.9.4", 17 | "@astrojs/cloudflare": "^8.1.0", 18 | "@astrojs/react": "^3.6.2", 19 | "@astrojs/tailwind": "^5.1.1", 20 | "@headlessui/react": "^2.1.9", 21 | "@radix-ui/react-dialog": "^1.1.2", 22 | "@radix-ui/react-icons": "^1.3.0", 23 | "@radix-ui/react-slot": "^1.1.0", 24 | "astro": "^4.15.12", 25 | "class-variance-authority": "^0.7.0", 26 | "clsx": "^2.1.1", 27 | "framer-motion": "^11.11.1", 28 | "lucide-react": "^0.447.0", 29 | "react": "18.3.1", 30 | "react-dom": "18.3.1", 31 | "react-player": "^2.16.0", 32 | "tailwind-merge": "^2.5.4", 33 | "tailwindcss-animate": "^1.0.7", 34 | "typescript": "^5.3.3" 35 | }, 36 | "devDependencies": { 37 | "@acme/eslint-config": "workspace:*", 38 | "@acme/prettier-config": "workspace:*", 39 | "@acme/tailwind-config": "workspace:*", 40 | "@acme/tsconfig": "workspace:*", 41 | "@types/react": "~18.3.12", 42 | "@types/react-dom": "18.3.0", 43 | "autoprefixer": "^10.4.20", 44 | "postcss": "^8.4.47", 45 | "tailwindcss": "^3.4.13" 46 | }, 47 | "prettier": "@acme/prettier-config" 48 | } 49 | -------------------------------------------------------------------------------- /apps/astro/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/astro/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /apps/astro/public/logos/astro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apps/astro/public/logos/clerk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/astro/public/logos/cloudflare.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/astro/public/logos/d1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/astro/public/logos/expo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/astro/public/logos/r2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/astro/public/logos/trpc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | tRPC 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/astro/public/logos/workers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | CloudFrame Workers 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /apps/astro/src/components/react/footer.tsx: -------------------------------------------------------------------------------- 1 | import { Github } from 'lucide-react'; 2 | 3 | export function Footer() { 4 | return ( 5 | 48 | ); 49 | } -------------------------------------------------------------------------------- /apps/astro/src/components/react/header.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import {Fragment, type ReactNode} from 'react' 3 | import {Popover, Transition} from '@headlessui/react' 4 | import clsx from 'clsx' 5 | import {Github} from 'lucide-react' 6 | 7 | function MobileNavLink({href, children}: { href: string, children: ReactNode }) { 8 | return ( 9 | 10 | {children} 11 | 12 | ) 13 | } 14 | 15 | function MobileNavIcon({open}: { open: boolean }) { 16 | return ( 17 | 39 | ) 40 | } 41 | 42 | function MobileNavigation() { 43 | return ( 44 | 45 | 49 | {({open}) => } 50 | 51 | 52 | 61 | 62 | 63 | 72 | 76 | Features 77 | Documentation 78 |
79 | 80 | GitHub 81 | 82 |
83 |
84 |
85 |
86 | ) 87 | } 88 | 89 | export function Header() { 90 | return ( 91 |
92 | {/* Glassmorphism background */} 93 |
94 | 95 |
96 | 122 |
123 |
124 | ) 125 | } -------------------------------------------------------------------------------- /apps/astro/src/components/react/hero.tsx: -------------------------------------------------------------------------------- 1 | import { GithubIcon } from 'lucide-react' 2 | import { Button } from '@/components/ui/button' 3 | 4 | const technologies = [ 5 | [ 6 | { name: 'Expo', description: 'Cross-platform mobile apps', logo: '/logos/expo.svg', isCloudflare: false }, 7 | { name: 'Astro', description: 'High-performance web framework', logo: '/logos/astro.svg', isCloudflare: false }, 8 | { name: 'Clerk', description: 'Authentication & user management', logo: '/logos/clerk.svg', isCloudflare: false }, 9 | { name: 'tRPC', description: 'End-to-end typesafe APIs', logo: '/logos/trpc.svg', isCloudflare: false }, 10 | ], 11 | [ 12 | { name: 'Turborepo', description: 'Monorepo build system', logo: '/logos/turborepo.svg', isCloudflare: false }, 13 | { name: 'Cloudflare Workers', description: 'Edge computing platform', logo: '/logos/workers.svg', isCloudflare: true }, 14 | { name: 'Cloudflare D1', description: 'Serverless SQL database', logo: '/logos/d1.svg', isCloudflare: true }, 15 | { name: 'Cloudflare R2', description: 'Object storage service', logo: '/logos/r2.svg', isCloudflare: true }, 16 | ], 17 | ] 18 | 19 | export function Hero() { 20 | return ( 21 |
22 | {/* Gradient overlay */} 23 |
24 | 25 | {/* Floating shapes */} 26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 | 34 | Cloudflare Native Web Starter Kit 35 | 36 |

37 | Build AI-powered apps 38 | with Cloudflare Workers 39 |

40 | 41 |

42 | A complete starter template for building AI-powered mobile and web applications using Cloudflare workers, 43 | featuring cross-platform support, edge computing, and modern development practices. 44 |

45 | 46 | 54 | 55 |
56 | 59 |
60 | 61 | Built to scale and develop with ease 62 | 63 |
64 |
65 | 66 |
67 |
68 | {technologies.map((group, groupIndex) => ( 69 |
73 |
74 | {group.map((tech) => ( 75 |
79 |
80 | {/* Gradient overlay */} 81 |
82 | 83 | {/* Logo container */} 84 |
85 |
86 | {tech.name} 97 |
98 |
99 |
100 |
101 |
102 | {tech.name} 103 |
104 |
105 | {tech.description} 106 |
107 |
108 |
109 | ))} 110 |
111 |
112 | ))} 113 |
114 |
115 |
116 |
117 |
118 | ) 119 | } 120 | -------------------------------------------------------------------------------- /apps/astro/src/components/react/primary-features.tsx: -------------------------------------------------------------------------------- 1 | import { Code2Icon, ShieldIcon, ZapIcon, DatabaseIcon, CloudIcon, RocketIcon, SparklesIcon, SmartphoneIcon } from 'lucide-react' 2 | 3 | const primaryFeatures = [ 4 | { 5 | title: 'Cross-Platform Mobile', 6 | description: 7 | 'Build native mobile applications with Expo that work seamlessly across iOS and Android platforms.', 8 | icon: SmartphoneIcon, 9 | secondaryIcon: RocketIcon, 10 | }, 11 | { 12 | title: 'Edge Computing', 13 | description: 14 | 'Leverage Cloudflare Workers for serverless compute at the edge, bringing your code closer to your users.', 15 | icon: CloudIcon, 16 | secondaryIcon: ZapIcon, 17 | }, 18 | { 19 | title: 'Secure Authentication', 20 | description: 21 | 'Built-in authentication with Clerk ensures your users and data remain protected with enterprise-grade security.', 22 | icon: ShieldIcon, 23 | secondaryIcon: SparklesIcon, 24 | }, 25 | { 26 | title: 'Edge Database & Storage', 27 | description: 28 | 'Store and retrieve data efficiently with D1 Database and R2 Storage, optimized for edge computing.', 29 | icon: DatabaseIcon, 30 | secondaryIcon: Code2Icon, 31 | }, 32 | ] 33 | 34 | export function PrimaryFeatures() { 35 | return ( 36 |
41 | {/* Gradient overlay */} 42 |
43 | 44 |
45 |
46 |

Production Ready

47 |

48 | Everything you need to build AI apps 49 |

50 |

51 | Start with a fully configured development environment and production-ready infrastructure. 52 | Focus on building your AI features, not setting up tooling. 53 |

54 |
55 | 56 |
57 |
58 | {primaryFeatures.map((feature) => ( 59 |
60 | {/* Glassmorphism card */} 61 |
62 |
63 | 64 | {/* Content */} 65 |
66 |
67 |
68 |
70 |
71 |
73 |
74 |
{feature.title}
75 |
76 |

{feature.description}

77 |
78 |
79 |
80 |
81 | ))} 82 |
83 |
84 |
85 |
86 | ) 87 | } -------------------------------------------------------------------------------- /apps/astro/src/components/react/secondary-features.tsx: -------------------------------------------------------------------------------- 1 | import { BookOpenIcon, PuzzleIcon, WorkflowIcon, BotIcon } from 'lucide-react' 2 | 3 | const secondaryFeatures = [ 4 | { 5 | name: 'AI Story Generation', 6 | summary: 'Powered by Workers AI', 7 | description: 8 | 'Generate AI-powered stories about users\' daily activities and create accompanying images using Cloudflare Workers AI capabilities.', 9 | icon: BotIcon, 10 | }, 11 | { 12 | name: 'Example Implementation', 13 | summary: 'Learn by example', 14 | description: 15 | 'Includes a complete example app showcasing authentication, AI processing, background jobs, and database patterns. Use it as a reference or starting point.', 16 | icon: BookOpenIcon, 17 | }, 18 | { 19 | name: 'Integration Patterns', 20 | summary: 'Best practices built-in', 21 | description: 22 | 'See how to integrate Clerk auth, Workers AI, D1 database, and R2 storage with proper error handling and type safety using tRPC.', 23 | icon: PuzzleIcon, 24 | }, 25 | { 26 | name: 'Durable Workflows', 27 | summary: 'Reliable AI processing', 28 | description: 29 | 'Process AI tasks reliably with durable Cloudflare Workers, ensuring your AI operations complete successfully even with longer processing times.', 30 | icon: WorkflowIcon, 31 | }, 32 | ] 33 | 34 | export function SecondaryFeatures() { 35 | return ( 36 |
41 | {/* Gradient overlay */} 42 |
43 | 44 |
45 |
46 |

AI-First Development

47 |

48 | Built for AI applications 49 |

50 |

51 | Includes everything you need to build AI-powered applications with proper error handling, 52 | scalability, and user experience best practices. 53 |

54 |
55 | 56 |
57 |
58 | {secondaryFeatures.map((feature) => ( 59 |
60 |
61 |
62 | 63 |
64 | {/* Icon */} 65 |
66 | 67 |
68 | 69 | {/* Content */} 70 |
71 |

72 | {feature.name} 73 |

74 |

75 | {feature.description} 76 |

77 |
78 |
79 |
80 |
81 | ))} 82 |
83 |
84 |
85 |
86 | ) 87 | } -------------------------------------------------------------------------------- /apps/astro/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /apps/astro/src/components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as DialogPrimitive from "@radix-ui/react-dialog" 3 | import { Cross2Icon } from "@radix-ui/react-icons" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const Dialog = DialogPrimitive.Root 8 | 9 | const DialogTrigger = DialogPrimitive.Trigger 10 | 11 | const DialogPortal = DialogPrimitive.Portal 12 | 13 | const DialogClose = DialogPrimitive.Close 14 | 15 | const DialogOverlay = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef 18 | >(({ className, ...props }, ref) => ( 19 | 27 | )) 28 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName 29 | 30 | const DialogContent = React.forwardRef< 31 | React.ElementRef, 32 | React.ComponentPropsWithoutRef 33 | >(({ className, children, ...props }, ref) => ( 34 | 35 | 36 | 44 | {children} 45 | 46 | 47 | Close 48 | 49 | 50 | 51 | )) 52 | DialogContent.displayName = DialogPrimitive.Content.displayName 53 | 54 | const DialogHeader = ({ 55 | className, 56 | ...props 57 | }: React.HTMLAttributes) => ( 58 |
65 | ) 66 | DialogHeader.displayName = "DialogHeader" 67 | 68 | const DialogFooter = ({ 69 | className, 70 | ...props 71 | }: React.HTMLAttributes) => ( 72 |
79 | ) 80 | DialogFooter.displayName = "DialogFooter" 81 | 82 | const DialogTitle = React.forwardRef< 83 | React.ElementRef, 84 | React.ComponentPropsWithoutRef 85 | >(({ className, ...props }, ref) => ( 86 | 94 | )) 95 | DialogTitle.displayName = DialogPrimitive.Title.displayName 96 | 97 | const DialogDescription = React.forwardRef< 98 | React.ElementRef, 99 | React.ComponentPropsWithoutRef 100 | >(({ className, ...props }, ref) => ( 101 | 106 | )) 107 | DialogDescription.displayName = DialogPrimitive.Description.displayName 108 | 109 | export { 110 | Dialog, 111 | DialogPortal, 112 | DialogOverlay, 113 | DialogTrigger, 114 | DialogClose, 115 | DialogContent, 116 | DialogHeader, 117 | DialogFooter, 118 | DialogTitle, 119 | DialogDescription, 120 | } 121 | -------------------------------------------------------------------------------- /apps/astro/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /apps/astro/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /apps/astro/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import '../styles/globals.css'; 3 | import { Header } from '../components/react/header'; 4 | import { Footer } from '../components/react/footer'; 5 | 6 | // Example prop for page titles (you can add more props as needed) 7 | export interface Props { 8 | title: string; 9 | } 10 | 11 | const { title } = Astro.props; 12 | --- 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {title} 23 | 24 | 25 |
26 |
27 | {/* This is where the page content will be injected */} 28 |
29 |