diff --git a/AdminPanel/client/.env b/AdminPanel/client/.env
index 56dee1ac..d6721def 100644
--- a/AdminPanel/client/.env
+++ b/AdminPanel/client/.env
@@ -1 +1,2 @@
-REACT_APP_BACKEND_URL=http://localhost:9000
\ No newline at end of file
+REACT_APP_BACKEND_URL=http://localhost:9000
+REACT_APP_BASE_PATH=/admin
diff --git a/AdminPanel/client/.env.example b/AdminPanel/client/.env.example
index 56dee1ac..b6f8791f 100644
--- a/AdminPanel/client/.env.example
+++ b/AdminPanel/client/.env.example
@@ -1 +1,12 @@
-REACT_APP_BACKEND_URL=http://localhost:9000
\ No newline at end of file
+# Admin Panel Environment Variables
+# Copy this file to .env for local development
+
+# Backend URL for API calls
+REACT_APP_BACKEND_URL=http://localhost:9000
+
+# Base path for the admin panel (empty for root deployment)
+# Examples:
+# REACT_APP_BASE_PATH= # Root deployment: http://localhost:3000/
+# REACT_APP_BASE_PATH=/admin # Under /admin: http://localhost:3000/admin/
+# REACT_APP_BASE_PATH=/docserver-admin # Under /docserver-admin: http://localhost:3000/docserver-admin/
+REACT_APP_BASE_PATH=/admin
diff --git a/AdminPanel/client/package-lock.json b/AdminPanel/client/package-lock.json
index 8eed51d7..1ba9d979 100644
--- a/AdminPanel/client/package-lock.json
+++ b/AdminPanel/client/package-lock.json
@@ -2768,6 +2768,12 @@
"is-obj": "^2.0.0"
}
},
+ "dotenv": {
+ "version": "17.2.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
+ "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
+ "dev": true
+ },
"dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
diff --git a/AdminPanel/client/package.json b/AdminPanel/client/package.json
index 6295dc9d..7884270b 100644
--- a/AdminPanel/client/package.json
+++ b/AdminPanel/client/package.json
@@ -3,7 +3,7 @@
"version": "1.3.0",
"private": true,
"scripts": {
- "start": "set \"REACT_APP_BACKEND_URL=http://localhost:9000\" && webpack serve --mode=development",
+ "start": "webpack serve --mode=development",
"build": "webpack --mode=production"
},
"dependencies": {
@@ -29,6 +29,7 @@
"babel-loader": "8.2.0",
"copy-webpack-plugin": "11.0.0",
"css-loader": "^6.2.0",
+ "dotenv": "^17.2.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "5.5.0",
"sass": "^1.77.0",
diff --git a/AdminPanel/client/public/index.html b/AdminPanel/client/public/index.html
index 81bb060f..9ae0a010 100644
--- a/AdminPanel/client/public/index.html
+++ b/AdminPanel/client/public/index.html
@@ -2,7 +2,7 @@
-
+
diff --git a/AdminPanel/client/src/App.js b/AdminPanel/client/src/App.js
index cc16be57..39f57bab 100644
--- a/AdminPanel/client/src/App.js
+++ b/AdminPanel/client/src/App.js
@@ -1,32 +1,37 @@
import {Provider} from 'react-redux';
-import {Routes, Route, Navigate} from 'react-router-dom';
+import {Routes, Route, Navigate, BrowserRouter} from 'react-router-dom';
import './App.css';
import {store} from './store';
import AuthWrapper from './components/AuthWrapper/AuthWrapper';
import ConfigLoader from './components/ConfigLoader/ConfigLoader';
import Menu from './components/Menu/Menu';
import {menuItems} from './config/menuItems';
+import {getBasePath} from './utils/basePath';
function App() {
+ const basePath = getBasePath();
+
return (
-
-
-
-
-
-
-
- } />
- {menuItems.map(item => (
- } />
- ))}
-
-
+
+
+
+
+
+
+
+
+ } />
+ {menuItems.map(item => (
+ } />
+ ))}
+
+
+
-
-
-
+
+
+
);
}
diff --git a/AdminPanel/client/src/api/index.js b/AdminPanel/client/src/api/index.js
index 11e1a3a4..35b7dd02 100644
--- a/AdminPanel/client/src/api/index.js
+++ b/AdminPanel/client/src/api/index.js
@@ -1,7 +1,8 @@
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL ?? '';
+const API_BASE_PATH = '/api/v1/admin';
export const fetchStatistics = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/stat`);
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/stat`);
if (!response.ok) {
throw new Error('Failed to fetch statistics');
}
@@ -9,7 +10,7 @@ export const fetchStatistics = async () => {
};
export const fetchConfiguration = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/config`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/config`, {
credentials: 'include'
});
if (!response.ok) {
@@ -19,7 +20,7 @@ export const fetchConfiguration = async () => {
};
export const fetchConfigurationSchema = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/config/schema`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/config/schema`, {
credentials: 'include'
});
if (!response.ok) {
@@ -29,7 +30,7 @@ export const fetchConfigurationSchema = async () => {
};
export const updateConfiguration = async configData => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/config`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/config`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
@@ -54,7 +55,7 @@ export const updateConfiguration = async configData => {
};
export const fetchCurrentUser = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/me`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/me`, {
method: 'GET',
credentials: 'include' // Include cookies in the request
});
@@ -70,7 +71,7 @@ export const fetchCurrentUser = async () => {
};
export const login = async ({tenantName, secret}) => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/login`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -90,7 +91,7 @@ export const login = async ({tenantName, secret}) => {
};
export const logout = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/logout`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -106,7 +107,7 @@ export const logout = async () => {
};
export const rotateWopiKeys = async () => {
- const response = await fetch(`${BACKEND_URL}/api/v1/admin/wopi/rotate-keys`, {
+ const response = await fetch(`${BACKEND_URL}${API_BASE_PATH}/wopi/rotate-keys`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
diff --git a/AdminPanel/client/src/index.js b/AdminPanel/client/src/index.js
index 6f50428e..5fbe9e45 100644
--- a/AdminPanel/client/src/index.js
+++ b/AdminPanel/client/src/index.js
@@ -1,6 +1,5 @@
import {StrictMode} from 'react';
import ReactDOM from 'react-dom/client';
-import {BrowserRouter} from 'react-router-dom';
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
import App from './App';
@@ -18,9 +17,7 @@ const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-
-
-
+
);
diff --git a/AdminPanel/client/src/utils/basePath.js b/AdminPanel/client/src/utils/basePath.js
new file mode 100644
index 00000000..92ac9204
--- /dev/null
+++ b/AdminPanel/client/src/utils/basePath.js
@@ -0,0 +1,15 @@
+/**
+ * Utility functions for handling BASE_PATH environment variable
+ */
+
+// Get the base path from environment variable, with fallback to empty string
+export const getBasePath = () => {
+ return process.env.REACT_APP_BASE_PATH || '';
+};
+
+// Create a full path by combining base path with the given path
+export const createPath = (path) => {
+ const basePath = getBasePath();
+ const cleanPath = path.startsWith('/') ? path : `/${path}`;
+ return `${basePath}${cleanPath}`;
+};
diff --git a/AdminPanel/client/webpack.config.js b/AdminPanel/client/webpack.config.js
index 33fa3870..23c86507 100644
--- a/AdminPanel/client/webpack.config.js
+++ b/AdminPanel/client/webpack.config.js
@@ -2,6 +2,19 @@ const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
+const dotenv = require('dotenv');
+
+// Load environment variables from .env files
+// Priority: .env.local > .env.development/.env.production > .env
+const envFiles = [
+ '.env.local',
+ process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development',
+ '.env'
+];
+
+envFiles.forEach(file => {
+ dotenv.config({ path: file });
+});
module.exports = {
entry: './src/index.js',
@@ -39,7 +52,8 @@ module.exports = {
]
}),
new webpack.DefinePlugin({
- 'process.env.REACT_APP_BACKEND_URL': JSON.stringify(process.env.REACT_APP_BACKEND_URL)
+ 'process.env.REACT_APP_BACKEND_URL': JSON.stringify(process.env.REACT_APP_BACKEND_URL),
+ 'process.env.REACT_APP_BASE_PATH': JSON.stringify(process.env.REACT_APP_BASE_PATH || '')
})
],