Cleanup folders & hotfixes on ci/cd
Run Tests on Branches / Backend Tests (push) Failing after 3m0s
Run Tests on Branches / Frontend Tests (push) Successful in 2m10s

This commit is contained in:
Antoni Nuñez Romeu
2026-07-06 16:36:57 +02:00
parent 190b3d163d
commit ed741104ba
363 changed files with 24899 additions and 54423 deletions
+215 -470
View File
@@ -1,79 +1,161 @@
# 💊 FarmaFinder
# FarmaFinder
A web application to search for medicines from the official Spanish CIMA database and find which pharmacies sell them.
## Features
## Features
### Web App (Desktop/PWA)
- 🔍 **Real-time medicine search** from CIMA API (Agencia Española de Medicamentos)
- 💾 **Redis caching** for improved performance
- 📍 View pharmacies that sell a specific medicine
- 💰 See prices and stock availability
- 📱 Responsive design for mobile and desktop
- ⚙️ **Admin Panel** - Manage pharmacies and link medicines
- 🔐 **Secure authentication** - Login required to access admin features
- Real-time medicine search from CIMA API (Agencia Espanola de Medicamentos)
- Redis caching for improved performance
- View pharmacies that sell a specific medicine
- See prices and stock availability
- Responsive design for mobile and desktop
- Admin Panel - Manage pharmacies and link medicines
- Secure authentication - Login required to access admin features
- Add, edit, and delete pharmacies
- Search medicines from CIMA database
- Link medicines to pharmacies with prices and stock
### Mobile App (React Native)
- 📱 **Native iOS/Android** experience with Expo
- 🔍 **Medicine search** with real-time results
- 🗺️ **Interactive map** with pharmacy markers
- 📷 **Barcode scanner** for quick medicine lookup
- 🔔 **Push notifications** for availability alerts
- 🔐 **Biometric authentication** (Face ID / Touch ID)
- 💾 **Offline cache** for favorite medicines
- Native iOS/Android experience with Expo
- Medicine search with real-time results
- Interactive map with pharmacy markers
- Barcode scanner for quick medicine lookup
- Push notifications for availability alerts
- Biometric authentication (Face ID / Touch ID)
- Offline cache for favorite medicines
## 🛠️ Tech Stack
## Tech Stack
### Backend
- **Runtime**: Node.js + Express
- **Database**: SQLite (for pharmacies and relationships)
- **Cache**: Redis
- **External API**: CIMA (Centro de Información online de Medicamentos de la AEMPS)
| App | Stack |
|-----|-------|
| Backend | Node.js + Express, SQLite, Redis |
| Frontend (Web) | React + Vite, Capacitor |
| Frontend (Mobile) | Expo SDK 57 + React Native, Zustand, Axios + TanStack Query |
| Build system | Turborepo |
| Package manager | npm workspaces |
### Frontend (Web/PWA)
- **Framework**: React + Vite
- **Mobile wrapper**: Capacitor (for hybrid mobile builds)
## Prerequisites
### Frontend (Mobile - React Native)
- **Framework**: Expo SDK 57 + React Native
- **Navigation**: Expo Router v4
- **State**: Zustand
- **HTTP**: Axios + TanStack Query
- **Maps**: react-native-maps
- **Camera**: expo-camera (barcode scanning)
- **Auth**: expo-local-authentication (biometrics)
- **Notifications**: expo-notifications
- **Build**: EAS Build
- Node.js v20+
- npm v9+
- Redis server v6.0+ (or use Docker)
- Docker + Docker Compose v2 (optional, for containerized deployment)
## 📋 Prerequisites
## Project Structure
- Node.js (v18 or higher)
- npm or yarn
- **Redis server** (v6.0 or higher)
This is a **Turborepo monorepo**. All applications live under `apps/`:
## 🐳 Docker Setup (Recommended)
```
FarmaFinder/
├── package.json # Root: workspaces + turbo scripts
├── turbo.json # Turborepo task configuration
├── docker-compose.yml # Full stack: backend + frontend + Redis + Postgres
├── apps/
│ ├── backend/ # Node.js + Express API
│ │ ├── Dockerfile
│ │ ├── server.js # Express server and API routes
│ │ ├── cima-service.js # CIMA API integration with Redis cache
│ │ ├── redis-client.js # Redis connection configuration
│ │ ├── seed.js # Database seeding script
│ │ ├── create-admin.js # Admin user creation script
│ │ └── package.json
│ │
│ ├── frontend/ # React + Vite (Desktop/PWA)
│ │ ├── Dockerfile
│ │ ├── nginx.conf # Nginx config for Docker
│ │ ├── src/
│ │ │ ├── components/ # React components
│ │ │ ├── views/ # View components (Public/Admin)
│ │ │ ├── App.jsx
│ │ │ └── main.jsx
│ │ └── package.json
│ │
│ ├── frontend-mobile/ # Expo + React Native (iOS/Android)
│ │ ├── app/ # Expo Router screens
│ │ ├── components/
│ │ ├── services/
│ │ ├── store/
│ │ └── package.json
│ │
│ ├── scraper/ # Puppeteer scraper (standalone)
│ │ └── package.json
│ │
│ └── pip-platform/ # Python FastAPI platform (separate docker-compose)
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── pyproject.toml
├── API/ # Shared API source files
├── scripts/ # Build/utility scripts
└── docs/ # Documentation
```
Runs the full stack (backend, frontend, Redis) with a single command.
## Quick Start
**Prerequisites**: Docker and Docker Compose v2.
### Install dependencies
```bash
# Copy and configure environment (optional — defaults work for local dev)
cp backend/.env.example backend/.env
# Edit backend/.env to set SESSION_SECRET and any other vars
npm install
```
This installs all workspace dependencies (backend, frontend, mobile, scraper) via npm workspaces.
### Development
```bash
# Start everything (backend + frontend)
npm run dev
# Start only backend
npm run dev:backend
# Start only frontend
npm run dev:frontend
# Start mobile app
npx turbo run dev --filter=frontend-mobile
```
### Build
```bash
# Build all apps
npm run build
# Build only frontend
npm run build:web
```
### Test
```bash
# Run all tests
npm test
# Test specific app
npm test --workspace=farma-clic-backend
npm test --workspace=farma-clic-frontend
```
## Docker Setup
Runs the full stack (backend, frontend, Redis, Postgres) with a single command.
```bash
# Copy and configure environment (optional - defaults work for local dev)
cp apps/backend/.env.example apps/backend/.env
docker compose up --build
```
App available at `http://localhost:3000`.
App available at `http://localhost:4000` (frontend) and `http://localhost:3001` (backend API).
**First run create an admin user:**
**First run - create an admin user:**
```bash
docker compose exec backend node create-admin.js
# Default: admin / admin123 change after first login
# Default: admin / admin123 - change after first login
```
**Seed sample pharmacies:**
@@ -86,62 +168,37 @@ docker compose exec backend node seed.js
docker compose down
```
Database is persisted in a named Docker volume (`backend_data`). To wipe it:
Database is persisted in named Docker volumes (`backend_data`, `postgres_data`). To wipe:
```bash
docker compose down -v
```
---
## 🚀 Manual Setup Instructions
## Manual Setup
### 1. Install Redis
**On Ubuntu/Debian:**
**Ubuntu/Debian:**
```bash
sudo apt-get update
sudo apt-get install redis-server
sudo systemctl start redis-server
sudo systemctl enable redis-server
```
**On macOS (using Homebrew):**
**macOS:**
```bash
brew install redis
brew services start redis
```
**On Windows:**
Download and install from: https://redis.io/download
**Using Docker:**
**Docker:**
```bash
docker run -d -p 6379:6379 redis:alpine
```
Verify Redis is running:
```bash
redis-cli ping
# Should respond with: PONG
```
Verify: `redis-cli ping` should respond `PONG`.
### 2. Install Application Dependencies
### 2. Configure Environment (Optional)
**Install backend dependencies:**
```bash
cd backend
npm install
```
**Install frontend dependencies:**
```bash
cd ../frontend
npm install
```
### 3. Configure Environment (Optional)
Create a `.env` file in the `backend` directory if you need custom Redis settings:
Create `apps/backend/.env`:
```env
REDIS_HOST=localhost
@@ -150,246 +207,79 @@ REDIS_PASSWORD=
SESSION_SECRET=your-secret-key-here
```
### 4. Initialize Database
### 3. Initialize Database
**Seed the database with sample pharmacies:**
```bash
cd backend
npm run seed
# Seed sample pharmacies
npm run dev --workspace=farma-clic-backend -- run seed
# Create admin user (default: admin / admin123)
npm run dev --workspace=farma-clic-backend -- run create-admin
```
**Create admin user:**
### 4. Run
```bash
npm run create-admin
```
This creates a default admin user with:
- Username: `admin`
- Password: `admin123`
⚠️ **Important**: Change the default password after first login!
You can customize credentials using environment variables:
```bash
ADMIN_USERNAME=myadmin ADMIN_PASSWORD=mypassword npm run create-admin
```
### 5. Running the Application
**Start Redis** (if not running):
```bash
redis-server
```
**Start the backend server:**
```bash
cd backend
npm start
```
The backend will run on `http://localhost:3001`
**Start the frontend development server:**
```bash
cd frontend
npm run dev
```
The frontend will run on `http://localhost:3000`
**Open your browser** and navigate to `http://localhost:3000`
## API Endpoints
## 🎯 How to Use
### Public
- `GET /api/medicines/search?q=<query>` - Search medicines (CIMA API, cached in Redis)
- `GET /api/medicines/:nregistro` - Medicine details
- `GET /api/medicines/:nregistro/pharmacies` - Pharmacies selling a medicine
- `GET /api/pharmacies` - All pharmacies
### Public Search
1. Type the name of a medicine in the search bar
2. The app will search the CIMA database in real-time
3. Click on a medicine to see which pharmacies have it
4. View prices and stock availability
### Admin Panel
1. Click the "⚙️ Admin Panel" button
2. Login with your credentials (default: `admin` / `admin123`)
3. **Pharmacies Tab**: Add, edit, or delete pharmacies
4. **Medicines Tab**: Search medicines from the CIMA database
5. **Link Medicine Tab**: Associate medicines with pharmacies and set prices/stock
## 📁 Project Structure
```
FarmaFinder/
├── docker-compose.yml # Full stack: backend + frontend + Redis
├── backend/
│ ├── Dockerfile
│ ├── server.js # Express server and API routes
│ ├── cima-service.js # CIMA API integration with Redis cache
│ ├── redis-client.js # Redis connection configuration
│ ├── seed.js # Database seeding script
│ ├── create-admin.js # Admin user creation script
│ ├── .env.example # Environment variable template
│ └── package.json
├── frontend/ # React + Vite (Desktop/PWA)
│ ├── Dockerfile
│ ├── nginx.conf # Nginx config (Docker): serves SPA + proxies /api
│ ├── src/
│ │ ├── components/ # React components
│ │ │ ├── admin/ # Admin panel components
│ │ │ ├── PharmacyMap.jsx # Leaflet map (OpenStreetMap)
│ │ │ └── ...
│ │ ├── views/ # View components (Public/Admin)
│ │ ├── App.jsx # Main app component
│ │ └── main.jsx # Entry point
│ ├── index.html
│ └── package.json
├── frontend-mobile/ # Expo + React Native (iOS/Android)
│ ├── app/
│ │ ├── _layout.tsx # Root layout with providers
│ │ ├── (tabs)/ # Bottom tab navigation
│ │ │ ├── index.tsx # Home (medicine search)
│ │ │ ├── map.tsx # Pharmacy map
│ │ │ └── profile.tsx # User profile
│ │ ├── medicine/[id].tsx # Medicine detail
│ │ ├── pharmacy/[id].tsx # Pharmacy detail
│ │ ├── auth/ # Login/Register screens
│ │ └── scanner.tsx # Barcode scanner
│ ├── components/ # Reusable UI components
│ ├── services/ # API and business logic
│ ├── store/ # Zustand state management
│ ├── hooks/ # Custom React hooks
│ ├── constants/ # Theme and config
│ ├── types/ # TypeScript types
│ ├── eas.json # EAS Build configuration
│ └── package.json
├── android/ # Capacitor Android project
├── ios/ # Capacitor iOS project
└── README.md
```
## 🔌 API Endpoints
### Public API
- `GET /api/medicines/search?q=<query>` - Search medicines from CIMA API (cached in Redis)
- `GET /api/medicines/:nregistro` - Get medicine details from CIMA
- `GET /api/medicines/:nregistro/pharmacies` - Get pharmacies selling a medicine
- `GET /api/pharmacies` - Get all pharmacies
### Authentication API
- `POST /api/auth/login` - Login (requires username and password)
### Auth
- `POST /api/auth/login` - Login
- `POST /api/auth/logout` - Logout
- `GET /api/auth/check` - Check authentication status
- `GET /api/auth/check` - Check auth status
### Admin API (All require authentication)
- `POST /api/admin/pharmacies` - Add a new pharmacy
- `PUT /api/admin/pharmacies/:id` - Update a pharmacy
- `DELETE /api/admin/pharmacies/:id` - Delete a pharmacy
- `GET /api/admin/medicines?q=<query>` - Search medicines from CIMA (for admin)
- `GET /api/admin/pharmacies/:id/medicines` - Get medicines linked to a pharmacy
### Admin (requires authentication)
- `POST /api/admin/pharmacies` - Add pharmacy
- `PUT /api/admin/pharmacies/:id` - Update pharmacy
- `DELETE /api/admin/pharmacies/:id` - Delete pharmacy
- `GET /api/admin/medicines?q=<query>` - Search medicines
- `GET /api/admin/pharmacies/:id/medicines` - Linked medicines
- `POST /api/admin/pharmacy-medicines` - Link medicine to pharmacy
- `PUT /api/admin/pharmacy-medicines/:id` - Update price/stock
- `DELETE /api/admin/pharmacy-medicines/:id` - Remove medicine from pharmacy
- `DELETE /api/admin/pharmacy-medicines/:id` - Remove link
## 💾 Database Schema
## Database Schema
### SQLite Tables
**pharmacies**
- `id`: Integer (Primary Key)
- `name`: Text (Pharmacy name)
- `address`: Text (Full address)
- `phone`: Text (Contact phone)
- `latitude`: Real (GPS coordinate)
- `longitude`: Real (GPS coordinate)
**pharmacies**: `id`, `name`, `address`, `phone`, `latitude`, `longitude`
**pharmacy_medicines**
- `id`: Integer (Primary Key)
- `pharmacy_id`: Integer (Foreign Key → pharmacies.id)
- `medicine_nregistro`: Text (CIMA medicine registration number)
- `medicine_name`: Text (Cached medicine name)
- `price`: Real (Price in EUR)
- `stock`: Integer (Available units)
**pharmacy_medicines**: `id`, `pharmacy_id`, `medicine_nregistro`, `medicine_name`, `price`, `stock`
**users**
- `id`: Integer (Primary Key)
- `username`: Text (Unique)
- `password_hash`: Text (Bcrypt hashed)
- `created_at`: DateTime
**users**: `id`, `username`, `password_hash`, `created_at`
### Redis Cache Structure
### Redis Cache
- `medicines:search:{query}` - Search results (TTL: 1 hour)
- `medicine:{nregistro}` - Medicine details (TTL: 24 hours)
- `medicines:search:{query}` - Search results (TTL: 1h)
- `medicine:{nregistro}` - Medicine details (TTL: 24h)
## 🔧 Architecture Changes
### Migration from Local Database to CIMA API
The application now uses the **CIMA (Centro de Información online de Medicamentos)** API as the source of truth for medicine data, with Redis caching for performance:
**Benefits:**
- ✅ Always up-to-date medicine information
- ✅ Official data from Spanish health authorities
- ✅ Reduced database maintenance
- ✅ Fast responses thanks to Redis cache
- ✅ Fallback to stale cache if API is down
**Changes:**
- Medicines are no longer stored locally in SQLite
- Medicine searches query the CIMA API
- Results are cached in Redis for performance
- `pharmacy_medicines` now uses `medicine_nregistro` (CIMA registration number) instead of local `medicine_id`
## 📱 Mobile App Setup (React Native)
## Mobile App Setup
### Prerequisites
- Node.js (v18 or higher)
- npm or yarn
- **Expo CLI**: `npm install -g expo-cli`
- **EAS CLI**: `npm install -g eas-cli`
- **iOS**: Xcode (Mac only) + CocoaPods
- **Android**: Android Studio + Android SDK
- Node.js v20+
- Expo CLI: `npm install -g expo-cli`
- EAS CLI: `npm install -g eas-cli`
- iOS: Xcode + CocoaPods (Mac only)
- Android: Android Studio + SDK
### Quick Start
### Development
```bash
# Install all dependencies (backend + web + mobile)
npm run install:all
# Install dependencies (already done via npm install at root)
# Start mobile development server
npm run dev:mobile
# Start mobile dev server
npx turbo run dev --filter=frontend-mobile
# Scan QR code with Expo Go app (iOS/Android)
```
### Development Build
For native features (camera, biometrics, notifications), use a development build:
```bash
# Install EAS CLI
npm install -g eas-cli
# Login to Expo
eas login
# Create development build
eas build --profile development --platform ios
eas build --profile development --platform android
```
### Project Structure
```
frontend-mobile/
├── app/ # Expo Router screens
│ ├── (tabs)/ # Bottom tab navigation
│ ├── auth/ # Login/Register
│ ├── medicine/ # Medicine detail
│ ├── pharmacy/ # Pharmacy detail
│ └── scanner.tsx # Barcode scanner
├── components/ # Reusable UI components
├── services/ # API and business logic
├── store/ # Zustand state management
├── hooks/ # Custom React hooks
├── constants/ # Theme and config
└── types/ # TypeScript types
# Scan QR code with Expo Go app
```
### Native Features
@@ -402,217 +292,72 @@ frontend-mobile/
| Maps | `react-native-maps` |
| Secure Storage | `expo-secure-store` |
### EAS Build Profiles
| Profile | Platform | Build Type | Use Case |
|---------|----------|------------|----------|
| `development` | iOS | Simulator | Local testing on Mac |
| `development` | Android | APK | Local testing on device |
| `preview` | Android | APK | Internal testing & sharing |
| `production` | Android | AAB | Google Play Store submission |
**Note:** iOS builds require Apple Developer account ($99/year) and are configured separately.
### Environment Configuration
The mobile app uses the same backend API as the web app. Configure the API URL in:
```typescript
// frontend-mobile/constants/config.ts
const ENV = {
development: {
API_BASE_URL: 'http://localhost:3001/api',
},
production: {
API_BASE_URL: 'https://your-production-api.com/api',
},
};
```
## 🚀 Production Deployment (Mobile)
### Distribution Flow
```
Development → EAS Build → App Store / Google Play → User Device
```
The mobile app is a **native application** that runs directly on the device. No Docker or server needed - users download it from the app stores.
### Step 1: Setup Expo Account
### EAS Build
```bash
# Install EAS CLI
npm install -g eas-cli
cd apps/frontend-mobile
# Create account at https://expo.dev
# Development build
eas build --profile development --platform ios
eas build --profile development --platform android
# Login
eas login
```
### Step 2: Initialize EAS Project
```bash
cd frontend-mobile
eas init
```
This generates a `projectId` - add it to `app.json`:
```json
"extra": {
"eas": {
"projectId": "your-project-id"
}
}
```
### Step 3: Configure Credentials
**Android (Google Play):**
1. Create developer account ($25 one-time fee)
2. Create project in Google Cloud Console
3. Enable Play Developer API
4. Download `google-service-account.json`
5. Place in `frontend-mobile/` directory
**iOS (App Store):**
1. Join Apple Developer Program ($99/year)
2. Create App ID in Apple Developer portal
3. Generate certificates and provisioning profiles
4. Update `eas.json` with your credentials
### Step 4: Build for Production
```bash
# Android (Google Play)
# Production build
eas build --profile production --platform android
# iOS (App Store)
eas build --profile production --platform ios
```
### Step 5: Submit to Stores
```bash
# Submit to Google Play
# Submit to stores
eas submit --profile production --platform android
# Submit to App Store
eas submit --profile production --platform ios
```
### OTA Updates (Without App Store Review)
### Environment Configuration
Push updates directly to users without going through store review:
```bash
# Install expo-updates
npx expo install expo-updates
# Send update
eas update --branch production --message "Fix: improved search"
```typescript
// apps/frontend-mobile/constants/config.ts
const ENV = {
development: { API_BASE_URL: 'http://localhost:3001/api' },
production: { API_BASE_URL: 'https://your-production-api.com/api' },
};
```
### Useful Commands
| Command | Description |
|---------|-------------|
| `eas build:list` | View previous builds |
| `eas build:cancel <id>` | Cancel a build |
| `eas submit:list` | View previous submissions |
| `eas update` | Send OTA update |
## 🐛 Troubleshooting
## Troubleshooting
### Redis Connection Issues
If you see "Redis Client Error":
```bash
# Check if Redis is running
redis-cli ping
# Start Redis if needed
redis-server
redis-cli ping # Should respond: PONG
redis-server # Start if not running
redis-cli FLUSHALL # Clear cache
```
### CIMA API Timeout
- Check internet connection
- CIMA API may be temporarily unavailable
- App falls back to cached data
If medicine searches are slow or failing:
- Check your internet connection
- The CIMA API may be temporarily unavailable
- The app will use cached data if available
### Database Issues
Reset the database:
### Database Reset
```bash
cd backend
cd apps/backend
rm database.sqlite
npm run seed
npm run create-admin
```
## 📝 Development
### Backend
**Start with auto-reload:**
### Turborepo Cache Issues
```bash
cd backend
npm run dev
npx turbo clean # Clear Turbo cache
rm -rf node_modules # Full reset
npm install
```
### Frontend (Web/PWA)
## External Resources
**Start development server:**
```bash
cd frontend
npm run dev
```
- [CIMA API](https://cima.aemps.es/)
- [Turborepo](https://turbo.build/repo)
- [Redis](https://redis.io/documentation)
- [React](https://react.dev)
- [Express](https://expressjs.com)
- [Expo](https://docs.expo.dev)
### Frontend (Mobile - React Native)
**Install all dependencies:**
```bash
npm run install:all
```
**Start Expo dev server:**
```bash
npm run dev:mobile
```
**Start for specific platform:**
```bash
npm run dev:mobile:android # Android emulator
npm run dev:mobile:ios # iOS simulator
```
**Build with EAS:**
```bash
npm run build:mobile # Production build
```
**Submit to stores:**
```bash
npm run submit:android # Google Play
npm run submit:ios # App Store
```
### Clear Redis cache
```bash
redis-cli FLUSHALL
```
## 🌐 External Resources
- **CIMA API**: https://cima.aemps.es/
- **Redis Documentation**: https://redis.io/documentation
- **React Documentation**: https://react.dev
- **Express Documentation**: https://expressjs.com
## 📄 License
## License
ISC