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_PASSWORD=QijU8e2A8p3FE8WS8esR
|
||||||
SMTP_SECURE=true
|
SMTP_SECURE=true
|
||||||
|
|
||||||
# NextAuth Configuration
|
NEXTAUTH_URL=https://todo.martin-bierschenk.de
|
||||||
NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
# NEXTAUTH_URL=http://localhost:3000
|
||||||
NEXTAUTH_URL=http://localhost:3000
|
|
||||||
|
|
||||||
# Google OAuth (Replace with your credentials)
|
# Google OAuth (Replace with your credentials)
|
||||||
GOOGLE_CLIENT_ID=196368743757-1fn17q2ecg5n8rej4tu96khltno173he.apps.googleusercontent.com
|
GOOGLE_CLIENT_ID=196368743757-1fn17q2ecg5n8rej4tu96khltno173he.apps.googleusercontent.com
|
||||||
GOOGLE_CLIENT_SECRET=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
|
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)
|
# 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
|
# APPLE_SECRET=your-apple-secret
|
||||||
|
|
||||||
# Base URL for the application
|
# 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.js
|
||||||
.next/
|
.next/
|
||||||
|
|
||||||
|
certificates
|
||||||
3
SETUP.md
3
SETUP.md
@ -83,7 +83,8 @@ To enable "Sign in with Google":
|
|||||||
5. Choose **Web application**
|
5. Choose **Web application**
|
||||||
6. Add these redirect URIs:
|
6. Add these redirect URIs:
|
||||||
- `http://localhost:3000/api/auth/callback/google`
|
- `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**
|
7. Copy your **Client ID** and **Client Secret**
|
||||||
|
|
||||||
### 2. Update Environment Variables
|
### 2. Update Environment Variables
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
||||||
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
||||||
- NEXT_PUBLIC_BASE_URL=http://localhost:3000
|
- NEXT_PUBLIC_BASE_URL=https://todo.martin-bierschenk.de
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
- DATABASE_URL=postgresql://root:WKE7xeZohxdZit7eObjG@db:5432/My-Weekly-ToDo-List?schema=public
|
||||||
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
- NEXTAUTH_SECRET=Ca7EoJzZSMJO2CkqwdWd
|
||||||
- NEXT_PUBLIC_BASE_URL=http://localhost:13000
|
- NEXT_PUBLIC_BASE_URL=https://todo.martin-bierschenk.de
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
serverExternalPackages: ['ical.js', 'tsdav'],
|
experimental: {
|
||||||
|
serverComponentsExternalPackages: ['ical.js', 'tsdav'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
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",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev -H 0.0.0.0",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
|||||||
@ -25,6 +25,11 @@ export async function GET(request: NextRequest) {
|
|||||||
|
|
||||||
const oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUri);
|
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
|
// Generate the authorization URL
|
||||||
const authUrl = oauth2Client.generateAuthUrl({
|
const authUrl = oauth2Client.generateAuthUrl({
|
||||||
access_type: 'offline',
|
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 type { Metadata } from 'next';
|
||||||
import { Inter } from 'next/font/google';
|
import { Inter } from 'next/font/google';
|
||||||
|
import './globals.css';
|
||||||
|
import { Providers } from './providers';
|
||||||
|
|
||||||
const inter = Inter({ subsets: ['latin'] });
|
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({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
@ -15,9 +18,7 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={inter.className}>
|
<body className={inter.className}>
|
||||||
<SessionProvider>
|
<Providers>{children}</Providers>
|
||||||
{children}
|
|
||||||
</SessionProvider>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 {
|
export interface AppleCalendar {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
color?: string;
|
||||||
isPrimary?: boolean;
|
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
|
// Since tsdav creates a complex object graph, we'll wrap this in a try/catch
|
||||||
const calendars = await client.fetchCalendars();
|
const calendars = await client.fetchCalendars();
|
||||||
|
|
||||||
return calendars.map(cal => ({
|
const mappedCalendars = calendars.map(cal => ({
|
||||||
id: cal.url, // Using URL as ID for CalDAV
|
id: cal.url, // Using URL as ID for CalDAV
|
||||||
title: (cal.displayName as string) || 'Untitled Calendar',
|
title: (cal.displayName as string) || 'Untitled Calendar',
|
||||||
|
color: cal.calendarColor,
|
||||||
isPrimary: false, // Hard to determine primary in generic CalDAV
|
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) {
|
} catch (error) {
|
||||||
console.error('Apple Calendar validation failed:', error);
|
console.error('Apple Calendar validation failed:', error);
|
||||||
throw new Error('Invalid credentials or unable to connect to iCloud.');
|
throw new Error('Invalid credentials or unable to connect to iCloud.');
|
||||||
|
|||||||
@ -319,7 +319,7 @@ export const getCalendarEvents = async (
|
|||||||
source: 'apple' as const,
|
source: 'apple' as const,
|
||||||
calendarId,
|
calendarId,
|
||||||
calendarTitle: calendars.find(c => c.id === calendarId)?.title || 'Apple Calendar',
|
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') {
|
} else if (connection.provider === 'outlook') {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user