diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..44c93a7 --- /dev/null +++ b/.dockerignore @@ -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 diff --git a/Dockerfile b/Dockerfile index c179cde..966338d 100644 --- a/Dockerfile +++ b/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"] + diff --git a/README.md b/README.md index e69de29..24d11e3 100644 --- a/README.md +++ b/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! diff --git a/backend/data/families.json b/backend/data/families.json new file mode 100644 index 0000000..f677668 --- /dev/null +++ b/backend/data/families.json @@ -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 } + ] \ No newline at end of file diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 0000000..1cb8c37 --- /dev/null +++ b/backend/server.js @@ -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`); +}); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c6fba30 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/public/script.js b/public/script.js index f1057e7..2e213a5 100644 --- a/public/script.js +++ b/public/script.js @@ -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) {