Initial commit
This commit is contained in:
28
tools/mock-api-down.mjs
Normal file
28
tools/mock-api-down.mjs
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* The API in maintenance: every `/public/v1` endpoint answers 503 with the
|
||||
* notice already resolved to a language, which is exactly what the real one
|
||||
* does while the flag is on.
|
||||
*
|
||||
* npm run mock:down
|
||||
* PIREON_API_URL=http://localhost:4600/api npm run preview
|
||||
*/
|
||||
import { createServer } from 'node:http';
|
||||
|
||||
// Every /public/v1 endpoint answers 503 while the flag is on, with the notice
|
||||
// already resolved to the request's language.
|
||||
createServer((req, res) => {
|
||||
res.statusCode = 503;
|
||||
res.setHeader('content-type', 'application/json');
|
||||
res.setHeader('Retry-After', '3600');
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
message: 'Estamos migrando el Gitea. Volvemos en un rato.',
|
||||
maintenance: true,
|
||||
locale: 'es',
|
||||
since: '2026-08-25T09:00:00Z',
|
||||
// Set here so the "back on" line gets exercised; the real API usually
|
||||
// leaves this null.
|
||||
until: '2026-08-25T18:00:00Z',
|
||||
})
|
||||
);
|
||||
}).listen(4600, () => console.log('Mock API (maintenance) on http://localhost:4600/api'));
|
||||
105
tools/mock-api.mjs
Normal file
105
tools/mock-api.mjs
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* A stand-in for the Pireon public API, with enough content to see every
|
||||
* section of the landing at once.
|
||||
*
|
||||
* The real API answers `{"data": []}` for every collection until the panel has
|
||||
* something published, so the page renders as a hero and a contact form and
|
||||
* nothing else — correct, but impossible to review. Point the app at this
|
||||
* instead:
|
||||
*
|
||||
* npm run mock # this server, on :4599
|
||||
* npm run start:mock # the app against it
|
||||
*
|
||||
* `mock-api-down.mjs` is the other half: every endpoint answering 503, which is
|
||||
* how the maintenance banner gets exercised.
|
||||
*/
|
||||
import { createServer } from 'node:http';
|
||||
|
||||
const tech = (id, name, slug, category, color, ring) => ({
|
||||
id, name, slug, category, color, ring, projects_count: 3, members_count: 2,
|
||||
});
|
||||
|
||||
const technologies = [
|
||||
tech(1, 'Angular', 'angular', 'framework', '#dd0031', 'adopt'),
|
||||
tech(2, 'TypeScript', 'typescript', 'language', '#3178c6', 'adopt'),
|
||||
tech(3, 'Laravel', 'laravel', 'framework', '#ff2d20', 'trial'),
|
||||
tech(4, 'Postgres', 'postgres', 'datastore', '#336791', 'assess'),
|
||||
tech(5, 'Kubernetes', 'kubernetes', 'infrastructure', '#326ce5', 'hold'),
|
||||
];
|
||||
|
||||
const project = (id, title, slug, status, featured) => ({
|
||||
id, locale: 'es', title, slug, status,
|
||||
tagline: 'Una herramienta pequeña que resuelve un problema concreto y aburrido.',
|
||||
is_featured: featured, needs_help: id % 2 === 0,
|
||||
gallery: [], services: [], testimonials: [], talks: [],
|
||||
demo_url: 'https://example.com', updated_at: '2026-01-01T00:00:00Z',
|
||||
technologies: technologies.slice(0, 3), members: [], calls: [],
|
||||
repositories: [{ id, provider: 'gitea', full_name: `pireon/${slug}`, url: `https://git.example/${slug}`, languages: {}, topics: [], stars: 42 * id, forks: 3, contributors_count: 4, open_issues_count: 2, is_archived: false, is_dormant: false, good_first_issues: [] }],
|
||||
alternates: {}, path: `/proyectos/${slug}`,
|
||||
});
|
||||
|
||||
const member = (id, name, slug, founder) => ({
|
||||
id, name, slug, headline: 'Backend y cosas que nadie quiere tocar',
|
||||
links: { web: 'https://example.com' }, github_username: slug,
|
||||
is_founder: founder, is_current: id !== 4, technologies: [], projects: [],
|
||||
alternates: {}, path: `/equipo/${slug}`,
|
||||
});
|
||||
|
||||
const data = {
|
||||
'/public/v1/site': {
|
||||
locales: [
|
||||
{ code: 'es', name: 'Español', flag: '🇪🇸', is_default: true },
|
||||
{ code: 'en', name: 'English', flag: '🇬🇧', is_default: false },
|
||||
{ code: 'ca', name: 'Català', flag: '🏳️', is_default: false },
|
||||
],
|
||||
default_locale: 'es', content_version: 12, submissions_enabled: true,
|
||||
stats: { projects: 9, active_projects: 4, members: 7, open_calls: 2, stars: 1280, good_first_issues: 5 },
|
||||
},
|
||||
'/public/v1/services': [
|
||||
{ id: 1, locale: 'es', title: 'Producto a medida', slug: 'producto', tagline: 'Del boceto a producción sin capas que nadie pidió.', icon: '🛠', is_featured: true, position: 1, projects: [], technologies: [], testimonials: [], alternates: {} },
|
||||
{ id: 2, locale: 'es', title: 'Rescate de sistemas', slug: 'rescate', tagline: 'Código heredado que hay que entender antes de tocar.', icon: '🧭', is_featured: false, position: 2, projects: [], technologies: [], testimonials: [], alternates: {} },
|
||||
{ id: 3, locale: 'es', title: 'Formación', slug: 'formacion', tagline: 'Enseñamos lo que usamos, con el repositorio abierto.', icon: '📚', is_featured: false, position: 3, projects: [], technologies: [], testimonials: [], alternates: {} },
|
||||
],
|
||||
'/public/v1/projects': [project(1, 'Catálogo abierto', 'catalogo', 'active', true), project(2, 'Migrador de datos', 'migrador', 'completed', false), project(3, 'Panel de métricas', 'panel', 'paused', false)],
|
||||
'/public/v1/clients': [
|
||||
{ id: 1, name: 'Ajuntament de Girona', slug: 'girona', sector: 'Público', url: 'https://example.com', projects_count: 2 },
|
||||
{ id: 2, name: 'Cooperativa Verd', slug: 'verd', sector: 'Cooperativa', projects_count: 1 },
|
||||
{ id: 3, name: 'Fundació Oberta', slug: 'oberta', projects_count: 3 },
|
||||
],
|
||||
// Four, so the carousel actually has a second page and shows its controls.
|
||||
'/public/v1/testimonials': [
|
||||
{ id: 1, quote: 'Entregaron antes de tiempo y nos dejaron la documentación que nunca habíamos tenido.', original_locale: 'es', is_original: true, author: { name: 'Marta Ruiz', role: 'Directora técnica' }, client: { id: 1, name: 'Ajuntament de Girona', slug: 'girona', projects_count: 2 } },
|
||||
{ id: 2, quote: 'Explican las decisiones. Eso vale más que el código.', original_locale: 'es', is_original: true, author: { name: 'Joan Pi', role: 'CTO' } },
|
||||
{ id: 3, quote: 'Heredamos un sistema que nadie entendía. Lo documentaron, lo simplificaron y nos enseñaron a mantenerlo nosotros.', original_locale: 'es', is_original: true, author: { name: 'Nuria Sanz', role: 'Responsable de sistemas' }, client: { id: 2, name: 'Cooperativa Verd', slug: 'verd', projects_count: 1 } },
|
||||
{ id: 4, quote: 'Trabajar con el repositorio abierto desde el primer día nos quitó el miedo a depender de nadie.', original_locale: 'es', is_original: true, author: { name: 'Pau Ferrer', role: 'Dirección' }, client: { id: 3, name: 'Fundació Oberta', slug: 'oberta', projects_count: 3 } },
|
||||
],
|
||||
'/public/v1/radar': technologies,
|
||||
'/public/v1/members': [member(1, 'Ada Lovelace', 'ada', true), member(2, 'Linus T.', 'linus', false), member(3, 'Grace Hopper', 'grace', true), member(4, 'Alan Turing', 'alan', false)],
|
||||
'/public/v1/calls': [
|
||||
{ id: 1, locale: 'es', title: 'Frontend Angular', slug: 'frontend-angular', description: 'Ayúdanos con el catálogo público: componentes, accesibilidad y tests.', status: 'open', hours_per_week: 4, duration_weeks: 8, seniority: 'any', closes_at: '2026-10-01T00:00:00Z', project: project(1, 'Catálogo abierto', 'catalogo', 'active', true), technologies: technologies.slice(0, 2), alternates: {} },
|
||||
{ id: 2, locale: 'es', title: 'Infra y despliegues', slug: 'infra', description: 'Automatizar lo que ahora hacemos a mano una vez por semana.', status: 'open', hours_per_week: 2, seniority: 'mid', project: project(3, 'Panel de métricas', 'panel', 'paused', false), technologies: technologies.slice(4), alternates: {} },
|
||||
],
|
||||
'/public/v1/good-first-issues': [
|
||||
{ number: 12, title: 'Añadir mensajes de error traducidos al formulario', url: 'https://git.example/issues/12', labels: ['good first issue'], is_good_first_issue: true, is_help_wanted: false, is_available: true, comments: 3, repository: { full_name: 'pireon/catalogo', primary_language: 'TypeScript' } },
|
||||
{ number: 31, title: 'Documentar el proceso de despliegue', url: 'https://git.example/issues/31', labels: ['help wanted'], is_good_first_issue: false, is_help_wanted: true, is_available: true, comments: 0, repository: { full_name: 'pireon/panel', primary_language: 'PHP' } },
|
||||
],
|
||||
'/public/v1/talks': [
|
||||
{ id: 1, title: 'Servir HTML otra vez', language: 'es', summary: 'Por qué volvimos a renderizar en el servidor y qué nos costó.', event: 'Codemotion', city: 'Madrid', held_at: '2026-05-12T00:00:00Z', video_url: 'https://example.com/video' },
|
||||
{ id: 2, title: 'Una API para un frontend que no controlas', language: 'es', event: 'DevFest', city: 'Barcelona', held_at: '2026-03-02T00:00:00Z', slides_url: 'https://example.com/slides' },
|
||||
],
|
||||
};
|
||||
|
||||
createServer((req, res) => {
|
||||
const path = new URL(req.url, 'http://x').pathname.replace(/^\/api/, '');
|
||||
const payload = data[path];
|
||||
|
||||
res.setHeader('content-type', 'application/json');
|
||||
|
||||
if (payload === undefined) {
|
||||
res.statusCode = 404;
|
||||
res.end(JSON.stringify({ message: 'not found' }));
|
||||
return;
|
||||
}
|
||||
|
||||
res.end(JSON.stringify({ data: payload }));
|
||||
}).listen(4599, () => console.log('Mock API on http://localhost:4599/api'));
|
||||
Reference in New Issue
Block a user