implement down load file

This commit is contained in:
stephen 2026-01-15 13:00:59 +11:00
parent fe9eb915e1
commit 1abeab6e46
5 changed files with 119 additions and 7 deletions

View file

@ -355,4 +355,18 @@ Now we will go now and add metadata
Now we have the metadata along with some great filters on the data board page
Now we are going to implement an an update of a file or folder stored. 12/1/2026
Now we are going to implement an an update of a file or folder stored. 12/1/2026
For download there 2 function we need to implement
1) down load the file to ~/user/Downloads, this should work for any file type including pdf
2) if the file is a pdf open the file in another tab in the browser as most browser support reading a pdf
For download there 2 function we need to implement
1) down load the file to ~/user/Downloads, this should work for any file type including pdf
2) if the file is a pdf open the file in another tab in the browser as most browser support reading a pdf
So we need to implement 2 new actions, downLoad file and open pdf to read. Does this make sense ?

Binary file not shown.

View file

@ -0,0 +1,66 @@
// src/app/api/download/route.ts
"use server"
import { NextRequest, NextResponse } from 'next/server';
import { auth } from "@/auth"; // Import the auth function from your central config
import { prisma } from '@/lib/prisma';
export async function GET(request: NextRequest) {
try {
// 1. Check Authentication using the V5 auth() helper
const session = await auth();
// In V5, tokens are usually handled in the session callback
if (!session || !session.accessToken) {
return new NextResponse("Unauthorized - No Access Token found", { status: 401 });
}
// 2. Get parameters from URL
const { searchParams } = new URL(request.url);
const fileNodeId = searchParams.get('id');
const mode = searchParams.get('mode') === 'inline' ? 'inline' : 'attachment';
if (!fileNodeId) {
return new NextResponse("File ID is required", { status: 400 });
}
// 3. Find the file in your Postgres FileNode table
const fileNode = await prisma.fileNode.findUnique({
where: { id: fileNodeId }
});
if (!fileNode || !fileNode.oneDriveId) {
return new NextResponse("File not found in database", { status: 404 });
}
// 4. Fetch the file stream from Microsoft Graph
const graphResponse = await fetch(
`https://graph.microsoft.com/v1.0/me/drive/items/${fileNode.oneDriveId}/content`,
{
headers: {
Authorization: `Bearer ${session.accessToken}`,
},
}
);
if (!graphResponse.ok) {
const errorText = await graphResponse.text();
console.error('MS Graph Error:', errorText);
return new NextResponse(`OneDrive Error: ${graphResponse.statusText}`, { status: graphResponse.status });
}
// 5. Stream the response directly to the client
return new NextResponse(graphResponse.body, {
status: 200,
headers: {
'Content-Type': fileNode.mimeType || 'application/octet-stream',
// Note: Using encodeURIComponent for filename to handle special characters
'Content-Disposition': `${mode}; filename="${encodeURIComponent(fileNode.name)}"`,
},
});
} catch (error) {
console.error('Download error:', error);
return new NextResponse("Internal Server Error", { status: 500 });
}
}

View file

@ -1,3 +1,4 @@
// src/auth.ts
import NextAuth from "next-auth";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";
@ -10,6 +11,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
callbacks: {
async jwt({ token, account, user }) {
// 1. Handle OAuth tokens (from first sign-in)
// This captures the tokens directly from the Microsoft Azure response
if (account) {
token.accessToken = account.access_token;
token.refreshToken = account.refresh_token;
@ -17,10 +19,10 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
}
// 2. Attach User ID and Role to the token
// When 'user' exists, it's the first time we've fetched this user from the DB during login
// This runs when the user first logs in
if (user) {
token.sub = user.id;
// @ts-ignore - 'role' exists on our custom User model
// @ts-ignore - 'role' is a custom field in your Postgres User table
token.role = user.role;
}
@ -29,10 +31,16 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
async session({ session, token }) {
// 3. Pass values from the JWT Token into the Client-facing Session
if (session?.user && token.sub) {
session.user.id = token.sub;
// @ts-ignore - Attaching the role so the Navbar and Settings page can see it
session.user.role = token.role;
// This makes the tokens and IDs available to your API routes and Components
if (session?.user) {
session.user.id = token.sub as string;
// @ts-ignore - Attaching the role for UI permissions
session.user.role = token.role as string;
// IMPORTANT: We must attach the accessToken here so the
// /api/download route can use it to fetch from MS Graph
session.accessToken = token.accessToken as string;
}
return session;
},

24
src/types/next-auth.d.ts vendored Normal file
View file

@ -0,0 +1,24 @@
// src/types/next-auth.d.ts
import NextAuth, { DefaultSession } from "next-auth"
import { JWT } from "next-auth/jwt"
declare module "next-auth" {
/**
* Returned by `useSession`, `auth`, and received as a prop on the `SessionProvider` React Context
*/
interface Session {
accessToken?: string;
user: {
id: string;
role?: string;
} & DefaultSession["user"]
}
}
declare module "next-auth/jwt" {
/** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
interface JWT {
accessToken?: string;
role?: string;
}
}