Skip to content
Merged
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
69 changes: 37 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Type check
run: npm run type-check

# - name: Run tests
# run: npm run test

- name: Build
run: npm run build
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci


- name: Type check
run: npm run type-check


# - name: Run tests
# run: npm run test


- name: Build
run: npm run build -- --webpack
6 changes: 6 additions & 0 deletions app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@ import { authOptions } from "@/lib/auth"
// should only export valid Route handlers (GET, POST, etc.). Exporting
// arbitrary values like `authOptions` from a Route file causes type errors
// during build.

// Note: For Cloudflare Pages compatibility, this route needs edge runtime
// but NextAuth v4 has crypto dependencies. Consider upgrading to Auth.js v5
// or using a different auth solution for full edge compatibility.
// export const runtime = 'edge'

const handler = NextAuth(authOptions as any)
export { handler as GET, handler as POST }
4 changes: 2 additions & 2 deletions app/api/resources/[domain]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export const runtime = 'edge';

export async function GET(
request: NextRequest,
{ params }: { params: { domain: string } }
{ params }: { params: Promise<{ domain: string }> }
) {
try {
const domain = params.domain;
const { domain } = await params;
const domainResources = resources[domain as keyof typeof resources] || [];

return NextResponse.json(domainResources);
Expand Down
15 changes: 12 additions & 3 deletions lib/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ export const authOptions: NextAuthOptions = {
}
return false;
},
async jwt({ token, user, trigger }) {
// Fetch user's admin status from Supabase on sign in or session update
if (token.email) {
async jwt({ token, user, trigger, session }) {
// Fetch user's admin status from Supabase only on initial sign in
// Subsequent requests will use the cached value in the JWT token
if (user) {
// Initial sign in - fetch admin status
try {
const { data: userData } = await supabaseAdmin
.from('users')
Expand All @@ -87,8 +89,15 @@ export const authOptions: NextAuthOptions = {
}
} catch (error) {
console.error('Error fetching user admin status:', error);
token.isAdmin = false;
}
}

// Handle session updates (e.g., when admin status changes)
if (session && trigger === 'update') {
token.isAdmin = session.isAdmin;
}

return token;
},
async session({ session, token }) {
Expand Down
58 changes: 15 additions & 43 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { getToken } from "next-auth/jwt";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createClient } from "@supabase/supabase-js";

export async function middleware(request: NextRequest) {
const token = await getToken({
const token = await getToken({
req: request,
secret: process.env.NEXTAUTH_SECRET
});

const isAdminRoute = request.nextUrl.pathname.startsWith("/admin-dashboard");
const isDashboardRoute = request.nextUrl.pathname.startsWith("/dashboard");

// Protect dashboard and admin routes
if (isDashboardRoute || isAdminRoute) {
// Redirect to sign in if not authenticated
Expand All @@ -26,48 +25,21 @@ export async function middleware(request: NextRequest) {
return NextResponse.redirect(new URL("/auth/error", request.url));
}

// Check admin status directly from Supabase
if (token.email) {
try {
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false
}
}
);

const { data: userData, error } = await supabase
.from('users')
.select('is_admin')
.eq('email', token.email)
.single();

const isAdmin = userData?.is_admin === 1;

// Admin-only routes protection
if (isAdminRoute) {
if (!isAdmin) {
// Non-admin users trying to access admin dashboard - redirect to regular dashboard
return NextResponse.redirect(new URL("/dashboard", request.url));
}
}

// Allow admins to access both /dashboard and /admin-dashboard
// No redirect needed for admins accessing regular dashboard
} catch (error) {
console.error('Error checking admin status in middleware:', error);
// If there's an error, allow regular dashboard access but not admin
if (isAdminRoute) {
return NextResponse.redirect(new URL("/dashboard", request.url));
}
// Admin-only routes protection
if (isAdminRoute) {
// For Cloudflare Pages compatibility, admin check is done client-side
// Middleware only protects the route with authentication
// The page itself will check admin status from the JWT token
if (!token.isAdmin) {
// Non-admin users trying to access admin dashboard - redirect to regular dashboard
return NextResponse.redirect(new URL("/dashboard", request.url));
}
}

// Allow admins to access both /dashboard and /admin-dashboard
// No redirect needed for admins accessing regular dashboard
}

return NextResponse.next();
}

Expand Down
13 changes: 4 additions & 9 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ const nextConfig = {
// Use standalone output only for Docker deployments
// Cloudflare Pages uses its own build process
output: process.env.NEXT_OUTPUT_STANDALONE === 'true' ? 'standalone' : undefined,
eslint: {
// Prevent lint errors from failing the production build. Team can fix lint issues separately.
ignoreDuringBuilds: true,
},
images: {
remotePatterns: [
{
Expand All @@ -26,11 +22,10 @@ const nextConfig = {
},
],
},
experimental: {
serverActions: {
bodySizeLimit: '2mb',
},
},
// Turbopack configuration (Next.js 16+)
// Empty object to silence warning - webpack config is used for this project
turbopack: {},
// Webpack configuration (for backwards compatibility)
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
Expand Down
Loading
Loading