Skip to content

Commit 562a5af

Browse files
committed
feat: enhance pre-commit hook, update Next.js config for Docker, and add health check API
1 parent 4ecd9d8 commit 562a5af

17 files changed

Lines changed: 65 additions & 61 deletions

File tree

.githooks/pre-commit

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/bin/sh
22
#
33
# PairUX Pre-commit Hook
4-
# Validates build, tests, and linting before allowing commits
4+
# Runs lint, test, and build before allowing commits
55
#
66
# To install: git config core.hooksPath .githooks
77
# Or run: pnpm setup:hooks
@@ -15,7 +15,6 @@ echo ""
1515
# Colors for output
1616
RED='\033[0;31m'
1717
GREEN='\033[0;32m'
18-
YELLOW='\033[1;33m'
1918
NC='\033[0m' # No Color
2019

2120
# Track if any check fails
@@ -25,11 +24,11 @@ FAILED=0
2524
run_check() {
2625
local name="$1"
2726
local cmd="$2"
28-
27+
2928
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
3029
echo "📋 $name"
3130
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
32-
31+
3332
if eval "$cmd"; then
3433
echo "${GREEN}$name passed${NC}"
3534
echo ""
@@ -49,27 +48,14 @@ if [ -z "$STAGED_FILES" ]; then
4948
exit 0
5049
fi
5150

52-
# Check if there are any TypeScript/JavaScript files
53-
TS_FILES=$(echo "$STAGED_FILES" | grep -E '\.(ts|tsx|js|jsx|mjs)$' || true)
51+
# 1. Linting
52+
run_check "ESLint" "pnpm lint"
5453

55-
if [ -n "$TS_FILES" ]; then
56-
# 1. Type checking
57-
run_check "TypeScript Type Check" "pnpm typecheck 2>/dev/null || echo 'Skipping typecheck (no tsconfig found)'"
58-
59-
# 2. Linting
60-
run_check "ESLint" "pnpm lint 2>/dev/null || echo 'Skipping lint (no eslint config found)'"
61-
62-
# 3. Format check
63-
run_check "Prettier Format Check" "pnpm format:check 2>/dev/null || echo 'Skipping format check'"
64-
fi
65-
66-
# 4. Run tests (only if test files exist)
67-
if [ -d "apps" ] || [ -d "packages" ]; then
68-
run_check "Vitest Tests" "pnpm test --run 2>/dev/null || echo 'Skipping tests (no test files found)'"
69-
fi
54+
# 2. Run tests
55+
run_check "Tests" "pnpm test"
7056

71-
# 5. Build check (ensure it compiles)
72-
run_check "Build Check" "pnpm build 2>/dev/null || echo 'Skipping build (no build targets found)'"
57+
# 3. Build check
58+
run_check "Build" "pnpm build"
7359

7460
# Final result
7561
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

apps/web/next.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ const nextConfig: NextConfig = {
44
// Enable React strict mode for better development experience
55
reactStrictMode: true,
66

7+
// Standalone output for Docker deployment
8+
output: 'standalone',
9+
710
// Transpile shared packages
811
transpilePackages: ['@pairux/shared-types'],
912

apps/web/src/app/api/auth/forgot-password/route.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { forgotPasswordSchema } from '@/lib/validations';
43
import { successResponse, errorResponse, handleApiError } from '@/lib/api';
54
import { headers } from 'next/headers';
65

76
export async function POST(request: Request) {
87
try {
9-
const body = await request.json();
8+
const body: unknown = await request.json();
109
const { email } = forgotPasswordSchema.parse(body);
1110

1211
const supabase = await createClient();
1312
const headersList = await headers();
14-
const origin = headersList.get('origin') || process.env.NEXT_PUBLIC_APP_URL;
13+
const origin = headersList.get('origin') ?? process.env.NEXT_PUBLIC_APP_URL ?? '';
1514

1615
const { error } = await supabase.auth.resetPasswordForEmail(email, {
1716
redirectTo: `${origin}/reset-password`,

apps/web/src/app/api/auth/reset-password/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { resetPasswordSchema } from '@/lib/validations';
43
import { successResponse, errorResponse, handleApiError } from '@/lib/api';
54

65
export async function POST(request: Request) {
76
try {
8-
const body = await request.json();
7+
const body: unknown = await request.json();
98
const { password } = resetPasswordSchema.parse(body);
109

1110
const supabase = await createClient();

apps/web/src/app/api/auth/session/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { successResponse, handleApiError } from '@/lib/api';
43

@@ -29,7 +28,7 @@ export async function GET() {
2928
id: user.id,
3029
email: user.email,
3130
},
32-
profile: profile || null,
31+
profile,
3332
});
3433
} catch (error) {
3534
return handleApiError(error);

apps/web/src/app/api/auth/signup/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { signupSchema } from '@/lib/validations';
43
import { successResponse, errorResponse, handleApiError } from '@/lib/api';
54

65
export async function POST(request: Request) {
76
try {
8-
const body = await request.json();
7+
const body: unknown = await request.json();
98
const { email, password, displayName } = signupSchema.parse(body);
109

1110
const supabase = await createClient();
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NextResponse } from 'next/server';
2+
3+
export function GET() {
4+
return NextResponse.json({ status: 'ok', timestamp: new Date().toISOString() });
5+
}

apps/web/src/app/api/sessions/[sessionId]/route.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('DELETE /api/sessions/[sessionId]', () => {
107107
method: 'DELETE',
108108
});
109109
const response = await DELETE(request, createParams('test-session-id'));
110-
const body = await response.json();
110+
await response.json(); // consume response body
111111

112112
expect(response.status).toBe(200);
113113
expect(mockSupabase.rpc).toHaveBeenCalledWith('end_session', {

apps/web/src/app/api/sessions/[sessionId]/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { successResponse, errorResponse, handleApiError } from '@/lib/api';
43

@@ -47,13 +46,14 @@ export async function DELETE(_request: Request, { params }: RouteParams) {
4746
}
4847

4948
// End session using RPC function
50-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
49+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
5150
const { data, error } = await (supabase.rpc as any)('end_session', {
5251
p_session_id: sessionId,
5352
});
5453

5554
if (error) {
5655
console.error('End session error:', error);
56+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
5757
return errorResponse(error.message, 400);
5858
}
5959

apps/web/src/app/api/sessions/join/[joinCode]/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/restrict-template-expressions */
21
import { createClient } from '@/lib/supabase/server';
32
import { guestJoinSchema } from '@/lib/validations';
43
import { successResponse, errorResponse, handleApiError } from '@/lib/api';
@@ -41,7 +40,7 @@ export async function GET(_request: Request, { params }: RouteParams) {
4140

4241
return successResponse({
4342
...sessionData,
44-
participant_count: count || 0,
43+
participant_count: count ?? 0,
4544
});
4645
} catch (error) {
4746
return handleApiError(error);
@@ -52,7 +51,7 @@ export async function GET(_request: Request, { params }: RouteParams) {
5251
export async function POST(request: Request, { params }: RouteParams) {
5352
try {
5453
const { joinCode } = await params;
55-
const body = await request.json().catch(() => ({}));
54+
const body = await request.json().catch(() => ({})) as { displayName?: string };
5655

5756
const supabase = await createClient();
5857

@@ -69,18 +68,19 @@ export async function POST(request: Request, { params }: RouteParams) {
6968
displayName = parsed.data.displayName;
7069
} else {
7170
// Authenticated user can optionally provide display name
72-
displayName = body.displayName || undefined;
71+
displayName = body.displayName ?? undefined;
7372
}
7473

7574
// Join session using RPC function
76-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
75+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
7776
const { data, error } = await (supabase.rpc as any)('join_session', {
7877
p_join_code: joinCode.toUpperCase(),
7978
p_display_name: displayName,
8079
});
8180

8281
if (error) {
8382
console.error('Join session error:', error);
83+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
8484
return errorResponse(error.message, 400);
8585
}
8686

0 commit comments

Comments
 (0)