Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ services:
networks:
- space-network
healthcheck:
test: [ "CMD", "mongo", "--username", "${MONGO_INITDB_ROOT_USERNAME:-root}", "--password", "${MONGO_INITDB_ROOT_PASSWORD:-4dm1n}", "--authenticationDatabase", "admin", "--eval", "db.adminCommand('ping')" ]
test: [ "CMD", "mongosh", "--username", "${MONGO_INITDB_ROOT_USERNAME:-root}", "--password", "${MONGO_INITDB_ROOT_PASSWORD:-4dm1n}", "--authenticationDatabase", "admin", "--eval", "db.adminCommand('ping')" ]
interval: 5s
timeout: 5s
retries: 3
Expand Down Expand Up @@ -55,10 +55,10 @@ services:
networks:
- space-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/healthcheck"]
interval: 1m30s
timeout: 30s
retries: 5
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/v1/healthcheck', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))"]
interval: 5s
timeout: 5s
retries: 3
start_period: 30s
space-client:
container_name: space-client
Expand All @@ -68,10 +68,23 @@ services:
args:
VITE_ENVIRONMENT: production
VITE_SPACE_BASE_URL: http://localhost:5403/api/v1 # Change to http://localhost/api/v1 if running SPACE in kubernetes
# VITE_FRONTEND_BASE_PATH: /space # Uncomment and set to the base path of the frontend if it's not served at the root (e.g., /space)
depends_on:
- space-server
networks:
- space-network
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://localhost:5050').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
nginx:
restart: always
container_name: space-nginx
Expand All @@ -85,6 +98,12 @@ services:
- space-client
networks:
- space-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5403/api/v1/healthcheck"]
interval: 5s
timeout: 5s
retries: 3
start_period: 5s
volumes:
space-mongodb:
driver: local
Expand Down
21 changes: 20 additions & 1 deletion frontend/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ RUN pnpm install
# Copy the rest of the application source code to the container
RUN mkdir src
COPY ./src ./src
COPY ./public ./public

# Copy tsconfig.json to the container
COPY ./tsconfig.json .
Expand All @@ -26,12 +27,30 @@ COPY ./vite.config.ts .
COPY ./index.html .

ARG VITE_ENVIRONMENT
ARG VITE_FRONTEND_BASE_PATH
ARG VITE_FRONTEND_BASE_PATH="/"
ARG VITE_SPACE_BASE_URL

ENV VITE_FRONTEND_BASE_PATH=${VITE_FRONTEND_BASE_PATH}


# Build your application
RUN pnpm run build
RUN pnpm add -D serve

# Conditional Reorganization Block:
# This only runs if VITE_FRONTEND_BASE_PATH is defined and is NOT equal to "/"
RUN if [ -n "$VITE_FRONTEND_BASE_PATH" ] && [ "$VITE_FRONTEND_BASE_PATH" != "/" ]; then \
# Extract the directory name (e.g., from "/space/" to "space") to exclude it from the find command
FOLDER_NAME=$(echo "$VITE_FRONTEND_BASE_PATH" | sed 's/\///g'); \
echo "Reorganizing dist for subpath: $VITE_FRONTEND_BASE_PATH"; \
# Create the nested directory structure
mkdir -p ./dist${VITE_FRONTEND_BASE_PATH} && \
# Move everything EXCEPT index.html and the newly created subfolder into the subfolder
find ./dist -mindepth 1 -maxdepth 1 ! -name "index.html" ! -name "$FOLDER_NAME" \
-exec mv -t ./dist${VITE_FRONTEND_BASE_PATH}/ {} +; \
else \
echo "Skipping reorganization: App will be served from root (/)"; \
fi

# Specify the command to start the server
CMD ["pnpm", "exec", "serve", "-s", "./dist", "-l", "5050"]
10 changes: 5 additions & 5 deletions frontend/index.html
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Alex-GF are you sure this is needed? I believe vite will add the base URL automatically to the routes. At least it was like this when I built it locally.

Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
<link rel="shortcut icon" href="/favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/png" href="%BASE_URL%favicon/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="%BASE_URL%favicon/favicon.svg" />
<link rel="shortcut icon" href="%BASE_URL%favicon/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="%BASE_URL%favicon/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="SPACE" />
<link rel="manifest" href="/favicon/site.webmanifest" />
<link rel="manifest" href="%BASE_URL%favicon/site.webmanifest" />

<title>SPACE</title>
</head>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/contracts/ContractsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export default function ContractsTable({ contracts, page, setPage, limit, setLim
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/${c.userContact?.userId}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}/contracts/${c.userContact?.userId}`)}
className="px-3 py-1 bg-indigo-600 hover:bg-indigo-700 text-white rounded-md text-sm transition-colors cursor-pointer"
>
View
Expand Down
44 changes: 22 additions & 22 deletions frontend/src/layouts/logged-view/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,34 @@ import { AiOutlineDashboard } from 'react-icons/ai';
import OrganizationSelector from '@/components/OrganizationSelector';

const mainTabs = [
{ label: 'Overview', path: '/', icon: <FiHome size={22} /> },
{ label: 'Contracts Dashboard', path: '/contracts/dashboard', icon: <AiOutlineDashboard size={22}/> },
{ label: 'Members', path: '/members', icon: <FiUsers size={22} /> },
{ label: 'API Keys', path: '/api-keys', icon: <FiKey size={22} /> },
{ label: 'Services Management', path: '/services', icon: <FiServer size={22} /> },
{ label: 'Overview', path: '', icon: <FiHome size={22} /> },
{ label: 'Contracts Dashboard', path: 'contracts/dashboard', icon: <AiOutlineDashboard size={22}/> },
{ label: 'Members', path: 'members', icon: <FiUsers size={22} /> },
{ label: 'API Keys', path: 'api-keys', icon: <FiKey size={22} /> },
{ label: 'Services Management', path: 'services', icon: <FiServer size={22} /> },
];

const settingsTabs = [
{ label: 'Organization Settings', path: '/organization-settings', icon: <FiUsers size={18} /> },
{ label: 'Profile Settings', path: '/settings', icon: <FiUser size={18} /> },
{ label: 'Organization Settings', path: 'organization-settings', icon: <FiUsers size={18} /> },
{ label: 'Profile Settings', path: 'settings', icon: <FiUser size={18} /> },
];

const adminOnlyTabs = [
{ label: 'Users Management', path: '/users', icon: <FiUsers size={22} />, adminOnly: true },
{ label: 'Organizations', path: '/organizations', icon: <FiLayers size={22} />, adminOnly: true },
{ label: 'Instance Monitoring', path: '/instance-monitoring', icon: <FiActivity size={22} />, adminOnly: true },
{ label: 'Users Management', path: 'users', icon: <FiUsers size={22} />, adminOnly: true },
{ label: 'Organizations', path: 'organizations', icon: <FiLayers size={22} />, adminOnly: true },
{ label: 'Instance Monitoring', path: 'instance-monitoring', icon: <FiActivity size={22} />, adminOnly: true },
];

function getSelectedTab(pathname: string) {
if (pathname.startsWith('/members')) return '/members';
if (pathname.startsWith('/api-keys')) return '/api-keys';
if (pathname.startsWith('/services')) return '/services';
if (pathname.startsWith('/organization-settings')) return '/organization-settings';
if (pathname.startsWith('/settings')) return '/settings';
if (pathname.startsWith('/contracts/dashboard')) return '/contracts/dashboard';
if (pathname.startsWith('/instance-monitoring')) return '/instance-monitoring';
if (pathname.startsWith('/users')) return '/users';
if (pathname.startsWith('/organizations')) return '/organizations';
if (pathname.startsWith('members')) return 'members';
if (pathname.startsWith('api-keys')) return 'api-keys';
if (pathname.startsWith('services')) return 'services';
if (pathname.startsWith('organization-settings')) return 'organization-settings';
if (pathname.startsWith('settings')) return 'settings';
if (pathname.startsWith('contracts/dashboard')) return 'contracts/dashboard';
if (pathname.startsWith('instance-monitoring')) return 'instance-monitoring';
if (pathname.startsWith('users')) return 'users';
if (pathname.startsWith('organizations')) return 'organizations';
return '/';
}

Expand Down Expand Up @@ -106,7 +106,7 @@ export default function Sidebar({
(selected === tab.path
? 'bg-indigo-100 dark:bg-gray-800 font-bold' : '')
}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}${tab.path}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}${tab.path}`)}
aria-current={selected === tab.path ? 'page' : undefined}
>
{tab.icon}
Expand Down Expand Up @@ -155,7 +155,7 @@ export default function Sidebar({
(selected === tab.path
? 'bg-indigo-100 dark:bg-gray-800 font-bold' : '')
}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}${tab.path}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}${tab.path}`)}
aria-current={selected === tab.path ? 'page' : undefined}
>
{tab.icon}
Expand All @@ -182,7 +182,7 @@ export default function Sidebar({
(selected === tab.path
? 'bg-indigo-100 dark:bg-gray-800 font-bold' : '')
}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}${tab.path}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}${tab.path}`)}
aria-current={selected === tab.path ? 'page' : undefined}
>
{tab.icon}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/contracts/ContractDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default function ContractDetailPage() {
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/dashboard`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}contracts/dashboard`)}
className="inline-flex items-center px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors cursor-pointer"
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
Expand All @@ -125,7 +125,7 @@ export default function ContractDetailPage() {
<motion.button
whileHover={{ scale: 1.1, x: -4 }}
whileTap={{ scale: 0.95 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/dashboard`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}contracts/dashboard`)}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors cursor-pointer"
>
<svg className="w-6 h-6 text-gray-600 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
Expand Down Expand Up @@ -166,7 +166,7 @@ export default function ContractDetailPage() {
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/dashboard`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}contracts/dashboard`)}
className="inline-flex items-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors shadow-lg hover:shadow-xl cursor-pointer"
>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/login/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default function LoginPage() {
<div className="text-center mt-4">
<button
type="button"
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/register`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}register`)}
className="cursor-pointer text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 text-sm font-medium transition-colors"
>
Don't have an account? Register here
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/not-found/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function NotFoundPage() {
</p>

<button
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH ?? '/'}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}`)}
className="cursor-pointer inline-flex items-center gap-2 px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg shadow-md transition-all hover:shadow-lg"
>
<FiHome size={20} />
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/register/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function RegisterPage() {
await registerUser({ username, password });
await login(username, password);
setSuccess(true);
navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH ?? '/'}`);
navigate(`${import.meta.env.BASE_URL}`);
} catch (error: any) {
setError(error.message || 'Registration failed. Please try again.');
}
Expand Down Expand Up @@ -129,7 +129,7 @@ export default function RegisterPage() {
<div className="text-center mt-4">
<button
type="button"
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH ?? '/'}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}`)}
className="text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 text-sm font-medium transition-colors"
>
Already have an account? Log in
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/services/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default function ServicesPage() {
layout
whileHover={{ scale: 1.03, boxShadow: "0 4px 24px 0 rgba(99,102,241,0.10)" }}
whileTap={{ scale: 0.98 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/services/${encodeURIComponent(service.name)}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}services/${encodeURIComponent(service.name)}`)}
className="flex items-center gap-4 w-full bg-white/80 dark:bg-gray-900 rounded-lg shadow border border-gray-200 dark:border-gray-800 px-6 py-5 transition cursor-pointer hover:bg-indigo-50 dark:hover:bg-gray-800 focus:outline-none"
>
<span className="bg-indigo-100 dark:bg-gray-800 text-indigo-600 dark:text-indigo-300 rounded-full p-2">
Expand Down
10 changes: 5 additions & 5 deletions frontend/src/pages/welcome/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default function WelcomePage() {
Recent Services
</h2>
<button
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/services`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}services`)}
className="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 flex items-center gap-1 cursor-pointer transition-colors"
>
View all <FiArrowRight />
Expand All @@ -177,7 +177,7 @@ export default function WelcomePage() {
<FiServer className="mx-auto mb-2" size={32} />
<p>No services yet</p>
<button
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/services`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}services`)}
className="mt-3 text-sm text-indigo-600 dark:text-indigo-400 hover:underline cursor-pointer"
>
Create your first service
Expand All @@ -191,7 +191,7 @@ export default function WelcomePage() {
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: idx * 0.1 }}
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/services/${service.name}`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}services/${service.name}`)}
className="cursor-pointer p-4 bg-gradient-to-r from-indigo-50 to-blue-50 dark:from-gray-700 dark:to-gray-600 rounded-lg hover:shadow-md transition-shadow border border-indigo-100 dark:border-gray-600"
>
<div className="flex items-center justify-between">
Expand All @@ -217,7 +217,7 @@ export default function WelcomePage() {
Recent Contracts
</h2>
<button
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/dashboard`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}contracts/dashboard`)}
className="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 flex items-center gap-1 cursor-pointer transition-colors"
>
View all <FiArrowRight />
Expand All @@ -234,7 +234,7 @@ export default function WelcomePage() {
<FiFileText className="mx-auto mb-2" size={32} />
<p>No contracts yet</p>
<button
onClick={() => navigate(`${import.meta.env.VITE_FRONTEND_BASE_PATH}/contracts/dashboard`)}
onClick={() => navigate(`${import.meta.env.BASE_URL}contracts/dashboard`)}
className="mt-3 text-sm text-blue-600 dark:text-blue-400 hover:underline cursor-pointer"
>
View contracts dashboard
Expand Down
Loading
Loading