implement down load file
This commit is contained in:
parent
fe9eb915e1
commit
1abeab6e46
5 changed files with 119 additions and 7 deletions
|
|
@ -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 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 ?
|
||||||
BIN
docs/notes.pdf
BIN
docs/notes.pdf
Binary file not shown.
66
src/app/api/download/route.ts
Normal file
66
src/app/api/download/route.ts
Normal 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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/auth.ts
20
src/auth.ts
|
|
@ -1,3 +1,4 @@
|
||||||
|
// src/auth.ts
|
||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
import { PrismaAdapter } from "@auth/prisma-adapter";
|
import { PrismaAdapter } from "@auth/prisma-adapter";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
@ -10,6 +11,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, account, user }) {
|
async jwt({ token, account, user }) {
|
||||||
// 1. Handle OAuth tokens (from first sign-in)
|
// 1. Handle OAuth tokens (from first sign-in)
|
||||||
|
// This captures the tokens directly from the Microsoft Azure response
|
||||||
if (account) {
|
if (account) {
|
||||||
token.accessToken = account.access_token;
|
token.accessToken = account.access_token;
|
||||||
token.refreshToken = account.refresh_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
|
// 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) {
|
if (user) {
|
||||||
token.sub = user.id;
|
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;
|
token.role = user.role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,10 +31,16 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||||
|
|
||||||
async session({ session, token }) {
|
async session({ session, token }) {
|
||||||
// 3. Pass values from the JWT Token into the Client-facing Session
|
// 3. Pass values from the JWT Token into the Client-facing Session
|
||||||
if (session?.user && token.sub) {
|
// This makes the tokens and IDs available to your API routes and Components
|
||||||
session.user.id = token.sub;
|
if (session?.user) {
|
||||||
// @ts-ignore - Attaching the role so the Navbar and Settings page can see it
|
session.user.id = token.sub as string;
|
||||||
session.user.role = token.role;
|
|
||||||
|
// @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;
|
return session;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
24
src/types/next-auth.d.ts
vendored
Normal file
24
src/types/next-auth.d.ts
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue