{"id":41612588,"url":"https://github.com/arc42/trainings.arc42.org-site","last_synced_at":"2026-01-24T12:19:45.586Z","repository":{"id":220942747,"uuid":"752984581","full_name":"arc42/trainings.arc42.org-site","owner":"arc42","description":"Schedule of our upcoming trainings","archived":false,"fork":false,"pushed_at":"2025-11-16T09:28:22.000Z","size":4468,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-11-16T11:22:45.849Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://arc42-subtle-ads-backend.vercel.app/api","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arc42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.MD","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-02-05T08:42:22.000Z","updated_at":"2025-11-16T09:28:25.000Z","dependencies_parsed_at":"2024-04-21T09:36:36.077Z","dependency_job_id":"9fd3b3b5-0927-4d15-af9e-94d951f8817a","html_url":"https://github.com/arc42/trainings.arc42.org-site","commit_stats":null,"previous_names":["arc42/trainings.arc42.org-site"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/arc42/trainings.arc42.org-site","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc42%2Ftrainings.arc42.org-site","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc42%2Ftrainings.arc42.org-site/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc42%2Ftrainings.arc42.org-site/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc42%2Ftrainings.arc42.org-site/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arc42","download_url":"https://codeload.github.com/arc42/trainings.arc42.org-site/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arc42%2Ftrainings.arc42.org-site/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28727519,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T10:24:43.181Z","status":"ssl_error","status_checked_at":"2026-01-24T10:24:36.112Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-24T12:19:43.310Z","updated_at":"2026-01-24T12:19:45.581Z","avatar_url":"https://github.com/arc42.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# trainings.arc42.org-site\n\nThis repository powers [trainings.arc42.org](https://trainings.arc42.org), which displays a list of upcoming arc42 training dates, and includes backend functionality to dynamically serve these dates on other arc42-related sites.\n\n# Overview\n\nThis project includes both frontend and backend functionality, used by multiple arc42-related sites to show consistent, up-to-date training info.\n\n## Key Process\n\nAll training dates are maintained in a single HTML file ([`/_includes/_subtle-ads.html`](/_includes/_subtle-ads.html)) and distributed across sites via:\n\n- A static Jekyll include on trainings.arc42.org\n- A backend API used by other sites (served via Vercel)\n \n## Updating Training Dates (requires write access)\n\nTo change or add training dates:\n\n1. Edit [`/_includes/_subtle-ads.html`](/_includes/_subtle-ads.html)\n2. Commit and push your changes\n\nThis automatically updates the content:\n\n- On trainings.arc42.org (via Jekyll include)\n- Across other arc42 sites (via the backend API)\n\n## Backend API\n\nThe backend is deployed on Vercel as a simple serverless function, written in the format Vercel expects for [Next.js API routes](https://nextjs.org/docs/api-routes/introduction).\n\nIt reads the contents of `_subtle-ads.html` from the filesystem and serves it as raw HTML via this endpoint:\n\n```\nhttps://arc42-subtle-ads-backend.vercel.app/api\n```\n\nThe endpoint returns the HTML with appropriate CORS and caching headers. The backend is automatically redeployed on each push to this repository, ensuring that updates to the training data are reflected across all consuming sites.\n\n### Further Details\n\nThe function for the backend is located in [`/api/index.js`](/api/index.js).  \nHere’s the full implementation:\n\n```\nconst fs = require('fs').promises;\nconst path = require('path');\n\n// Enable CORS headers for browser access\nconst allowCors = fn =\u003e async (req, res) =\u003e {\n  res.setHeader('Access-Control-Allow-Credentials', true);\n  res.setHeader('Access-Control-Allow-Origin', '*');\n  res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,PATCH,DELETE,POST,PUT');\n  res.setHeader(\n    'Access-Control-Allow-Headers',\n    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, hx-target, hx-current-url, hx-request, hx-trigger'\n  );\n  if (req.method === 'OPTIONS') {\n    res.status(200).end();\n    return;\n  }\n  return await fn(req, res);\n};\n\nconst handler = async (req, res) =\u003e {\n  try {\n    const delay = (duration) =\u003e new Promise(resolve =\u003e setTimeout(resolve, duration));\n    await delay(6000); // artificial delay for testing\n\n    const filePath = path.join(__dirname, '..', '_includes', '_subtle-ads.html');\n    const htmlContent = await fs.readFile(filePath, 'utf8');\n\n    res.setHeader('Content-Type', 'text/html');\n    res.setHeader('Cache-Control', 'public, max-age=3600');\n    res.status(200).end(htmlContent);\n  } catch (error) {\n    res.status(500).end('Error loading the HTML file.');\n  }\n};\n\nmodule.exports = allowCors(handler);\n```\n\nBecause the backend is part of the same repository as the `_subtle-ads.html` file, we can access the training data at runtime using a relative path:\n\n`const filePath = path.join(__dirname, '..', '_includes', '_subtle-ads.html');`\n\n### How Deployment Works\n\nWhen you commit and push changes to the repo:\n\n- **GitHub** rebuilds the Jekyll frontend (`trainings.arc42.org`)\n- **Vercel** detects the push and automatically re-deploys the serverless backend\n\nDuring that deployment, the contents of the repository (including `_subtle-ads.html`) are bundled and made available in the serverless function’s read-only filesystem. This ensures the backend API always serves the latest training data—without any additional steps.\n\n\n## Frontend Integration\n\n- **trainings.arc42.org** includes `_subtle-ads.html` directly via Jekyll and does *not* use the backend. This ensures availability even if the backend fails.\n- **All other arc42 sites** load the training data dynamically using HTMX, which fetches the HTML from the backend API and replaces a placeholder div. On these sites, the HTMX snippet is contained in a Jekyll include as well, and can be inserted via `{% include subtle-ads/subtle-ads.html %}`.\n\n## Fallback Behavior\n\nIf the backend is unreachable or blocked (e.g. by browser settings), users are directed to [trainings.arc42.org](https://trainings.arc42.org), which always reflects the latest content via the static include.\n\n## Created with [OneFlow Jekyl Theme](https://oneflow-jekyll-theme.github.io/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farc42%2Ftrainings.arc42.org-site","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farc42%2Ftrainings.arc42.org-site","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farc42%2Ftrainings.arc42.org-site/lists"}