瀏覽代碼

feat(auth): add user ownership to file uploads with kinde integration

- Add userId field to File model for tracking file ownership
- Update file upload server action to accept userId parameter
- Create getFilesByUserId action for user-specific file queries
- Integrate Kinde auth in FilesTable component for user identification
- Add database migration for new userId column

BREAKING CHANGE: uploadFile action now requires userId parameter
vidane 6 月之前
父節點
當前提交
8d6eb64a9f

+ 2 - 1
.env

@@ -1,5 +1,6 @@
 # PostgreSQL Database Configuration
 # PostgreSQL Database Configuration
-DATABASE_URL="postgresql://postgres:postgres@localhost:5432/vtorio?schema=public"
+#DATABASE_URL="postgresql://postgres:postgres@localhost:5432/vtorio?schema=public"
+DATABASE_URL=postgresql://root:post.Acc.967@vixflix.online:5432/vtoriodb?schema=public
 
 
 # Project Root Path (for legacy file system operations)
 # Project Root Path (for legacy file system operations)
 ROOT_PATH="c:/Source/gogs/vtorio"
 ROOT_PATH="c:/Source/gogs/vtorio"

+ 4 - 1
.vscode/settings.json

@@ -13,5 +13,8 @@
   },
   },
   "debug.javascript.terminalOptions": {
   "debug.javascript.terminalOptions": {
     "shell": "C:\\Windows\\System32\\cmd.exe"
     "shell": "C:\\Windows\\System32\\cmd.exe"
-  }
+  },
+  "cSpell.words": [
+    "Kinde"
+  ]
 }
 }

+ 3 - 1
app/actions/file-upload.ts

@@ -2,7 +2,7 @@
 
 
 import { prisma } from '@/lib/prisma';
 import { prisma } from '@/lib/prisma';
 
 
-export async function uploadFile(file: File) {
+export async function uploadFile(file: File, userId: string) {
   try {
   try {
     const bytes = await file.arrayBuffer();
     const bytes = await file.arrayBuffer();
     const buffer = Buffer.from(bytes);
     const buffer = Buffer.from(bytes);
@@ -13,6 +13,7 @@ export async function uploadFile(file: File) {
         mimetype: file.type,
         mimetype: file.type,
         size: file.size,
         size: file.size,
         data: buffer,
         data: buffer,
+        userId: userId,
       },
       },
     });
     });
 
 
@@ -21,6 +22,7 @@ export async function uploadFile(file: File) {
       filename: uploadedFile.filename,
       filename: uploadedFile.filename,
       mimetype: uploadedFile.mimetype,
       mimetype: uploadedFile.mimetype,
       size: uploadedFile.size,
       size: uploadedFile.size,
+      userId: uploadedFile.userId,
       createdAt: uploadedFile.createdAt.toISOString(),
       createdAt: uploadedFile.createdAt.toISOString(),
       updatedAt: uploadedFile.updatedAt.toISOString(),
       updatedAt: uploadedFile.updatedAt.toISOString(),
     };
     };

+ 31 - 0
app/actions/files.ts

@@ -10,6 +10,7 @@ export interface FileData {
     filename: string;
     filename: string;
     mimetype: string;
     mimetype: string;
     size: number;
     size: number;
+    userId: string | null;
     createdAt: Date;
     createdAt: Date;
     updatedAt: Date;
     updatedAt: Date;
 }
 }
@@ -22,6 +23,7 @@ export async function getFiles(): Promise<FileData[]> {
                 filename: true,
                 filename: true,
                 mimetype: true,
                 mimetype: true,
                 size: true,
                 size: true,
+                userId: true,
                 createdAt: true,
                 createdAt: true,
                 updatedAt: true,
                 updatedAt: true,
             },
             },
@@ -39,6 +41,35 @@ export async function getFiles(): Promise<FileData[]> {
     }
     }
 }
 }
 
 
+export async function getFilesByUserId(userId: string): Promise<FileData[]> {
+    try {
+        const files = await prisma.file.findMany({
+            where: {
+                userId: userId,
+            },
+            select: {
+                id: true,
+                filename: true,
+                mimetype: true,
+                size: true,
+                userId: true,
+                createdAt: true,
+                updatedAt: true,
+            },
+            orderBy: {
+                createdAt: 'desc',
+            },
+        });
+
+        return files;
+    } catch (error) {
+        console.error("Error listing files by user:", error);
+        throw new Error("Failed to list files by user");
+    } finally {
+        await prisma.$disconnect();
+    }
+}
+
 export async function downloadFile(id: string): Promise<{
 export async function downloadFile(id: string): Promise<{
     data: Uint8Array;
     data: Uint8Array;
     filename: string;
     filename: string;

+ 4 - 1
app/components/filesTable.tsx

@@ -1,5 +1,6 @@
 "use client";
 "use client";
 
 
+import { useKindeBrowserClient } from "@kinde-oss/kinde-auth-nextjs";
 import React, { useState, useEffect } from "react";
 import React, { useState, useEffect } from "react";
 import {
 import {
   ColumnDef,
   ColumnDef,
@@ -42,6 +43,7 @@ export function FilesTable() {
   const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
   const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
   const [files, setFiles] = useState<FileData[]>([]);
   const [files, setFiles] = useState<FileData[]>([]);
   const [isUploading, setIsUploading] = useState(false);
   const [isUploading, setIsUploading] = useState(false);
+  const { user } = useKindeBrowserClient();
 
 
   const { data, isLoading, isError, error, refetch } = useQuery({
   const { data, isLoading, isError, error, refetch } = useQuery({
     queryKey: ["files"],
     queryKey: ["files"],
@@ -189,7 +191,8 @@ export function FilesTable() {
       // Import the server action
       // Import the server action
       const { uploadFile } = await import('../actions/file-upload');
       const { uploadFile } = await import('../actions/file-upload');
 
 
-      const uploadedFile = await uploadFile(file);
+      const userId = user?.id ? user.id : "unknown-user-id";
+      const uploadedFile = await uploadFile(file, userId);
 
 
       // Use the uploadedFile response to update the table immediately
       // Use the uploadedFile response to update the table immediately
       const newFile: FileData = {
       const newFile: FileData = {

+ 2 - 0
prisma/migrations/20250724191304_add_userid_to_files/migration.sql

@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "File" ADD COLUMN     "userId" TEXT;

+ 1 - 0
prisma/schema.prisma

@@ -16,6 +16,7 @@ model File {
   mimetype  String
   mimetype  String
   size      Int
   size      Int
   data      Bytes    // PostgreSQL ByteA type for blob storage
   data      Bytes    // PostgreSQL ByteA type for blob storage
+  userId    String?   // Kinde user ID that uploaded the file
   createdAt DateTime @default(now())
   createdAt DateTime @default(now())
   updatedAt DateTime @updatedAt
   updatedAt DateTime @updatedAt
 }
 }