From bc47e1d39a7eff0deac63d25f10c858b3a25fc82 Mon Sep 17 00:00:00 2001 From: dimitar Date: Sat, 2 Nov 2024 00:08:04 +0100 Subject: [PATCH] download working --- .gitignore | 2 + .../src/documents/documents.controller.ts | 24 +- .../src/documents/documents.service.ts | 312 +++++++++++------- backend/imk-backend/src/s3/s3.service.ts | 82 ++--- frontend/imk/src/App.jsx | 23 +- .../src/components/dashboard/Dashboard.jsx | 55 ++- frontend/imk/src/components/login/login.jsx | 158 +++------ .../protectedRoute/ProtectedRoute.jsx | 31 +- frontend/imk/src/hooks/useAuth.js | 29 -- frontend/imk/src/hooks/useAuth.jsx | 84 +++++ frontend/imk/src/services/api.js | 99 +++++- 11 files changed, 544 insertions(+), 355 deletions(-) delete mode 100644 frontend/imk/src/hooks/useAuth.js create mode 100644 frontend/imk/src/hooks/useAuth.jsx diff --git a/.gitignore b/.gitignore index dc6ddfd..3848b6c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ backend/imk-backend/dist backend/imk-backend/test frontend/imk/node_modules frontend/imk/dist +node_modules + diff --git a/backend/imk-backend/src/documents/documents.controller.ts b/backend/imk-backend/src/documents/documents.controller.ts index 947a24f..200f08b 100644 --- a/backend/imk-backend/src/documents/documents.controller.ts +++ b/backend/imk-backend/src/documents/documents.controller.ts @@ -1,19 +1,33 @@ -import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common'; + +import { Controller, Get, Param, Req, Res, UseGuards } from '@nestjs/common'; +import { Response } from 'express'; import { DocumentsService } from './documents.service'; import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'; @Controller('documents') export class DocumentsController { constructor(private readonly documentsService: DocumentsService) {} + @Get('shared/:userId') async getSharedDocuments(@Param('userId') userId: string) { - console.log('userId', userId); return this.documentsService.getClientDocuments(parseInt(userId)); } @Get('shared/download/:key') @UseGuards(JwtAuthGuard) - async downloadDocument(@Param('key') key: string, @Req() req) { - return this.documentsService.downloadDocument(key, req.user.id); + async downloadDocument( + @Param('key') key: string, + @Req() req, + @Res() res: Response + ) { + const file = await this.documentsService.downloadDocument(key, req.user.id); + + res.set({ + 'Content-Type': file.contentType, + 'Content-Length': file.contentLength, + 'Content-Disposition': `attachment; filename="${file.fileName}"`, + }); + + res.send(file.buffer); } -} +} \ No newline at end of file diff --git a/backend/imk-backend/src/documents/documents.service.ts b/backend/imk-backend/src/documents/documents.service.ts index f974c16..ddb69c8 100644 --- a/backend/imk-backend/src/documents/documents.service.ts +++ b/backend/imk-backend/src/documents/documents.service.ts @@ -1,99 +1,199 @@ -// import { Injectable } from '@nestjs/common'; +// // import { Injectable } from '@nestjs/common'; +// // import { PrismaService } from '../prisma/prisma.service'; +// // //import { Document } from '@prisma/client'; + +// // @Injectable() +// // export class DocumentsService { +// // downloadDocument(key: string, id: any) { +// // throw new Error('Method not implemented.'); +// // } +// // constructor(private readonly prisma: PrismaService) {} + +// // async getClientDocuments(clientId: number) { +// // // return this.prisma.document.findMany({ +// // // where: { +// // // sharedWithId: clientId, +// // // }, +// // // include: { +// // // sharedWith: { +// // // select: { +// // // id: true, +// // // name: true, +// // // email: true, +// // // }, +// // // }, +// // // }, +// // // }); +// // return this.prisma.document.findMany({ +// // where: { +// // sharedWithId: clientId, +// // }, +// // orderBy: { +// // createdAt: 'desc', +// // }, +// // }); +// // } +// // } +// import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common'; // import { PrismaService } from '../prisma/prisma.service'; -// //import { Document } from '@prisma/client'; +// import { S3Service } from '../s3/s3.service'; +// import { Document, User } from '@prisma/client'; // @Injectable() // export class DocumentsService { -// downloadDocument(key: string, id: any) { -// throw new Error('Method not implemented.'); -// } -// constructor(private readonly prisma: PrismaService) {} +// constructor( +// private prisma: PrismaService, +// private s3Service: S3Service, +// ) {} -// async getClientDocuments(clientId: number) { -// // return this.prisma.document.findMany({ -// // where: { -// // sharedWithId: clientId, -// // }, -// // include: { -// // sharedWith: { -// // select: { -// // id: true, -// // name: true, -// // email: true, -// // }, -// // }, -// // }, -// // }); -// return this.prisma.document.findMany({ +// async findAllByClient(userId: number): Promise { +// const documents = await this.prisma.document.findMany({ // where: { -// sharedWithId: clientId, +// sharedWithId: userId, +// }, +// include: { +// sharedWith: { +// select: { +// id: true, +// name: true, +// email: true, +// }, +// }, // }, // orderBy: { // createdAt: 'desc', // }, // }); + +// return documents; +// } +// async getClientDocuments(clientId: number) { +// // return this.prisma.document.findMany({ +// // where: { +// // sharedWithId: clientId, +// // }, +// // include: { +// // sharedWith: { +// // select: { +// // id: true, +// // name: true, +// // email: true, +// // }, +// // }, +// // }, +// // }); +// return this.prisma.document.findMany({ +// where: { +// sharedWithId: clientId, +// }, +// orderBy: { +// createdAt: 'desc', +// }, +// }); +// } + +// async downloadDocument(s3Key: string, userId: number): Promise { +// // Verify document exists and user has access +// const document = await this.prisma.document.findFirst({ +// where: { +// s3Key: s3Key, +// sharedWithId: userId, +// }, +// }); + +// if (!document) { +// throw new NotFoundException('Document not found or access denied'); +// } + +// try { +// // Get document stream from S3 +// const fileStream = await this.s3Service.getObject(s3Key).createReadStream(); +// return fileStream; +// } catch (error) { +// console.error('Error downloading document:', error); +// throw new NotFoundException('Document file not found in storage'); +// } +// } + +// async getAllDocuments(): Promise { +// return this.prisma.document.findMany({ +// include: { +// sharedWith: { +// select: { +// id: true, +// name: true, +// email: true, +// }, +// }, +// }, +// orderBy: { +// createdAt: 'desc', +// }, +// }); +// } + +// async shareDocument(documentId: number, userId: number): Promise { +// const document = await this.prisma.document.update({ +// where: { id: documentId }, +// data: { +// sharedWithId: userId, +// }, +// include: { +// sharedWith: { +// select: { +// id: true, +// name: true, +// email: true, +// }, +// }, +// }, +// }); + +// return document; +// } + +// async updateStatus(documentId: number, status: string): Promise { +// return this.prisma.document.update({ +// where: { id: documentId }, +// data: { status }, +// include: { +// sharedWith: { +// select: { +// id: true, +// name: true, +// email: true, +// }, +// }, +// }, +// }); // } // } import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { S3Service } from '../s3/s3.service'; import { Document, User } from '@prisma/client'; +import { Readable } from 'stream'; @Injectable() export class DocumentsService { constructor( - private prisma: PrismaService, - private s3Service: S3Service, + private readonly prisma: PrismaService, + private readonly s3Service: S3Service, ) {} - async findAllByClient(userId: number): Promise { - const documents = await this.prisma.document.findMany({ + async getClientDocuments(clientId: number) { + return this.prisma.document.findMany({ where: { - sharedWithId: userId, - }, - include: { - sharedWith: { - select: { - id: true, - name: true, - email: true, - }, - }, + sharedWithId: clientId, }, orderBy: { createdAt: 'desc', }, }); - - return documents; } - async getClientDocuments(clientId: number) { - // return this.prisma.document.findMany({ - // where: { - // sharedWithId: clientId, - // }, - // include: { - // sharedWith: { - // select: { - // id: true, - // name: true, - // email: true, - // }, - // }, - // }, - // }); - return this.prisma.document.findMany({ - where: { - sharedWithId: clientId, - }, - orderBy: { - createdAt: 'desc', - }, - }); - } - async downloadDocument(s3Key: string, userId: number): Promise { - // Verify document exists and user has access + async downloadDocument(s3Key: string, userId: number) { + // Verify document access const document = await this.prisma.document.findFirst({ where: { s3Key: s3Key, @@ -106,65 +206,31 @@ export class DocumentsService { } try { - // Get document stream from S3 - const fileStream = await this.s3Service.getObject(s3Key).createReadStream(); - return fileStream; + const s3Response = await this.s3Service.getObject(s3Key); + + if (!s3Response.Body) { + throw new Error('No file content received from S3'); + } + + // Convert the response body to a buffer + const streamBody = s3Response.Body as Readable; + const chunks: Buffer[] = []; + + for await (const chunk of streamBody) { + chunks.push(Buffer.from(chunk)); + } + + const fileBuffer = Buffer.concat(chunks); + + return { + buffer: fileBuffer, + contentType: s3Response.ContentType || 'application/octet-stream', + contentLength: s3Response.ContentLength, + fileName: s3Key.split('/').pop() || 'download' + }; } catch (error) { - console.error('Error downloading document:', error); - throw new NotFoundException('Document file not found in storage'); + console.error('Error downloading from S3:', error); + throw new NotFoundException('Failed to download file'); } } - - async getAllDocuments(): Promise { - return this.prisma.document.findMany({ - include: { - sharedWith: { - select: { - id: true, - name: true, - email: true, - }, - }, - }, - orderBy: { - createdAt: 'desc', - }, - }); - } - - async shareDocument(documentId: number, userId: number): Promise { - const document = await this.prisma.document.update({ - where: { id: documentId }, - data: { - sharedWithId: userId, - }, - include: { - sharedWith: { - select: { - id: true, - name: true, - email: true, - }, - }, - }, - }); - - return document; - } - - async updateStatus(documentId: number, status: string): Promise { - return this.prisma.document.update({ - where: { id: documentId }, - data: { status }, - include: { - sharedWith: { - select: { - id: true, - name: true, - email: true, - }, - }, - }, - }); - } } \ No newline at end of file diff --git a/backend/imk-backend/src/s3/s3.service.ts b/backend/imk-backend/src/s3/s3.service.ts index 655ed25..8139317 100644 --- a/backend/imk-backend/src/s3/s3.service.ts +++ b/backend/imk-backend/src/s3/s3.service.ts @@ -5,6 +5,7 @@ import { DeleteObjectCommand, GetObjectCommand, ListObjectsCommand, + PutObjectCommand, } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { Upload } from '@aws-sdk/lib-storage'; @@ -14,7 +15,6 @@ import { ConfigService } from '@nestjs/config'; export class S3Service { private s3Client: S3Client; private readonly logger = new Logger(S3Service.name); - s3: any; constructor(private configService: ConfigService) { this.s3Client = new S3Client({ @@ -28,45 +28,54 @@ export class S3Service { }); } - - async uploadFile(file: Express.Multer.File, key: string): Promise { + async getObject(key: string) { try { - const uniqueKey = `${Date.now()}-${key}`; // Ensure unique keys - const upload = new Upload({ - client: this.s3Client, - params: { - Bucket: process.env.AWS_S3_BUCKET_NAME, - Key: uniqueKey, - Body: file.buffer, - }, + const command = new GetObjectCommand({ + Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), + Key: key, }); - const result = await upload.done(); - console.log(`File uploaded successfully: ${uniqueKey}`); - console.log(result); - return uniqueKey; + const response = await this.s3Client.send(command); + return response; } catch (error) { - console.error(`Error uploading file: ${error.message}`); + this.logger.error(`Error getting object from S3: ${error.message}`); + throw error; + } + } + async uploadFile(file: Express.Multer.File, folder: string): Promise { + try { + const key = `${folder}/${Date.now()}-${file.originalname}`; + + const command = new PutObjectCommand({ + Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), + Key: key, + Body: file.buffer, + ContentType: file.mimetype, + }); + + await this.s3Client.send(command); + return key; + } catch (error) { + this.logger.error(`Error uploading file to S3: ${error.message}`); throw error; } } async deleteFile(key: string): Promise { - const command = new DeleteObjectCommand({ - Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), - Key: key, - }); + try { + const command = new DeleteObjectCommand({ + Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), + Key: key, + }); - await this.s3Client.send(command); - } - getObject(key: string) { - return this.s3.getObject({ - Bucket: process.env.AWS_S3_BUCKET, - Key: key, - }); + await this.s3Client.send(command); + } catch (error) { + this.logger.error(`Error deleting file from S3: ${error.message}`); + throw error; + } } - async getFileUrl(key: string): Promise { + async getFileUrl(key: string): Promise { const command = new GetObjectCommand({ Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), Key: key, @@ -75,21 +84,20 @@ export class S3Service { const url = await getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); // URL expires in 1 hour return url; } + async testConnection(): Promise { try { - const command = new ListObjectsCommand({ - Bucket: this.configService.get('AWS_S3_BUCKET_NAME'), - MaxKeys: 1, - }); - - const response = await this.s3Client.send(command); - this.logger.log( - `Successfully connected to S3. Bucket contains ${response.Contents?.length || 0} objects.`, - ); + await this.getObject('test-connection'); return true; } catch (error) { + if (error.name === 'NoSuchKey') { + // This is expected as we're just testing connection + return true; + } this.logger.error('Failed to connect to S3', error); return false; } } } + + \ No newline at end of file diff --git a/frontend/imk/src/App.jsx b/frontend/imk/src/App.jsx index 182ebf5..39c4a86 100644 --- a/frontend/imk/src/App.jsx +++ b/frontend/imk/src/App.jsx @@ -1,4 +1,5 @@ -import './App.css' +// import './App.css' +import { AuthProvider } from './hooks/useAuth'; import Navbar from './components/navbar/Navbar' import { BrowserRouter, Routes, Route } from "react-router-dom"; import Home from './pages/homepage/Home' @@ -12,15 +13,16 @@ import Gallery from './components/gallery/Gallery.jsx'; import Certificates from './components/Certificates/Certificates.jsx'; import Clients from './components/clients/clients'; import AdminPanel from './components/adminPanel/AdminPanel'; -import Login from './components/login/login'; import Dashboard from './components/dashboard/Dashboard'; +import Login from './components/login/login'; import ProtectedRoute from './components/protectedRoute/ProtectedRoute'; function App() { return ( -
- + +
+ } /> @@ -31,9 +33,13 @@ function App() { } /> } /> } /> - } /> + + + + } /> + } /> @@ -45,8 +51,9 @@ function App() { } />
- -
+
+
+ ) } diff --git a/frontend/imk/src/components/dashboard/Dashboard.jsx b/frontend/imk/src/components/dashboard/Dashboard.jsx index e0a7b3c..c8eed40 100644 --- a/frontend/imk/src/components/dashboard/Dashboard.jsx +++ b/frontend/imk/src/components/dashboard/Dashboard.jsx @@ -4,6 +4,7 @@ import { format } from 'date-fns'; import { FiFolder, FiDownload, FiChevronRight, FiChevronDown } from 'react-icons/fi'; import { useAuth } from '../../hooks/useAuth'; import { getSharedDocuments } from '../../services/api'; +import api from '../../services/api'; function Dashboard() { const [documents, setDocuments] = useState([]); @@ -42,16 +43,52 @@ function Dashboard() { }, {}); }; - const handleDownload = async (s3Key, fileName) => { - try { - window.open(`http://localhost:3000/documents/shared/download/${s3Key}`, '_blank'); - } catch (err) { - console.error('Error downloading document:', err); - alert('Failed to download document. Please try again.'); - } - }; +// const handleDownload = async (s3Key, fileName) => { +// try { +// const response = await api.get(`/documents/shared/download/${s3Key}`, { +// responseType: 'blob', +// }); +// const url = window.URL.createObjectURL(new Blob([response.data])); +// const link = document.createElement('a'); +// link.href = url; +// link.setAttribute('download', fileName); // or use the actual filename +// document.body.appendChild(link); +// link.click(); +// link.parentNode.removeChild(link); +// } catch (err) { +// console.error('Error downloading document:', err); +// alert('Failed to download document. Please try again.'); +// } +// }; +// const groupDocumentsByCompanyAndDate = (docs) => { +// return docs.reduce((acc, doc) => { +// const folderName = `${doc.sharedWith?.name || 'Unknown'}-${format(new Date(doc.createdAt), 'yyyy-MM-dd')}`; +// if (!acc[folderName]) { +// acc[folderName] = []; +// } +// acc[folderName].push(doc); +// return acc; +// }, {}); +// }; - const toggleFolder = (folderName) => { +const handleDownload = async (s3Key, fileName) => { + try { + const response = await api.get(`/documents/shared/download/${s3Key}`, { + responseType: 'blob', + }); + const url = window.URL.createObjectURL(new Blob([response.data])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', fileName); // or use the actual filename + document.body.appendChild(link); + link.click(); + link.parentNode.removeChild(link); + } catch (err) { + console.error('Error downloading document:', err); + alert('Failed to download document. Please try again.'); + } +}; +const toggleFolder = (folderName) => { setExpandedFolders(prev => ({ ...prev, [folderName]: !prev[folderName] diff --git a/frontend/imk/src/components/login/login.jsx b/frontend/imk/src/components/login/login.jsx index 699bed9..dd34108 100644 --- a/frontend/imk/src/components/login/login.jsx +++ b/frontend/imk/src/components/login/login.jsx @@ -1,157 +1,73 @@ import { useState } from 'react'; +import { useAuth } from '../../hooks/useAuth'; import { useNavigate } from 'react-router-dom'; -import { motion } from 'framer-motion'; -import axios from 'axios'; const Login = () => { - const navigate = useNavigate(); - const [formData, setFormData] = useState({ - username: '', - password: '' - }); + const [username, setUsername] = useState(''); // Changed from email to username + const [password, setPassword] = useState(''); const [error, setError] = useState(''); - const [isLoading, setIsLoading] = useState(false); - - const handleInputChange = (e) => { - const { name, value } = e.target; - setFormData(prev => ({ - ...prev, - [name]: value - })); - setError(''); - }; + const { login } = useAuth(); + const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); - setIsLoading(true); setError(''); - + try { - const response = await axios.post('http://localhost:3000/auth/login', formData, { - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (response.data.access_token) { - // Store the token - localStorage.setItem('token', response.data.access_token); - - // Redirect based on user role (assuming you have this info) - const isAdmin = response.data.isAdmin; // Adjust based on your API response - navigate(isAdmin ? '/admin' : '/dashboard'); - } else { - setError('Invalid response from server'); - } + await login(username, password); // Changed to pass username and password separately + navigate('/dashboard'); } catch (err) { - setError( - err.response?.data?.message || - 'Неуспешна најава. Проверете ги вашите податоци.' - ); - } finally { - setIsLoading(false); + setError('Invalid credentials'); } }; return ( -
- - {/* Header */} -
-

- Најавете се +
+
+
+

+ Sign in to your account

-

- Внесете ги вашите податоци за најава -

- - {/* Form */} -
-
- {/* Username Field */} + + {error && ( +
+ {error} +
+ )} +
- setUsername(e.target.value)} />
- - {/* Password Field */}
- setPassword(e.target.value)} />
- {/* Error Message */} - {error && ( -
+
- )} - - {/* Submit Button */} - + Sign in + +
- +
); }; diff --git a/frontend/imk/src/components/protectedRoute/ProtectedRoute.jsx b/frontend/imk/src/components/protectedRoute/ProtectedRoute.jsx index 1038380..d28b57f 100644 --- a/frontend/imk/src/components/protectedRoute/ProtectedRoute.jsx +++ b/frontend/imk/src/components/protectedRoute/ProtectedRoute.jsx @@ -1,23 +1,22 @@ -import { Navigate, useLocation } from 'react-router-dom'; import { useAuth } from '../../hooks/useAuth'; -// eslint-disable-next-line react/prop-types -function ProtectedRoute({ children, adminOnly = false }) { - const { user, loading } = useAuth(); - const location = useLocation(); +import { useNavigate } from 'react-router-dom'; +import { useEffect } from 'react'; - if (loading) { +const ProtectedRoute = ({ children }) => { + const { user, isLoading } = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + if (!isLoading && !user) { + navigate('/login'); + } + }, [user, isLoading, navigate]); + + if (isLoading) { return
Loading...
; } - if (!user) { - return ; - } - - if (adminOnly && !user.isAdmin) { - return ; - } - - return children; -} + return user ? children : null; +}; export default ProtectedRoute; \ No newline at end of file diff --git a/frontend/imk/src/hooks/useAuth.js b/frontend/imk/src/hooks/useAuth.js deleted file mode 100644 index 0cf4ad9..0000000 --- a/frontend/imk/src/hooks/useAuth.js +++ /dev/null @@ -1,29 +0,0 @@ -import { useState, useEffect } from 'react'; -import { getUserInfo } from '../services/api'; -import * as jwtDecode from 'jwt-decode'; - -export function useAuth() { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - async function fetchUser() { - try { - const token = localStorage.getItem('token'); - if (token) { - const decodedToken = jwtDecode.jwtDecode(token); - const response = await getUserInfo(decodedToken.sub); - setUser(response.data); - } - } catch (error) { - console.error('Failed to fetch user info:', error); - } finally { - setLoading(false); - } - } - - fetchUser(); - }, []); - - return { user, loading }; -} \ No newline at end of file diff --git a/frontend/imk/src/hooks/useAuth.jsx b/frontend/imk/src/hooks/useAuth.jsx new file mode 100644 index 0000000..8b7e621 --- /dev/null +++ b/frontend/imk/src/hooks/useAuth.jsx @@ -0,0 +1,84 @@ +import { createContext, useContext, useState, useEffect } from 'react'; +import api from '../services/api'; + +const AuthContext = createContext(null); + +export const AuthProvider = ({ children }) => { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchUser = async () => { + try { + const token = localStorage.getItem('token'); + if (!token) { + setIsLoading(false); + return; + } + + const response = await api.get('/auth/user-info'); // Updated endpoint + setUser(response.data); + } catch (error) { + console.error('Failed to fetch user info:', error); + localStorage.removeItem('token'); + } finally { + setIsLoading(false); + } + }; + + fetchUser(); + }, []); + const login = async (username, password) => { + try { + const response = await api.post('/auth/login', { username, password }); + const { access_token } = response.data; // Make sure this matches your backend response + + localStorage.setItem('token', access_token); + + // After setting token, fetch user info + const userResponse = await api.get('/auth/user-info'); + setUser(userResponse.data); + + return userResponse.data; + } catch (error) { + console.error('Login error:', error); + throw error; + } + }; + + // const login = async (username, password) => { // Changed parameters + // try { + // const response = await api.post('/auth/login', { username, password }); + // const { token } = response.data; // Updated to match backend response + // localStorage.setItem('token', token); + + // // Fetch user info after successful login + // const userResponse = await api.get('/auth/user-info'); + // setUser(userResponse.data); + + // return userResponse.data; + // } catch (error) { + // console.error('Login error:', error); + // throw error; + // } + // }; + + const logout = () => { + localStorage.removeItem('token'); + setUser(null); + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; \ No newline at end of file diff --git a/frontend/imk/src/services/api.js b/frontend/imk/src/services/api.js index 7a8f567..1a5eca0 100644 --- a/frontend/imk/src/services/api.js +++ b/frontend/imk/src/services/api.js @@ -4,17 +4,102 @@ import axios from 'axios'; const API_URL = 'http://localhost:3000'; const api = axios.create({ - baseURL: API_URL + baseURL: API_URL, + // withCredentials: true, }); -api.interceptors.request.use((config) => { - const token = localStorage.getItem('token'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; +// const api = axios.create({ +// baseURL: 'http://localhost:3000', +// }); + +// Add a request interceptor +api.interceptors.request.use( + (config) => { + const token = localStorage.getItem('token'); + if (token) { + config.headers['Authorization'] = `Bearer ${token}`; // Make sure this matches your backend expectation + } + return config; + }, + (error) => { + return Promise.reject(error); } - return config; -}); +); + +export const downloadDocument = async (documentId) => { + try { + const response = await api.get(`/documents/download/${documentId}`, { + responseType: 'blob', + headers: { + Authorization: `Bearer ${localStorage.getItem('token')}`, + } + }); + return response.data; + } catch (error) { + console.error('Download error:', error); + throw error; + } +}; + +// Request interceptor +// api.interceptors.request.use((config) => { +// const token = localStorage.getItem('token'); +// if (token) { +// config.headers.Authorization = `Bearer ${token}`; +// } +// return config; +// }); + +// // Response interceptor +// api.interceptors.response.use( +// (response) => response, +// (error) => { +// if (error.response?.status === 401) { +// localStorage.removeItem('token'); +// } +// return Promise.reject(error); +// } +// ); + +// const api = axios.create({ +// baseURL: API_URL, +// withCredentials: true, +// }); + +// api.interceptors.request.use((config) => { +// const token = localStorage.getItem('token'); +// if (token) { +// config.headers.Authorization = `Bearer ${token}`; +// } +// return config; +// }); // export const createUser = (userData) => api.post('/admin/users', userData); + +// const api = axios.create({ +// baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000', +// withCredentials: true, +// }); + +// // Request interceptor +// api.interceptors.request.use((config) => { +// const token = localStorage.getItem('token'); +// if (token) { +// config.headers.Authorization = `Bearer ${token}`; +// } +// return config; +// }); + +// // Response interceptor +// api.interceptors.response.use( +// (response) => response, +// (error) => { +// if (error.response?.status === 401) { +// localStorage.removeItem('token'); +// window.location.href = '/login'; +// } +// return Promise.reject(error); +// } +// ); export const createUser = (userData) => { return api.post('/admin/users', { name: userData.name,