Configure app for HTTPS reverse proxy on custom domain
This commit is contained in:
parent
64955ad07b
commit
46dbf2ac02
10
.env.local
10
.env.local
@ -8,14 +8,14 @@ SMTP_USERNAME=mail@carrylight.de
|
||||
SMTP_PASSWORD=QijU8e2A8p3FE8WS8esR
|
||||
SMTP_SECURE=true
|
||||
|
||||
# NextAuth Configuration
|
||||
NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
NEXTAUTH_URL=https://todo.martin-bierschenk.de
|
||||
# NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# Google OAuth (Replace with your credentials)
|
||||
GOOGLE_CLIENT_ID=196368743757-1fn17q2ecg5n8rej4tu96khltno173he.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
GOOGLE_REDIRECT_URI=http://localhost:3000/api/calendar/google/oauth
|
||||
GOOGLE_REDIRECT_URI=https://todo.martin-bierschenk.de/api/calendar/google/oauth
|
||||
# GOOGLE_REDIRECT_URI=http://localhost:3000/api/calendar/google/oauth
|
||||
|
||||
|
||||
# Apple Sign In (Replace with your credentials when ready)
|
||||
@ -23,4 +23,4 @@ GOOGLE_REDIRECT_URI=http://localhost:3000/api/calendar/google/oauth
|
||||
# APPLE_SECRET=your-apple-secret
|
||||
|
||||
# Base URL for the application
|
||||
NEXT_PUBLIC_BASE_URL=http://localhost:3001
|
||||
NEXT_PUBLIC_BASE_URL=https://todo.martin-bierschenk.de
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ node_modules/
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
||||
certificates
|
||||
3
SETUP.md
3
SETUP.md
@ -83,7 +83,8 @@ To enable "Sign in with Google":
|
||||
5. Choose **Web application**
|
||||
6. Add these redirect URIs:
|
||||
- `http://localhost:3000/api/auth/callback/google`
|
||||
- `http://your-domain.com/api/auth/callback/google` (for production)
|
||||
- `https://todo.martin-bierschenk.de/api/auth/callback/google` (via Reverse Proxy)
|
||||
- `https://todo.martin-bierschenk.de/api/calendar/google/oauth` (via Reverse Proxy)
|
||||
7. Copy your **Client ID** and **Client Secret**
|
||||
|
||||
### 2. Update Environment Variables
|
||||
|
||||
@ -8,7 +8,7 @@ services:
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
||||
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
||||
- NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
||||
- NEXT_PUBLIC_BASE_URL=https://todo.martin-bierschenk.de
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
|
||||
@ -8,7 +8,7 @@ services:
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
||||
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
||||
- NEXT_PUBLIC_BASE_URL=http://localhost:13000
|
||||
- NEXT_PUBLIC_BASE_URL=https://todo.martin-bierschenk.de
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
serverExternalPackages: ['ical.js', 'tsdav'],
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['ical.js', 'tsdav'],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev -H 0.0.0.0",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
|
||||
@ -25,6 +25,11 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);
|
||||
|
||||
console.log('--- Google OAuth Debug ---');
|
||||
console.log('Redirect URI:', redirectUri);
|
||||
console.log('Client ID starts with:', clientId?.substring(0, 10));
|
||||
console.log('--------------------------');
|
||||
|
||||
// Generate the authorization URL
|
||||
const authUrl = oauth2Client.generateAuthUrl({
|
||||
access_type: 'offline',
|
||||
|
||||
32
src/app/error.tsx
Normal file
32
src/app/error.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
// Log the error to an error reporting service
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-4 p-4 text-center">
|
||||
<h2 className="text-xl font-semibold">Something went wrong!</h2>
|
||||
<p className="text-gray-500">{error.message || 'An unexpected error occurred'}</p>
|
||||
<button
|
||||
className="rounded bg-blue-500 px-4 py-2 text-white transition-colors hover:bg-blue-600"
|
||||
onClick={
|
||||
// Attempt to recover by trying to re-render the segment
|
||||
() => reset()
|
||||
}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
src/app/global-error.tsx
Normal file
28
src/app/global-error.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
'use client';
|
||||
|
||||
export default function GlobalError({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
<div className="flex h-screen w-full flex-col items-center justify-center bg-white text-black">
|
||||
<h2 className="mb-4 text-2xl font-bold">Something went wrong!</h2>
|
||||
<p className="mb-4 text-gray-600">
|
||||
{error.message || 'A critical error occurred'}
|
||||
</p>
|
||||
<button
|
||||
className="rounded bg-blue-500 px-6 py-3 text-white transition-colors hover:bg-blue-600"
|
||||
onClick={() => reset()}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@ -1,12 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { SessionProvider } from 'next-auth/react';
|
||||
import './globals.css';
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
import { Providers } from './providers';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'My Weekly To Do List',
|
||||
description: 'A simple, designy to-do app.',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
@ -15,9 +18,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
<SessionProvider>
|
||||
{children}
|
||||
</SessionProvider>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
16
src/app/not-found.tsx
Normal file
16
src/app/not-found.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-4 bg-white p-4 text-black">
|
||||
<h2 className="text-2xl font-bold">Not Found</h2>
|
||||
<p className="text-gray-600">Could not find requested resource</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="rounded bg-blue-500 px-4 py-2 text-white transition-colors hover:bg-blue-600"
|
||||
>
|
||||
Return Home
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
src/app/providers.tsx
Normal file
7
src/app/providers.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { SessionProvider } from 'next-auth/react';
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
return <SessionProvider>{children}</SessionProvider>;
|
||||
}
|
||||
@ -16,6 +16,7 @@ export interface AppleCalendarEvent {
|
||||
export interface AppleCalendar {
|
||||
id: string;
|
||||
title: string;
|
||||
color?: string;
|
||||
isPrimary?: boolean;
|
||||
}
|
||||
|
||||
@ -47,11 +48,15 @@ export const validateCredentials = async (email: string, appSpecificPassword: st
|
||||
// Since tsdav creates a complex object graph, we'll wrap this in a try/catch
|
||||
const calendars = await client.fetchCalendars();
|
||||
|
||||
return calendars.map(cal => ({
|
||||
const mappedCalendars = calendars.map(cal => ({
|
||||
id: cal.url, // Using URL as ID for CalDAV
|
||||
title: (cal.displayName as string) || 'Untitled Calendar',
|
||||
color: cal.calendarColor,
|
||||
isPrimary: false, // Hard to determine primary in generic CalDAV
|
||||
}));
|
||||
|
||||
console.log('[APPLE CALENDAR] Found calendars:', mappedCalendars.map(c => ({ title: c.title, color: c.color })));
|
||||
return mappedCalendars;
|
||||
} catch (error) {
|
||||
console.error('Apple Calendar validation failed:', error);
|
||||
throw new Error('Invalid credentials or unable to connect to iCloud.');
|
||||
|
||||
@ -319,7 +319,7 @@ export const getCalendarEvents = async (
|
||||
source: 'apple' as const,
|
||||
calendarId,
|
||||
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
|
||||
backgroundColor: '#FF3B30' // Value for Apple Color (Red) or derive from calendar?
|
||||
backgroundColor: calendars.find(c => c.id === calendarId)?.color || '#FF3B30'
|
||||
})));
|
||||
}
|
||||
} else if (connection.provider === 'outlook') {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user