diff options
| -rw-r--r-- | frontend/app/api/logout/route.ts | 32 | ||||
| -rw-r--r-- | frontend/components/auth/AuthButtons.tsx | 30 | ||||
| -rw-r--r-- | frontend/components/drive/DriveDirectoryClient.tsx | 40 | ||||
| -rw-r--r-- | frontend/components/drive/DriveDirectoryView.tsx | 21 | ||||
| -rw-r--r-- | frontend/components/drive/DriveHeader.tsx | 15 |
5 files changed, 96 insertions, 42 deletions
diff --git a/frontend/app/api/logout/route.ts b/frontend/app/api/logout/route.ts new file mode 100644 index 0000000..51de324 --- /dev/null +++ b/frontend/app/api/logout/route.ts | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | import { NextRequest } from 'next/server'; | ||
| 2 | import { redirect } from 'next/navigation'; | ||
| 3 | import { Auth_tinyauth_endpoint, Auth_tinyauth_public_endpoint } from '@/lib/auth_shared'; | ||
| 4 | |||
| 5 | export async function POST(request: NextRequest) { | ||
| 6 | try { | ||
| 7 | // Get the current session cookie | ||
| 8 | const cookies = request.cookies.getAll(); | ||
| 9 | const sessionCookie = cookies.find(cookie => cookie.name.includes('tinyauth-session')); | ||
| 10 | |||
| 11 | if (sessionCookie) { | ||
| 12 | // Call tinyauth logout endpoint to invalidate the session | ||
| 13 | const logoutResponse = await fetch(`${Auth_tinyauth_endpoint()}/auth/logout`, { | ||
| 14 | method: 'POST', | ||
| 15 | headers: { | ||
| 16 | 'Cookie': `${sessionCookie.name}=${sessionCookie.value}` | ||
| 17 | } | ||
| 18 | }); | ||
| 19 | |||
| 20 | // Note: We don't need to check the response status as we'll redirect anyway | ||
| 21 | } | ||
| 22 | |||
| 23 | // Redirect to the public logout endpoint which should clear cookies client-side | ||
| 24 | const publicLogoutUrl = `${Auth_tinyauth_public_endpoint()}/auth/logout`; | ||
| 25 | return Response.redirect(publicLogoutUrl, 302); | ||
| 26 | |||
| 27 | } catch (error) { | ||
| 28 | console.error('Logout error:', error); | ||
| 29 | // Even if logout fails, redirect to home | ||
| 30 | return redirect('/'); | ||
| 31 | } | ||
| 32 | } \ No newline at end of file | ||
diff --git a/frontend/components/auth/AuthButtons.tsx b/frontend/components/auth/AuthButtons.tsx new file mode 100644 index 0000000..5b5053d --- /dev/null +++ b/frontend/components/auth/AuthButtons.tsx | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | import { LogIn, LogOut } from "lucide-react" | ||
| 2 | import { Button } from "@/components/ui/button" | ||
| 3 | import { Auth_get_user } from "@/lib/auth" | ||
| 4 | import { Auth_tinyauth_public_endpoint } from "@/lib/auth_shared" | ||
| 5 | |||
| 6 | export async function AuthButtons() { | ||
| 7 | const user = await Auth_get_user() | ||
| 8 | |||
| 9 | if (user.isLoggedIn) { | ||
| 10 | return ( | ||
| 11 | <form action="/api/logout" method="post"> | ||
| 12 | <Button type="submit" variant="outline"> | ||
| 13 | <LogOut className="mr-2 h-4 w-4" /> | ||
| 14 | Logout | ||
| 15 | </Button> | ||
| 16 | </form> | ||
| 17 | ) | ||
| 18 | } | ||
| 19 | |||
| 20 | const authUrl = `${Auth_tinyauth_public_endpoint()}/auth/google` | ||
| 21 | |||
| 22 | return ( | ||
| 23 | <Button asChild> | ||
| 24 | <a href={authUrl}> | ||
| 25 | <LogIn className="mr-2 h-4 w-4" /> | ||
| 26 | Login | ||
| 27 | </a> | ||
| 28 | </Button> | ||
| 29 | ) | ||
| 30 | } \ No newline at end of file | ||
diff --git a/frontend/components/drive/DriveDirectoryClient.tsx b/frontend/components/drive/DriveDirectoryClient.tsx index d48be61..3058a68 100644 --- a/frontend/components/drive/DriveDirectoryClient.tsx +++ b/frontend/components/drive/DriveDirectoryClient.tsx | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | import type React from "react" | 3 | import type React from "react" |
| 4 | import { useState, useRef } from "react" | 4 | import { useState, useRef } from "react" |
| 5 | import Link from "next/link" | 5 | import Link from "next/link" |
| 6 | import { Auth_tinyauth_public_endpoint } from "@/lib/auth_shared" | ||
| 7 | import { | 6 | import { |
| 8 | ChevronRight, | 7 | ChevronRight, |
| 9 | File, | 8 | File, |
| @@ -12,12 +11,9 @@ import { | |||
| 12 | Trash2, | 11 | Trash2, |
| 13 | Move, | 12 | Move, |
| 14 | MoreHorizontal, | 13 | MoreHorizontal, |
| 15 | HardDrive, | ||
| 16 | Edit, | 14 | Edit, |
| 17 | Link as LinkIcon, | 15 | Link as LinkIcon, |
| 18 | Info, | 16 | Info, |
| 19 | LogIn, | ||
| 20 | LogOut, | ||
| 21 | FolderPlus, | 17 | FolderPlus, |
| 22 | } from "lucide-react" | 18 | } from "lucide-react" |
| 23 | import { Button } from "@/components/ui/button" | 19 | import { Button } from "@/components/ui/button" |
| @@ -39,6 +35,7 @@ import { UPLOAD_MAX_FILE_SIZE, UPLOAD_MAX_FILES } from "@/lib/constants" | |||
| 39 | import { DriveMoveDialog } from "./DriveMoveDialog" | 35 | import { DriveMoveDialog } from "./DriveMoveDialog" |
| 40 | import { StorageUsage } from "./StorageUsage" | 36 | import { StorageUsage } from "./StorageUsage" |
| 41 | import type { StorageData } from "@/lib/storage" | 37 | import type { StorageData } from "@/lib/storage" |
| 38 | import type { UserAuth } from "@/lib/auth_types" | ||
| 42 | 39 | ||
| 43 | function formatFileSize(bytes: number): string { | 40 | function formatFileSize(bytes: number): string { |
| 44 | if (bytes === 0) return "0 Bytes" | 41 | if (bytes === 0) return "0 Bytes" |
| @@ -75,9 +72,10 @@ interface DriveDirectoryClientProps { | |||
| 75 | files: DriveLsEntry[] | 72 | files: DriveLsEntry[] |
| 76 | breadcrumbs: Breadcrumb[] | 73 | breadcrumbs: Breadcrumb[] |
| 77 | storageData: StorageData | 74 | storageData: StorageData |
| 75 | user: UserAuth | ||
| 78 | } | 76 | } |
| 79 | 77 | ||
| 80 | export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: DriveDirectoryClientProps) { | 78 | export function DriveDirectoryClient({ path, files, breadcrumbs, storageData, user }: DriveDirectoryClientProps) { |
| 81 | const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set()) | 79 | const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set()) |
| 82 | const [renameDialogOpen, setRenameDialogOpen] = useState(false) | 80 | const [renameDialogOpen, setRenameDialogOpen] = useState(false) |
| 83 | const [infoDialogOpen, setInfoDialogOpen] = useState(false) | 81 | const [infoDialogOpen, setInfoDialogOpen] = useState(false) |
| @@ -89,7 +87,6 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: | |||
| 89 | const fileInputRef = useRef<HTMLInputElement>(null) | 87 | const fileInputRef = useRef<HTMLInputElement>(null) |
| 90 | const [uploading, setUploading] = useState(false) | 88 | const [uploading, setUploading] = useState(false) |
| 91 | 89 | ||
| 92 | const [isLoggedIn, setIsLoggedIn] = useState(true) // Mock logged in state | ||
| 93 | 90 | ||
| 94 | const toggleFileSelection = (filePath: string) => { | 91 | const toggleFileSelection = (filePath: string) => { |
| 95 | const newSelected = new Set(selectedFiles) | 92 | const newSelected = new Set(selectedFiles) |
| @@ -311,17 +308,6 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: | |||
| 311 | } | 308 | } |
| 312 | } | 309 | } |
| 313 | 310 | ||
| 314 | const handleLogin = () => { | ||
| 315 | // Redirect to tinyauth Google OAuth endpoint | ||
| 316 | const authUrl = `${Auth_tinyauth_public_endpoint()}/auth/google` | ||
| 317 | window.location.href = authUrl | ||
| 318 | } | ||
| 319 | |||
| 320 | const handleLogout = () => { | ||
| 321 | // Handle logout (would typically clear tokens, etc.) | ||
| 322 | setIsLoggedIn(false) | ||
| 323 | // Could also redirect to logout endpoint | ||
| 324 | } | ||
| 325 | 311 | ||
| 326 | const handleMove = async (destinationPath: string) => { | 312 | const handleMove = async (destinationPath: string) => { |
| 327 | // TODO: Implement actual move API calls | 313 | // TODO: Implement actual move API calls |
| @@ -379,15 +365,10 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: | |||
| 379 | } | 365 | } |
| 380 | 366 | ||
| 381 | return ( | 367 | return ( |
| 382 | <div className="container mx-auto p-6 space-y-6"> | 368 | <div className="space-y-6"> |
| 383 | {/* Header with Breadcrumbs */} | 369 | {/* Navigation and Actions */} |
| 384 | <div className="flex items-center justify-between"> | 370 | <div className="flex items-center justify-between"> |
| 385 | <div className="flex items-center gap-4"> | 371 | <div className="flex items-center gap-4"> |
| 386 | <div className="flex items-center gap-2"> | ||
| 387 | <HardDrive className="h-6 w-6" /> | ||
| 388 | <h1 className="text-2xl font-bold">FCT Drive</h1> | ||
| 389 | </div> | ||
| 390 | |||
| 391 | {/* Breadcrumbs */} | 372 | {/* Breadcrumbs */} |
| 392 | <nav className="flex items-center gap-1 text-sm text-muted-foreground"> | 373 | <nav className="flex items-center gap-1 text-sm text-muted-foreground"> |
| 393 | {breadcrumbs.map((crumb, index) => ( | 374 | {breadcrumbs.map((crumb, index) => ( |
| @@ -423,17 +404,6 @@ export function DriveDirectoryClient({ path, files, breadcrumbs, storageData }: | |||
| 423 | <Upload className="mr-2 h-4 w-4" /> | 404 | <Upload className="mr-2 h-4 w-4" /> |
| 424 | {uploading ? "Uploading..." : "Upload Files"} | 405 | {uploading ? "Uploading..." : "Upload Files"} |
| 425 | </Button> | 406 | </Button> |
| 426 | {isLoggedIn ? ( | ||
| 427 | <Button variant="outline" onClick={handleLogout}> | ||
| 428 | <LogOut className="mr-2 h-4 w-4" /> | ||
| 429 | Logout | ||
| 430 | </Button> | ||
| 431 | ) : ( | ||
| 432 | <Button onClick={handleLogin}> | ||
| 433 | <LogIn className="mr-2 h-4 w-4" /> | ||
| 434 | Login | ||
| 435 | </Button> | ||
| 436 | )} | ||
| 437 | </div> | 407 | </div> |
| 438 | </div> | 408 | </div> |
| 439 | 409 | ||
diff --git a/frontend/components/drive/DriveDirectoryView.tsx b/frontend/components/drive/DriveDirectoryView.tsx index 3558537..d2f30ee 100644 --- a/frontend/components/drive/DriveDirectoryView.tsx +++ b/frontend/components/drive/DriveDirectoryView.tsx | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | import { DriveLsEntry } from "@/lib/drive_types" | 1 | import { DriveLsEntry } from "@/lib/drive_types" |
| 2 | import { DriveDirectoryClient } from "./DriveDirectoryClient" | 2 | import { DriveDirectoryClient } from "./DriveDirectoryClient" |
| 3 | import { DriveHeader } from "./DriveHeader" | ||
| 3 | import type { StorageData } from "@/lib/storage" | 4 | import type { StorageData } from "@/lib/storage" |
| 5 | import { Auth_get_user } from "@/lib/auth" | ||
| 4 | 6 | ||
| 5 | interface DriveDirectoryViewProps { | 7 | interface DriveDirectoryViewProps { |
| 6 | path: string | 8 | path: string |
| @@ -43,16 +45,21 @@ function sortFiles(files: DriveLsEntry[]): DriveLsEntry[] { | |||
| 43 | }); | 45 | }); |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | export function DriveDirectoryView({ path, files, storageData }: DriveDirectoryViewProps) { | 48 | export async function DriveDirectoryView({ path, files, storageData }: DriveDirectoryViewProps) { |
| 47 | const sortedFiles = sortFiles(files) | 49 | const sortedFiles = sortFiles(files) |
| 48 | const breadcrumbs = generateBreadcrumbs(path) | 50 | const breadcrumbs = generateBreadcrumbs(path) |
| 51 | const user = await Auth_get_user() | ||
| 49 | 52 | ||
| 50 | return ( | 53 | return ( |
| 51 | <DriveDirectoryClient | 54 | <div className="container mx-auto p-6 space-y-6"> |
| 52 | path={path} | 55 | <DriveHeader /> |
| 53 | files={sortedFiles} | 56 | <DriveDirectoryClient |
| 54 | breadcrumbs={breadcrumbs} | 57 | path={path} |
| 55 | storageData={storageData} | 58 | files={sortedFiles} |
| 56 | /> | 59 | breadcrumbs={breadcrumbs} |
| 60 | storageData={storageData} | ||
| 61 | user={user} | ||
| 62 | /> | ||
| 63 | </div> | ||
| 57 | ) | 64 | ) |
| 58 | } \ No newline at end of file | 65 | } \ No newline at end of file |
diff --git a/frontend/components/drive/DriveHeader.tsx b/frontend/components/drive/DriveHeader.tsx new file mode 100644 index 0000000..718d031 --- /dev/null +++ b/frontend/components/drive/DriveHeader.tsx | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | import { HardDrive } from "lucide-react" | ||
| 2 | import { AuthButtons } from "@/components/auth/AuthButtons" | ||
| 3 | |||
| 4 | export async function DriveHeader() { | ||
| 5 | return ( | ||
| 6 | <div className="flex items-center justify-between"> | ||
| 7 | <div className="flex items-center gap-2"> | ||
| 8 | <HardDrive className="h-6 w-6" /> | ||
| 9 | <h1 className="text-2xl font-bold">FCT Drive</h1> | ||
| 10 | </div> | ||
| 11 | |||
| 12 | <AuthButtons /> | ||
| 13 | </div> | ||
| 14 | ) | ||
| 15 | } \ No newline at end of file | ||
