Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI #45

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft

CLI #45

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/cli.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CLI
on:
push:
paths:
- 'cli/**'
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: 🔦 Run linter
run: bun run lint
- name: 🪐 Check TypeScript
run: bun run typecheck
49 changes: 19 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,27 @@ Everything you need to build a production ready SaaS, it's a opinionated stack b
[Next.js](https://nextjs.org/) - Framework<br>
[Turborepo](https://turbo.build) - Build system<br>
[Biome](https://biomejs.dev) - Linter, formatter<br>
[Sherif](https://github.com/QuiiBz/sherif) - Linter for monorepos<br>
[TailwindCSS](https://tailwindcss.com/) - Styling<br>
[Shadcn](https://ui.shadcn.com/) - UI components<br>
[TypeScript](https://www.typescriptlang.org/) - Type safety<br>
[Supabase](https://supabase.com/) - Authentication, database, storage<br>
[Upstash](https://upstash.com/) - Cache and rate limiting<br>
[React Email](https://react.email/) - Email templates<br>
[Resend](https://resend.com/) - Email delivery<br>
[i18n](https://next-international.vercel.app/) - Internationalization<br>
[Sentry](https://sentry.io/) - Error handling/monitoring<br>
[Dub](https://dub.sh/) - Sharable links<br>
[Trigger.dev](https://trigger.dev/) - Background jobs<br>
[OpenPanel](https://openpanel.dev/) - Analytics<br>
[Polar](https://polar.sh) - Billing (coming soon)<br>
[react-safe-action](https://next-safe-action.dev) - Validated Server Actions<br>
[nuqs](https://nuqs.47ng.com/) - Type-safe search params state manager<br>
[next-themes](https://next-themes-example.vercel.app/) - Theme manager<br>

## Services directory (optional)
[Upstash](https://upstash.com/) - Cache and rate limiting<br>
[Trigger.dev](https://trigger.dev/) - Background jobs<br>
[Polar](https://polar.sh) - Billing (coming soon)<br>
[OpenPanel](https://openpanel.dev/) - Analytics<br>
[Sentry](https://sentry.io/) - Error handling/monitoring<br>
[Resend](https://resend.com/) - Email delivery<br>


## Directory Structure

```
Expand Down Expand Up @@ -70,38 +74,17 @@ Everything you need to build a production ready SaaS, it's a opinionated stack b

Bun<br>
Docker<br>
Upstash<br>
Dub<br>
Trigger.dev<br>
Resend<br>
Supabase<br>
Sentry<br>
OpenPanel<br>

## Getting Started

Clone this repo locally with the following command:

```bash
bunx degit midday-ai/v1 v1
```

1. Install dependencies using bun:

```sh
bun i
```

2. Copy `.env.example` to `.env` and update the variables.

```sh
# Copy .env.example to .env for each app
cp apps/api/.env.example apps/api/.env
cp apps/app/.env.example apps/app/.env
cp apps/web/.env.example apps/web/.env
bunx v1-run@latest init
```

4. Start the development server from either bun or turbo:
Start the development server from either bun or turbo:

```ts
bun dev // starts everything in development mode (web, app, api, email)
Expand All @@ -115,6 +98,12 @@ bun migrate // run migrations
bun seed // run seed
```

## Add services

```bash
bunx v1-run@latest add service <service>
```

## How to use
This boilerplate is inspired by our work on Midday, and it's designed to serve as a reference for real-world apps. Feel free to dive into the code and see how we've tackled various features. Whether you're looking to understand authentication flows, database interactions, or UI components, you'll find practical, battle-tested implementations throughout the codebase. It's not just a starting point; it's a learning resource that can help you build your own applications.

Expand All @@ -124,7 +113,7 @@ With this, you have a great starting point for your own project.

Vercel deployment will guide you through creating a Supabase account and project.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fmidday-ai%2Fv1&env=RESEND_API_KEY,UPSTASH_REDIS_REST_URL,UPSTASH_REDIS_REST_TOKEN,SENTRY_AUTH_TOKEN,NEXT_PUBLIC_SENTRY_DSN,SENTRY_ORG,SENTRY_PROJECT,DUB_API_KEY,NEXT_PUBLIC_OPENPANEL_CLIENT_ID,OPENPANEL_SECRET_KEY&project-name=create-v1&repository-name=create-v1&redirect-url=https%3A%2F%2Fv1.run&demo-title=Create%20v1&demo-description=An%20open-source%20starter%20kit%20based%20on%20Midday.&demo-url=https%3A%2F%2Fv1.run&demo-image=https%3A%2F%2Fv1.run%2Fopengraph-image.png&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fmidday-ai%2Fv1&project-name=create-v1&repository-name=create-v1&redirect-url=https%3A%2F%2Fv1.run&demo-title=Create%20v1&demo-description=An%20open-source%20starter%20kit%20based%20on%20Midday.&demo-url=https%3A%2F%2Fv1.run&demo-image=https%3A%2F%2Fv1.run%2Fopengraph-image.png&integration-ids=oac_VqOgBHqhEoFTPzGkPd7L0iH6)

## Recognition

Expand Down
22 changes: 1 addition & 21 deletions apps/app/.env.example
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
# Supabase
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_KEY=

# Resend
RESEND_API_KEY=

# Upstash
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

# Dub
DUB_API_KEY=

# OpenPanel
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=
OPENPANEL_SECRET_KEY=

# Sentry
SENTRY_AUTH_TOKEN=
NEXT_PUBLIC_SENTRY_DSN=
SENTRY_ORG=
SENTRY_PROJECT=
SUPABASE_SERVICE_KEY=
13 changes: 1 addition & 12 deletions apps/app/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import "./src/env.mjs";
import { withSentryConfig } from "@sentry/nextjs";

/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ["@v1/supabase"],
experimental: {
instrumentationHook: process.env.NODE_ENV === "production",
},
};

export default withSentryConfig(nextConfig, {
silent: !process.env.CI,
telemetry: false,
widenClientFileUpload: true,
hideSourceMaps: true,
disableLogger: true,
tunnelRoute: "/monitoring",
});
export default nextConfig;
5 changes: 0 additions & 5 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@v1/analytics": "workspace:*",
"@v1/kv": "workspace:*",
"@v1/supabase": "workspace:*",
"@v1/ui": "workspace:*",
"dub": "^0.36.5",
"geist": "^1.3.1",
"next": "14.2.7",
"next-international": "^1.2.4",
Expand All @@ -28,8 +25,6 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@sentry/nextjs": "^8",
"@supabase/sentry-js-integration": "^0.2.0",
"@types/node": "^22",
"@types/react": "^18",
"@types/react-dom": "^18",
Expand Down
6 changes: 0 additions & 6 deletions apps/app/src/actions/post/schema.ts

This file was deleted.

18 changes: 0 additions & 18 deletions apps/app/src/actions/post/share-link-action.ts

This file was deleted.

58 changes: 6 additions & 52 deletions apps/app/src/actions/safe-action.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import * as Sentry from "@sentry/nextjs";
import { setupAnalytics } from "@v1/analytics/server";
import { ratelimit } from "@v1/kv/ratelimit";
import { logger } from "@v1/logger";
import { getUser } from "@v1/supabase/queries";
import { createClient } from "@v1/supabase/server";
import {
DEFAULT_SERVER_ERROR_MESSAGE,
createSafeActionClient,
} from "next-safe-action";
import { headers } from "next/headers";
import { z } from "zod";

const handleServerError = (e: Error) => {
console.error("Action error:", e.message);
Expand All @@ -27,17 +22,6 @@ export const actionClient = createSafeActionClient({

export const actionClientWithMeta = createSafeActionClient({
handleServerError,
defineMetadataSchema() {
return z.object({
name: z.string(),
track: z
.object({
event: z.string(),
channel: z.string(),
})
.optional(),
});
},
});

export const authActionClient = actionClientWithMeta
Expand All @@ -54,26 +38,8 @@ export const authActionClient = actionClientWithMeta

return result;
})
.use(async ({ next, metadata }) => {
const ip = headers().get("x-forwarded-for");

const { success, remaining } = await ratelimit.limit(
`${ip}-${metadata.name}`,
);

if (!success) {
throw new Error("Too many requests");
}

return next({
ctx: {
ratelimit: {
remaining,
},
},
});
})
.use(async ({ next, metadata }) => {
.use(async ({ next }) => {
const {
data: { user },
} = await getUser();
Expand All @@ -83,22 +49,10 @@ export const authActionClient = actionClientWithMeta
throw new Error("Unauthorized");
}

if (metadata) {
const analytics = await setupAnalytics({
userId: user.id,
});

if (metadata.track) {
analytics.track(metadata.track);
}
}

return Sentry.withServerActionInstrumentation(metadata.name, async () => {
return next({
ctx: {
supabase,
user,
},
});
return next({
ctx: {
supabase,
user,
},
});
});
3 changes: 0 additions & 3 deletions apps/app/src/actions/user/update-post-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import { updateUserSchema } from "./schema";

export const updateUserAction = authActionClient
.schema(updateUserSchema)
.metadata({
name: "update-user",
})
.action(async ({ parsedInput: input, ctx: { user } }) => {
const result = await updateUser(user.id, input);

Expand Down
12 changes: 0 additions & 12 deletions apps/app/src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,17 @@ export const env = createEnv({
PORT: z.coerce.number().default(3000),
},
server: {
OPENPANEL_SECRET_KEY: z.string(),
RESEND_API_KEY: z.string(),
SUPABASE_SERVICE_KEY: z.string(),
UPSTASH_REDIS_REST_TOKEN: z.string(),
UPSTASH_REDIS_REST_URL: z.string(),
},
client: {
NEXT_PUBLIC_OPENPANEL_CLIENT_ID: z.string(),
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(),
NEXT_PUBLIC_SUPABASE_URL: z.string(),
},
runtimeEnv: {
NEXT_PUBLIC_OPENPANEL_CLIENT_ID:
process.env.NEXT_PUBLIC_OPENPANEL_CLIENT_ID,
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,
OPENPANEL_SECRET_KEY: process.env.OPENPANEL_SECRET_KEY,
PORT: process.env.PORT,
RESEND_API_KEY: process.env.RESEND_API_KEY,
SUPABASE_SERVICE_KEY: process.env.SUPABASE_SERVICE_KEY,
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
VERCEL_URL: process.env.VERCEL_URL,
},
skipValidation: !!process.env.CI || !!process.env.SKIP_ENV_VALIDATION,
Expand Down
Empty file added apps/app/src/services/.gitkeep
Empty file.
6 changes: 0 additions & 6 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
# OpenPanel
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=
OPENPANEL_SECRET_KEY=

# Cal.com
NEXT_PUBLIC_CAL_LINK=
2 changes: 0 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@calcom/embed-react": "^1.5.0",
"@v1/analytics": "workspace:*",
"@v1/ui": "workspace:*",
"geist": "^1.3.1",
"next": "14.2.7",
Expand Down
3 changes: 0 additions & 3 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import "@v1/ui/globals.css";
import { Footer } from "@/components/footer";
import { Header } from "@/components/header";
import { Provider as AnalyticsProvider } from "@v1/analytics/client";
import { cn } from "@v1/ui/cn";
import { GeistMono } from "geist/font/mono";
import { GeistSans } from "geist/font/sans";
Expand Down Expand Up @@ -36,8 +35,6 @@ export default function RootLayout({
<Header />
{children}
<Footer />

<AnalyticsProvider />
</body>
</html>
);
Expand Down
15 changes: 0 additions & 15 deletions apps/web/src/app/talk-to-us/page.tsx

This file was deleted.

Loading
Loading