Merge pull request 'Fixing Dockerfile with missing files' (#45) from dockerfile_parapharmacy_api_fix into main
Build & Push Docker Images / Detect Changes (push) Successful in 12s
Build & Push Docker Images / Backend Tests (push) Has been skipped
Build & Push Docker Images / Parapharmacy API Tests (push) Successful in 1m40s
Build & Push Docker Images / Pip Platform Tests (push) Has been skipped
Build & Push Docker Images / Frontend Tests (push) Has been skipped
Build & Push Docker Images / Build Backend (push) Has been skipped
Build & Push Docker Images / Build Parapharmacy API (push) Successful in 29s
Build & Push Docker Images / Build Pip Platform (push) Has been skipped
Build & Push Docker Images / Build Frontend (push) Has been skipped
Build & Push Docker Images / Deploy (push) Has been skipped
Build & Push Docker Images / Detect Changes (push) Successful in 12s
Build & Push Docker Images / Backend Tests (push) Has been skipped
Build & Push Docker Images / Parapharmacy API Tests (push) Successful in 1m40s
Build & Push Docker Images / Pip Platform Tests (push) Has been skipped
Build & Push Docker Images / Frontend Tests (push) Has been skipped
Build & Push Docker Images / Build Backend (push) Has been skipped
Build & Push Docker Images / Build Parapharmacy API (push) Successful in 29s
Build & Push Docker Images / Build Pip Platform (push) Has been skipped
Build & Push Docker Images / Build Frontend (push) Has been skipped
Build & Push Docker Images / Deploy (push) Has been skipped
Reviewed-on: #45
This commit was merged in pull request #45.
This commit is contained in:
@@ -36,6 +36,9 @@ RUN npm install --omit=dev
|
|||||||
# Copy source code
|
# Copy source code
|
||||||
COPY apps/parapharmacy-api/src/ ./src/
|
COPY apps/parapharmacy-api/src/ ./src/
|
||||||
|
|
||||||
|
# Copy scripts
|
||||||
|
COPY apps/parapharmacy-api/scripts/ ./scripts/
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 3002
|
EXPOSE 3002
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -101,6 +101,12 @@ services:
|
|||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
MONGODB_URI: mongodb://mongodb:27017/parapharmacy
|
MONGODB_URI: mongodb://mongodb:27017/parapharmacy
|
||||||
CORS_ORIGIN: ${CORS_ORIGIN:-https://farmacias.hacecalor.net}
|
CORS_ORIGIN: ${CORS_ORIGIN:-https://farmacias.hacecalor.net}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO", "/dev/null", "http://localhost:3002/api/health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 15s
|
||||||
depends_on:
|
depends_on:
|
||||||
- mongodb
|
- mongodb
|
||||||
|
|
||||||
@@ -152,7 +158,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
|
|
||||||
# --- N8N Init: import workflows on first deploy ---
|
# --- N8N Init: import workflows, activate, and seed DB ---
|
||||||
n8n-init:
|
n8n-init:
|
||||||
image: n8nio/n8n:latest
|
image: n8nio/n8n:latest
|
||||||
entrypoint: ["/bin/sh", "/home/node/init-import.sh"]
|
entrypoint: ["/bin/sh", "/home/node/init-import.sh"]
|
||||||
@@ -162,13 +168,19 @@ services:
|
|||||||
DB_POSTGRESDB_DATABASE: farmafinder
|
DB_POSTGRESDB_DATABASE: farmafinder
|
||||||
DB_POSTGRESDB_USER: farmafinder
|
DB_POSTGRESDB_USER: farmafinder
|
||||||
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:-change-me-in-production}
|
DB_POSTGRESDB_PASSWORD: ${PG_PASSWORD:-change-me-in-production}
|
||||||
|
N8N_OWNER_EMAIL: ${N8N_EMAIL:-admin@farmafinder.com}
|
||||||
|
N8N_OWNER_PASSWORD: ${N8N_PASSWORD:-change-me}
|
||||||
volumes:
|
volumes:
|
||||||
- n8n_data:/home/node/.n8n
|
- n8n_data:/home/node/.n8n
|
||||||
- ./n8n/workflows:/home/node/workflows
|
- ./n8n/workflows:/home/node/workflows
|
||||||
- ./n8n/init-import.sh:/home/node/init-import.sh:ro
|
- ./n8n/init-import.sh:/home/node/init-import.sh:ro
|
||||||
|
- ./n8n/activate-workflows.js:/home/node/activate-workflows.js:ro
|
||||||
|
- ./n8n/seed.json:/home/node/seed.json:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
n8n:
|
n8n:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
parapharmacy-api:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
backend_data:
|
backend_data:
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// Activate all inactive n8n workflows via API.
|
||||||
|
// Usage: node activate-workflows.js <email> <password>
|
||||||
|
import http from 'node:http';
|
||||||
|
|
||||||
|
const [, , email, password] = process.argv;
|
||||||
|
if (!email || !password) {
|
||||||
|
console.error('Usage: node activate-workflows.js <email> <password>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HOST = 'n8n';
|
||||||
|
const PORT = 5678;
|
||||||
|
|
||||||
|
function request(method, path, body, cookie) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const opts = {
|
||||||
|
hostname: HOST, port: PORT, path, method,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
};
|
||||||
|
if (cookie) opts.headers['Cookie'] = cookie;
|
||||||
|
|
||||||
|
const req = http.request(opts, (res) => {
|
||||||
|
const chunks = [];
|
||||||
|
res.on('data', (c) => chunks.push(c));
|
||||||
|
res.on('end', () => {
|
||||||
|
const raw = Buffer.concat(chunks).toString();
|
||||||
|
const setCookie = res.headers['set-cookie'];
|
||||||
|
let cookieVal = cookie;
|
||||||
|
if (setCookie) {
|
||||||
|
const match = setCookie.find((c) => c.startsWith('n8n-auth='));
|
||||||
|
if (match) cookieVal = match.split(';')[0];
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
try { data = JSON.parse(raw); } catch { data = raw; }
|
||||||
|
resolve({ status: res.statusCode, data, cookie: cookieVal });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on('error', reject);
|
||||||
|
if (body) req.write(JSON.stringify(body));
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// 1. Login
|
||||||
|
console.log('[activate] Logging in...');
|
||||||
|
const login = await request('POST', '/api/v1/login', { email, password });
|
||||||
|
if (!login.cookie) {
|
||||||
|
console.error('[activate] Login failed — no session cookie.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log('[activate] Login OK.');
|
||||||
|
|
||||||
|
// 2. List workflows
|
||||||
|
const list = await request('GET', '/api/v1/workflows', null, login.cookie);
|
||||||
|
const workflows = list.data?.data || list.data || [];
|
||||||
|
if (!Array.isArray(workflows)) {
|
||||||
|
console.error('[activate] Unexpected response:', JSON.stringify(list.data).slice(0, 200));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Activate inactive ones
|
||||||
|
let activated = 0;
|
||||||
|
for (const wf of workflows) {
|
||||||
|
if (!wf.active) {
|
||||||
|
const res = await request('PATCH', `/api/v1/workflows/${wf.id}`, { active: true }, login.cookie);
|
||||||
|
if (res.status < 300) {
|
||||||
|
console.log(`[activate] Activated: ${wf.name || wf.id}`);
|
||||||
|
activated++;
|
||||||
|
} else {
|
||||||
|
console.error(`[activate] Failed to activate ${wf.id}: HTTP ${res.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[activate] Done. ${activated} workflow(s) activated.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('[activate] Error:', err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
+35
-5
@@ -1,22 +1,52 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# One-shot init: import workflows into n8n on first deploy.
|
# One-shot init: import workflows, activate them, and seed the DB.
|
||||||
# Skips if already imported (flag file in shared volume).
|
# Skips if already done (flag file in shared n8n_data volume).
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
FLAG="/home/node/.n8n/.workflows-imported"
|
FLAG="/home/node/.n8n/.workflows-imported"
|
||||||
|
|
||||||
if [ -f "$FLAG" ]; then
|
if [ -f "$FLAG" ]; then
|
||||||
echo "[n8n-init] Workflows already imported, skipping."
|
echo "[n8n-init] Already initialized, skipping."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── 1. Wait for n8n ──────────────────────────────────────────────────
|
||||||
echo "[n8n-init] Waiting for n8n to be ready..."
|
echo "[n8n-init] Waiting for n8n to be ready..."
|
||||||
until wget -qO /dev/null http://n8n:5678/healthz 2>/dev/null; do
|
until wget -qO /dev/null http://n8n:5678/healthz 2>/dev/null; do
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
echo "[n8n-init] n8n is up."
|
||||||
|
|
||||||
echo "[n8n-init] Importing workflows from /home/node/workflows/..."
|
# ── 2. Import workflows ──────────────────────────────────────────────
|
||||||
|
echo "[n8n-init] Importing workflows..."
|
||||||
n8n import:workflow --separate --input=/home/node/workflows/
|
n8n import:workflow --separate --input=/home/node/workflows/
|
||||||
|
echo "[n8n-init] Workflows imported."
|
||||||
|
|
||||||
|
# ── 3. Activate workflows via n8n API (Node.js) ─────────────────────
|
||||||
|
if [ -f /home/node/activate-workflows.js ]; then
|
||||||
|
echo "[n8n-init] Activating workflows via n8n API..."
|
||||||
|
node /home/node/activate-workflows.js "${N8N_OWNER_EMAIL}" "${N8N_OWNER_PASSWORD}" || \
|
||||||
|
echo "[n8n-init] WARNING: Workflow activation failed. Activate manually via n8n UI."
|
||||||
|
else
|
||||||
|
echo "[n8n-init] WARNING: activate-workflows.js not found, skipping activation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 4. Seed parapharmacy DB ──────────────────────────────────────────
|
||||||
|
echo "[n8n-init] Waiting for parapharmacy-api to be ready..."
|
||||||
|
until wget -qO /dev/null http://parapharmacy-api:3002/api/health 2>/dev/null; do
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -f /home/node/seed.json ]; then
|
||||||
|
echo "[n8n-init] Seeding parapharmacy products..."
|
||||||
|
SEED_RESP=$(wget -qO- --post-file=/home/node/seed.json \
|
||||||
|
--header="Content-Type: application/json" \
|
||||||
|
http://parapharmacy-api:3002/api/products/bulk 2>&1)
|
||||||
|
echo "[n8n-init] Seed result: $SEED_RESP"
|
||||||
|
else
|
||||||
|
echo "[n8n-init] WARNING: seed.json not found, skipping seed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Done ─────────────────────────────────────────────────────────────
|
||||||
touch "$FLAG"
|
touch "$FLAG"
|
||||||
echo "[n8n-init] Import complete."
|
echo "[n8n-init] Initialization complete."
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[
|
||||||
|
{"name":"Bioderma Atoderm Crema Hidratante 500ml","brand":"Bioderma","category":"Dermocosmética","price":18.95,"source":"seed","source_product_id":"seed_bioderma_atoderm","currency":"EUR","available":true},
|
||||||
|
{"name":"La Roche-Posay Anthelios Airlicium FPS50+","brand":"La Roche-Posay","category":"Solar","price":19.95,"source":"seed","source_product_id":"seed_lrp_anthelios","currency":"EUR","available":true},
|
||||||
|
{"name":"Mustela Gel de Ducha 500ml","brand":"Mustela","category":"Bebé","price":12.50,"source":"seed","source_product_id":"seed_mustela_gel","currency":"EUR","available":true},
|
||||||
|
{"name":"Centrum Multivitaminicos 30 comprimidos","brand":"Centrum","category":"Vitaminas","price":15.80,"source":"seed","source_product_id":"seed_centrum_multi","currency":"EUR","available":true},
|
||||||
|
{"name":"Dolocordalpan 1g Paracetamol 20 sobres","brand":"Dolocordalpan","category":"Analgésicos","price":4.95,"source":"seed","source_product_id":"seed_dolocordalpan","currency":"EUR","available":true},
|
||||||
|
{"name":"Nurofen Flash 400mg 20 cápsulas","brand":"Nurofen","category":"Antiinflamatorios","price":6.75,"source":"seed","source_product_id":"seed_nurofen_flash","currency":"EUR","available":true},
|
||||||
|
{"name":"Omeprazol Cinfa 20mg 28 cápsulas","brand":"Cinfa","category":"Gastrointestinal","price":8.50,"source":"seed","source_product_id":"seed_omeprazol_cinfa","currency":"EUR","available":true},
|
||||||
|
{"name":"Vichy Mineral 89 Sérum Hidratante 30ml","brand":"Vichy","category":"Dermocosmética","price":25.90,"source":"seed","source_product_id":"seed_vichy_mineral89","currency":"EUR","available":true},
|
||||||
|
{"name":"Avène Agua Termal 300ml","brand":"Avène","category":"Dermocosmética","price":9.95,"source":"seed","source_product_id":"seed_avene_agua","currency":"EUR","available":true},
|
||||||
|
{"name":"CeraVe Crema Hidratante 340g","brand":"CeraVe","category":"Dermocosmética","price":14.95,"source":"seed","source_product_id":"seed_cerave_crema","currency":"EUR","available":true},
|
||||||
|
{"name":"Ibuprofeno Alter 600mg 20 comprimidos","brand":"Alter","category":"Antiinflamatorios","price":5.20,"source":"seed","source_product_id":"seed_ibuprofeno_alter","currency":"EUR","available":true},
|
||||||
|
{"name":"Salonpas Parches Analgésicos 5 unidades","brand":"Salonpas","category":"Analgésicos","price":7.80,"source":"seed","source_product_id":"seed_salonpas","currency":"EUR","available":true},
|
||||||
|
{"name":"Fisiomer Spray Nasal 135ml","brand":"Fisiomer","category":"Respiratorio","price":11.50,"source":"seed","source_product_id":"seed_fisiomer","currency":"EUR","available":true},
|
||||||
|
{"name":"Thealoz Duo Colirio 10ml","brand":"Thea","category":"Oftalmología","price":12.95,"source":"seed","source_product_id":"seed_thealoz","currency":"EUR","available":true},
|
||||||
|
{"name":"Neutrogena Crema Manos Noruega 50ml","brand":"Neutrogena","category":"Dermocosmética","price":4.50,"source":"seed","source_product_id":"seed_neutrogena_manos","currency":"EUR","available":true},
|
||||||
|
{"name":"Capricare 1 Leche en polvo 400g","brand":"Capricare","category":"Fórmulas lácteas","price":14.95,"source":"seed","source_product_id":"seed_capricare1","currency":"EUR","available":true},
|
||||||
|
{"name":"Nutribén 2 Leche 800g","brand":"Nutribén","category":"Fórmulas lácteas","price":16.50,"source":"seed","source_product_id":"seed_nutriben2","currency":"EUR","available":true},
|
||||||
|
{"name":"Bebelin Vitamina C 1g 20 comprimidos","brand":"Bebelín","category":"Vitaminas","price":6.95,"source":"seed","source_product_id":"seed_bebelin_vitc","currency":"EUR","available":true},
|
||||||
|
{"name":"Lacer Pasta Dientes Sensibilidad 75ml","brand":"Lacer","category":"Oral","price":5.80,"source":"seed","source_product_id":"seed_lacer_pasta","currency":"EUR","available":true},
|
||||||
|
{"name":"Fotosan Crema Solar FPS50 200ml","brand":"Fotosan","category":"Solar","price":22.50,"source":"seed","source_product_id":"seed_fotosan_solar","currency":"EUR","available":true}
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user