Skip to content
Closed
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
12 changes: 9 additions & 3 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# For turbo builds, prefixes with NEXT_PUBLIC_ are automatically considered to force a rebuild
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key

# Database Configuration (Supabase PostgreSQL)
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres

# Optional: Supabase Service Role Key (for admin operations)
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
172 changes: 172 additions & 0 deletions apps/web/SUPABASE_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Supabase Integration Setup

This guide will help you set up Supabase authentication and database integration for the Tech Companies Portugal application.

## Prerequisites

1. A Supabase account (sign up at [supabase.com](https://supabase.com))
2. Node.js and npm installed

## Step 1: Create a Supabase Project

1. Go to [supabase.com](https://supabase.com) and sign in
2. Click "New Project"
3. Choose your organization
4. Enter project details:
- Name: `tech-companies-portugal` (or your preferred name)
- Database Password: Choose a strong password
- Region: Choose the closest region to your users
5. Click "Create new project"

## Step 2: Get Your Supabase Credentials

1. In your Supabase dashboard, go to **Settings** → **API**
2. Copy the following values:
- **Project URL** (starts with `https://`)
- **anon public** key (starts with `eyJ`)
- **service_role** key (starts with `eyJ`, keep this secret!)

## Step 3: Set Up Environment Variables

1. Copy `.env.example` to `.env.local`:
```bash
cp .env.example .env.local
```

2. Update `.env.local` with your Supabase credentials:
```env
NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key_here
```

## Step 4: Set Up Database Schema

1. Generate the database migration:
```bash
npm run db:generate
```

2. Apply the migration to your Supabase database:
```bash
npm run db:migrate
```

## Step 5: Configure Supabase Authentication

1. In your Supabase dashboard, go to **Authentication** → **Settings**
2. Configure the following:

**Site URL**: `http://localhost:3000` (for development)

**Redirect URLs**: Add these URLs:
- `http://localhost:3000/auth/callback`
- `http://localhost:3000/api/auth/callback`
- `https://your-domain.com/auth/callback` (for production)
- `https://your-domain.com/api/auth/callback` (for production)

3. **Email Templates** (optional):
- Customize the confirmation and reset password email templates
- Update the sender email address

## Step 6: Enable Email Authentication

1. In **Authentication** → **Providers**
2. Make sure **Email** is enabled
3. Configure email settings:
- **Enable email confirmations**: ✅ (recommended)
- **Enable secure email change**: ✅ (recommended)
- **Enable double confirm changes**: ✅ (recommended)

## Step 7: Test the Integration

1. Start the development server:
```bash
npm run dev
```

2. Navigate to `http://localhost:3000/auth`
3. Try signing up with a new account
4. Check your email for the confirmation link
5. Sign in with your credentials

## Database Schema

The application uses the following database schema:

### `user_profiles` table
- `id` (UUID, Primary Key): User ID from Supabase Auth
- `email` (Text): User's email address
- `full_name` (Text): User's full name
- `avatar_url` (Text): URL to user's avatar image
- `is_active` (Boolean): Whether the user account is active
- `created_at` (Timestamp): When the profile was created
- `updated_at` (Timestamp): When the profile was last updated

## Features Implemented

### Authentication
- ✅ Email/password sign up and sign in
- ✅ Email confirmation
- ✅ Password reset
- ✅ Sign out functionality
- ✅ Protected routes (configurable)
- ✅ User session management

### User Interface
- ✅ Sign in/Sign up form with validation
- ✅ Loading states and error handling
- ✅ Success messages
- ✅ Responsive design
- ✅ Consistent styling with existing design system

### Database Integration
- ✅ User profile creation on sign up
- ✅ Drizzle ORM integration
- ✅ Database migrations
- ✅ Type-safe database operations

## Security Considerations

1. **Environment Variables**: Never commit `.env.local` to version control
2. **Service Role Key**: Keep the service role key secret and only use it server-side
3. **CORS**: Configure CORS settings in Supabase if needed
4. **Rate Limiting**: Consider implementing rate limiting for auth endpoints
5. **Email Verification**: Enable email confirmation for production

## Production Deployment

1. Update environment variables with production URLs
2. Set up proper redirect URLs in Supabase
3. Configure email templates for your domain
4. Set up monitoring and logging
5. Consider implementing additional security measures

## Troubleshooting

### Common Issues

1. **"Invalid API key" error**:
- Check that your environment variables are correctly set
- Verify the API keys in your Supabase dashboard

2. **"Redirect URL not allowed" error**:
- Add your redirect URLs to the Supabase authentication settings
- Make sure the URLs match exactly (including protocol and port)

3. **Database connection errors**:
- Verify your `DATABASE_URL` is correct
- Check that your database password is properly URL-encoded
- Ensure your IP is allowed in Supabase database settings

4. **Email not sending**:
- Check Supabase email settings
- Verify your email templates are configured
- Check spam folder for confirmation emails

### Getting Help

- [Supabase Documentation](https://supabase.com/docs)
- [Supabase Discord](https://discord.supabase.com)
- [Drizzle Documentation](https://orm.drizzle.team)
10 changes: 10 additions & 0 deletions apps/web/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Config } from 'drizzle-kit'

export default {
schema: './src/lib/db/schema.ts',
out: './src/lib/db/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
} satisfies Config
20 changes: 20 additions & 0 deletions apps/web/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { updateSession } from '@/lib/supabase/middleware'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
return await updateSession(request)
}

export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* Feel free to modify this pattern to include more paths.
*/
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}
11 changes: 10 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,31 @@
"clean": "git clean -xdf .next .turbo node_modules playwright-report test-results",
"test": "echo 'No tests to run'",
"test:e2e": "start-server-and-test start http://localhost:3000 'playwright test'",
"test:e2e:ui": "start-server-and-test start http://localhost:3000 'playwright test --ui'"
"test:e2e:ui": "start-server-and-test start http://localhost:3000 'playwright test --ui'",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
},
"dependencies": {
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-label": "2.1.1",
"@radix-ui/react-select": "2.1.4",
"@radix-ui/react-slot": "1.1.1",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.51.0",
"@tech-companies-portugal/analytics": "*",
"@types/pg": "^8.15.4",
"cheerio": "1.0.0-rc.12",
"class-variance-authority": "0.7.0",
"clsx": "2.1.0",
"date-fns": "3.3.1",
"drizzle-orm": "^0.44.3",
"geist": "1.3.1",
"lucide-react": "0.469.0",
"motion": "11.17.0",
"next": "15.2.3",
"nuqs": "2.3.1",
"postgres": "^3.4.7",
"react": "19.0.0",
"react-countup": "6.5.3",
"react-dom": "19.0.0",
Expand All @@ -47,6 +55,7 @@
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"autoprefixer": "^10.0.1",
"drizzle-kit": "^0.31.4",
"postcss": "^8",
"start-server-and-test": "2.0.10",
"tailwindcss": "^3.3.0",
Expand Down
26 changes: 26 additions & 0 deletions apps/web/src/app/api/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createClient } from '@/lib/supabase/server'
import { createUserProfile } from '@/lib/auth'
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url)
const code = searchParams.get('code')
const next = searchParams.get('next') ?? '/'

if (code) {
const supabase = createClient()
const { data, error } = await supabase.auth.exchangeCodeForSession(code)

if (!error && data.user) {
// Create user profile if it doesn't exist
await createUserProfile(
data.user.id,
data.user.email!,
data.user.user_metadata?.full_name
)
}
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(`${origin}${next}`)
}
Loading
Loading