{"id":39176342,"url":"https://github.com/onecx/docker-spa-base","last_synced_at":"2026-05-21T09:04:54.081Z","repository":{"id":218944678,"uuid":"626870256","full_name":"onecx/docker-spa-base","owner":"onecx","description":"OneCx docker SPA base image for web application","archived":false,"fork":false,"pushed_at":"2026-01-16T07:12:47.000Z","size":93,"stargazers_count":0,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-16T22:13:48.643Z","etag":null,"topics":["devops"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/onecx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2023-04-12T10:20:31.000Z","updated_at":"2026-01-16T07:10:36.000Z","dependencies_parsed_at":"2024-02-05T16:59:52.331Z","dependency_job_id":"f42f26ce-cf86-4d9c-9160-3b54f0fca0c2","html_url":"https://github.com/onecx/docker-spa-base","commit_stats":null,"previous_names":["onecx/docker-spa-base"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/onecx/docker-spa-base","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onecx%2Fdocker-spa-base","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onecx%2Fdocker-spa-base/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onecx%2Fdocker-spa-base/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onecx%2Fdocker-spa-base/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/onecx","download_url":"https://codeload.github.com/onecx/docker-spa-base/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onecx%2Fdocker-spa-base/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28520526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T22:11:28.393Z","status":"ssl_error","status_checked_at":"2026-01-17T22:11:27.841Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["devops"],"created_at":"2026-01-17T22:28:46.497Z","updated_at":"2026-05-21T09:04:54.076Z","avatar_url":"https://github.com/onecx.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Base image for single page apps   \n\nThis image is basedon in NginX. \nIt exposes NginX http server on port 8080, provides a simple static endpoint for healthcheck at `/healthcheck` url. All images and fonts will have cache headers set to 1 month, as well as webpack hashed assets(js and css bundles) as they have hashes included in their names. \n\n# Usage\n\n```\nFROM oci://ghcr.io/onecx/docker-spa-base:v1\n# Copy applicaiton build\nCOPY --from=build --chown=101:0 /ng-app/dist $DIR_HTML\n# Define application nginx locations\nCOPY nginx/locations.conf $DIR_LOCATION/locations.conf\n# Define list of application environments\nENV CONFIG_ENV BFF_URL APP_BASE_HREF\n# Application environments default values\nENV BFF_URL http://my-bff:8080/\nENV APP_BASE_HREF /my-ui/\n```\n\n# Configuration\n\nStructure:\n* `default.conf` - nginx server configuration\n* `locations/common.conf` - common locations for the server like errors 400, 404, ...\n* `locations/base.conf` - base locations for the application.\n* `entrypoint.sh` - default entrypoint script\n\nApplication needs to define `CONFIG_ENV_LIST` environment variables in the `Dockerfile` as list of environment variable names to replace in configuration files. Default value:\n```\nENV CONFIG_ENV_LIST BFF_URL APP_BASE_HREF APP_ID CORS_ENABLED\n```\nBy default, the static html assets will be served under root path `/`, but this can be configured by setting an env var `APP_BASE_HREF`. If you set it, it should start and end with a forward slash e.g. `/my-app/`.\n\n\n## CORS Configuration\n\nCORS headers allow your SPA to be accessed from trusted cross-origin domains (e.g., micro-frontend shell, portals).\n\n\u003e **Note:** The `cors-test/` folder in this repository is for local development and testing only. It is **not** included in the production Docker image.\n\n### How CORS works in this image\n\nCORS is configured using Nginx maps (`00-cors-map.conf`) that validate incoming `Origin` headers against an explicit allowlist. **Only matched origins receive CORS headers**; unmatched origins receive zero CORS headers.\n\n### Configuration Variables\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `CORS_ENABLED` | `false` | Enable/disable CORS header emission |\n| `CORS_ALLOW_ORIGIN` | `\"\"` | Exact origin to allow (e.g., `https://shell.example.com`) |\n| `CORS_ALLOW_REGEX_ORIGIN` | `^$` | Regex pattern for multiple origins (e.g., `^https://(shell\\|portal)\\.example\\.com$`) |\n| `CORS_ALLOW_CREDENTIALS` | `false` | Allow cookies/credentials in cross-origin requests (set true only with cookie-based auth) |\n| `CORS_ALLOW_HEADERS` | `Authorization, Origin, X-Requested-With, Content-Type, Accept` | Allowed request headers |\n| `CORS_ALLOW_METHODS` | `GET, POST, OPTIONS, HEAD` | Allowed HTTP methods |\n\n### Usage Examples\n\n**Option 1: Single Trusted Origin**\n```dockerfile\nFROM oci://ghcr.io/onecx/docker-spa-base:v1\nENV CORS_ENABLED true\nENV CORS_ALLOW_ORIGIN https://shell.example.com\n```\n\n**Option 2: Multiple Origins via Regex**\n```dockerfile\nFROM oci://ghcr.io/onecx/docker-spa-base:v1\nENV CORS_ENABLED true\nENV CORS_ALLOW_ORIGIN \"\"\nENV CORS_ALLOW_REGEX_ORIGIN ^https://(shell|portal)\\.example\\.com$\n```\n\n**Option 3: Public API (any origin, no credentials)**\n```dockerfile\nFROM oci://ghcr.io/onecx/docker-spa-base:v1\nENV CORS_ENABLED true\nENV CORS_ALLOW_ORIGIN \"*\"\nENV CORS_ALLOW_CREDENTIALS false\n```\n\n**Option 4: Credentials with Single Origin** (only if using cookie-based auth, not JWT)\n```dockerfile\nFROM oci://ghcx.io/onecx/docker-spa-base:v1\nENV CORS_ENABLED true\nENV CORS_ALLOW_ORIGIN https://shell.example.com\nENV CORS_ALLOW_CREDENTIALS true\n```\n\n### Do I need both `CORS_ALLOW_ORIGIN` and `CORS_ALLOW_REGEX_ORIGIN`?\n\n**No.** Configure only one:\n\n- **Use `CORS_ALLOW_ORIGIN`** for a single trusted origin\n  - Keep `CORS_ALLOW_REGEX_ORIGIN=^$` (default)\n\n- **Use `CORS_ALLOW_REGEX_ORIGIN`** for multiple origins\n  - Keep `CORS_ALLOW_ORIGIN=\"\"` (empty)\n\nIf both are set, the map checks them in order; whichever matches first wins.\n\n### Testing CORS Configuration\n\nThe `cors-test/` folder contains helper files for local testing only and is **NOT** included in the production Docker image.\n\n#### Quick test with curl (static assets)\n\nStart container with mounted test assets:\n```bash\nDOCKER_API_VERSION=1.44 docker run --rm -p 8080:8080 \\\n  -v \"$PWD/cors-test/assets:/usr/share/nginx/html\" \\\n  -e CORS_ENABLED=true \\\n  -e APP_BASE_HREF=/ \\\n  -e CORS_ALLOW_ORIGIN=https://trusted.example \\\n  -e CORS_ALLOW_REGEX_ORIGIN=^$ \\\n  spa-base-cors-test\n```\n\nTest with untrusted origin (should have **NO** `Access-Control-Allow-Origin` header):\n```bash\ncurl -v -H \"Origin: https://evil.example\" http://localhost:8080/test.woff2\n```\n\nTest with trusted origin (should have `Access-Control-Allow-Origin: https://trusted.example` header):\n```bash\ncurl -v -H \"Origin: https://trusted.example\" http://localhost:8080/test.woff2\n```\n\n#### Full browser test with CORS test client\n\n1. Build and start container with mounted assets:\n```bash\nDOCKER_API_VERSION=1.44 docker run --rm -p 8080:8080 \\\n  -v \"$PWD/cors-test/assets:/usr/share/nginx/html\" \\\n  -e CORS_ENABLED=true \\\n  -e APP_BASE_HREF=/ \\\n  -e CORS_ALLOW_ORIGIN=\"\" \\\n  -e CORS_ALLOW_REGEX_ORIGIN='^https://(trusted\\.example|shell\\.example)$' \\\n  spa-base-cors-test\n```\n\n2. In another terminal, serve the test client:\n```bash\ncd cors-test/client\npython3 -m http.server 9000\n```\n\n3. Open `http://localhost:9000` in your browser and click the test buttons to see real browser CORS behavior.\n\n### Security Notes\n\n- **Default is safe**: `CORS_ENABLED=false` means CORS headers are never sent\n- **Never use `* + credentials`**: Combining `CORS_ALLOW_ORIGIN=*` with `CORS_ALLOW_CREDENTIALS=true` is a security vulnerability\n- **JWT tokens are safer**: If you use JWT Bearer tokens (recommended), set `CORS_ALLOW_CREDENTIALS=false`; credentials in CORS are only needed for cookie-based auth\n- **Prefer explicit allowlists**: Use exact origins or narrow regex patterns, not broad wildcards\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonecx%2Fdocker-spa-base","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonecx%2Fdocker-spa-base","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonecx%2Fdocker-spa-base/lists"}