{"_id":"@avelor/vhost","_rev":"2-04c4915f7774f8d100e13e642df234b7","name":"@avelor/vhost","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@avelor/vhost","version":"0.1.0","keywords":["apache","vhost","virtual-host","apache2","reverse-proxy","developer-tools"],"author":{"name":"Avelor","email":"cruz@avelor.es"},"license":"MIT","_id":"@avelor/vhost@0.1.0","maintainers":[{"name":"christianecg","email":"contacto@christianecg.com"}],"homepage":"https://github.com/avelor-es/vhost#readme","bugs":{"url":"https://github.com/avelor-es/vhost/issues"},"bin":{"vhost":"bin/vhost.js"},"dist":{"shasum":"2423843b9028c830fc52d9361162ca2674c4b1c0","tarball":"https://registry.npmjs.org/@avelor/vhost/-/vhost-0.1.0.tgz","fileCount":15,"integrity":"sha512-BMKubE6g/o/5udpLpul2bdR0ix+0EvCoMNBfo/3SL4kCac+AHK+F+Zan2fVquwls4jcutT958mk+X5RYUJVPvg==","signatures":[{"sig":"MEUCIFxx3ZVc36xvN/Rus80ycyKFSeyE54fh2/++Tp6NaeC4AiEA1uenGOv4xtPnRzaa0uxOwZhgDOxhPJYMtNGEPPYl9ZA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35176},"type":"module","engines":{"node":">=18"},"gitHead":"e4d6f1953adb8ee0f5fb434be93d9d9464cdffc8","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"christianecg","email":"contacto@christianecg.com"},"repository":{"url":"git+https://github.com/avelor-es/vhost.git","type":"git"},"_npmVersion":"11.6.2","description":"Apache virtual host manager. Declarative sites, catch-all protection, and built-in error pages.","directories":{},"_nodeVersion":"24.13.0","dependencies":{"js-yaml":"^4.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/vhost_0.1.0_1780448326019_0.20060793337562877","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@avelor/vhost","version":"0.1.1","description":"Apache virtual host manager. Declarative sites, catch-all protection, and built-in error pages.","keywords":["apache","vhost","virtual-host","apache2","reverse-proxy","developer-tools"],"license":"MIT","author":{"name":"Avelor","email":"cruz@avelor.es"},"repository":{"type":"git","url":"git+https://github.com/avelor-es/vhost.git"},"publishConfig":{"access":"public"},"type":"module","bin":{"vhost":"bin/vhost.js"},"scripts":{"test":"node --test test/*.test.js"},"dependencies":{"js-yaml":"^4.1.0"},"engines":{"node":">=18"},"gitHead":"3be527b9b63ad96dd2c983afe13490d4d162607a","_id":"@avelor/vhost@0.1.1","bugs":{"url":"https://github.com/avelor-es/vhost/issues"},"homepage":"https://github.com/avelor-es/vhost#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-ZzhGICTjmQYiU6Wv4S1nUN2lyRQg2/ZBHxA0y1fMPH2BGpvRpnnjKtyuUuE3zP0sIp1IRU+27Td5mmsYzpkH4w==","shasum":"17c0fe1107df9bedc234d4dac2509953fbbca711","tarball":"https://registry.npmjs.org/@avelor/vhost/-/vhost-0.1.1.tgz","fileCount":16,"unpackedSize":37016,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGmP0PzKSpt6ERToihnmp2LFBH/hc+BfuH/PYxxOnRveAiEAzUZQzME+lp37rxAe1G1HmJ1ypn2ogZeJ3FuNssZI0Ks="}]},"_npmUser":{"name":"christianecg","email":"contacto@christianecg.com"},"directories":{},"maintainers":[{"name":"christianecg","email":"contacto@christianecg.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vhost_0.1.1_1780523393693_0.19712274454363965"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-03T00:58:45.849Z","modified":"2026-06-03T21:49:53.987Z","0.1.0":"2026-06-03T00:58:46.153Z","0.1.1":"2026-06-03T21:49:53.851Z"},"bugs":{"url":"https://github.com/avelor-es/vhost/issues"},"author":{"name":"Avelor","email":"cruz@avelor.es"},"license":"MIT","homepage":"https://github.com/avelor-es/vhost#readme","keywords":["apache","vhost","virtual-host","apache2","reverse-proxy","developer-tools"],"repository":{"type":"git","url":"git+https://github.com/avelor-es/vhost.git"},"description":"Apache virtual host manager. Declarative sites, catch-all protection, and built-in error pages.","maintainers":[{"name":"christianecg","email":"contacto@christianecg.com"}],"readme":"# @avelor/vhost\n\nApache virtual host manager. Define your sites in a YAML file, run one command, and Apache is configured.\n\n![demo](demo.gif)\n\n```\nsudo vhost apply\n```\n\n```\n  ✓  catch-all              protected\n  ✓  error pages            /var/www/vhost-errors\n  ✓  errors conf            vhost-errors.conf\n\n  ✓  avelor.com            managed   /home/avelor/avelor\n  ✓  api.avelor.es          managed   :3000\n  ✓  app.avelor.es          managed   :4000\n\n  ✓  apache configtest\n  ✓  apache reload\n```\n\n---\n\n## Requirements\n\n- Node.js 18+\n- Apache2 with `a2ensite`, `a2enconf`, `apache2ctl`, `mod_headers`\n- Debian / Ubuntu\n\n`mod_headers` is required for the `X-Powered-By` response header on error pages. Enable it if not already active:\n\n```sh\nsudo a2enmod headers && sudo systemctl reload apache2\n```\n\n---\n\n## Install\n\n```sh\nnpm install -g @avelor/vhost\n```\n\n---\n\n## Usage\n\n**1. Create `sites.yml`:**\n\n```sh\nvhost init\n```\n\n**2. Add your sites:**\n\n```yaml\nsites:\n  myblog.com:\n    aliases: [www.myblog.com]\n    mode: static\n    root: /var/www/myblog\n\n  api.myapp.com:\n    mode: proxy\n    port: 3000\n```\n\n**3. Apply:**\n\n```sh\nsudo vhost apply\n```\n\nApache is configured, enabled, and reloaded.\n\n---\n\n## Commands\n\n| Command | Description |\n|---|---|\n| `vhost init` | Create `sites.yml` in the current directory |\n| `vhost new [domain]` | Add a site interactively (or via flags) |\n| `sudo vhost apply [domain]` | Generate configs, enable sites, reload Apache |\n| `sudo vhost remove <domain>` | Disable and remove a site |\n| `vhost check` | Diff `sites.yml` against current Apache state |\n| `vhost status` | Show enabled sites |\n\n---\n\n## Site configuration\n\n### Static site\n\n```yaml\nsites:\n  example.com:\n    mode: static\n    root: /var/www/example.com\n    aliases: [www.example.com]   # optional\n```\n\n### Reverse proxy\n\n```yaml\nsites:\n  api.example.com:\n    mode: proxy\n    port: 3000\n    aliases: [api2.example.com]  # optional\n```\n\n### Manual SSL\n\nBy default, SSL is left for Certbot to manage. To provide your own certificates:\n\n```yaml\nsites:\n  example.com:\n    mode: static\n    root: /var/www/example.com\n    ssl:\n      cert: /etc/ssl/certs/example.pem\n      key:  /etc/ssl/private/example.key\n```\n\n---\n\n## What `apply` does\n\nFor each site, `vhost apply` generates a `.conf` in `/etc/apache2/sites-available/` containing:\n\n- HTTP → HTTPS redirect\n- HTTPS VirtualHost (static or proxy)\n- SSL block (Certbot placeholder or custom cert)\n- Per-site error and access logs\n\nIt also sets up two pieces of infrastructure the first time:\n\n**Catch-all** — any request to an unconfigured domain returns a clean error page instead of falling through to the first VirtualHost alphabetically, which is the default Apache behavior.\n\n**Error pages** — built-in pages for 400, 401, 403, 404, 405, 408, 429, 500, 502, 503, and 504. Each error code has four variants served via content negotiation:\n\n| `Accept` header | Response |\n|---|---|\n| `text/html` | Styled HTML page |\n| `application/json` | `{ \"code\": 404, \"message\": \"Page not found.\", \"source\": \"@avelor/vhost\" }` |\n| `application/xml` | `<error><code>404</code><message>…</message></error>` |\n| `text/plain` | `404 Page not found.` |\n\nAll responses include an `X-Powered-By: avelor/vhost` header. The `source` field in JSON and the `X-Powered-By` header make it easy to distinguish vhost error responses from your own API errors.\n\nUseful when the same server hosts both web apps and APIs.\n\n---\n\n## `vhost new`\n\nInteractive when no flags are given:\n\n```\n$ vhost new\n\n  Domain: api.myapp.com\n  Aliases (space-separated, optional):\n  Mode: [1] static  [2] proxy → 2\n  Proxy port: 3000\n  SSL via certbot? [Y/n]:\n\n  ✓  api.myapp.com added to sites.yml\n\n  Next steps:\n    sudo vhost apply api.myapp.com\n    sudo certbot --apache -d api.myapp.com\n```\n\nOr skip the prompts with flags:\n\n```sh\nvhost new api.myapp.com --mode proxy --port 3000\nvhost new myblog.com --mode static --root /var/www/myblog\n```\n\nPartial flags work too — only the missing fields are asked.\n\n---\n\n## `vhost check`\n\nCompares `sites.yml` against Apache's actual state and exits `1` if anything is off.\n\n```\n  ✓  catch-all\n  ✓  vhost-errors.conf\n  ✓  error pages              /var/www/vhost-errors\n\n  ✓  avelor.com              managed   /home/avelor/avelor\n  !  api.avelor.es            manual config   → run: vhost apply api.avelor.es to migrate\n  ✖  app.avelor.es            not applied → run: vhost apply app.avelor.es\n  !  old.avelor.es            manual config   not in sites.yml\n\n  ✖ 3 issues found.\n```\n\nA site is **managed** when its `.conf` was generated by `vhost apply`. **Manual config** means the file exists but was written by hand — it works, but `sites.yml` is not the source of truth yet.\n\n---\n\n## Migrating existing configs\n\n`vhost` is designed to be adopted gradually. Your existing Apache configs keep working untouched.\n\nThe typical flow:\n\n```sh\n# 1. Set up infrastructure without touching existing sites\nsudo vhost apply\n\n# 2. Add one site at a time\nvhost new avelor.com --mode static --root /home/avelor/avelor\n\n# 3. Apply only that site — overwrites the manual .conf\nsudo vhost apply avelor.com\n\n# 4. Check progress\nvhost check\n```\n\nWhen `vhost check` shows no `manual config` warnings, the migration is complete.\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}