Convert to full-stack app with Docker support
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Restructured app with backend/ and public/ directories - Created Express backend with /api/search endpoint - Added health check endpoint at /api/health - Optimized Dockerfile with multi-stage build - Added docker-compose.yml for easy deployment - Updated README with comprehensive documentation - Added .dockerignore for optimized builds - Backend listens on 0.0.0.0 for Docker compatibility
This commit is contained in:
parent
20f41d6030
commit
5a7585f755
50
.dockerignore
Normal file
50
.dockerignore
Normal file
@ -0,0 +1,50 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Docker
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
.dockerignore
|
||||
|
||||
# Kubernetes
|
||||
oramap/
|
||||
|
||||
# Build artifacts
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Other
|
||||
old-project/
|
||||
frontend/node_modules/
|
||||
README copy.md
|
||||
*.md
|
||||
!backend/
|
||||
!public/
|
||||
run_ora_map.sh
|
||||
nfsshare/
|
||||
nul
|
||||
33
Dockerfile
33
Dockerfile
@ -1,10 +1,35 @@
|
||||
FROM node:24-slim
|
||||
# Multi-stage build for Ora Map Application
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Production stage
|
||||
FROM base AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
# Copy backend package files
|
||||
COPY backend/package*.json ./backend/
|
||||
WORKDIR /app/backend
|
||||
RUN npm install --production
|
||||
|
||||
COPY . .
|
||||
# Copy backend source and data
|
||||
COPY backend/ ./
|
||||
WORKDIR /app
|
||||
|
||||
# Copy public frontend files
|
||||
COPY public/ ./public/
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Set environment variable
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||
|
||||
# Start the application
|
||||
WORKDIR /app/backend
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
|
||||
180
README.md
180
README.md
@ -0,0 +1,180 @@
|
||||
# 🗺️ Ora Map - Family Location Mapping Application
|
||||
|
||||
A full-stack web application for mapping and searching family locations in Yemen using interactive maps.
|
||||
|
||||
## 📋 Table of Contents
|
||||
- [Features](#features)
|
||||
- [Tech Stack](#tech-stack)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Docker Deployment](#docker-deployment)
|
||||
- [Development](#development)
|
||||
- [API Endpoints](#api-endpoints)
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🔍 **Family Search**: Search for families by name with autocomplete suggestions
|
||||
- 🗺️ **Interactive Map**: Leaflet-based map with multiple tile layer options
|
||||
- 📍 **Location Markers**: View family locations with city information
|
||||
- 🎨 **Modern UI**: Clean and responsive design
|
||||
- 🐳 **Docker Ready**: Containerized for easy deployment
|
||||
- 💚 **Health Checks**: Built-in health monitoring
|
||||
|
||||
## 🛠️ Tech Stack
|
||||
|
||||
**Backend:**
|
||||
- Node.js
|
||||
- Express.js
|
||||
- JSON data storage
|
||||
|
||||
**Frontend:**
|
||||
- HTML5/CSS3
|
||||
- JavaScript (ES6+)
|
||||
- Leaflet.js (interactive maps)
|
||||
- Fuse.js (fuzzy search)
|
||||
|
||||
**DevOps:**
|
||||
- Docker
|
||||
- Docker Compose
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
oramap/
|
||||
├── backend/
|
||||
│ ├── server.js # Express server
|
||||
│ ├── package.json # Backend dependencies
|
||||
│ └── data/
|
||||
│ └── families.json # Family location data
|
||||
├── public/
|
||||
│ ├── index.html # Frontend HTML
|
||||
│ ├── script.js # Frontend JavaScript
|
||||
│ └── style.css # Styles
|
||||
├── Dockerfile # Multi-stage Docker build
|
||||
├── docker-compose.yml # Docker Compose configuration
|
||||
└── .dockerignore # Docker ignore rules
|
||||
```
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Prerequisites
|
||||
- Node.js (v18 or higher)
|
||||
- Docker & Docker Compose (optional, for containerized deployment)
|
||||
|
||||
### Local Development
|
||||
|
||||
1. **Install backend dependencies:**
|
||||
```bash
|
||||
cd backend
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **Start the server:**
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
3. **Open your browser:**
|
||||
Navigate to `http://localhost:3000`
|
||||
|
||||
## 🐳 Docker Deployment
|
||||
|
||||
### Using Docker Compose (Recommended)
|
||||
|
||||
1. **Build and start the application:**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
2. **View logs:**
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
3. **Stop the application:**
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Using Docker directly
|
||||
|
||||
1. **Build the image:**
|
||||
```bash
|
||||
docker build -t oramap:latest .
|
||||
```
|
||||
|
||||
2. **Run the container:**
|
||||
```bash
|
||||
docker run -d -p 3000:3000 --name oramap-app oramap:latest
|
||||
```
|
||||
|
||||
3. **Check health:**
|
||||
```bash
|
||||
docker ps
|
||||
```
|
||||
|
||||
## 💻 Development
|
||||
|
||||
### Running in Development Mode
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Adding New Families
|
||||
|
||||
Edit `backend/data/families.json` and add entries in the following format:
|
||||
|
||||
```json
|
||||
{
|
||||
"family": "Family Name (Hebrew)",
|
||||
"city": "City Name (Hebrew)",
|
||||
"lat": 15.3545,
|
||||
"lng": 44.2064
|
||||
}
|
||||
```
|
||||
|
||||
## 📡 API Endpoints
|
||||
|
||||
### Search Families
|
||||
```
|
||||
GET /api/search?family={familyName}
|
||||
```
|
||||
Returns matching family records with location data.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "http://localhost:3000/api/search?family=Kafe"
|
||||
```
|
||||
|
||||
### Health Check
|
||||
```
|
||||
GET /api/health
|
||||
```
|
||||
Returns server health status.
|
||||
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2026-03-24T10:30:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
## 🗺️ Available Family Names
|
||||
|
||||
- Kafe (קאפח)
|
||||
- Shiheb (שחב-שבח)
|
||||
- Uzeyri (עזירי-עוזרי)
|
||||
- Salumi (סלומי-שלומי)
|
||||
- Afgin (עפג'ין)
|
||||
- Eraki (עראקי)
|
||||
|
||||
## 📝 License
|
||||
|
||||
ISC
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions, issues, and feature requests are welcome!
|
||||
10
backend/data/families.json
Normal file
10
backend/data/families.json
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{ "family": "Kafe (קאפח)", "city": "Sana'a (צנעא)", "lat": 15.3545, "lng": 44.2064 },
|
||||
{ "family": "Shiheb (שחב-שבח)", "city": "Sana'a (צנעא)", "lat": 15.3545, "lng": 44.2064 },
|
||||
{ "family": "Uzeyri (עזירי-עוזרי)", "city": "Sana'a (צנעא)", "lat": 15.3545, "lng": 44.2064 },
|
||||
{ "family": "Uzeyri (עזירי-עוזרי)", "city": "Manakhah (מנאכה)", "lat": 15.3019, "lng": 43.5983 },
|
||||
{ "family": "Uzeyri (עזירי-עוזרי)", "city": "Dhamar (ד'מאר)", "lat": 14.5424, "lng": 44.4056 },
|
||||
{ "family": "Salumi (סלומי-שלומי)", "city": "Al Kafla (אל קפלה)", "lat": 16.0240, "lng": 43.9790 },
|
||||
{ "family": "Afgin (עפג'ין)", "city": "Sa'dah (צעדה)", "lat": 16.9402, "lng": 43.7639 },
|
||||
{ "family": "Eraki (עראקי)", "city": "Sana'a (צנעא)", "lat": 15.3545, "lng": 44.2064 }
|
||||
]
|
||||
28
backend/server.js
Normal file
28
backend/server.js
Normal file
@ -0,0 +1,28 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const app = express();
|
||||
const families = require('./data/families.json');
|
||||
|
||||
// Serve static files from the public directory
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
// API endpoint for family search
|
||||
app.get('/api/search', (req, res) => {
|
||||
const query = req.query.family?.toLowerCase();
|
||||
if (!query) {
|
||||
return res.json([]);
|
||||
}
|
||||
const matches = families.filter(fam => fam.family.toLowerCase().includes(query));
|
||||
res.json(matches);
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
app.listen(port, '0.0.0.0', () => {
|
||||
console.log(`🗺️ Ora Map Server running at http://localhost:${port}`);
|
||||
console.log(`📍 API endpoint: http://localhost:${port}/api/search`);
|
||||
});
|
||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@ -0,0 +1,27 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
oramap:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: oramap:latest
|
||||
container_name: oramap-app
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
||||
interval: 30s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
networks:
|
||||
- oramap-network
|
||||
|
||||
networks:
|
||||
oramap-network:
|
||||
driver: bridge
|
||||
@ -32,7 +32,7 @@ async function searchFamily() {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/search?family=${encodeURIComponent(familyName)}`);
|
||||
const response = await fetch(`/api/search?family=${encodeURIComponent(familyName)}`);
|
||||
const familyResult = await response.json();
|
||||
|
||||
if (!familyResult.length) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user