opt for prod
This commit is contained in:
parent
d9f9aaedc5
commit
2958bf69ed
@ -36,3 +36,6 @@ ADMIN_EMAIL=taratur@gmail.com
|
|||||||
DEFAULT_ADMIN_EMAIL=taratur@gmail.com
|
DEFAULT_ADMIN_EMAIL=taratur@gmail.com
|
||||||
DEFAULT_ADMIN_PASSWORD=irina7654321
|
DEFAULT_ADMIN_PASSWORD=irina7654321
|
||||||
DEFAULT_ADMIN_NAME=admin
|
DEFAULT_ADMIN_NAME=admin
|
||||||
|
|
||||||
|
CORS_ORIGIN=http://localhost:5173
|
||||||
|
NODE_ENV=development
|
||||||
|
|||||||
179
backend/package-lock.json
generated
179
backend/package-lock.json
generated
@ -32,6 +32,7 @@
|
|||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pg": "^8.13.1",
|
"pg": "^8.13.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rimraf": "^5.0.0",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -41,7 +42,7 @@
|
|||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.17.28",
|
||||||
"@types/passport-jwt": "^4.0.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
@ -2947,6 +2948,65 @@
|
|||||||
"node-pre-gyp": "bin/node-pre-gyp"
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nestjs/cli": {
|
"node_modules/@nestjs/cli": {
|
||||||
"version": "10.4.9",
|
"version": "10.4.9",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz",
|
||||||
@ -7378,6 +7438,69 @@
|
|||||||
"node": "^10.12.0 || >=12.0.0"
|
"node": "^10.12.0 || >=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/flat-cache/node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/flat-cache/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/flat-cache/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/flat-cache/node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/flatted": {
|
"node_modules/flatted": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||||
@ -10825,64 +10948,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rimraf": {
|
"node_modules/rimraf": {
|
||||||
"version": "3.0.2",
|
"version": "5.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
|
||||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
"integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
|
||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^10.3.7"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rimraf": "bin.js"
|
"rimraf": "dist/esm/bin.mjs"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rimraf/node_modules/brace-expansion": {
|
|
||||||
"version": "1.1.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"concat-map": "0.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rimraf/node_modules/glob": {
|
|
||||||
"version": "7.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
|
||||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
|
||||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^3.1.1",
|
|
||||||
"once": "^1.3.0",
|
|
||||||
"path-is-absolute": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rimraf/node_modules/minimatch": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^1.1.7"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/run-async": {
|
"node_modules/run-async": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
"@types/bcrypt": "^5.0.2",
|
"@types/bcrypt": "^5.0.2",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.17.28",
|
||||||
"@types/passport-jwt": "^4.0.1",
|
"@types/passport-jwt": "^4.0.1",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
"@types/supertest": "^2.0.12",
|
"@types/supertest": "^2.0.12",
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// src/main.ts
|
|
||||||
import { NestFactory } from "@nestjs/core";
|
import { NestFactory } from "@nestjs/core";
|
||||||
import { AppModule } from "./app.module";
|
import { AppModule } from "./app.module";
|
||||||
import { Logger, ValidationPipe } from "@nestjs/common";
|
import { Logger, ValidationPipe } from "@nestjs/common";
|
||||||
@ -15,9 +14,26 @@ async function bootstrap() {
|
|||||||
|
|
||||||
// Enable CORS
|
// Enable CORS
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: true,
|
origin: [
|
||||||
|
"https://www.placebo.mk",
|
||||||
|
"https://placebo.mk",
|
||||||
|
"http://localhost:5173",
|
||||||
|
],
|
||||||
methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
||||||
credentials: true,
|
credentials: true,
|
||||||
|
allowedHeaders: [
|
||||||
|
"Origin",
|
||||||
|
"X-Requested-With",
|
||||||
|
"Content-Type",
|
||||||
|
"Accept",
|
||||||
|
"Authorization",
|
||||||
|
],
|
||||||
|
exposedHeaders: [
|
||||||
|
'Access-Control-Allow-Origin',
|
||||||
|
'Access-Control-Allow-Credentials',
|
||||||
|
],
|
||||||
|
preflightContinue: false,
|
||||||
|
optionsSuccessStatus: 204,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global pipes
|
// Global pipes
|
||||||
|
|||||||
@ -2,6 +2,11 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
container_name: imk-backend
|
container_name: imk-backend
|
||||||
build:
|
build:
|
||||||
context: ./backend
|
context: ./backend
|
||||||
@ -12,6 +17,7 @@ services:
|
|||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- PORT=3000
|
- PORT=3000
|
||||||
- DATABASE_URL=postgresql://postgres:postgres@imk-postgres:5432/postgres?schema=public
|
- DATABASE_URL=postgresql://postgres:postgres@imk-postgres:5432/postgres?schema=public
|
||||||
|
- FRONTEND_URL=https://www.placebo.mk
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
deploy:
|
deploy:
|
||||||
@ -40,7 +46,7 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 15s
|
start_period: 15s
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
postgres:
|
postgres:
|
||||||
container_name: imk-postgres
|
container_name: imk-postgres
|
||||||
image: postgres:14-alpine
|
image: postgres:14-alpine
|
||||||
@ -60,7 +66,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
networks:
|
networks:
|
||||||
- app_network
|
- app_network
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: imk-redis
|
container_name: imk-redis
|
||||||
@ -77,7 +83,7 @@ services:
|
|||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
restart: unless-stopped
|
restart: always
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
app_network:
|
app_network:
|
||||||
|
|||||||
@ -1,28 +1,41 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from "react";
|
||||||
import { getAllUsers, getAllDocuments, getUserInfo, createUser, resetUserPassword } from '../../services/api';
|
import {
|
||||||
import DocumentUpload from '../documentUpload/DocumentUpload';
|
getAllUsers,
|
||||||
import { useNavigate } from 'react-router-dom';
|
getAllDocuments,
|
||||||
import { FiUsers, FiFile, FiUpload, FiUserPlus, FiLoader, FiKey } from 'react-icons/fi';
|
getUserInfo,
|
||||||
|
createUser,
|
||||||
|
resetUserPassword,
|
||||||
|
} from "../../services/api";
|
||||||
|
import DocumentUpload from "../documentUpload/DocumentUpload";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
FiUsers,
|
||||||
|
FiFile,
|
||||||
|
FiUpload,
|
||||||
|
FiUserPlus,
|
||||||
|
FiLoader,
|
||||||
|
FiKey,
|
||||||
|
} from "react-icons/fi";
|
||||||
|
|
||||||
function AdminPanel() {
|
function AdminPanel() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [activeTab, setActiveTab] = useState('documents');
|
const [activeTab, setActiveTab] = useState("documents");
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [documents, setDocuments] = useState([]);
|
const [documents, setDocuments] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState("");
|
||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [resetPasswordModal, setResetPasswordModal] = useState({
|
const [resetPasswordModal, setResetPasswordModal] = useState({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
userId: null,
|
userId: null,
|
||||||
userName: '',
|
userName: "",
|
||||||
newPassword: '',
|
newPassword: "",
|
||||||
});
|
});
|
||||||
const [newUser, setNewUser] = useState({
|
const [newUser, setNewUser] = useState({
|
||||||
name: '',
|
name: "",
|
||||||
email: '',
|
email: "",
|
||||||
password: '',
|
password: "",
|
||||||
isAdmin: false
|
isAdmin: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -39,32 +52,32 @@ function AdminPanel() {
|
|||||||
try {
|
try {
|
||||||
const response = await getUserInfo();
|
const response = await getUserInfo();
|
||||||
if (!response?.data?.isAdmin) {
|
if (!response?.data?.isAdmin) {
|
||||||
navigate('/');
|
navigate("/");
|
||||||
} else {
|
} else {
|
||||||
setIsAdmin(true);
|
setIsAdmin(true);
|
||||||
await fetchData();
|
await fetchData();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Admin check failed:', error);
|
// console.error("Admin check failed:", error);
|
||||||
navigate('/');
|
navigate("/");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setError("");
|
||||||
try {
|
try {
|
||||||
if (activeTab === 'users') {
|
if (activeTab === "users") {
|
||||||
const response = await getAllUsers();
|
const response = await getAllUsers();
|
||||||
setUsers(response.data);
|
setUsers(response.data);
|
||||||
} else if (activeTab === 'documents') {
|
} else if (activeTab === "documents") {
|
||||||
const response = await getAllDocuments();
|
const response = await getAllDocuments();
|
||||||
console.log('Documents data:', response.data);
|
// console.log('Documents data:', response.data);
|
||||||
setDocuments(response.data);
|
setDocuments(response.data);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Fetch error:', err);
|
// console.error("Fetch error:", err);
|
||||||
setError('Failed to fetch data. Please try again.');
|
setError("Failed to fetch data. Please try again.");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@ -75,41 +88,44 @@ function AdminPanel() {
|
|||||||
try {
|
try {
|
||||||
await createUser(newUser);
|
await createUser(newUser);
|
||||||
setNewUser({
|
setNewUser({
|
||||||
name: '',
|
name: "",
|
||||||
email: '',
|
email: "",
|
||||||
password: '',
|
password: "",
|
||||||
isAdmin: false
|
isAdmin: false,
|
||||||
});
|
});
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Failed to create user');
|
setError("Failed to create user");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetPassword = async (e) => {
|
const handleResetPassword = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
await resetUserPassword(resetPasswordModal.userId, resetPasswordModal.newPassword);
|
await resetUserPassword(
|
||||||
|
resetPasswordModal.userId,
|
||||||
|
resetPasswordModal.newPassword,
|
||||||
|
);
|
||||||
setResetPasswordModal({
|
setResetPasswordModal({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
userId: null,
|
userId: null,
|
||||||
userName: '',
|
userName: "",
|
||||||
newPassword: '',
|
newPassword: "",
|
||||||
});
|
});
|
||||||
// Show success message
|
// Show success message
|
||||||
setError('Password reset successful');
|
setError("Password reset successful");
|
||||||
setTimeout(() => setError(''), 3000);
|
setTimeout(() => setError(""), 3000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Failed to reset password');
|
setError("Failed to reset password");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isAdmin) return null;
|
if (!isAdmin) return null;
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: 'documents', name: 'Documents', icon: FiFile },
|
{ id: "documents", name: "Documents", icon: FiFile },
|
||||||
{ id: 'users', name: 'Users', icon: FiUsers },
|
{ id: "users", name: "Users", icon: FiUsers },
|
||||||
{ id: 'upload', name: 'Upload Document', icon: FiUpload }
|
{ id: "upload", name: "Upload Document", icon: FiUpload },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
@ -127,7 +143,9 @@ function AdminPanel() {
|
|||||||
<div className="min-h-screen bg-gradient-to-br from-primary-900 to-primary-800 p-6">
|
<div className="min-h-screen bg-gradient-to-br from-primary-900 to-primary-800 p-6">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
<header className="mb-8 mt-20">
|
<header className="mb-8 mt-20">
|
||||||
<h1 className="text-3xl font-bold text-white mb-2">Admin Dashboard</h1>
|
<h1 className="text-3xl font-bold text-white mb-2">
|
||||||
|
Admin Dashboard
|
||||||
|
</h1>
|
||||||
<p className="text-neutral-400">Manage users and documents</p>
|
<p className="text-neutral-400">Manage users and documents</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -139,9 +157,11 @@ function AdminPanel() {
|
|||||||
onClick={() => setActiveTab(id)}
|
onClick={() => setActiveTab(id)}
|
||||||
className={`
|
className={`
|
||||||
px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors
|
px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors
|
||||||
${activeTab === id
|
${
|
||||||
? 'bg-primary-600 text-white'
|
activeTab === id
|
||||||
: 'text-neutral-400 hover:bg-primary-700/50 hover:text-white'}
|
? "bg-primary-600 text-white"
|
||||||
|
: "text-neutral-400 hover:bg-primary-700/50 hover:text-white"
|
||||||
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Icon className="w-4 h-4" />
|
<Icon className="w-4 h-4" />
|
||||||
@ -167,11 +187,13 @@ function AdminPanel() {
|
|||||||
type="password"
|
type="password"
|
||||||
placeholder="New Password"
|
placeholder="New Password"
|
||||||
value={resetPasswordModal.newPassword}
|
value={resetPasswordModal.newPassword}
|
||||||
onChange={(e) => setResetPasswordModal({
|
onChange={(e) =>
|
||||||
...resetPasswordModal,
|
setResetPasswordModal({
|
||||||
newPassword: e.target.value
|
...resetPasswordModal,
|
||||||
})}
|
newPassword: e.target.value,
|
||||||
className="w-full bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
})
|
||||||
|
}
|
||||||
|
className="w-full bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
||||||
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
||||||
focus:ring-1 focus:ring-primary-500"
|
focus:ring-1 focus:ring-primary-500"
|
||||||
required
|
required
|
||||||
@ -180,20 +202,22 @@ function AdminPanel() {
|
|||||||
<div className="flex justify-end space-x-3">
|
<div className="flex justify-end space-x-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setResetPasswordModal({
|
onClick={() =>
|
||||||
isOpen: false,
|
setResetPasswordModal({
|
||||||
userId: null,
|
isOpen: false,
|
||||||
userName: '',
|
userId: null,
|
||||||
newPassword: '',
|
userName: "",
|
||||||
})}
|
newPassword: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
className="px-4 py-2 text-neutral-400 hover:text-white transition-colors"
|
className="px-4 py-2 text-neutral-400 hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex items-center justify-center space-x-2 px-4 py-2
|
className="flex items-center justify-center space-x-2 px-4 py-2
|
||||||
bg-primary-600 hover:bg-primary-700 text-white rounded-lg
|
bg-primary-600 hover:bg-primary-700 text-white rounded-lg
|
||||||
transition-colors shadow-lg"
|
transition-colors shadow-lg"
|
||||||
>
|
>
|
||||||
<FiKey className="w-4 h-4" />
|
<FiKey className="w-4 h-4" />
|
||||||
@ -206,30 +230,48 @@ function AdminPanel() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
{activeTab === 'documents' && (
|
{activeTab === "documents" && (
|
||||||
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl overflow-hidden shadow-xl">
|
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl overflow-hidden shadow-xl">
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-primary-700">
|
<tr className="border-b border-primary-700">
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Title</th>
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Status</th>
|
Title
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Uploaded By</th>
|
</th>
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Shared With</th>
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Created At</th>
|
Status
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Uploaded By
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Shared With
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Created At
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{documents.map((doc) => (
|
{documents.map((doc) => (
|
||||||
<tr key={doc.id} className="border-b border-primary-700/50 hover:bg-primary-700/30">
|
<tr
|
||||||
|
key={doc.id}
|
||||||
|
className="border-b border-primary-700/50 hover:bg-primary-700/30"
|
||||||
|
>
|
||||||
<td className="px-6 py-4 text-white">{doc.title}</td>
|
<td className="px-6 py-4 text-white">{doc.title}</td>
|
||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4">
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
|
<span
|
||||||
doc.status === 'completed' ? 'bg-green-500/20 text-green-300' :
|
className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||||
doc.status === 'pending' ? 'bg-yellow-500/20 text-yellow-300' :
|
doc.status === "completed"
|
||||||
doc.status === 'uploading' ? 'bg-primary-500/20 text-primary-300' :
|
? "bg-green-500/20 text-green-300"
|
||||||
'bg-neutral-500/20 text-neutral-300'
|
: doc.status === "pending"
|
||||||
}`}>
|
? "bg-yellow-500/20 text-yellow-300"
|
||||||
|
: doc.status === "uploading"
|
||||||
|
? "bg-primary-500/20 text-primary-300"
|
||||||
|
: "bg-neutral-500/20 text-neutral-300"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{doc.status}
|
{doc.status}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
@ -237,13 +279,16 @@ function AdminPanel() {
|
|||||||
{doc.uploadedBy?.name} ({doc.uploadedBy?.email})
|
{doc.uploadedBy?.name} ({doc.uploadedBy?.email})
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-neutral-300">
|
<td className="px-6 py-4 text-neutral-300">
|
||||||
{doc.sharedWith && doc.sharedWith.length > 0
|
{doc.sharedWith && doc.sharedWith.length > 0
|
||||||
? doc.sharedWith.map(user => (
|
? doc.sharedWith.map((user) => (
|
||||||
<div key={user.id} className="whitespace-nowrap">
|
<div
|
||||||
|
key={user.id}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
{user.name} ({user.email})
|
{user.name} ({user.email})
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
: 'None'}
|
: "None"}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-neutral-300">
|
<td className="px-6 py-4 text-neutral-300">
|
||||||
{new Date(doc.createdAt).toLocaleString()}
|
{new Date(doc.createdAt).toLocaleString()}
|
||||||
@ -256,17 +301,24 @@ function AdminPanel() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'users' && (
|
{activeTab === "users" && (
|
||||||
<>
|
<>
|
||||||
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 mb-6 shadow-xl">
|
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 mb-6 shadow-xl">
|
||||||
<h2 className="text-xl font-bold text-white mb-4">Create New User</h2>
|
<h2 className="text-xl font-bold text-white mb-4">
|
||||||
<form onSubmit={handleCreateUser} className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
Create New User
|
||||||
|
</h2>
|
||||||
|
<form
|
||||||
|
onSubmit={handleCreateUser}
|
||||||
|
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value={newUser.name}
|
value={newUser.name}
|
||||||
onChange={(e) => setNewUser({ ...newUser, name: e.target.value })}
|
onChange={(e) =>
|
||||||
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
setNewUser({ ...newUser, name: e.target.value })
|
||||||
|
}
|
||||||
|
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
||||||
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
||||||
focus:ring-1 focus:ring-primary-500"
|
focus:ring-1 focus:ring-primary-500"
|
||||||
required
|
required
|
||||||
@ -275,8 +327,10 @@ function AdminPanel() {
|
|||||||
type="email"
|
type="email"
|
||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
value={newUser.email}
|
value={newUser.email}
|
||||||
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
|
onChange={(e) =>
|
||||||
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
setNewUser({ ...newUser, email: e.target.value })
|
||||||
|
}
|
||||||
|
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
||||||
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
||||||
focus:ring-1 focus:ring-primary-500"
|
focus:ring-1 focus:ring-primary-500"
|
||||||
required
|
required
|
||||||
@ -285,8 +339,10 @@ function AdminPanel() {
|
|||||||
type="password"
|
type="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={newUser.password}
|
value={newUser.password}
|
||||||
onChange={(e) => setNewUser({ ...newUser, password: e.target.value })}
|
onChange={(e) =>
|
||||||
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
setNewUser({ ...newUser, password: e.target.value })
|
||||||
|
}
|
||||||
|
className="bg-primary-700/30 border border-primary-600 rounded-lg px-4 py-2
|
||||||
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
text-white placeholder-neutral-400 focus:outline-none focus:border-primary-500
|
||||||
focus:ring-1 focus:ring-primary-500"
|
focus:ring-1 focus:ring-primary-500"
|
||||||
required
|
required
|
||||||
@ -296,16 +352,20 @@ function AdminPanel() {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="isAdmin"
|
id="isAdmin"
|
||||||
checked={newUser.isAdmin}
|
checked={newUser.isAdmin}
|
||||||
onChange={(e) => setNewUser({ ...newUser, isAdmin: e.target.checked })}
|
onChange={(e) =>
|
||||||
className="rounded border-primary-600 bg-primary-700/30 text-primary-500
|
setNewUser({ ...newUser, isAdmin: e.target.checked })
|
||||||
|
}
|
||||||
|
className="rounded border-primary-600 bg-primary-700/30 text-primary-500
|
||||||
focus:ring-primary-500"
|
focus:ring-primary-500"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="isAdmin" className="text-white">Is Admin</label>
|
<label htmlFor="isAdmin" className="text-white">
|
||||||
|
Is Admin
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex items-center justify-center space-x-2 px-4 py-2
|
className="flex items-center justify-center space-x-2 px-4 py-2
|
||||||
bg-primary-600 hover:bg-primary-700 text-white rounded-lg
|
bg-primary-600 hover:bg-primary-700 text-white rounded-lg
|
||||||
transition-colors shadow-lg"
|
transition-colors shadow-lg"
|
||||||
>
|
>
|
||||||
<FiUserPlus className="w-4 h-4" />
|
<FiUserPlus className="w-4 h-4" />
|
||||||
@ -318,31 +378,46 @@ function AdminPanel() {
|
|||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-primary-700">
|
<tr className="border-b border-primary-700">
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Name</th>
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Email</th>
|
Name
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Role</th>
|
</th>
|
||||||
<th className="px-6 py-4 text-left text-sm text-neutral-400">Actions</th>
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Email
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Role
|
||||||
|
</th>
|
||||||
|
<th className="px-6 py-4 text-left text-sm text-neutral-400">
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{users.map((user) => (
|
{users.map((user) => (
|
||||||
<tr key={user.id} className="border-b border-primary-700/50 hover:bg-primary-700/30">
|
<tr
|
||||||
|
key={user.id}
|
||||||
|
className="border-b border-primary-700/50 hover:bg-primary-700/30"
|
||||||
|
>
|
||||||
<td className="px-6 py-4 text-white">{user.name}</td>
|
<td className="px-6 py-4 text-white">{user.name}</td>
|
||||||
<td className="px-6 py-4 text-white">{user.email}</td>
|
<td className="px-6 py-4 text-white">{user.email}</td>
|
||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4">
|
||||||
<span className={`px-3 py-1 rounded-full text-xs font-medium
|
<span
|
||||||
${user.isAdmin ? 'bg-primary-500/20 text-primary-300' : 'bg-neutral-500/20 text-neutral-300'}`}>
|
className={`px-3 py-1 rounded-full text-xs font-medium
|
||||||
{user.isAdmin ? 'Admin' : 'User'}
|
${user.isAdmin ? "bg-primary-500/20 text-primary-300" : "bg-neutral-500/20 text-neutral-300"}`}
|
||||||
|
>
|
||||||
|
{user.isAdmin ? "Admin" : "User"}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4">
|
<td className="px-6 py-4">
|
||||||
<button
|
<button
|
||||||
onClick={() => setResetPasswordModal({
|
onClick={() =>
|
||||||
isOpen: true,
|
setResetPasswordModal({
|
||||||
userId: user.id,
|
isOpen: true,
|
||||||
userName: user.name,
|
userId: user.id,
|
||||||
newPassword: '',
|
userName: user.name,
|
||||||
})}
|
newPassword: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
className="flex items-center space-x-1 text-neutral-400 hover:text-white transition-colors"
|
className="flex items-center space-x-1 text-neutral-400 hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
<FiKey className="w-4 h-4" />
|
<FiKey className="w-4 h-4" />
|
||||||
@ -357,7 +432,7 @@ function AdminPanel() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === 'upload' && (
|
{activeTab === "upload" && (
|
||||||
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 shadow-xl">
|
<div className="bg-primary-800/50 backdrop-blur-lg rounded-xl p-6 shadow-xl">
|
||||||
<DocumentUpload />
|
<DocumentUpload />
|
||||||
</div>
|
</div>
|
||||||
@ -368,4 +443,4 @@ function AdminPanel() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminPanel;
|
export default AdminPanel;
|
||||||
|
|||||||
70
frontend/src/hooks/_useAuth.jsx
Normal file
70
frontend/src/hooks/_useAuth.jsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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 });
|
||||||
|
console.log("Login response:", response.data); // Debug log
|
||||||
|
|
||||||
|
const { access_token } = response.data;
|
||||||
|
localStorage.setItem("token", access_token);
|
||||||
|
|
||||||
|
// Fetch user info after login
|
||||||
|
const userResponse = await api.get("/auth/user-info");
|
||||||
|
const userData = userResponse.data;
|
||||||
|
|
||||||
|
setUser(userData);
|
||||||
|
return userData; // Return the user data for redirect logic
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Login error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
setUser(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={{ user, isLoading, login, logout }}>
|
||||||
|
{children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAuth = () => {
|
||||||
|
const context = useContext(AuthContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error("useAuth must be used within an AuthProvider");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// frontend/src/hooks/useAuth.jsx
|
||||||
import { createContext, useContext, useState, useEffect } from "react";
|
import { createContext, useContext, useState, useEffect } from "react";
|
||||||
import api from "../services/api";
|
import api from "../services/api";
|
||||||
|
|
||||||
@ -6,6 +7,7 @@ const AuthContext = createContext(null);
|
|||||||
export const AuthProvider = ({ children }) => {
|
export const AuthProvider = ({ children }) => {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
@ -16,11 +18,12 @@ export const AuthProvider = ({ children }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await api.get("/auth/user-info"); // Updated endpoint
|
const response = await api.get("/auth/user-info");
|
||||||
setUser(response.data);
|
setUser(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch user info:", error);
|
console.error("Failed to fetch user info:", error);
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
|
setError(error.message);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -32,17 +35,15 @@ export const AuthProvider = ({ children }) => {
|
|||||||
const login = async (username, password) => {
|
const login = async (username, password) => {
|
||||||
try {
|
try {
|
||||||
const response = await api.post("/auth/login", { username, password });
|
const response = await api.post("/auth/login", { username, password });
|
||||||
console.log("Login response:", response.data); // Debug log
|
|
||||||
|
|
||||||
const { access_token } = response.data;
|
const { access_token } = response.data;
|
||||||
|
|
||||||
localStorage.setItem("token", access_token);
|
localStorage.setItem("token", access_token);
|
||||||
|
|
||||||
// Fetch user info after login
|
// Fetch user info after successful login
|
||||||
const userResponse = await api.get("/auth/user-info");
|
const userResponse = await api.get("/auth/user-info");
|
||||||
const userData = userResponse.data;
|
setUser(userResponse.data);
|
||||||
|
|
||||||
setUser(userData);
|
return userResponse.data;
|
||||||
return userData; // Return the user data for redirect logic
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Login error:", error);
|
console.error("Login error:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@ -55,7 +56,7 @@ export const AuthProvider = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ user, isLoading, login, logout }}>
|
<AuthContext.Provider value={{ user, isLoading, error, login, logout }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const API_URL = "http://localhost:3000";
|
const API_URL =
|
||||||
|
process.env.NODE_ENV === "production"
|
||||||
|
? "https://imkapi.oblak.solutions"
|
||||||
|
: "http://localhost:3000";
|
||||||
|
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: API_URL,
|
baseURL: API_URL,
|
||||||
@ -112,5 +115,14 @@ export const forgotPassword = (email) =>
|
|||||||
api.post("/auth/forgot-password", { email });
|
api.post("/auth/forgot-password", { email });
|
||||||
export const resetPassword = (token, newPassword) =>
|
export const resetPassword = (token, newPassword) =>
|
||||||
api.post("/auth/reset-password", { token, newPassword });
|
api.post("/auth/reset-password", { token, newPassword });
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
@ -4,14 +4,29 @@ import react from "@vitejs/plugin-react";
|
|||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
// server: {
|
||||||
port: 5173,
|
// port: 5173,
|
||||||
proxy: {
|
// proxy: {
|
||||||
"/api": {
|
// "/api": {
|
||||||
target: "http://localhost:3000",
|
// target: "http://localhost:3000",
|
||||||
changeOrigin: true,
|
// changeOrigin: true,
|
||||||
secure: false,
|
// secure: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
build: {
|
||||||
|
outDir: "dist",
|
||||||
|
sourcemap: false,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ["react", "react-dom", "react-router-dom"],
|
||||||
|
ui: ["@headlessui/react", "@heroicons/react"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
define: {
|
||||||
|
"process.env.API_URL": JSON.stringify("https://imkapi.oblak.solutions"),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user