Skip to content

Commit a78bc14

Browse files
authored
Merge pull request #34 from hack4impact/project-structure
Initial architecture
2 parents 54c47a7 + 77a06fa commit a78bc14

38 files changed

Lines changed: 9744 additions & 1 deletion

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
2+
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY=sb_publishable_your-key
3+
DATABASE_URL=postgresql://postgres:password@db.your-project.supabase.co:6543/postgres

.gitignore

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# dependencies
2+
/node_modules
3+
/.pnp
4+
.pnp.*
5+
.yarn/*
6+
!.yarn/patches
7+
!.yarn/plugins
8+
!.yarn/releases
9+
!.yarn/versions
10+
11+
# testing
12+
/coverage
13+
14+
# next.js
15+
/.next/
16+
/out/
17+
18+
# production
19+
/build
20+
21+
# misc
22+
.DS_Store
23+
*.pem
24+
Thumbs.db
25+
26+
# debug
27+
npm-debug.log*
28+
yarn-debug.log*
29+
yarn-error.log*
30+
.pnpm-debug.log*
31+
32+
# env files
33+
.env
34+
.env.*
35+
!.env.example
36+
37+
# vercel
38+
.vercel
39+
40+
# typescript
41+
*.tsbuildinfo
42+
next-env.d.ts
43+
44+
# editors
45+
.vscode/*
46+
!.vscode/settings.json
47+
!.vscode/extensions.json
48+
.idea/
49+
*.swp
50+
*.swo
51+
*~
52+
53+
# drizzle
54+
drizzle/meta/
55+
56+
# supabase
57+
.supabase/
58+
59+
60+
.agents/

.prettierrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"tabWidth": 3
3+
}

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- BEGIN:nextjs-agent-rules -->
2+
# This is NOT the Next.js you know
3+
4+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
5+
<!-- END:nextjs-agent-rules -->

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

README.md

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,98 @@
1-
# mcld-project
1+
# mcld-project
2+
3+
## Stack
4+
5+
- **Framework**: Next.js 16 (App Router, Turbopack)
6+
- **Auth**: Supabase Auth (email/password, SSR via `@supabase/ssr`)
7+
- **Database**: PostgreSQL via Supabase + Drizzle ORM
8+
- **UI**: shadcn/ui (Radix) + Tailwind
9+
- **Hosting**: Vercel
10+
11+
## Setup
12+
13+
### 1. Install dependencies
14+
15+
```bash
16+
pnpm install
17+
```
18+
19+
### 2. Configure environment
20+
21+
```bash
22+
cp .env.example .env.local
23+
sudo nano .env
24+
```
25+
26+
Fill in your Supabase credentials from [supabase.com/dashboard](https://supabase.com/dashboard) > Project Settings > API (or Dashboard > Framework > Next.js > Environment Variables):
27+
28+
only on `.env.local`:
29+
30+
- `NEXT_PUBLIC_SUPABASE_URL` — Project URL
31+
- `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY` — Publishable (anon) key
32+
33+
only on `.env`:
34+
35+
- `DATABASE_URL` — Connection string (Dashboard > Direct (connextion string) > Copy connection string (for the password go to Database > Settings > Database password > Reset password))
36+
37+
### 3. Configure Supabase Auth (if you dont use port 3000)
38+
39+
In your Supabase dashboard under **Authentication > URL Configuration**:
40+
41+
- **Site URL**: `http://localhost:PORT`
42+
- **Redirect URLs**: add `http://localhost:PORT/auth/callback`
43+
44+
Make sure **Email** provider is enabled under **Authentication > Sign In/Providers**.
45+
46+
### 4. Set up the database
47+
48+
```bash
49+
pnpm db:push # push schema to Supabase
50+
pnpm db:generate # generate migration files
51+
pnpm db:migrate # run migrations
52+
```
53+
54+
### 5. Run
55+
56+
```bash
57+
pnpm dev
58+
```
59+
60+
Open [http://localhost:3000](http://localhost:3000). You'll be redirected to `/login` if not authenticated.
61+
62+
## Adding shadcn/ui components
63+
64+
```bash
65+
npx shadcn@latest add <component>
66+
```
67+
68+
Examples:
69+
70+
```bash
71+
npx shadcn@latest add button
72+
npx shadcn@latest add card input label
73+
npx shadcn@latest add dialog dropdown-menu
74+
```
75+
76+
Components are installed to `components/ui/`. Browse available components at [ui.shadcn.com](https://ui.shadcn.com).
77+
78+
## Project structure
79+
80+
```
81+
app/
82+
layout.tsx # Root layout (Helvetica font)
83+
page.tsx # Home (protected, shows user + sign out)
84+
login/
85+
page.tsx # Login / signup form
86+
actions.ts # Server actions (login, signup, signout)
87+
auth/
88+
callback/
89+
route.ts # Email confirmation callback
90+
components/ui/ # shadcn/ui components
91+
utils/supabase/
92+
client.ts # Browser Supabase client
93+
server.ts # Server Supabase client
94+
middleware.ts # Session refresh + auth redirect logic
95+
proxy.ts # Next.js 16 proxy (replaces middleware.ts)
96+
drizzle/ # Drizzle schema + migrations
97+
drizzle.config.ts # Drizzle config
98+
```

app/auth/callback/route.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NextResponse } from "next/server";
2+
import { createClient } from "@/utils/supabase/server";
3+
4+
export async function GET(request: Request) {
5+
const { searchParams, origin } = new URL(request.url);
6+
const code = searchParams.get("code");
7+
const next = searchParams.get("next") ?? "/";
8+
9+
if (code) {
10+
const supabase = await createClient();
11+
const { error } = await supabase.auth.exchangeCodeForSession(code);
12+
if (!error) {
13+
return NextResponse.redirect(`${origin}${next}`);
14+
}
15+
}
16+
17+
return NextResponse.redirect(`${origin}/login?error=Could+not+authenticate`);
18+
}

app/favicon.ico

25.3 KB
Binary file not shown.

app/globals.css

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
@import "tailwindcss";
2+
@import "tw-animate-css";
3+
@import "shadcn/tailwind.css";
4+
5+
@custom-variant dark (&:is(.dark *));
6+
7+
@theme inline {
8+
--color-background: var(--background);
9+
--color-foreground: var(--foreground);
10+
--font-sans: "Helvetica", "Arial", sans-serif;
11+
--font-mono: ui-monospace, monospace;
12+
--font-heading: "Helvetica", "Arial", sans-serif;
13+
--color-sidebar-ring: var(--sidebar-ring);
14+
--color-sidebar-border: var(--sidebar-border);
15+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16+
--color-sidebar-accent: var(--sidebar-accent);
17+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18+
--color-sidebar-primary: var(--sidebar-primary);
19+
--color-sidebar-foreground: var(--sidebar-foreground);
20+
--color-sidebar: var(--sidebar);
21+
--color-chart-5: var(--chart-5);
22+
--color-chart-4: var(--chart-4);
23+
--color-chart-3: var(--chart-3);
24+
--color-chart-2: var(--chart-2);
25+
--color-chart-1: var(--chart-1);
26+
--color-ring: var(--ring);
27+
--color-input: var(--input);
28+
--color-border: var(--border);
29+
--color-destructive: var(--destructive);
30+
--color-accent-foreground: var(--accent-foreground);
31+
--color-accent: var(--accent);
32+
--color-muted-foreground: var(--muted-foreground);
33+
--color-muted: var(--muted);
34+
--color-secondary-foreground: var(--secondary-foreground);
35+
--color-secondary: var(--secondary);
36+
--color-primary-foreground: var(--primary-foreground);
37+
--color-primary: var(--primary);
38+
--color-popover-foreground: var(--popover-foreground);
39+
--color-popover: var(--popover);
40+
--color-card-foreground: var(--card-foreground);
41+
--color-card: var(--card);
42+
--radius-sm: calc(var(--radius) * 0.6);
43+
--radius-md: calc(var(--radius) * 0.8);
44+
--radius-lg: var(--radius);
45+
--radius-xl: calc(var(--radius) * 1.4);
46+
--radius-2xl: calc(var(--radius) * 1.8);
47+
--radius-3xl: calc(var(--radius) * 2.2);
48+
--radius-4xl: calc(var(--radius) * 2.6);
49+
}
50+
51+
:root {
52+
--background: oklch(1 0 0);
53+
--foreground: oklch(0.145 0 0);
54+
--card: oklch(1 0 0);
55+
--card-foreground: oklch(0.145 0 0);
56+
--popover: oklch(1 0 0);
57+
--popover-foreground: oklch(0.145 0 0);
58+
--primary: oklch(0.205 0 0);
59+
--primary-foreground: oklch(0.985 0 0);
60+
--secondary: oklch(0.97 0 0);
61+
--secondary-foreground: oklch(0.205 0 0);
62+
--muted: oklch(0.97 0 0);
63+
--muted-foreground: oklch(0.556 0 0);
64+
--accent: oklch(0.97 0 0);
65+
--accent-foreground: oklch(0.205 0 0);
66+
--destructive: oklch(0.577 0.245 27.325);
67+
--border: oklch(0.922 0 0);
68+
--input: oklch(0.922 0 0);
69+
--ring: oklch(0.708 0 0);
70+
--chart-1: oklch(0.87 0 0);
71+
--chart-2: oklch(0.556 0 0);
72+
--chart-3: oklch(0.439 0 0);
73+
--chart-4: oklch(0.371 0 0);
74+
--chart-5: oklch(0.269 0 0);
75+
--radius: 0.625rem;
76+
--sidebar: oklch(0.985 0 0);
77+
--sidebar-foreground: oklch(0.145 0 0);
78+
--sidebar-primary: oklch(0.205 0 0);
79+
--sidebar-primary-foreground: oklch(0.985 0 0);
80+
--sidebar-accent: oklch(0.97 0 0);
81+
--sidebar-accent-foreground: oklch(0.205 0 0);
82+
--sidebar-border: oklch(0.922 0 0);
83+
--sidebar-ring: oklch(0.708 0 0);
84+
}
85+
86+
.dark {
87+
--background: oklch(0.145 0 0);
88+
--foreground: oklch(0.985 0 0);
89+
--card: oklch(0.205 0 0);
90+
--card-foreground: oklch(0.985 0 0);
91+
--popover: oklch(0.205 0 0);
92+
--popover-foreground: oklch(0.985 0 0);
93+
--primary: oklch(0.922 0 0);
94+
--primary-foreground: oklch(0.205 0 0);
95+
--secondary: oklch(0.269 0 0);
96+
--secondary-foreground: oklch(0.985 0 0);
97+
--muted: oklch(0.269 0 0);
98+
--muted-foreground: oklch(0.708 0 0);
99+
--accent: oklch(0.269 0 0);
100+
--accent-foreground: oklch(0.985 0 0);
101+
--destructive: oklch(0.704 0.191 22.216);
102+
--border: oklch(1 0 0 / 10%);
103+
--input: oklch(1 0 0 / 15%);
104+
--ring: oklch(0.556 0 0);
105+
--chart-1: oklch(0.87 0 0);
106+
--chart-2: oklch(0.556 0 0);
107+
--chart-3: oklch(0.439 0 0);
108+
--chart-4: oklch(0.371 0 0);
109+
--chart-5: oklch(0.269 0 0);
110+
--sidebar: oklch(0.205 0 0);
111+
--sidebar-foreground: oklch(0.985 0 0);
112+
--sidebar-primary: oklch(0.488 0.243 264.376);
113+
--sidebar-primary-foreground: oklch(0.985 0 0);
114+
--sidebar-accent: oklch(0.269 0 0);
115+
--sidebar-accent-foreground: oklch(0.985 0 0);
116+
--sidebar-border: oklch(1 0 0 / 10%);
117+
--sidebar-ring: oklch(0.556 0 0);
118+
}
119+
120+
@layer base {
121+
* {
122+
@apply border-border outline-ring/50;
123+
}
124+
body {
125+
@apply bg-background text-foreground;
126+
}
127+
html {
128+
@apply font-sans;
129+
}
130+
}

app/layout.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { Metadata } from "next";
2+
import "./globals.css";
3+
4+
export const metadata: Metadata = {
5+
title: "Create Next App",
6+
description: "Generated by create next app",
7+
};
8+
9+
export default function RootLayout({
10+
children,
11+
}: Readonly<{
12+
children: React.ReactNode;
13+
}>) {
14+
return (
15+
<html lang="en" className="h-full antialiased">
16+
<body className="min-h-full flex flex-col font-[Helvetica,Arial,sans-serif]">
17+
{children}
18+
</body>
19+
</html>
20+
);
21+
}

0 commit comments

Comments
 (0)