Spaces:
Build error
Build error
Commit
·
2321a58
1
Parent(s):
a03dbc8
update
Browse files- Dockerfile +3 -3
- README-Docker.md +0 -46
- docker-compose.yml +0 -43
- metsa-backend/.env.example +0 -25
- metsa-backend/app/config.py +1 -1
- metsa-backend/app/main.py +24 -15
Dockerfile
CHANGED
@@ -51,8 +51,8 @@ COPY --from=python-deps /usr/local/bin /usr/local/bin
|
|
51 |
# Copy backend application code
|
52 |
COPY metsa-backend/ ./
|
53 |
|
54 |
-
# Copy built frontend files from the builder stage to
|
55 |
-
COPY --from=frontend-builder /app/frontend/out ./
|
56 |
|
57 |
# Create uploads directory structure
|
58 |
RUN mkdir -p uploads/commercial \
|
@@ -64,7 +64,7 @@ ENV PORT=8000
|
|
64 |
ENV APP_NAME="Document Portal API"
|
65 |
ENV APP_VERSION="1.0.0"
|
66 |
ENV DEBUG=True
|
67 |
-
ENV MONGODB_URL=mongodb://
|
68 |
ENV DATABASE_NAME=document_portal
|
69 |
ENV SECRET_KEY=your-secret-key-here-change-in-production
|
70 |
ENV ALGORITHM=HS256
|
|
|
51 |
# Copy backend application code
|
52 |
COPY metsa-backend/ ./
|
53 |
|
54 |
+
# Copy built frontend files from the builder stage to the expected location
|
55 |
+
COPY --from=frontend-builder /app/frontend/out ./metsa-frontend/out
|
56 |
|
57 |
# Create uploads directory structure
|
58 |
RUN mkdir -p uploads/commercial \
|
|
|
64 |
ENV APP_NAME="Document Portal API"
|
65 |
ENV APP_VERSION="1.0.0"
|
66 |
ENV DEBUG=True
|
67 |
+
ENV MONGODB_URL=mongodb+srv://lawyerlit11:3uN39xD2C2lGiMyY@cluster0.rqm6zmq.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
|
68 |
ENV DATABASE_NAME=document_portal
|
69 |
ENV SECRET_KEY=your-secret-key-here-change-in-production
|
70 |
ENV ALGORITHM=HS256
|
README-Docker.md
DELETED
@@ -1,46 +0,0 @@
|
|
1 |
-
# Metsa App - Docker Setup
|
2 |
-
|
3 |
-
This repository runs the Next.js frontend and FastAPI backend together using a single container image (with docker-compose for convenience).
|
4 |
-
|
5 |
-
## Prerequisites
|
6 |
-
- Docker 24+
|
7 |
-
- Docker Compose plugin (`docker compose`) or Docker Desktop
|
8 |
-
- Make (optional; Windows users can use Git Bash or WSL)
|
9 |
-
|
10 |
-
## Quick start
|
11 |
-
|
12 |
-
```bash
|
13 |
-
# Build image
|
14 |
-
make build
|
15 |
-
# Start
|
16 |
-
make up
|
17 |
-
# Tail logs
|
18 |
-
make logs
|
19 |
-
# Stop
|
20 |
-
make down
|
21 |
-
```
|
22 |
-
|
23 |
-
Without Make:
|
24 |
-
```bash
|
25 |
-
docker compose build
|
26 |
-
docker compose up -d
|
27 |
-
docker compose logs -f --tail=200
|
28 |
-
```
|
29 |
-
|
30 |
-
App URLs:
|
31 |
-
- Frontend: http://localhost:3000
|
32 |
-
- Backend: http://localhost:8000 (FastAPI docs: http://localhost:8000/docs)
|
33 |
-
|
34 |
-
## How it works
|
35 |
-
- Multi-stage Dockerfile builds the Next.js app, then runs both the built Next app and the FastAPI server in the final runtime image.
|
36 |
-
- A small start.sh launches the backend (Uvicorn) in background and then starts Next.js.
|
37 |
-
- docker-compose exposes ports 3000 and 8000 and mounts `metsa-backend/uploads` so files persist across restarts.
|
38 |
-
|
39 |
-
## Customizing
|
40 |
-
- Frontend API base URL can be overridden via NEXT_PUBLIC_API_URL (defaults to http://localhost:8000/api/v1).
|
41 |
-
- For production, set proper environment variables in docker-compose.yml and secure the backend settings.
|
42 |
-
|
43 |
-
## Notes
|
44 |
-
- This single-container approach is simple for development and small deployments. For production, consider separate services and a reverse proxy.
|
45 |
-
- Ensure MongoDB is reachable by the backend. Update `metsa-backend/app/config.py` MONGODB_URL or use an external MongoDB service/container.
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker-compose.yml
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
version: '3.8'
|
2 |
-
|
3 |
-
services:
|
4 |
-
app:
|
5 |
-
build:
|
6 |
-
context: .
|
7 |
-
dockerfile: Dockerfile
|
8 |
-
ports:
|
9 |
-
- "8000:8000"
|
10 |
-
environment:
|
11 |
-
- NODE_ENV=production
|
12 |
-
- NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
|
13 |
-
- DATABASE_URL=mongodb://mongo:27017/metsa
|
14 |
-
- SECRET_KEY=your-secret-key-change-in-production
|
15 |
-
- ALGORITHM=HS256
|
16 |
-
- ACCESS_TOKEN_EXPIRE_MINUTES=30
|
17 |
-
volumes:
|
18 |
-
- ./metsa-backend/uploads:/app/metsa-backend/uploads
|
19 |
-
- ./metsa-backend/.env:/app/metsa-backend/.env
|
20 |
-
depends_on:
|
21 |
-
- mongo
|
22 |
-
networks:
|
23 |
-
- metsa-network
|
24 |
-
|
25 |
-
mongo:
|
26 |
-
image: mongo:7.0
|
27 |
-
ports:
|
28 |
-
- "27017:27017"
|
29 |
-
volumes:
|
30 |
-
- mongo-data:/data/db
|
31 |
-
environment:
|
32 |
-
- MONGO_INITDB_ROOT_USERNAME=admin
|
33 |
-
- MONGO_INITDB_ROOT_PASSWORD=admin123
|
34 |
-
- MONGO_INITDB_DATABASE=metsa
|
35 |
-
networks:
|
36 |
-
- metsa-network
|
37 |
-
|
38 |
-
volumes:
|
39 |
-
mongo-data:
|
40 |
-
|
41 |
-
networks:
|
42 |
-
metsa-network:
|
43 |
-
driver: bridge
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
metsa-backend/.env.example
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
# Application
|
2 |
-
APP_NAME="Document Portal API"
|
3 |
-
APP_VERSION="1.0.0"
|
4 |
-
DEBUG=True
|
5 |
-
|
6 |
-
# MongoDB
|
7 |
-
MONGODB_URL=mongodb://localhost:27017
|
8 |
-
DATABASE_NAME=document_portal
|
9 |
-
|
10 |
-
# Security
|
11 |
-
SECRET_KEY=your-secret-key-here-change-in-production
|
12 |
-
ALGORITHM=HS256
|
13 |
-
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
14 |
-
|
15 |
-
# Email
|
16 |
-
SMTP_HOST=smtp.gmail.com
|
17 |
-
SMTP_PORT=587
|
18 |
-
SMTP_USER=your-email@gmail.com
|
19 |
-
SMTP_PASSWORD=your-app-password
|
20 |
-
EMAILS_FROM_EMAIL=noreply@metsa.com
|
21 |
-
EMAILS_FROM_NAME=Metsa Document Portal
|
22 |
-
|
23 |
-
# File Upload
|
24 |
-
UPLOAD_DIR=uploads
|
25 |
-
MAX_FILE_SIZE=10485760
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
metsa-backend/app/config.py
CHANGED
@@ -9,7 +9,7 @@ class Settings(BaseModel):
|
|
9 |
BASE_URL: str = "http://localhost:8000"
|
10 |
|
11 |
# MongoDB
|
12 |
-
MONGODB_URL: str = "mongodb://
|
13 |
DATABASE_NAME: str = "document_portal"
|
14 |
|
15 |
# Security
|
|
|
9 |
BASE_URL: str = "http://localhost:8000"
|
10 |
|
11 |
# MongoDB
|
12 |
+
MONGODB_URL: str = "mongodb+srv://lawyerlit11:3uN39xD2C2lGiMyY@cluster0.rqm6zmq.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"
|
13 |
DATABASE_NAME: str = "document_portal"
|
14 |
|
15 |
# Security
|
metsa-backend/app/main.py
CHANGED
@@ -61,47 +61,56 @@ app.add_middleware(
|
|
61 |
allow_headers=["*"],
|
62 |
)
|
63 |
|
64 |
-
# Include routers
|
65 |
app.include_router(auth.router, prefix="/api/v1")
|
66 |
app.include_router(users.router, prefix="/api/v1")
|
67 |
app.include_router(documents.router, prefix="/api/v1")
|
68 |
app.include_router(notifications.router, prefix="/api/v1")
|
69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
@app.get("/")
|
71 |
async def root():
|
|
|
|
|
|
|
72 |
return {
|
73 |
"message": "Welcome to Metsa Document Portal API",
|
74 |
"version": settings.APP_VERSION,
|
75 |
"docs": "/docs"
|
76 |
}
|
77 |
|
78 |
-
# Serve Next.js static export (built assets)
|
79 |
-
FRONTEND_DIR = Path(__file__).resolve().parents[2] / "metsa-frontend" / "out"
|
80 |
-
INDEX_FILE = FRONTEND_DIR / "index.html"
|
81 |
-
|
82 |
@app.get("/{full_path:path}")
|
83 |
async def serve_frontend(full_path: str):
|
84 |
# API routes are already handled by routers with /api/v1 prefix
|
85 |
# This catches everything else and serves static files from Next.js export
|
86 |
target = (FRONTEND_DIR / full_path).resolve()
|
87 |
try:
|
88 |
-
|
89 |
-
|
90 |
except Exception:
|
91 |
-
|
|
|
|
|
|
|
92 |
|
93 |
if target.is_dir():
|
94 |
index_in_dir = target / "index.html"
|
95 |
if index_in_dir.exists():
|
96 |
-
return FileResponse(index_in_dir)
|
|
|
97 |
if target.exists() and target.is_file():
|
98 |
return FileResponse(target)
|
|
|
99 |
# Fallback to top-level index.html for SPA routes
|
100 |
if INDEX_FILE.exists():
|
101 |
-
return FileResponse(INDEX_FILE)
|
|
|
102 |
raise HTTPException(status_code=404, detail="Frontend not built")
|
103 |
-
|
104 |
-
|
105 |
-
@app.get("/health")
|
106 |
-
async def health_check():
|
107 |
-
return {"status": "healthy"}
|
|
|
61 |
allow_headers=["*"],
|
62 |
)
|
63 |
|
64 |
+
# Include routers (API routes)
|
65 |
app.include_router(auth.router, prefix="/api/v1")
|
66 |
app.include_router(users.router, prefix="/api/v1")
|
67 |
app.include_router(documents.router, prefix="/api/v1")
|
68 |
app.include_router(notifications.router, prefix="/api/v1")
|
69 |
|
70 |
+
# Health check endpoint (before catch-all)
|
71 |
+
@app.get("/health")
|
72 |
+
async def health_check():
|
73 |
+
return {"status": "healthy"}
|
74 |
+
|
75 |
+
# Serve Next.js static export (built assets)
|
76 |
+
FRONTEND_DIR = Path(__file__).resolve().parents[1] / "metsa-frontend" / "out"
|
77 |
+
INDEX_FILE = FRONTEND_DIR / "index.html"
|
78 |
+
|
79 |
@app.get("/")
|
80 |
async def root():
|
81 |
+
# Serve the frontend index.html for the root route
|
82 |
+
if INDEX_FILE.exists():
|
83 |
+
return FileResponse(INDEX_FILE, media_type="text/html")
|
84 |
return {
|
85 |
"message": "Welcome to Metsa Document Portal API",
|
86 |
"version": settings.APP_VERSION,
|
87 |
"docs": "/docs"
|
88 |
}
|
89 |
|
|
|
|
|
|
|
|
|
90 |
@app.get("/{full_path:path}")
|
91 |
async def serve_frontend(full_path: str):
|
92 |
# API routes are already handled by routers with /api/v1 prefix
|
93 |
# This catches everything else and serves static files from Next.js export
|
94 |
target = (FRONTEND_DIR / full_path).resolve()
|
95 |
try:
|
96 |
+
# Prevent path traversal
|
97 |
+
target.relative_to(FRONTEND_DIR)
|
98 |
except Exception:
|
99 |
+
# Fallback to index.html for SPA routes
|
100 |
+
if INDEX_FILE.exists():
|
101 |
+
return FileResponse(INDEX_FILE, media_type="text/html")
|
102 |
+
raise HTTPException(status_code=404, detail="Not Found")
|
103 |
|
104 |
if target.is_dir():
|
105 |
index_in_dir = target / "index.html"
|
106 |
if index_in_dir.exists():
|
107 |
+
return FileResponse(index_in_dir, media_type="text/html")
|
108 |
+
|
109 |
if target.exists() and target.is_file():
|
110 |
return FileResponse(target)
|
111 |
+
|
112 |
# Fallback to top-level index.html for SPA routes
|
113 |
if INDEX_FILE.exists():
|
114 |
+
return FileResponse(INDEX_FILE, media_type="text/html")
|
115 |
+
|
116 |
raise HTTPException(status_code=404, detail="Frontend not built")
|
|
|
|
|
|
|
|
|
|