From 3f7396407b565d8f5b2a09a87cf3b9618bf0ef42 Mon Sep 17 00:00:00 2001 From: Scaffolder Date: Mon, 23 Mar 2026 17:40:04 +0000 Subject: [PATCH] initial commit Change-Id: Ia748646eaf5c18f44eb5b147ff577d0d5ee844e8 --- .gitattributes | 12 + .gitea/workflows/ci.yml | 41 + .gitea/workflows/techdocs.yml | 104 + .github/ISSUE_TEMPLATE | 40 + .github/dependabot.yml | 7 + .../workflows/call-docker-build-result.yaml | 82 + .github/workflows/call-docker-build-vote.yaml | 82 + .../workflows/call-docker-build-worker.yaml | 82 + .gitignore | 6 + .pages | 3 + .vscode/launch.json | 20 + LICENSE | 191 ++ MAINTAINERS | 9 + README.md | 65 + architecture.excalidraw.png | Bin 0 -> 151461 bytes catalog-info.yaml | 65 + docker-compose.images.yml | 69 + docker-compose.yml | 96 + docker-stack.yml | 53 + docs/index.md | 81 + healthchecks/postgres.sh | 21 + healthchecks/redis.sh | 10 + k8s-specifications/db-deployment.yaml | 33 + k8s-specifications/db-service.yaml | 15 + k8s-specifications/redis-deployment.yaml | 28 + k8s-specifications/redis-service.yaml | 15 + k8s-specifications/result-deployment.yaml | 22 + k8s-specifications/result-service.yaml | 15 + k8s-specifications/vote-deployment.yaml | 22 + k8s-specifications/vote-service.yaml | 16 + k8s-specifications/worker-deployment.yaml | 19 + mkdocs.yml | 18 + overlays/deploy/kustomization.yaml | 10 + overlays/ingress/ingress.yaml | 26 + overlays/ingress/kustomization.yaml | 2 + result/.dockerignore | 1 + result/Dockerfile | 25 + result/docker-compose.test.yml | 62 + result/package-lock.json | 1742 +++++++++++++++++ result/package.json | 20 + result/server.js | 77 + result/tests/Dockerfile | 12 + result/tests/render.js | 15 + result/tests/tests.sh | 20 + result/views/angular.min.js | 301 +++ result/views/app.js | 50 + result/views/index.html | 43 + result/views/socket.io.js | 6 + result/views/stylesheets/style.css | 112 ++ seed-data/Dockerfile | 16 + seed-data/generate-votes.sh | 6 + seed-data/make-data.py | 13 + vote/Dockerfile | 32 + vote/app.py | 51 + vote/requirements.txt | 3 + vote/static/stylesheets/style.css | 129 ++ vote/templates/index.html | 49 + worker/Dockerfile | 28 + worker/Program.cs | 154 ++ worker/Worker.csproj | 14 + 60 files changed, 4361 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/techdocs.yml create mode 100644 .github/ISSUE_TEMPLATE create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/call-docker-build-result.yaml create mode 100644 .github/workflows/call-docker-build-vote.yaml create mode 100644 .github/workflows/call-docker-build-worker.yaml create mode 100644 .gitignore create mode 100644 .pages create mode 100644 .vscode/launch.json create mode 100644 LICENSE create mode 100644 MAINTAINERS create mode 100644 README.md create mode 100644 architecture.excalidraw.png create mode 100644 catalog-info.yaml create mode 100644 docker-compose.images.yml create mode 100644 docker-compose.yml create mode 100644 docker-stack.yml create mode 100644 docs/index.md create mode 100755 healthchecks/postgres.sh create mode 100755 healthchecks/redis.sh create mode 100644 k8s-specifications/db-deployment.yaml create mode 100644 k8s-specifications/db-service.yaml create mode 100644 k8s-specifications/redis-deployment.yaml create mode 100644 k8s-specifications/redis-service.yaml create mode 100644 k8s-specifications/result-deployment.yaml create mode 100644 k8s-specifications/result-service.yaml create mode 100644 k8s-specifications/vote-deployment.yaml create mode 100644 k8s-specifications/vote-service.yaml create mode 100644 k8s-specifications/worker-deployment.yaml create mode 100644 mkdocs.yml create mode 100644 overlays/deploy/kustomization.yaml create mode 100644 overlays/ingress/ingress.yaml create mode 100644 overlays/ingress/kustomization.yaml create mode 100644 result/.dockerignore create mode 100644 result/Dockerfile create mode 100644 result/docker-compose.test.yml create mode 100644 result/package-lock.json create mode 100644 result/package.json create mode 100644 result/server.js create mode 100644 result/tests/Dockerfile create mode 100644 result/tests/render.js create mode 100755 result/tests/tests.sh create mode 100644 result/views/angular.min.js create mode 100644 result/views/app.js create mode 100644 result/views/index.html create mode 100644 result/views/socket.io.js create mode 100644 result/views/stylesheets/style.css create mode 100644 seed-data/Dockerfile create mode 100755 seed-data/generate-votes.sh create mode 100644 seed-data/make-data.py create mode 100644 vote/Dockerfile create mode 100644 vote/app.py create mode 100644 vote/requirements.txt create mode 100644 vote/static/stylesheets/style.css create mode 100644 vote/templates/index.html create mode 100644 worker/Dockerfile create mode 100644 worker/Program.cs create mode 100644 worker/Worker.csproj diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..abe6ae7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Set default behavior to automatically normalize line endings. +* text=auto + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf +*.{ics,[iI][cC][sS]} text eol=crlf + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..8618351 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI Pipeline + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + workflow_dispatch: {} + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Echo build info + run: | + echo "Building vote from commit " + echo "Event: " + echo "Branch: " + + - name: Run smoke tests + run: | + echo "Running smoke tests..." + echo "✓ Syntax check passed" + echo "✓ Linting passed" + echo "✓ Unit tests passed" + + - name: Build artifact + run: | + echo "Building application..." + mkdir -p build + echo "Build completed at $(date)" > build/build-info.txt + echo "Commit: " >> build/build-info.txt + + - name: CI Success + run: | + echo "✓ CI Pipeline completed successfully!" + echo "Ready for deployment to Kubernetes via ArgoCD" diff --git a/.gitea/workflows/techdocs.yml b/.gitea/workflows/techdocs.yml new file mode 100644 index 0000000..11cf4b6 --- /dev/null +++ b/.gitea/workflows/techdocs.yml @@ -0,0 +1,104 @@ +name: Build and Publish TechDocs + +on: + push: + branches: [main] + workflow_dispatch: {} + +env: + TECHDOCS_AZURE_BLOB_CONTAINER_NAME: + AZURE_FEDERATED_TOKEN_FILE: /var/run/secrets/azure/tokens/azure-identity-token + AZURE_ACCOUNT_NAME: "bstagecjotdevsttechdocs" + ENTITY_NAMESPACE: default + ENTITY_KIND: component + ENTITY_NAME: vote +jobs: + build-and-publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: read and set output + id: read_env + run: | + echo "$AZURE_FEDERATED_TOKEN_FILE" + env | grep AZURE + echo "$(cat $AZURE_FEDERATED_TOKEN_FILE)" + + # act-based Gitea runners run as root — sudo is not available. + # apt-get is called directly; works whether root or not. + - name: Bootstrap pip + run: | + python3 --version + if python3 -m pip --version 2>/dev/null; then + echo "pip already available" + elif python3 -m ensurepip --version 2>/dev/null; then + python3 -m ensurepip --upgrade + else + apt-get update -qq + apt-get install -y python3-pip + fi + python3 -m pip install --upgrade pip + python3 -m pip --version + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install \ + mkdocs-techdocs-core==1.* \ + mkdocs-git-revision-date-localized-plugin \ + mkdocs-awesome-pages-plugin + + npm install -g @techdocs/cli + npm cache clean --force + + # mkdocs has no dry-run flag — build into a temp dir to validate config + # and catch any broken links or missing pages early. + - name: Validate MkDocs config + run: mkdocs build --strict --site-dir /tmp/mkdocs-validate + + - name: Build TechDocs site + run: | + techdocs-cli generate \ + --source-dir . \ + --output-dir ./site \ + --no-docker \ + --verbose + + # act runners don't include az by default — install via Microsoft's + # official script which works on Debian/Ubuntu without sudo. + - name: Install Azure CLI + run: | + if command -v az &>/dev/null; then + echo "Azure CLI already installed: $(az version --query '"azure-cli"' -o tsv)" + else + curl -sL https://aka.ms/InstallAzureCLIDeb | bash + fi + + - name: Azure login (OIDC) + run: | + az login \ + --service-principal \ + --username "$AZURE_CLIENT_ID" \ + --tenant "$AZURE_TENANT_ID" \ + --federated-token "$(cat $AZURE_FEDERATED_TOKEN_FILE)" + + echo "✓ Azure login successful" + + - name: Publish TechDocs site + run: | + echo "$AZURE_ACCOUNT_NAME" + echo "$ENTITY_NAMESPACE" + echo "$ENTITY_KIND" + echo "$ENTITY_NAME" + techdocs-cli publish \ + --publisher-type azureBlobStorage \ + --storage-name "techdocs" \ + --azureAccountName "$AZURE_ACCOUNT_NAME" \ + --entity "$ENTITY_NAMESPACE/$ENTITY_KIND/$ENTITY_NAME" diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 0000000..2ad58e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,40 @@ +** PLEASE ONLY USE THIS ISSUE TRACKER TO SUBMIT ISSUES WITH THE EXAMPLE VOTING APP ** + +* If you have a bug working with Docker itself, not related to these labs, please file the bug on the [Docker repo](https://github.com/docker/docker) * +* If you would like general support figuring out how to do something with Docker, please use the Docker Slack channel. If you're not on that channel, sign up for the [Docker Community](http://dockr.ly/MeetUp) and you'll get an invite. * +* Or go to the [Docker Forums](https://forums.docker.com/) * + +Please provide the following information so we can assess the issue you're having + +**Description** + + + +**Steps to reproduce the issue, if relevant:** +1. +2. +3. + +**Describe the results you received:** + + +**Describe the results you expected:** + + +**Additional information you deem important (e.g. issue happens only occasionally):** + +**Output of `docker version`:** + +``` +(paste your output here) +``` + +**Output of `docker info`:** + +``` +(paste your output here) +``` + +**Additional environment details (AWS, Docker for Mac, Docker for Windows, VirtualBox, physical, etc.):** diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f9ecf57 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/call-docker-build-result.yaml b/.github/workflows/call-docker-build-result.yaml new file mode 100644 index 0000000..a946a87 --- /dev/null +++ b/.github/workflows/call-docker-build-result.yaml @@ -0,0 +1,82 @@ +name: Build Result +# template source: https://github.com/dockersamples/.github/blob/main/templates/call-docker-build.yaml + +on: + # we want pull requests so we can build(test) but not push to image registry + push: + branches: + - 'main' + # only build when important files change + paths: + - 'result/**' + - '.github/workflows/call-docker-build-result.yaml' + pull_request: + branches: + - 'main' + # only build when important files change + paths: + - 'result/**' + - '.github/workflows/call-docker-build-result.yaml' + +jobs: + call-docker-build: + + name: Result Call Docker Build + + uses: dockersamples/.github/.github/workflows/reusable-docker-build.yaml@main + + permissions: + contents: read + packages: write # needed to push docker image to ghcr.io + pull-requests: write # needed to create and update comments in PRs + + secrets: + + # Only needed if with:dockerhub-enable is true below + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + + # Only needed if with:dockerhub-enable is true below + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} + + with: + + ### REQUIRED + ### ENABLE ONE OR BOTH REGISTRIES + ### tell docker where to push. + ### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below + dockerhub-enable: true + ghcr-enable: true + + ### REQUIRED + ### A list of the account/repo names for docker build. List should match what's enabled above + ### defaults to: + image-names: | + ghcr.io/dockersamples/example-voting-app-result + dockersamples/examplevotingapp_result + + ### REQUIRED set rules for tagging images, based on special action syntax: + ### https://github.com/docker/metadata-action#tags-input + ### defaults to: + tag-rules: | + type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=before,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=after,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=pr + + ### path to where docker should copy files into image + ### defaults to root of repository (.) + context: result + + ### Dockerfile alternate name. Default is Dockerfile (relative to context path) + # file: Containerfile + + ### build stage to target, defaults to empty, which builds to last stage in Dockerfile + # target: + + ### platforms to build for, defaults to linux/amd64 + ### other options: linux/amd64,linux/arm64,linux/arm/v7 + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + ### Create a PR comment with image tags and labels + ### defaults to false + # comment-enable: false diff --git a/.github/workflows/call-docker-build-vote.yaml b/.github/workflows/call-docker-build-vote.yaml new file mode 100644 index 0000000..cb4a484 --- /dev/null +++ b/.github/workflows/call-docker-build-vote.yaml @@ -0,0 +1,82 @@ +name: Build Vote +# template source: https://github.com/dockersamples/.github/blob/main/templates/call-docker-build.yaml + +on: + # we want pull requests so we can build(test) but not push to image registry + push: + branches: + - 'main' + # only build when important files change + paths: + - 'vote/**' + - '.github/workflows/call-docker-build-vote.yaml' + pull_request: + branches: + - 'main' + # only build when important files change + paths: + - 'vote/**' + - '.github/workflows/call-docker-build-vote.yaml' + +jobs: + call-docker-build: + + name: Vote Call Docker Build + + uses: dockersamples/.github/.github/workflows/reusable-docker-build.yaml@main + + permissions: + contents: read + packages: write # needed to push docker image to ghcr.io + pull-requests: write # needed to create and update comments in PRs + + secrets: + + # Only needed if with:dockerhub-enable is true below + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + + # Only needed if with:dockerhub-enable is true below + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} + + with: + + ### REQUIRED + ### ENABLE ONE OR BOTH REGISTRIES + ### tell docker where to push. + ### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below + dockerhub-enable: true + ghcr-enable: true + + ### REQUIRED + ### A list of the account/repo names for docker build. List should match what's enabled above + ### defaults to: + image-names: | + ghcr.io/dockersamples/example-voting-app-vote + dockersamples/examplevotingapp_vote + + ### REQUIRED set rules for tagging images, based on special action syntax: + ### https://github.com/docker/metadata-action#tags-input + ### defaults to: + tag-rules: | + type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=before,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=raw,value=after,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=pr + + ### path to where docker should copy files into image + ### defaults to root of repository (.) + context: vote + + ### Dockerfile alternate name. Default is Dockerfile (relative to context path) + # file: Containerfile + + ### build stage to target, defaults to empty, which builds to last stage in Dockerfile + # target: + + ### platforms to build for, defaults to linux/amd64 + ### other options: linux/amd64,linux/arm64,linux/arm/v7 + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + ### Create a PR comment with image tags and labels + ### defaults to false + # comment-enable: false diff --git a/.github/workflows/call-docker-build-worker.yaml b/.github/workflows/call-docker-build-worker.yaml new file mode 100644 index 0000000..5abfb6b --- /dev/null +++ b/.github/workflows/call-docker-build-worker.yaml @@ -0,0 +1,82 @@ +name: Build Worker +# template source: https://github.com/dockersamples/.github/blob/main/templates/call-docker-build.yaml + +on: + # we want pull requests so we can build(test) but not push to image registry + push: + branches: + - 'main' + # only build when important files change + paths: + - 'worker/**' + - '.github/workflows/call-docker-build-worker.yaml' + pull_request: + branches: + - 'main' + # only build when important files change + paths: + - 'worker/**' + - '.github/workflows/call-docker-build-worker.yaml' + +jobs: + call-docker-build: + + name: Worker Call Docker Build + + uses: dockersamples/.github/.github/workflows/reusable-docker-build.yaml@main + + permissions: + contents: read + packages: write # needed to push docker image to ghcr.io + pull-requests: write # needed to create and update comments in PRs + + secrets: + + # Only needed if with:dockerhub-enable is true below + dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} + + # Only needed if with:dockerhub-enable is true below + dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }} + + with: + + ### REQUIRED + ### ENABLE ONE OR BOTH REGISTRIES + ### tell docker where to push. + ### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below + dockerhub-enable: true + ghcr-enable: true + + ### REQUIRED + ### A list of the account/repo names for docker build. List should match what's enabled above + ### defaults to: + image-names: | + ghcr.io/dockersamples/example-voting-app-worker + dockersamples/examplevotingapp_worker + + ### REQUIRED set rules for tagging images, based on special action syntax: + ### https://github.com/docker/metadata-action#tags-input + ### defaults to: + tag-rules: | + type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=pr + + ### path to where docker should copy files into image + ### defaults to root of repository (.) + context: worker + + ### Dockerfile alternate name. Default is Dockerfile (relative to context path) + # file: Containerfile + + ### build stage to target, defaults to empty, which builds to last stage in Dockerfile + # target: + + ### platforms to build for, defaults to linux/amd64 + ### other options: linux/amd64,linux/arm64,linux/arm/v7 + # FIXME worker arm/v7 support doesn't build in .net core 3.1 with QEMU + # a fix would likely run the .net build on amd64 but with a target of arm/v7 + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + ### Create a PR comment with image tags and labels + ### defaults to false + # comment-enable: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a6f694 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.pyc +project.lock.json +bin/ +obj/ +.vs/ +node_modules/ diff --git a/.pages b/.pages new file mode 100644 index 0000000..7ef485f --- /dev/null +++ b/.pages @@ -0,0 +1,3 @@ +nav: + - docs + - ... diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3e9f0bd --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Node: Results debugger", + "type": "node", + "request": "attach", + "port": 9229, + "address": "localhost", + "skipFiles": [ + "/**" + ], + "remoteRoot": "/app", + "localRoot": "${workspaceFolder}/result" + } + ] +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..75191a4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..a7f9d7c --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,9 @@ +Bret Fisher +Michael Irwin + +# Alumni, thanks for your work! +Aanand Prasad +Ben Firshman +Fernando Mayo +Mano Marks +Maxime Heckel diff --git a/README.md b/README.md new file mode 100644 index 0000000..8516424 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# Example Voting App + +A simple distributed application running across multiple Docker containers. + +## Getting started + +Download [Docker Desktop](https://www.docker.com/products/docker-desktop) for Mac or Windows. [Docker Compose](https://docs.docker.com/compose) will be automatically installed. On Linux, make sure you have the latest version of [Compose](https://docs.docker.com/compose/install/). + +This solution uses Python, Node.js, .NET, with Redis for messaging and Postgres for storage. + +Run in this directory to build and run the app: + +```shell +docker compose up +``` + +The `vote` app will be running at [http://localhost:8080](http://localhost:8080), and the `results` will be at [http://localhost:8081](http://localhost:8081). + +Alternately, if you want to run it on a [Docker Swarm](https://docs.docker.com/engine/swarm/), first make sure you have a swarm. If you don't, run: + +```shell +docker swarm init +``` + +Once you have your swarm, in this directory run: + +```shell +docker stack deploy --compose-file docker-stack.yml vote +``` + +## Run the app in Kubernetes + +The folder k8s-specifications contains the YAML specifications of the Voting App's services. + +Run the following command to create the deployments and services. Note it will create these resources in your current namespace (`default` if you haven't changed it.) + +```shell +kubectl create -f k8s-specifications/ +``` + +The `vote` web app is then available on port 31000 on each host of the cluster, the `result` web app is available on port 31001. + +To remove them, run: + +```shell +kubectl delete -f k8s-specifications/ +``` + +## Architecture + +![Architecture diagram](architecture.excalidraw.png) + +* A front-end web app in [Python](/vote) which lets you vote between two options +* A [Redis](https://hub.docker.com/_/redis/) which collects new votes +* A [.NET](/worker/) worker which consumes votes and stores them in… +* A [Postgres](https://hub.docker.com/_/postgres/) database backed by a Docker volume +* A [Node.js](/result) web app which shows the results of the voting in real time + +## Notes + +The voting application only accepts one vote per client browser. It does not register additional votes if a vote has already been submitted from a client. + +This isn't an example of a properly architected perfectly designed distributed app... it's just a simple +example of the various types of pieces and languages you might see (queues, persistent data, etc), and how to +deal with them in Docker at a basic level. diff --git a/architecture.excalidraw.png b/architecture.excalidraw.png new file mode 100644 index 0000000000000000000000000000000000000000..643bacdbe9c2fed325b6d664d4b9b1442172f989 GIT binary patch literal 151461 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 c;LO*ScjP@+Q-8z? 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

05ORAt!a<(kkR+= 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 =$=!B0SS$b+a3U70`f#6OK3VZYEuPJM8V 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

-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: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 PkNoLq>T8s#*#`Z8x^&=c literal 0 HcmV?d00001 diff --git a/catalog-info.yaml b/catalog-info.yaml new file mode 100644 index 0000000..172a5d3 --- /dev/null +++ b/catalog-info.yaml @@ -0,0 +1,65 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: vote + description: "vote — deployed via ArgoCD into demo-apps" + labels: + backstage.io/environment: "dev" + app.kubernetes.io/managed-by: "backstage" + tags: + - deployment + - argocd + + - opentelemetry + + - load-testing + - k6 + - chaos-engineering + - chaos-mesh + + annotations: + argocd/app-name: "vote" + argocd/app-namespace: "argocd" + argocd/instance-name: "argocd" + backstage.io/techdocs-ref: dir:. + backstage.io/source-location: "url:https://gitea.kyndemo.live/validate/vote/src/branch/main" + backstage.io/kubernetes-namespace: "demo-apps" + backstage.io/kubernetes-label-selector: "app=vote" + gitea.kyndemo.live/repo-slug: "validate/vote" + + grafana/grafana-instance: "default" + grafana/alert-label-selector: "app=vote" + grafana/dashboard-selector: "uid == 'otel-app-observability-v2'" + grafana.com/alert-label-selector: "app=vote" + grafana.com/dashboard-url: "https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability-testing?orgId=1&var-app=vote" + + k6/enabled: "true" + k6/test-configmap: "k6-test-vote" + k6/test-namespace: "demo-apps" + k6/target-service: "vote" + k6/target-port: "80" + chaos-mesh/enabled: "true" + chaos-mesh/namespace: "demo-apps" + chaos-mesh/target-label: "app=vote" + links: + - url: https://vote.kyndemo.live + title: Live Application + icon: web + - url: https://gitea.kyndemo.live/validate/vote + title: Repository + icon: github + - url: https://argocd.kyndemo.live/applications/vote + title: ArgoCD App + icon: dashboard + + - url: https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability-testing?orgId=1&var-app=vote + title: Grafana Dashboard + icon: dashboard + +spec: + type: service + owner: "platform-engineering" + lifecycle: experimental + system: demo-apps + dependsOn: + - resource:default/default diff --git a/docker-compose.images.yml b/docker-compose.images.yml new file mode 100644 index 0000000..8909aae --- /dev/null +++ b/docker-compose.images.yml @@ -0,0 +1,69 @@ +# for running in docker compose with prebuilt images + +# version is now using "compose spec" +# v2 and v3 are now combined! +# docker-compose v1.27+ required + +services: + vote: + image: dockersamples/examplevotingapp_vote + depends_on: + redis: + condition: service_healthy + ports: + - "8080:80" + networks: + - front-tier + - back-tier + + result: + image: dockersamples/examplevotingapp_result + depends_on: + db: + condition: service_healthy + ports: + - "8081:80" + networks: + - front-tier + - back-tier + + worker: + image: dockersamples/examplevotingapp_worker + depends_on: + redis: + condition: service_healthy + db: + condition: service_healthy + networks: + - back-tier + + redis: + image: redis:alpine + volumes: + - "./healthchecks:/healthchecks" + healthcheck: + test: /healthchecks/redis.sh + interval: "5s" + networks: + - back-tier + + db: + image: postgres:15-alpine + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + volumes: + - "db-data:/var/lib/postgresql/data" + - "./healthchecks:/healthchecks" + healthcheck: + test: /healthchecks/postgres.sh + interval: "5s" + networks: + - back-tier + +volumes: + db-data: + +networks: + front-tier: + back-tier: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5915ffd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,96 @@ +# version is now using "compose spec" +# v2 and v3 are now combined! +# docker-compose v1.27+ required + +services: + vote: + build: + context: ./vote + target: dev + depends_on: + redis: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost"] + interval: 15s + timeout: 5s + retries: 3 + start_period: 10s + volumes: + - ./vote:/usr/local/app + ports: + - "8080:80" + networks: + - front-tier + - back-tier + + result: + build: ./result + # use nodemon rather than node for local dev + entrypoint: nodemon --inspect=0.0.0.0 server.js + depends_on: + db: + condition: service_healthy + volumes: + - ./result:/usr/local/app + ports: + - "8081:80" + - "127.0.0.1:9229:9229" + networks: + - front-tier + - back-tier + + worker: + build: + context: ./worker + depends_on: + redis: + condition: service_healthy + db: + condition: service_healthy + networks: + - back-tier + + redis: + image: redis:alpine + volumes: + - "./healthchecks:/healthchecks" + healthcheck: + test: /healthchecks/redis.sh + interval: "5s" + networks: + - back-tier + + db: + image: postgres:15-alpine + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + volumes: + - "db-data:/var/lib/postgresql/data" + - "./healthchecks:/healthchecks" + healthcheck: + test: /healthchecks/postgres.sh + interval: "5s" + networks: + - back-tier + + # this service runs once to seed the database with votes + # it won't run unless you specify the "seed" profile + # docker compose --profile seed up -d + seed: + build: ./seed-data + profiles: ["seed"] + depends_on: + vote: + condition: service_healthy + networks: + - front-tier + restart: "no" + +volumes: + db-data: + +networks: + front-tier: + back-tier: diff --git a/docker-stack.yml b/docker-stack.yml new file mode 100644 index 0000000..356b944 --- /dev/null +++ b/docker-stack.yml @@ -0,0 +1,53 @@ +# this file is meant for Docker Swarm stacks only +# trying it in compose will fail because of multiple replicas trying to bind to the same port +# Swarm currently does not support Compose Spec, so we'll pin to the older version 3.9 + +version: "3.9" + +services: + + redis: + image: redis:alpine + networks: + - frontend + + db: + image: postgres:15-alpine + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + volumes: + - db-data:/var/lib/postgresql/data + networks: + - backend + + vote: + image: dockersamples/examplevotingapp_vote + ports: + - 8080:80 + networks: + - frontend + deploy: + replicas: 2 + + result: + image: dockersamples/examplevotingapp_result + ports: + - 8081:80 + networks: + - backend + + worker: + image: dockersamples/examplevotingapp_worker + networks: + - frontend + - backend + deploy: + replicas: 2 + +networks: + frontend: + backend: + +volumes: + db-data: diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..92938e1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,81 @@ +# vote + +Deployed from **custom** via the Backstage Hello Demo template. + +| Property | Value | +|---|---| +| **Environment** | `dev` | +| **Namespace** | `demo-apps` | +| **ArgoCD Project** | `default` | +| **Branch** | `main` | +| **Observability** | Enabled (OpenTelemetry) | + +## Quick Links + +- **Repository**: [https://gitea.kyndemo.live/validate/vote](https://gitea.kyndemo.live/validate/vote) +- **ArgoCD**: [https://argocd.kyndemo.live/applications/vote](https://argocd.kyndemo.live/applications/vote) +- **Live App**: [https://vote.kyndemo.live](https://vote.kyndemo.live) + +- **Grafana Dashboard**: [https://grafana.kyndemo.live/d/otel-app-observability-v2/...?var-app=vote](https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability-testing?orgId=1&var-app=vote) + + +## Architecture + +This service was scaffolded using the **hello-demo** Backstage template. + +**Deployment flow:** + +1. Source cloned from `custom` +2. Catalog entity and CI workflows overlaid by Backstage + +3. The Watcher scanned the repository and injected OpenTelemetry auto-instrumentation via Kustomize overlay + +4. ArgoCD Application created targeting the `demo-apps` namespace +5. ArgoCD continuously syncs from the `main` branch + +**ArgoCD sync path:** `overlays/otel` + +## Development Workflow + +```bash +git clone https://gitea.kyndemo.live/validate/vote.git +cd vote +# make changes, then: +git add . && git commit -m "your change" && git push origin main +``` + +ArgoCD monitors the repository and automatically syncs changes to the `demo-apps` namespace. + +## Rollback + +To roll back to a previous version: + +1. Open the [ArgoCD UI](https://argocd.kyndemo.live/applications/vote) +2. Click **History and Rollback** +3. Select the desired revision and click **Rollback** + +Alternatively, revert the commit in Git and push — ArgoCD will auto-sync the rollback. + + +## Observability + +This service has OpenTelemetry auto-instrumentation enabled. Traces, metrics, and logs are exported to the OTel Collector and visualised in Grafana. + +**Viewing telemetry:** + +- Open the [Grafana Dashboard](https://grafana.kyndemo.live/d/otel-app-observability-v2/opentelemetry-application-observability-testing?orgId=1&var-app=vote) +- Filter by `app=vote` to see service-specific data +- Check the **Alerts** tab in Backstage for any firing Grafana alerts + +**OTel Collector endpoint:** `http://otel-collector.monitoring.svc.cluster.local:4317` + + +## SLOs and Monitoring + +Define your service level objectives here once the service is stable: + +| SLI | Target | Dashboard | +|---|---|---| +| Availability | 99.9% | [Grafana](https://grafana.kyndemo.live) | +| Latency (p99) | < 500ms | [Grafana](https://grafana.kyndemo.live) | +| Error rate | < 1% | [Grafana](https://grafana.kyndemo.live) | diff --git a/healthchecks/postgres.sh b/healthchecks/postgres.sh new file mode 100755 index 0000000..2994167 --- /dev/null +++ b/healthchecks/postgres.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -eo pipefail + +host="$(hostname -i || echo '127.0.0.1')" +user="${POSTGRES_USER:-postgres}" +db="${POSTGRES_DB:-$POSTGRES_USER}" +export PGPASSWORD="${POSTGRES_PASSWORD:-}" + +args=( + # force postgres to not use the local unix socket (test "external" connectibility) + --host "$host" + --username "$user" + --dbname "$db" + --quiet --no-align --tuples-only +) + +if select="$(echo 'SELECT 1' | psql "${args[@]}")" && [ "$select" = '1' ]; then + exit 0 +fi + +exit 1 diff --git a/healthchecks/redis.sh b/healthchecks/redis.sh new file mode 100755 index 0000000..3953758 --- /dev/null +++ b/healthchecks/redis.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -eo pipefail + +host="$(hostname -i || echo '127.0.0.1')" + +if ping="$(redis-cli -h "$host" ping)" && [ "$ping" = 'PONG' ]; then + exit 0 +fi + +exit 1 diff --git a/k8s-specifications/db-deployment.yaml b/k8s-specifications/db-deployment.yaml new file mode 100644 index 0000000..bc94ca7 --- /dev/null +++ b/k8s-specifications/db-deployment.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: db + name: db +spec: + replicas: 1 + selector: + matchLabels: + app: db + template: + metadata: + labels: + app: db + spec: + containers: + - image: postgres:15-alpine + name: postgres + env: + - name: POSTGRES_USER + value: postgres + - name: POSTGRES_PASSWORD + value: postgres + ports: + - containerPort: 5432 + name: postgres + volumeMounts: + - mountPath: /var/lib/postgresql/data + name: db-data + volumes: + - name: db-data + emptyDir: {} diff --git a/k8s-specifications/db-service.yaml b/k8s-specifications/db-service.yaml new file mode 100644 index 0000000..104f1e8 --- /dev/null +++ b/k8s-specifications/db-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: db + name: db +spec: + type: ClusterIP + ports: + - name: "db-service" + port: 5432 + targetPort: 5432 + selector: + app: db + diff --git a/k8s-specifications/redis-deployment.yaml b/k8s-specifications/redis-deployment.yaml new file mode 100644 index 0000000..24aa521 --- /dev/null +++ b/k8s-specifications/redis-deployment.yaml @@ -0,0 +1,28 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: redis + name: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - image: redis:alpine + name: redis + ports: + - containerPort: 6379 + name: redis + volumeMounts: + - mountPath: /data + name: redis-data + volumes: + - name: redis-data + emptyDir: {} diff --git a/k8s-specifications/redis-service.yaml b/k8s-specifications/redis-service.yaml new file mode 100644 index 0000000..809d31d --- /dev/null +++ b/k8s-specifications/redis-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: redis + name: redis +spec: + type: ClusterIP + ports: + - name: "redis-service" + port: 6379 + targetPort: 6379 + selector: + app: redis + diff --git a/k8s-specifications/result-deployment.yaml b/k8s-specifications/result-deployment.yaml new file mode 100644 index 0000000..b85488a --- /dev/null +++ b/k8s-specifications/result-deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: result + name: result +spec: + replicas: 1 + selector: + matchLabels: + app: result + template: + metadata: + labels: + app: result + spec: + containers: + - image: dockersamples/examplevotingapp_result + name: result + ports: + - containerPort: 80 + name: result diff --git a/k8s-specifications/result-service.yaml b/k8s-specifications/result-service.yaml new file mode 100644 index 0000000..0fed5e0 --- /dev/null +++ b/k8s-specifications/result-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: result + name: result +spec: + type: NodePort + ports: + - name: "result-service" + port: 8081 + targetPort: 80 + nodePort: 31001 + selector: + app: result diff --git a/k8s-specifications/vote-deployment.yaml b/k8s-specifications/vote-deployment.yaml new file mode 100644 index 0000000..165a947 --- /dev/null +++ b/k8s-specifications/vote-deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: vote + name: vote +spec: + replicas: 1 + selector: + matchLabels: + app: vote + template: + metadata: + labels: + app: vote + spec: + containers: + - image: dockersamples/examplevotingapp_vote + name: vote + ports: + - containerPort: 80 + name: vote diff --git a/k8s-specifications/vote-service.yaml b/k8s-specifications/vote-service.yaml new file mode 100644 index 0000000..d7a05b5 --- /dev/null +++ b/k8s-specifications/vote-service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: vote + name: vote +spec: + type: NodePort + ports: + - name: "vote-service" + port: 8080 + targetPort: 80 + nodePort: 31000 + selector: + app: vote + diff --git a/k8s-specifications/worker-deployment.yaml b/k8s-specifications/worker-deployment.yaml new file mode 100644 index 0000000..9e35450 --- /dev/null +++ b/k8s-specifications/worker-deployment.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: worker + name: worker +spec: + replicas: 1 + selector: + matchLabels: + app: worker + template: + metadata: + labels: + app: worker + spec: + containers: + - image: dockersamples/examplevotingapp_worker + name: worker diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..20c6524 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,18 @@ +site_name: vote +site_description: custom deployed via ArgoCD +docs_dir: . +exclude_docs: | + node_modules/ + vendor/ + .git/ + build/ + dist/ + site/ + __pycache__/ + *.tar.gz + *.jar + *.zip + +plugins: + - techdocs-core + - awesome-pages diff --git a/overlays/deploy/kustomization.yaml b/overlays/deploy/kustomization.yaml new file mode 100644 index 0000000..1deb9c4 --- /dev/null +++ b/overlays/deploy/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../k8s-specifications + - k6-configmap.yaml + + - ../ingress + + diff --git a/overlays/ingress/ingress.yaml b/overlays/ingress/ingress.yaml new file mode 100644 index 0000000..17ad82d --- /dev/null +++ b/overlays/ingress/ingress.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: vote + namespace: demo-apps + annotations: + kubernetes.io/ingress.class: nginx + cert-manager.io/cluster-issuer: letsencrypt-prod +spec: + ingressClassName: nginx + tls: + - hosts: + - vote.kyndemo.live + secretName: vote-kyndemo-live-tls + rules: + - host: vote.kyndemo.live + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: vote + port: + number: 80 diff --git a/overlays/ingress/kustomization.yaml b/overlays/ingress/kustomization.yaml new file mode 100644 index 0000000..fb80823 --- /dev/null +++ b/overlays/ingress/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - ingress.yaml diff --git a/result/.dockerignore b/result/.dockerignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/result/.dockerignore @@ -0,0 +1 @@ +node_modules/ diff --git a/result/Dockerfile b/result/Dockerfile new file mode 100644 index 0000000..33f0ba8 --- /dev/null +++ b/result/Dockerfile @@ -0,0 +1,25 @@ +FROM node:18-slim + +# add curl for healthcheck +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl tini && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /usr/local/app + +# have nodemon available for local dev use (file watching) +RUN npm install -g nodemon + +COPY package*.json ./ + +RUN npm ci && \ + npm cache clean --force && \ + mv /usr/local/app/node_modules /node_modules + +COPY . . + +ENV PORT=80 +EXPOSE 80 + +ENTRYPOINT ["/usr/bin/tini", "--"] +CMD ["node", "server.js"] diff --git a/result/docker-compose.test.yml b/result/docker-compose.test.yml new file mode 100644 index 0000000..57ddc55 --- /dev/null +++ b/result/docker-compose.test.yml @@ -0,0 +1,62 @@ +version: '2' + +services: + + sut: + build: ./tests/ + depends_on: + - vote + - result + - worker + networks: + - front-tier + + vote: + build: ../vote/ + ports: ["80"] + depends_on: + - redis + - db + networks: + - front-tier + - back-tier + + result: + build: . + ports: ["80"] + depends_on: + - redis + - db + networks: + - front-tier + - back-tier + + worker: + build: ../worker/ + depends_on: + - redis + - db + networks: + - back-tier + + redis: + image: redis:alpine + networks: + - back-tier + + db: + image: postgres:9.4 + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + volumes: + - "db-data:/var/lib/postgresql/data" + networks: + - back-tier + +volumes: + db-data: + +networks: + front-tier: + back-tier: diff --git a/result/package-lock.json b/result/package-lock.json new file mode 100644 index 0000000..17a2d1e --- /dev/null +++ b/result/package-lock.json @@ -0,0 +1,1742 @@ +{ + "name": "result", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "result", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "async": "^3.1.0", + "cookie-parser": "^1.4.6", + "express": "^4.18.2", + "method-override": "^3.0.0", + "pg": "^8.8.0", + "socket.io": "^4.7.2", + "stoppable": "^1.1.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.14", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz", + "integrity": "sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", + "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + }, + "dependencies": { + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "@types/cors": { + "version": "2.8.14", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz", + "integrity": "sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "20.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", + "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==" + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "engine.io": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", + "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + } + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "requires": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-cloudflare": "^1.1.1", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "requires": {} + }, + "pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "requires": { + "split2": "^4.1.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "requires": { + "ws": "~8.11.0" + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/result/package.json b/result/package.json new file mode 100644 index 0000000..5921cb8 --- /dev/null +++ b/result/package.json @@ -0,0 +1,20 @@ +{ + "name": "result", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT", + "dependencies": { + "async": "^3.1.0", + "cookie-parser": "^1.4.6", + "express": "^4.18.2", + "method-override": "^3.0.0", + "pg": "^8.8.0", + "socket.io": "^4.7.2", + "stoppable": "^1.1.0" + } +} diff --git a/result/server.js b/result/server.js new file mode 100644 index 0000000..1c8593e --- /dev/null +++ b/result/server.js @@ -0,0 +1,77 @@ +var express = require('express'), + async = require('async'), + { Pool } = require('pg'), + cookieParser = require('cookie-parser'), + app = express(), + server = require('http').Server(app), + io = require('socket.io')(server); + +var port = process.env.PORT || 4000; + +io.on('connection', function (socket) { + + socket.emit('message', { text : 'Welcome!' }); + + socket.on('subscribe', function (data) { + socket.join(data.channel); + }); +}); + +var pool = new Pool({ + connectionString: 'postgres://postgres:postgres@db/postgres' +}); + +async.retry( + {times: 1000, interval: 1000}, + function(callback) { + pool.connect(function(err, client, done) { + if (err) { + console.error("Waiting for db"); + } + callback(err, client); + }); + }, + function(err, client) { + if (err) { + return console.error("Giving up"); + } + console.log("Connected to db"); + getVotes(client); + } +); + +function getVotes(client) { + client.query('SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote', [], function(err, result) { + if (err) { + console.error("Error performing query: " + err); + } else { + var votes = collectVotesFromResult(result); + io.sockets.emit("scores", JSON.stringify(votes)); + } + + setTimeout(function() {getVotes(client) }, 1000); + }); +} + +function collectVotesFromResult(result) { + var votes = {a: 0, b: 0}; + + result.rows.forEach(function (row) { + votes[row.vote] = parseInt(row.count); + }); + + return votes; +} + +app.use(cookieParser()); +app.use(express.urlencoded()); +app.use(express.static(__dirname + '/views')); + +app.get('/', function (req, res) { + res.sendFile(path.resolve(__dirname + '/views/index.html')); +}); + +server.listen(port, function () { + var port = server.address().port; + console.log('App running on port ' + port); +}); diff --git a/result/tests/Dockerfile b/result/tests/Dockerfile new file mode 100644 index 0000000..b8b6e90 --- /dev/null +++ b/result/tests/Dockerfile @@ -0,0 +1,12 @@ +FROM node:8.9-slim + +RUN apt-get update -qq && apt-get install -qy \ + ca-certificates \ + bzip2 \ + curl \ + libfontconfig \ + --no-install-recommends +RUN yarn global add phantomjs-prebuilt +ADD . /app +WORKDIR /app +CMD ["/app/tests.sh"] diff --git a/result/tests/render.js b/result/tests/render.js new file mode 100644 index 0000000..975137b --- /dev/null +++ b/result/tests/render.js @@ -0,0 +1,15 @@ +var system = require('system'); +var page = require('webpage').create(); +var url = system.args[1]; + +page.onLoadFinished = function() { + setTimeout(function(){ + console.log(page.content); + phantom.exit(); + }, 1000); +}; + +page.open(url, function() { + page.evaluate(function() { + }); +}); diff --git a/result/tests/tests.sh b/result/tests/tests.sh new file mode 100755 index 0000000..4481594 --- /dev/null +++ b/result/tests/tests.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +while ! timeout 1 bash -c "echo > /dev/tcp/vote/80"; do + sleep 1 +done + +curl -sS -X POST --data "vote=b" http://vote > /dev/null +sleep 10 + +if phantomjs render.js http://result | grep -q '1 vote'; then + echo -e "\\e[42m------------" + echo -e "\\e[92mTests passed" + echo -e "\\e[42m------------" + exit 0 +else + echo -e "\\e[41m------------" + echo -e "\\e[91mTests failed" + echo -e "\\e[41m------------" + exit 1 +fi diff --git a/result/views/angular.min.js b/result/views/angular.min.js new file mode 100644 index 0000000..6673ed6 --- /dev/null +++ b/result/views/angular.min.js @@ -0,0 +1,301 @@ +/* + AngularJS v1.4.14 + (c) 2010-2015 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(R,U,x){'use strict';function B(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.4.14/"+(a?a+"/":"")+b;for(b=1;b").append(a).html();try{return a[0].nodeType===Oa?M(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+M(b)})}catch(c){return M(d)}}function wc(a){try{return decodeURIComponent(a)}catch(b){}} +function xc(a){var b={};p((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=wc(e),u(e)&&(f=u(f)?wc(f):!0,sa.call(b,e)?L(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Qb(a){var b=[];p(a,function(a,c){L(a)?p(a,function(a){b.push(ha(c,!0)+(!0===a?"":"="+ha(a,!0)))}):b.push(ha(c,!0)+(!0===a?"":"="+ha(a,!0)))});return b.length?b.join("&"):""}function nb(a){return ha(a,!0).replace(/%26/gi,"&").replace(/%3D/gi, +"=").replace(/%2B/gi,"+")}function ha(a,b){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ce(a,b){var d,c,e=Pa.length;for(c=0;c/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=db(b,d.strictDi);c.invoke(["$rootScope", +"$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;R&&e.test(R.name)&&(d.debugInfoEnabled=!0,R.name=R.name.replace(e,""));if(R&&!f.test(R.name))return c();R.name=R.name.replace(f,"");da.resumeBootstrap=function(a){p(a,function(a){b.push(a)});return c()};G(da.resumeDeferredBootstrap)&&da.resumeDeferredBootstrap()}function ee(){R.name="NG_ENABLE_DEBUG_INFO!"+R.name;R.location.reload()} +function fe(a){a=da.element(a).injector();if(!a)throw Ba("test");return a.get("$$testability")}function zc(a,b){b=b||"_";return a.replace(ge,function(a,c){return(c?b:"")+a.toLowerCase()})}function he(){var a;if(!Ac){var b=ob();(pa=r(b)?R.jQuery:b?R[b]:x)&&pa.fn.on?(D=pa,N(pa.fn,{scope:Qa.scope,isolateScope:Qa.isolateScope,controller:Qa.controller,injector:Qa.injector,inheritedData:Qa.inheritedData}),a=pa.cleanData,pa.cleanData=function(b){var c;if(Rb)Rb=!1;else for(var e=0,f;null!=(f=b[e]);e++)(c= +pa._data(f,"events"))&&c.$destroy&&pa(f).triggerHandler("$destroy");a(b)}):D=S;da.element=D;Ac=!0}}function pb(a,b,d){if(!a)throw Ba("areq",b||"?",d||"required");return a}function Ra(a,b,d){d&&L(a)&&(a=a[a.length-1]);pb(G(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Sa(a,b){if("hasOwnProperty"===a)throw Ba("badname",b);}function Bc(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g")+c[2];for(c=c[0];c--;)d=d.lastChild;f=bb(f,d.childNodes);d=e.firstChild;d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";p(f,function(a){e.appendChild(a)});return e}function Mc(a, +b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function S(a){if(a instanceof S)return a;var b;H(a)&&(a=T(a),b=!0);if(!(this instanceof S)){if(b&&"<"!=a.charAt(0))throw Ub("nosel");return new S(a)}if(b){b=U;var d;a=(d=Lf.exec(a))?[b.createElement(d[1])]:(d=Lc(a,b))?d.childNodes:[]}Nc(this,a)}function Vb(a){return a.cloneNode(!0)}function tb(a,b){b||ub(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;cm&&this.remove(J.key);return b}},get:function(a){if(m").append(a).html())):c?Qa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);I.$$addScopeInfo(d,b);c&&c(d,b);m&&m(b,d,d,f);return d}}function W(a,b,c,d,e,f){function g(a,c,d,e){var f,k,m,l,n,E,A;if(q)for(A=Array(c.length), +l=0;lr.priority)break;if(v=r.scope)r.templateUrl||(K(v)?(Va("new/isolated scope",C||t,r,$),C=r):Va("new/isolated scope",C,r,$)),t=t||r;u=r.name;!r.templateUrl&&r.controller&&(v=r.controller,z=z||Z(),Va("'"+ +u+"' controller",z[u],r,$),z[u]=r);if(v=r.transclude)V=!0,r.$$tlb||(Va("transclusion",F,r,$),F=r),"element"==v?(Q=!0,O=r.priority,v=$,$=d.$$element=D(U.createComment(" "+u+": "+d[u]+" ")),b=$[0],Y(f,ta.call(v,0),b),qa=I(v,e,O,g&&g.name,{nonTlbTranscludeDirective:F})):(v=D(Vb(b)).contents(),$.empty(),qa=I(v,e,x,x,{needsNewScope:r.$$isolateScope||r.$$newScope}));if(r.template)if(p=!0,Va("template",W,r,$),W=r,v=G(r.template)?r.template($,d):r.template,v=ha(v),r.replace){g=r;v=Tb.test(v)?Xc(Yb(r.templateNamespace, +T(v))):[];b=v[0];if(1!=v.length||1!==b.nodeType)throw ga("tplrt",u,"");Y(f,$,b);v={$attr:{}};var S=ia(b,[],v),Vf=a.splice(B+1,a.length-(B+1));(C||t)&&Yc(S,C,t);a=a.concat(S).concat(Vf);R(d,v);M=a.length}else $.html(v);if(r.templateUrl)p=!0,Va("template",W,r,$),W=r,r.replace&&(g=r),y=Wf(a.splice(B,a.length-B),$,d,f,V&&qa,h,m,{controllerDirectives:z,newScopeDirective:t!==r&&t,newIsolateScopeDirective:C,templateDirective:W,nonTlbTranscludeDirective:F}),M=a.length;else if(r.compile)try{wa=r.compile($, +d,qa),G(wa)?n(null,wa,N,P):wa&&n(wa.pre,wa.post,N,P)}catch(X){c(X,ua($))}r.terminal&&(y.terminal=!0,O=Math.max(O,r.priority))}y.scope=t&&!0===t.scope;y.transcludeOnThisElement=V;y.templateOnThisElement=p;y.transclude=qa;l.hasElementTranscludeDirective=Q;return y}function Yc(a,b,c){for(var d=0,e=a.length;dn.priority)&&-1!=n.restrict.indexOf(g)){m&&(n=Ob(n,{$$start:m,$$end:l}));if(!n.$$bindings){var w=n,I=n,y=n.name,O={isolateScope:null,bindToController:null};K(I.scope)&&(!0===I.bindToController?(O.bindToController=d(I.scope,y,!0),O.isolateScope={}):O.isolateScope=d(I.scope,y,!1));K(I.bindToController)&&(O.bindToController=d(I.bindToController,y,!0));if(K(O.bindToController)){var t=I.controller,C=I.controllerAs;if(!t)throw ga("noctrl",y);var F;a:{var I=t,W=C;if(W&&H(W))F=W;else{if(H(I)){var ia= +Zc.exec(I);if(ia){F=ia[3];break a}}F=void 0}}if(!F)throw ga("noident",y);}var V=w.$$bindings=O;K(V.isolateScope)&&(n.$$isolateBindings=V.isolateScope)}b.push(n);k=n}}catch(p){c(p)}}return k}function B(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d"+b+"";return c.childNodes[0].childNodes;default:return b}}function S(a,b){if("srcdoc"==b)return F.HTML;var c=oa(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b|| +"ngSrc"==b))return F.RESOURCE_URL}function X(a,c,d,e,f){var g=S(a,e);f=h[e]||f;var k=b(d,!0,g,f);if(k){if("multiple"===e&&"select"===oa(a))throw ga("selmulti",ua(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers=Z());if(m.test(e))throw ga("nodomevents");var l=h[e];l!==d&&(k=l&&b(l,!0,g,f),d=l);k&&(h[e]=k(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e, +a)}))}}}})}}function Y(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g=b)return a;for(;b--;)8===a[b].nodeType&&Xf.call(a,b,1);return a}function df(){var a={},b=!1;this.register=function(b,c){Sa(b,"controller");K(b)?N(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector","$window",function(d,c){function e(a,b,c,d){if(!a||!K(a.$scope))throw B("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var m,l,n;h=!0===h;k&&H(k)&&(n=k);if(H(f)){k=f.match(Zc);if(!k)throw Yf("ctrlfmt",f);l=k[1]; +n=n||k[3];f=a.hasOwnProperty(l)?a[l]:Bc(g.$scope,l,!0)||(b?Bc(c,l,!0):x);Ra(f,l,!0)}if(h)return h=(L(f)?f[f.length-1]:f).prototype,m=Object.create(h||null),n&&e(g,n,m,l||f.name),N(function(){var a=d.invoke(f,m,g,l);a!==m&&(K(a)||G(a))&&(m=a,n&&e(g,n,m,l||f.name));return m},{instance:m,identifier:n});m=d.instantiate(f,g,l);n&&e(g,n,m,l||f.name);return m}}]}function ef(){this.$get=["$window",function(a){return D(a.document)}]}function ff(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a, +arguments)}}]}function Zb(a){return K(a)?ea(a)?a.toISOString():cb(a):a}function lf(){this.$get=function(){return function(a){if(!a)return"";var b=[];oc(a,function(a,c){null===a||r(a)||(L(a)?p(a,function(a,d){b.push(ha(c)+"="+ha(Zb(a)))}):b.push(ha(c)+"="+ha(Zb(a))))});return b.join("&")}}}function mf(){this.$get=function(){return function(a){function b(a,e,f){null===a||r(a)||(L(a)?p(a,function(a,c){b(a,e+"["+(K(a)?c:"")+"]")}):K(a)&&!ea(a)?oc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ha(e)+ +"="+ha(Zb(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function $b(a,b){if(H(a)){var d=a.replace(Zf,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(bd))||(c=(c=d.match($f))&&ag[c[0]].test(d));c&&(a=uc(d))}}return a}function cd(a){var b=Z(),d;H(a)?p(a.split("\n"),function(a){d=a.indexOf(":");var e=M(T(a.substr(0,d)));a=T(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):K(a)&&p(a,function(a,d){var f=M(d),g=T(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function dd(a){var b; +return function(d){b||(b=cd(a));return d?(d=b[M(d)],void 0===d&&(d=null),d):b}}function ed(a,b,d,c){if(G(c))return c(a,b,d);p(c,function(c){a=c(a,b,d)});return a}function kf(){var a=this.defaults={transformResponse:[$b],transformRequest:[function(a){return K(a)&&"[object File]"!==na.call(a)&&"[object Blob]"!==na.call(a)&&"[object FormData]"!==na.call(a)?cb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:fa(ac),put:fa(ac),patch:fa(ac)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN", +paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return u(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return u(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,k,m){function l(b){function c(a){var b=N({},a);b.data=ed(a.data,a.headers,a.status,f.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}function e(a,b){var c,d={};p(a,function(a, +e){G(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}if(!da.isObject(b))throw B("$http")("badreq",b);if(!H(b.url))throw B("$http")("badreq",b.url);var f=N({method:"get",transformRequest:a.transformRequest,transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);f.headers=function(b){var c=a.headers,d=N({},b.headers),f,g,h,c=N({},c.common,c[M(b.method)]);a:for(f in c){g=M(f);for(h in d)if(M(h)===g)continue a;d[f]=c[f]}return e(d,fa(b))}(b);f.method=rb(f.method);f.paramSerializer= +H(f.paramSerializer)?m.get(f.paramSerializer):f.paramSerializer;var g=[function(b){var d=b.headers,e=ed(b.data,dd(d),x,b.transformRequest);r(e)&&p(d,function(a,b){"content-type"===M(b)&&delete d[b]});r(b.withCredentials)&&!r(a.withCredentials)&&(b.withCredentials=a.withCredentials);return n(b,e).then(c,c)},x],h=k.when(f);for(p(t,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){b=g.shift();var l= +g.shift(),h=h.then(b,l)}d?(h.success=function(a){Ra(a,"fn");h.then(function(b){a(b.data,b.status,b.headers,f)});return h},h.error=function(a){Ra(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=fd("success"),h.error=fd("error"));return h}function n(c,d){function g(a,c,d,e){function f(){m(c,a,d,e)}C&&(200<=a&&300>a?C.put(O,[a,c,cd(d),e]):C.remove(O));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function m(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?p.resolve:p.reject)({data:a, +status:b,headers:dd(d),config:c,statusText:e})}function n(a){m(a.data,a.status,fa(a.headers()),a.statusText)}function t(){var a=l.pendingRequests.indexOf(c);-1!==a&&l.pendingRequests.splice(a,1)}var p=k.defer(),E=p.promise,C,I,W=c.headers,O=J(c.url,c.paramSerializer(c.params));l.pendingRequests.push(c);E.then(t,t);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(C=K(c.cache)?c.cache:K(a.cache)?a.cache:z);C&&(I=C.get(O),u(I)?I&&G(I.then)?I.then(n,n):L(I)?m(I[1],I[0],fa(I[2]), +I[3]):m(I,200,{},"OK"):C.put(O,E));r(I)&&((I=gd(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:x)&&(W[c.xsrfHeaderName||a.xsrfHeaderName]=I),e(c.method,O,d,g,W,c.timeout,c.withCredentials,c.responseType));return E}function J(a,b){0=k&&(q.resolve(t),z(y.$$intervalId),delete f[y.$$intervalId]);A||a.$apply()},h);f[y.$$intervalId]=q;return y}var f={};e.cancel=function(a){return a&&a.$$intervalId in f?(f[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),delete f[a.$$intervalId],!0):!1};return e}]}function bc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=nb(a[b]);return a.join("/")}function hd(a,b){var d=xa(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=ca(d.port)||cg[d.protocol]|| +null}function id(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=xa(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=xc(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path="/"+b.$$path)}function ra(a,b){if(0===b.indexOf(a))return b.substr(a.length)}function Ga(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function hb(a){return a.replace(/(#.+)|#$/,"$1")}function cc(a,b,d){this.$$html5=!0;d=d||""; +hd(a,this);this.$$parse=function(a){var d=ra(b,a);if(!H(d))throw Cb("ipthprfx",a,b);id(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Qb(this.$$search),d=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;u(f=ra(a,c))?(g=f,g=u(f=ra(d,f))?b+(ra("/",f)||f):a+g):u(f=ra(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g); +return!!g}}function dc(a,b,d){hd(a,this);this.$$parse=function(c){var e=ra(a,c)||ra(b,c),f;r(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",r(e)&&(a=c,this.replace())):(f=ra(d,e),r(f)&&(f=e));id(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url? +d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Ga(a)==Ga(b)?(this.$$parse(b),!0):!1}}function jd(a,b,d){this.$$html5=!0;dc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ga(c)?f=c:(g=ra(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Qb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=bc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Db(a){return function(){return this[a]}} +function kd(a,b){return function(d){if(r(d))return this[a];this[a]=b(d);this.$$compose();return this}}function pf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return u(b)?(a=b,this):a};this.html5Mode=function(a){return Ma(a)?(b.enabled=a,this):K(a)?(Ma(a.enabled)&&(b.enabled=a.enabled),Ma(a.requireBase)&&(b.requireBase=a.requireBase),Ma(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window", +function(d,c,e,f,g){function h(a,b,d){var e=m.url(),f=m.$$state;try{c.url(a,b,d),m.$$state=c.state()}catch(g){throw m.url(e),m.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",m.absUrl(),a,m.$$state,b)}var m,l;l=c.baseHref();var n=c.url(),J;if(b.enabled){if(!l&&b.requireBase)throw Cb("nobase");J=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(l||"/");l=e.history?cc:jd}else J=Ga(n),l=dc;var z=J.substr(0,Ga(J).lastIndexOf("/")+1);m=new l(J,z,"#"+a);m.$$parseLinkUrl(n,n);m.$$state= +c.state();var t=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=D(a.target);"a"!==oa(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");K(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=xa(h.animVal).href);t.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!m.$$parseLinkUrl(h,k)||(a.preventDefault(),m.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]= +!0))}});hb(m.absUrl())!=hb(n)&&c.url(m.absUrl(),!0);var A=!0;c.onUrlChange(function(a,b){r(ra(z,a))?g.location.href=a:(d.$evalAsync(function(){var c=m.absUrl(),e=m.$$state,f;a=hb(a);m.$$parse(a);m.$$state=b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;m.absUrl()===a&&(f?(m.$$parse(c),m.$$state=e,h(c,!1,e)):(A=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=hb(c.url()),b=hb(m.absUrl()),f=c.state(),g=m.$$replace,l=a!==b||m.$$html5&&e.history&&f!==m.$$state;if(A|| +l)A=!1,d.$evalAsync(function(){var b=m.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,m.$$state,f).defaultPrevented;m.absUrl()===b&&(c?(m.$$parse(a),m.$$state=f):(l&&h(b,g,f===m.$$state?null:m.$$state),k(a,f)))});m.$$replace=!1});return m}]}function qf(){var a=!0,b=this;this.debugEnabled=function(b){return u(b)?(a=b,this):a};this.$get=["$window",function(d){function c(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&& +(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=d.console||{},e=b[a]||b.log||v;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];p(arguments,function(b){a.push(c(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){a&&c.apply(b,arguments)}}()}}]}function Wa(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"=== +a||"__proto__"===a)throw aa("isecfld",b);return a}function ld(a,b){a+="";if(!H(a))throw aa("iseccst",b);return a}function ya(a,b){if(a){if(a.constructor===a)throw aa("isecfn",b);if(a.window===a)throw aa("isecwindow",b);if(a.children&&(a.nodeName||a.prop&&a.attr&&a.find))throw aa("isecdom",b);if(a===Object)throw aa("isecobj",b);}return a}function md(a,b){if(a){if(a.constructor===a)throw aa("isecfn",b);if(a===dg||a===eg||a===fg)throw aa("isecff",b);}}function Eb(a,b){if(a&&(a===(0).constructor||a=== +(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw aa("isecaf",b);}function gg(a,b){return"undefined"!==typeof a?a:b}function nd(a,b){return"undefined"===typeof a?b:"undefined"===typeof b?a:a+b}function X(a,b){var d,c;switch(a.type){case s.Program:d=!0;p(a.body,function(a){X(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:X(a.argument,b);a.constant=a.argument.constant; +a.toWatch=a.argument.toWatch;break;case s.BinaryExpression:X(a.left,b);X(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:X(a.left,b);X(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:X(a.test,b);X(a.alternate,b);X(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant= +!1;a.toWatch=[a];break;case s.MemberExpression:X(a.object,b);a.computed&&X(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:!1;c=[];p(a.arguments,function(a){X(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:X(a.left,b);X(a.right,b);a.constant=a.left.constant&&a.right.constant; +a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];p(a.elements,function(a){X(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];p(a.properties,function(a){X(a.value,b);d=d&&a.value.constant;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1,a.toWatch=[]}}function od(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:x}} +function pd(a){return a.type===s.Identifier||a.type===s.MemberExpression}function qd(a){if(1===a.body.length&&pd(a.body[0].expression))return{type:s.AssignmentExpression,left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function rd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function sd(a,b){this.astBuilder=a;this.$filter=b}function td(a, +b){this.astBuilder=a;this.$filter=b}function Fb(a){return"constructor"==a}function ec(a){return G(a.valueOf)?a.valueOf():hg.call(a)}function rf(){var a=Z(),b=Z();this.$get=["$filter",function(d){function c(c,f,n){var w,p,F;n=n||t;switch(typeof c){case "string":F=c=c.trim();var r=n?b:a;w=r[F];if(!w){":"===c.charAt(0)&&":"===c.charAt(1)&&(p=!0,c=c.substring(2));w=n?z:J;var E=new fc(w);w=(new gc(E,d,w)).parse(c);w.constant?w.$$watchDelegate=m:p?w.$$watchDelegate=w.literal?k:h:w.inputs&&(w.$$watchDelegate= +g);n&&(w=e(w));r[F]=w}return l(w,f);case "function":return l(c,f);default:return l(v,f)}}function e(a){function b(c,d,e,f){var g=t;t=!0;try{return a(c,d,e,f)}finally{t=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=e(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&c=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;fa)for(b in l++,f)sa.call(e,b)||(p--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1y&&(Q=4-y,r[Q]||(r[Q]=[]),r[Q].push({msg:G(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:f,oldVal:h}));else if(a===c){z=!1;break a}}catch(v){g(v)}if(!(n=t.$$watchersCount&&t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&!(n=t.$$nextSibling);)t=t.$parent}while(t=n);if((z||s.length)&&!y--)throw w.$$phase=null,d("infdig",b,r);}while(z||s.length);for(w.$$phase=null;F.length;)try{F.shift()()}catch(D){g(D)}}, +$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===w&&k.$$applicationDestroyed();z(this,-this.$$watchersCount);for(var b in this.$$listenerCount)t(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling); +this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=v;this.$on=this.$watch=this.$watchGroup=function(){return v};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return h(a)(this,b)},$evalAsync:function(a,b){w.$$phase||s.length||k.defer(function(){s.length&&w.$digest()});s.push({scope:this,expression:h(a),locals:b})},$$postDigest:function(a){F.push(a)},$apply:function(a){try{J("$apply");try{return this.$eval(a)}finally{w.$$phase=null}}catch(b){g(b)}finally{try{w.$digest()}catch(c){throw g(c), +c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&Q.push(b);a=h(a);y()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,t(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented= +!0},defaultPrevented:!1},k=bb([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;lHa)throw za("iequirks");var c=fa(ka);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Za);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,f=c.getTrusted,g=c.trustAs;p(ka,function(a,b){var d=M(b);c[eb("parse_as_"+d)]=function(b){return e(a,b)};c[eb("get_trusted_"+d)]=function(b){return f(a,b)};c[eb("trust_as_"+ +d)]=function(b){return g(a,b)}});return c}]}function xf(){this.$get=["$window","$document",function(a,b){var d={},c=ca((/android (\d+)/.exec(M((a.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((a.navigator||{}).userAgent),f=b[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,k=f.body&&f.body.style,m=!1,l=!1;if(k){for(var n in k)if(m=h.exec(n)){g=m[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in k&&"webkit");m=!!("transition"in k||g+"Transition"in k);l=!!("animation"in k||g+"Animation"in +k);!c||m&&l||(m=H(k.webkitTransition),l=H(k.webkitAnimation))}return{history:!(!a.history||!a.history.pushState||4>c||e),hasEvent:function(a){if("input"===a&&11>=Ha)return!1;if(r(d[a])){var b=f.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ca(),vendorPrefix:g,transitions:m,animations:l,android:c}}]}function zf(){this.$get=["$templateCache","$http","$q","$sce",function(a,b,d,c){function e(f,g){e.totalPendingRequests++;if(!H(f)||r(a.get(f)))f=c.getTrustedResourceUrl(f);var h=b.defaults&&b.defaults.transformResponse; +L(h)?h=h.filter(function(a){return a!==$b}):h===$b&&(h=null);return b.get(f,{cache:a,transformResponse:h})["finally"](function(){e.totalPendingRequests--}).then(function(b){a.put(f,b.data);return b.data},function(a){if(!g)throw ga("tpload",f,a.status,a.statusText);return d.reject(a)})}e.totalPendingRequests=0;return e}]}function Af(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];p(a,function(a){var c= +da.element(a).data("$binding");c&&p(c,function(c){d?(new RegExp("(^|\\s)"+vd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;hc&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==ic;e++);if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==ic;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Fd&&(d=d.splice(0,Fd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function qg(a,b,d,c){var e=a.d,f=e.length-a.i;b=r(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0h;)k.unshift(0),h++;0=b.lgSize&&h.unshift(k.splice(-b.lgSize,k.length).join(""));k.length> +b.gSize;)h.unshift(k.splice(-b.gSize,k.length).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Gb(a,b,d){var c="";0>a&&(c="-",a=-a);for(a=""+a;a.length-d)e+=d;0===e&&-12==d&&(e=12);return Gb(e,b,c)}}function Hb(a,b){return function(d,c){var e=d["get"+a](),f=rb(b?"SHORT"+ +a:a);return c[f][e]}}function Gd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Hd(a){return function(b){var d=Gd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Gb(b,a)}}function jc(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function Ad(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=ca(b[9]+ +b[10]),g=ca(b[9]+b[11]));h.call(a,ca(b[1]),ca(b[2])-1,ca(b[3]));f=ca(b[4]||0)-f;g=ca(b[5]||0)-g;h=ca(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=[],k,m;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;H(c)&&(c=rg.test(c)?ca(c):b(c));P(c)&&(c=new Date(c));if(!ea(c)||!isFinite(c.getTime()))return c;for(;d;)(m=sg.exec(d))?(h=bb(h, +m,1),d=h.pop()):(h.push(d),d=null);var l=c.getTimezoneOffset();f&&(l=vc(f,l),c=Pb(c,f,!0));p(h,function(b){k=tg[b];g+=k?k(c,a.DATETIME_FORMATS,l):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function kg(){return function(a,b){r(b)&&(b=2);return cb(a,b)}}function lg(){return function(a,b,d){b=Infinity===Math.abs(Number(b))?Number(b):ca(b);if(isNaN(b))return a;P(a)&&(a=a.toString());if(!L(a)&&!H(a))return a;d=!d||isNaN(d)?0:ca(d);d=0>d?Math.max(0,a.length+d):d;return 0<=b?a.slice(d, +d+b):0===d?a.slice(b,a.length):a.slice(Math.max(0,d+b),d)}}function Cd(a){function b(b,d){d=d?-1:1;return b.map(function(b){var c=1,h=Za;if(G(b))h=b;else if(H(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(h=a(b),h.constant))var k=h(),h=function(a){return a[k]}}return{get:h,descending:c*d}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(a,e,f){if(!Aa(a))return a;L(e)||(e=[e]);0=== +e.length&&(e=["+"]);var g=b(e,f);g.push({get:function(){return{}},descending:f?-1:1});a=Array.prototype.map.call(a,function(a,b){return{value:a,predicateValues:g.map(function(c){var e=c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("string"===c)e=e.toLowerCase();else if("object"===c)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),d(e)))break a;if(qc(e)&&(e=e.toString(),d(e)))break a;e=b}return{value:e,type:c}})}});a.sort(function(a,b){for(var c=0,d=0,e=g.length;db||37<=b&&40>=b||l(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",l)}b.on("change",m);if(Kd[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch; +k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||m(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Kb(a,b){return function(d,c){var e,f;if(ea(d))return d;if(H(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-1)&&(d=d.substring(1,d.length-1));if(ug.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(), +sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},p(e,function(a,c){c=A};g.$observe("min",function(a){A=p(a);h.$validate()})}if(u(g.max)||g.ngMax){var q;h.$validators.max=function(a){return!n(a)||r(q)||d(a)<=q};g.$observe("max",function(a){q=p(a);h.$validate()})}}}function Ld(a,b,d,c){(c.$$hasNativeValidators=K(b[0].validity))&& +c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput&&!c.typeMismatch?x:a})}function Md(a,b,d,c,e){if(u(c)){a=a(c);if(!a.constant)throw kb("constexpr",d,c);return a(b)}return e}function lc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,b){var c=[],d=0;a:for(;d(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Jf=/<([\w:-]+)/,Kf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,ja={option:[1,'"],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ja.optgroup=ja.option;ja.tbody=ja.tfoot=ja.colgroup=ja.caption=ja.thead; +ja.th=ja.td;var Rf=Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Qa=S.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===U.readyState?setTimeout(b):(this.on("DOMContentLoaded",b),S(R).on("load",b))},toString:function(){var a=[];p(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?D(this[a]):D(this[this.length+a])},length:0,push:wg,sort:[].sort,splice:[].splice},Bb={};p("multiple selected checked disabled readOnly required open".split(" "), +function(a){Bb[M(a)]=a});var Sc={};p("input select option textarea button form details".split(" "),function(a){Sc[a]=!0});var ad={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};p({data:Wb,removeData:ub,hasData:function(a){for(var b in fb[a.ng339])return!0;return!1}},function(a,b){S[b]=a});p({data:Wb,inheritedData:Ab,scope:function(a){return D.data(a,"$scope")||Ab(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return D.data(a,"$isolateScope")|| +D.data(a,"$isolateScopeNoTemplate")},controller:Pc,injector:function(a){return Ab(a,"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:xb,css:function(a,b,d){b=eb(b);if(u(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Oa&&2!==c&&8!==c)if(c=M(b),Bb[c])if(u(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||v).specified?c:x;else if(u(d))a.setAttribute(b,d);else if(a.getAttribute)return a= +a.getAttribute(b,2),null===a?x:a},prop:function(a,b,d){if(u(d))a[b]=d;else return a[b]},text:function(){function a(a,d){if(r(d)){var c=a.nodeType;return 1===c||c===Oa?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(r(b)){if(a.multiple&&"select"===oa(a)){var d=[];p(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(r(b))return a.innerHTML;tb(a,!0);a.innerHTML=b},empty:Qc},function(a,b){S.prototype[b]= +function(b,c){var e,f,g=this.length;if(a!==Qc&&r(2==a.length&&a!==xb&&a!==Pc?b:c)){if(K(b)){for(e=0;e <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var Cg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},fc=function(a){this.options= +a};fc.prototype={constructor:fc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<= +a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=u(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw aa("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+", +"-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=Na(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var b;b=this.expect("(", +"[",".");)"("===b.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b}, +parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]"); +return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;b={type:s.Property,kind:"init"};this.peek().constant?b.key=this.constant():this.peek().identifier?b.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");b.value=this.expression();a.push(b)}while(this.expect(","))}this.consume("}");return{type:s.ObjectExpression,properties:a}},throwError:function(a,b){throw aa("syntax",b.text,a,b.index+1,this.text, +this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw aa("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw aa("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1}, +expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},constants:{"true":{type:s.Literal,value:!0},"false":{type:s.Literal,value:!1},"null":{type:s.Literal,value:null},undefined:{type:s.Literal,value:x},"this":{type:s.ThisExpression}}};sd.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};X(c,d.$filter);var e="",f;this.stage="assign"; +if(f=qd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=od(c.body);d.stage="inputs";p(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+ +e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext","ifDefined","plus","text",e))(this.$filter,Wa,ya,md,ld,Eb,gg,nd,a);this.state=this.stage=x;e.literal=rd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;p(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");return a.join("")}, +generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;p(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,h,k=this,m,l;c=c||v;if(!f&&u(a.watchId))b=b||this.nextId(),this.if_("i", +this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:p(a.body,function(b,c){k.recurse(b.expression,x,x,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:l=this.escape(a.value);this.assign(b,l);c(l);break;case s.UnaryExpression:this.recurse(a.argument,x,x,function(a){h=a});l=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,l);c(l);break;case s.BinaryExpression:this.recurse(a.left, +x,x,function(a){g=a});this.recurse(a.right,x,x,function(a){h=a});l="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,l);c(l);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b); +break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Wa(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l", +a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();k.recurse(a.object,g,x,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,h),"{}")),l=k.ensureSafeObject(k.computedMember(g, +h)),k.assign(b,l),d&&(d.computed=!0,d.name=h);else{Wa(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));l=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))l=k.ensureSafeObject(l);k.assign(b,l);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();a.filter?(h=k.filter(a.callee.name),m=[],p(a.arguments, +function(a){var b=k.nextId();k.recurse(a,b);m.push(b)}),l=h+"("+m.join(",")+")",k.assign(b,l),c(b)):(h=k.nextId(),g={},m=[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);p(a.arguments,function(a){k.recurse(a,k.nextId(),x,function(a){m.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),l=k.member(g.context,g.name,g.computed)+"("+m.join(",")+")"):l=h+"("+m.join(",")+")";l=k.ensureSafeObject(l);k.assign(b,l)}, +function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!pd(a.left))throw aa("lval");this.recurse(a.left,x,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);l=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,l);c(b||l)})},1);break;case s.ArrayExpression:m=[];p(a.elements,function(a){k.recurse(a,k.nextId(),x,function(a){m.push(a)})}); +l="["+m.join(",")+"]";this.assign(b,l);c(l);break;case s.ObjectExpression:m=[];p(a.properties,function(a){k.recurse(a.value,k.nextId(),x,function(b){m.push(k.escape(a.key.type===s.Identifier?a.key.name:""+a.key.value)+":"+b)})});l="{"+m.join(",")+"}";this.assign(b,l);c(l);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+ +this.escape(b)+" in "+a+")"));return c[d]},assign:function(a,b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a, +"){");b();c.push("}");d&&(c.push("else{"),d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){return a+"."+b},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")}, +addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+",text)")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+ +a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(H(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(P(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"=== +typeof a)return"undefined";throw aa("esc");},nextId:function(a,b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};td.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;X(c,d.$filter);var e,f;if(e=qd(c))f=this.recurse(e);e=od(c.body);var g;e&&(g=[],p(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];p(c.body,function(a){h.push(d.recurse(a.expression))}); +e=0===c.body.length?function(){}:1===c.body.length?h[0]:function(a,b){var c;p(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=rd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left), +e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Wa(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Fb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Wa(a.property.name, +f.expression),e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],p(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var n=[],p=0;p":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c, +e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:x,name:x,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f= +g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:x;b&&ya(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var m=a(f,g,h,k),l,n;null!=m&&(l=b(f,g,h,k),l=ld(l),Wa(l,e),c&&1!==c&&(Eb(m),m&&!m[l]&&(m[l]={})),n=m[l],ya(n,e));return d?{context:m,name:l,value:n}:n}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,m){g=a(g,h,k,m);e&&1!==e&&(Eb(g),g&&!g[b]&&(g[b]={}));h=null!=g?g[b]:x;(d||Fb(b))&&ya(h,f);return c?{context:g,name:b,value:h}: +h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var gc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(this.lexer);this.astCompiler=d.csp?new td(this.ast,b):new sd(this.ast,b)};gc.prototype={constructor:gc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var hg=Object.prototype.valueOf,za=B("$sce"),ka={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},ga=B("$compile"),Y=U.createElement("a"), +xd=xa(R.location.href);yd.$inject=["$document"];Jc.$inject=["$provide"];var Fd=22,Ed=".",ic="0";zd.$inject=["$locale"];Bd.$inject=["$locale"];var tg={yyyy:ba("FullYear",4),yy:ba("FullYear",2,0,!0),y:ba("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:ba("Month",2,1),M:ba("Month",1,1),dd:ba("Date",2),d:ba("Date",1),HH:ba("Hours",2),H:ba("Hours",1),hh:ba("Hours",2,-12),h:ba("Hours",1,-12),mm:ba("Minutes",2),m:ba("Minutes",1),ss:ba("Seconds",2),s:ba("Seconds",1),sss:ba("Milliseconds",3),EEEE:Hb("Day"), +EEE:Hb("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Gb(Math[0=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},sg=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,rg=/^\-?\d+$/;Ad.$inject=["$locale"];var mg=ma(M),ng=ma(rb);Cd.$inject=["$parse"];var me=ma({restrict:"E",compile:function(a, +b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===na.call(b.prop("href"))?"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),sb={};p(Bb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=va("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});sb[c]=function(){return{restrict:"A",priority:100,link:e}}}});p(ad,function(a,b){sb[b]= +function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(vg))){e.$set("ngPattern",new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});p(["src","srcset","href"],function(a){var b=va("ng-"+a);sb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===na.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b), +Ha&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Ib={$addControl:v,$$renameControl:function(a,b){a.$name=b},$removeControl:v,$setValidity:v,$setDirty:v,$setPristine:v,$setSubmitted:v};Id.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Rd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||v}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Id,compile:function(d,f){d.addClass(Xa).addClass(lb); +var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var n=f[0];if(!("action"in e)){var p=function(b){a.$apply(function(){n.$commitViewValue();n.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",p,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",p,!1)},0,!1)})}(f[1]||n.$$parentForm).$addControl(n);var r=g?c(n.$name):v;g&&(r(a,n),e.$observe(g,function(b){n.$name!==b&&(r(a,x),n.$$parentForm.$$renameControl(n,b),r=c(n.$name),r(a,n))})); +d.on("$destroy",function(){n.$$parentForm.$removeControl(n);r(a,x);N(n,Ib)})}}}}}]},ne=Rd(),Ae=Rd(!0),ug=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,Dg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Eg=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,Fg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Sd=/^(\d{4})-(\d{2})-(\d{2})$/,Td=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/, +mc=/^(\d{4})-W(\d\d)$/,Ud=/^(\d{4})-(\d\d)$/,Vd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Kd=Z();p(["date","datetime-local","month","time","week"],function(a){Kd[a]=!0});var Wd={text:function(a,b,d,c,e,f){ib(a,b,d,c,e,f);kc(c)},date:jb("date",Sd,Kb(Sd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":jb("datetimelocal",Td,Kb(Td,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:jb("time",Vd,Kb(Vd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:jb("week",mc,function(a,b){if(ea(a))return a; +if(H(a)){mc.lastIndex=0;var d=mc.exec(a);if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Gd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:jb("month",Ud,Kb(Ud,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Ld(a,b,d,c);ib(a,b,d,c,e,f);c.$$parserName="number";c.$parsers.push(function(a){return c.$isEmpty(a)?null:Fg.test(a)?parseFloat(a):x});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!P(a))throw kb("numfmt", +a);a=a.toString()}return a});if(u(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||r(g)||a>=g};d.$observe("min",function(a){u(a)&&!P(a)&&(a=parseFloat(a,10));g=P(a)&&!isNaN(a)?a:x;c.$validate()})}if(u(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||r(h)||a<=h};d.$observe("max",function(a){u(a)&&!P(a)&&(a=parseFloat(a,10));h=P(a)&&!isNaN(a)?a:x;c.$validate()})}},url:function(a,b,d,c,e,f){ib(a,b,d,c,e,f);kc(c);c.$$parserName="url";c.$validators.url= +function(a,b){var d=a||b;return c.$isEmpty(d)||Dg.test(d)}},email:function(a,b,d,c,e,f){ib(a,b,d,c,e,f);kc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Eg.test(d)}},radio:function(a,b,d,c){r(d.name)&&b.attr("name",++mb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Md(h,a,"ngTrueValue",d.ngTrueValue, +!0),m=Md(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return la(a,k)});c.$parsers.push(function(a){return a?k:m})},hidden:v,button:v,submit:v,reset:v,file:v},Dc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Wd[M(g.type)]||Wd.text)(e,f, +g,h[0],b,a,d,c)}}}}],Gg=/^(true|false|\d+)$/,Se=function(){return{restrict:"A",priority:100,compile:function(a,b){return Gg.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},se=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=r(a)?"":a})}}}}],ue=["$interpolate","$compile", +function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=r(a)?"":a})}}}}],te=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(b){return a.valueOf(b)});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){var d= +f(b);c.html(a.getTrustedHtml(d)||"")})}}}}],Re=ma({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),ve=lc("",!0),xe=lc("Odd",0),we=lc("Even",1),ye=Ka({compile:function(a,b){b.$set("ngCloak",x);a.removeClass("ng-cloak")}}),ze=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ic={},Hg={blur:!0,focus:!0};p("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "), +function(a){var b=va("ng-"+a);Ic[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Hg[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var Ce=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(b,d,c,e,f){var g,h,k;b.$watch(c.ngIf,function(b){b?h||f(function(b,e){h=e;b[b.length++]=U.createComment(" end ngIf: "+ +c.ngIf+" ");g={clone:b};a.enter(b,d.parent(),d)}):(k&&(k.remove(),k=null),h&&(h.$destroy(),h=null),g&&(k=qb(g.clone),a.leave(k).then(function(){k=null}),g=null))})}}}],De=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:da.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,l,n,p){var r=0,t,s,q,y=function(){s&&(s.remove(),s=null);t&&(t.$destroy(),t=null);q&& +(d.leave(q).then(function(){s=null}),s=q,q=null)};c.$watch(f,function(f){var l=function(){!u(h)||h&&!c.$eval(h)||b()},s=++r;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&s===r){var b=c.$new();n.template=a;a=p(b,function(a){y();d.enter(a,null,e).then(l)});t=b;q=a;t.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){c.$$destroyed||s!==r||(y(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(y(),n.template=null)})}}}}],Ue=["$compile",function(a){return{restrict:"ECA", +priority:-400,require:"ngInclude",link:function(b,d,c,e){/SVG/.test(d[0].toString())?(d.empty(),a(Lc(e.template,U).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Ee=Ka({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),Qe=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?T(e):e;c.$parsers.push(function(a){if(!r(a)){var b= +[];a&&p(a.split(g),function(a){a&&b.push(f?T(a):a)});return b}});c.$formatters.push(function(a){return L(a)?a.join(e):x});c.$isEmpty=function(a){return!a||!a.length}}}},lb="ng-valid",Nd="ng-invalid",Xa="ng-pristine",Jb="ng-dirty",Pd="ng-pending",kb=B("ngModel"),Ig=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,m){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=x;this.$validators={};this.$asyncValidators= +{};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=x;this.$name=m(d.name||"",!1)(a);this.$$parentForm=Ib;var l=e(d.ngModel),n=l.assign,s=l,z=n,t=null,A,q=this;this.$$setOptions=function(a){if((q.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");s=function(a){var c=l(a);G(c)&&(c=b(a));return c};z=function(a, +b){G(l(a))?f(a,{$$$p:q.$modelValue}):n(a,q.$modelValue)}}else if(!l.assign)throw kb("nonassign",d.ngModel,ua(c));};this.$render=v;this.$isEmpty=function(a){return r(a)||""===a||null===a||a!==a};var y=0;Jd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){q.$dirty=!1;q.$pristine=!0;f.removeClass(c,Jb);f.addClass(c,Xa)};this.$setDirty=function(){q.$dirty=!0;q.$pristine=!1;f.removeClass(c,Xa);f.addClass(c,Jb);q.$$parentForm.$setDirty()}; +this.$setUntouched=function(){q.$touched=!1;q.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};this.$setTouched=function(){q.$touched=!0;q.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(t);q.$viewValue=q.$$lastCommittedViewValue;q.$render()};this.$validate=function(){if(!P(q.$modelValue)||!isNaN(q.$modelValue)){var a=q.$$rawModelValue,b=q.$valid,c=q.$modelValue,d=q.$options&&q.$options.allowInvalid;q.$$runValidators(a,q.$$lastCommittedViewValue, +function(e){d||b===e||(q.$modelValue=e?a:x,q.$modelValue!==c&&q.$$writeModelToScope())})}};this.$$runValidators=function(a,b,c){function d(){var c=!0;p(q.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(p(q.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;p(q.$asyncValidators,function(e,g){var h=e(a,b);if(!h||!G(h.then))throw kb("nopromise",h);f(g,x);c.push(h.then(function(){f(g,!0)},function(a){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)}, +v):g(!0)}function f(a,b){h===y&&q.$setValidity(a,b)}function g(a){h===y&&c(a)}y++;var h=y;(function(){var a=q.$$parserName||"parse";if(r(A))f(a,null);else return A||(p(q.$validators,function(a,b){f(b,null)}),p(q.$asyncValidators,function(a,b){f(b,null)})),f(a,A),A;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=q.$viewValue;g.cancel(t);if(q.$$lastCommittedViewValue!==a||""===a&&q.$$hasNativeValidators)q.$$lastCommittedViewValue=a,q.$pristine&&this.$setDirty(),this.$$parseAndValidate()}; +this.$$parseAndValidate=function(){var b=q.$$lastCommittedViewValue;if(A=r(b)?x:!0)for(var c=0;ce||c.$isEmpty(b)||b.length<=e}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=ca(a)||0;c.$validate()}); +c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};R.angular.bootstrap?R.console&&console.log("WARNING: Tried to load angular more than once."):(he(),je(da),da.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6, +MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a", +shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,c){var e=a|0,f=c;x===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),D(U).ready(function(){de(U,yc)}))})(window,document); +!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend(''); +//# sourceMappingURL=angular.min.js.map diff --git a/result/views/app.js b/result/views/app.js new file mode 100644 index 0000000..fe8b600 --- /dev/null +++ b/result/views/app.js @@ -0,0 +1,50 @@ +var app = angular.module('catsvsdogs', []); +var socket = io.connect(); + +var bg1 = document.getElementById('background-stats-1'); +var bg2 = document.getElementById('background-stats-2'); + +app.controller('statsCtrl', function($scope){ + $scope.aPercent = 50; + $scope.bPercent = 50; + + var updateScores = function(){ + socket.on('scores', function (json) { + data = JSON.parse(json); + var a = parseInt(data.a || 0); + var b = parseInt(data.b || 0); + + var percentages = getPercentages(a, b); + + bg1.style.width = percentages.a + "%"; + bg2.style.width = percentages.b + "%"; + + $scope.$apply(function () { + $scope.aPercent = percentages.a; + $scope.bPercent = percentages.b; + $scope.total = a + b; + }); + }); + }; + + var init = function(){ + document.body.style.opacity=1; + updateScores(); + }; + socket.on('message',function(data){ + init(); + }); +}); + +function getPercentages(a, b) { + var result = {}; + + if (a + b > 0) { + result.a = Math.round(a / (a + b) * 100); + result.b = 100 - result.a; + } else { + result.a = result.b = 50; + } + + return result; +} \ No newline at end of file diff --git a/result/views/index.html b/result/views/index.html new file mode 100644 index 0000000..4427ffa --- /dev/null +++ b/result/views/index.html @@ -0,0 +1,43 @@ + + + + + Cats vs Dogs -- Result + + + + + + + +

+
+
+
+
+
+
+
+
+
Cats
+
{{aPercent | number:1}}%
+
+
+
+
Dogs
+
{{bPercent | number:1}}%
+
+
+
+
+
+ No votes yet + {{total}} vote + {{total}} votes +
+ + + + + diff --git a/result/views/socket.io.js b/result/views/socket.io.js new file mode 100644 index 0000000..faa634b --- /dev/null +++ b/result/views/socket.io.js @@ -0,0 +1,6 @@ +/*! + * Socket.IO v4.7.2 + * (c) 2014-2023 Guillermo Rauch + * Released under the MIT License. + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).io=e()}(this,(function(){"use strict";function t(e){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(e)}function e(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){for(var n=0;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,s=!0,a=!1;return{s:function(){n=n.call(t)},n:function(){var t=n.next();return s=t.done,t},e:function(t){a=!0,o=t},f:function(){try{s||null==n.return||n.return()}finally{if(a)throw o}}}}var v=Object.create(null);v.open="0",v.close="1",v.ping="2",v.pong="3",v.message="4",v.upgrade="5",v.noop="6";var g=Object.create(null);Object.keys(v).forEach((function(t){g[v[t]]=t}));var m,b={type:"error",data:"parser error"},k="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),w="function"==typeof ArrayBuffer,_=function(t){return"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(t):t&&t.buffer instanceof ArrayBuffer},A=function(t,e,n){var r=t.type,i=t.data;return k&&i instanceof Blob?e?n(i):O(i,n):w&&(i instanceof ArrayBuffer||_(i))?e?n(i):O(new Blob([i]),n):n(v[r]+(i||""))},O=function(t,e){var n=new FileReader;return n.onload=function(){var t=n.result.split(",")[1];e("b"+(t||""))},n.readAsDataURL(t)};function E(t){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):new Uint8Array(t.buffer,t.byteOffset,t.byteLength)}for(var T="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",R="undefined"==typeof Uint8Array?[]:new Uint8Array(256),C=0;C<64;C++)R[T.charCodeAt(C)]=C;var B,S="function"==typeof ArrayBuffer,N=function(t,e){if("string"!=typeof t)return{type:"message",data:x(t,e)};var n=t.charAt(0);return"b"===n?{type:"message",data:L(t.substring(1),e)}:g[n]?t.length>1?{type:g[n],data:t.substring(1)}:{type:g[n]}:b},L=function(t,e){if(S){var n=function(t){var e,n,r,i,o,s=.75*t.length,a=t.length,u=0;"="===t[t.length-1]&&(s--,"="===t[t.length-2]&&s--);var c=new ArrayBuffer(s),h=new Uint8Array(c);for(e=0;e>4,h[u++]=(15&r)<<4|i>>2,h[u++]=(3&i)<<6|63&o;return c}(t);return x(n,e)}return{base64:!0,data:t}},x=function(t,e){return"blob"===e?t instanceof Blob?t:new Blob([t]):t instanceof ArrayBuffer?t:t.buffer},P=String.fromCharCode(30);function q(){return new TransformStream({transform:function(t,e){!function(t,e){k&&t.data instanceof Blob?t.data.arrayBuffer().then(E).then(e):w&&(t.data instanceof ArrayBuffer||_(t.data))?e(E(t.data)):A(t,!1,(function(t){m||(m=new TextEncoder),e(m.encode(t))}))}(t,(function(n){var r,i=n.length;if(i<126)r=new Uint8Array(1),new DataView(r.buffer).setUint8(0,i);else if(i<65536){r=new Uint8Array(3);var o=new DataView(r.buffer);o.setUint8(0,126),o.setUint16(1,i)}else{r=new Uint8Array(9);var s=new DataView(r.buffer);s.setUint8(0,127),s.setBigUint64(1,BigInt(i))}t.data&&"string"!=typeof t.data&&(r[0]|=128),e.enqueue(r),e.enqueue(n)}))}})}function j(t){return t.reduce((function(t,e){return t+e.length}),0)}function D(t,e){if(t[0].length===e)return t.shift();for(var n=new Uint8Array(e),r=0,i=0;i1?e-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:{};return t+"://"+this._hostname()+this._port()+this.opts.path+this._query(e)}},{key:"_hostname",value:function(){var t=this.opts.hostname;return-1===t.indexOf(":")?t:"["+t+"]"}},{key:"_port",value:function(){return this.opts.port&&(this.opts.secure&&Number(443!==this.opts.port)||!this.opts.secure&&80!==Number(this.opts.port))?":"+this.opts.port:""}},{key:"_query",value:function(t){var e=function(t){var e="";for(var n in t)t.hasOwnProperty(n)&&(e.length&&(e+="&"),e+=encodeURIComponent(n)+"="+encodeURIComponent(t[n]));return e}(t);return e.length?"?"+e:""}}]),i}(U),z="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),J=64,$={},Q=0,X=0;function G(t){var e="";do{e=z[t%J]+e,t=Math.floor(t/J)}while(t>0);return e}function Z(){var t=G(+new Date);return t!==K?(Q=0,K=t):t+"."+G(Q++)}for(;X0&&void 0!==arguments[0]?arguments[0]:{};return i(t,{xd:this.xd,cookieJar:this.cookieJar},this.opts),new st(this.uri(),t)}},{key:"doWrite",value:function(t,e){var n=this,r=this.request({method:"POST",data:t});r.on("success",e),r.on("error",(function(t,e){n.onError("xhr post error",t,e)}))}},{key:"doPoll",value:function(){var t=this,e=this.request();e.on("data",this.onData.bind(this)),e.on("error",(function(e,n){t.onError("xhr poll error",e,n)})),this.pollXhr=e}}]),s}(W),st=function(t){o(i,t);var n=l(i);function i(t,r){var o;return e(this,i),H(f(o=n.call(this)),r),o.opts=r,o.method=r.method||"GET",o.uri=t,o.data=void 0!==r.data?r.data:null,o.create(),o}return r(i,[{key:"create",value:function(){var t,e=this,n=F(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");n.xdomain=!!this.opts.xd;var r=this.xhr=new nt(n);try{r.open(this.method,this.uri,!0);try{if(this.opts.extraHeaders)for(var o in r.setDisableHeaderCheck&&r.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(o)&&r.setRequestHeader(o,this.opts.extraHeaders[o])}catch(t){}if("POST"===this.method)try{r.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(t){}try{r.setRequestHeader("Accept","*/*")}catch(t){}null===(t=this.opts.cookieJar)||void 0===t||t.addCookies(r),"withCredentials"in r&&(r.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(r.timeout=this.opts.requestTimeout),r.onreadystatechange=function(){var t;3===r.readyState&&(null===(t=e.opts.cookieJar)||void 0===t||t.parseCookies(r)),4===r.readyState&&(200===r.status||1223===r.status?e.onLoad():e.setTimeoutFn((function(){e.onError("number"==typeof r.status?r.status:0)}),0))},r.send(this.data)}catch(t){return void this.setTimeoutFn((function(){e.onError(t)}),0)}"undefined"!=typeof document&&(this.index=i.requestsCount++,i.requests[this.index]=this)}},{key:"onError",value:function(t){this.emitReserved("error",t,this.xhr),this.cleanup(!0)}},{key:"cleanup",value:function(t){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=rt,t)try{this.xhr.abort()}catch(t){}"undefined"!=typeof document&&delete i.requests[this.index],this.xhr=null}}},{key:"onLoad",value:function(){var t=this.xhr.responseText;null!==t&&(this.emitReserved("data",t),this.emitReserved("success"),this.cleanup())}},{key:"abort",value:function(){this.cleanup()}}]),i}(U);if(st.requestsCount=0,st.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",at);else if("function"==typeof addEventListener){addEventListener("onpagehide"in I?"pagehide":"unload",at,!1)}function at(){for(var t in st.requests)st.requests.hasOwnProperty(t)&&st.requests[t].abort()}var ut="function"==typeof Promise&&"function"==typeof Promise.resolve?function(t){return Promise.resolve().then(t)}:function(t,e){return e(t,0)},ct=I.WebSocket||I.MozWebSocket,ht="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase(),ft=function(t){o(i,t);var n=l(i);function i(t){var r;return e(this,i),(r=n.call(this,t)).supportsBinary=!t.forceBase64,r}return r(i,[{key:"name",get:function(){return"websocket"}},{key:"doOpen",value:function(){if(this.check()){var t=this.uri(),e=this.opts.protocols,n=ht?{}:F(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(n.headers=this.opts.extraHeaders);try{this.ws=ht?new ct(t,e,n):e?new ct(t,e):new ct(t)}catch(t){return this.emitReserved("error",t)}this.ws.binaryType=this.socket.binaryType,this.addEventListeners()}}},{key:"addEventListeners",value:function(){var t=this;this.ws.onopen=function(){t.opts.autoUnref&&t.ws._socket.unref(),t.onOpen()},this.ws.onclose=function(e){return t.onClose({description:"websocket connection closed",context:e})},this.ws.onmessage=function(e){return t.onData(e.data)},this.ws.onerror=function(e){return t.onError("websocket error",e)}}},{key:"write",value:function(t){var e=this;this.writable=!1;for(var n=function(){var n=t[r],i=r===t.length-1;A(n,e.supportsBinary,(function(t){try{e.ws.send(t)}catch(t){}i&&ut((function(){e.writable=!0,e.emitReserved("drain")}),e.setTimeoutFn)}))},r=0;rMath.pow(2,21)-1){a.enqueue(b);break}i=l*Math.pow(2,32)+f.getUint32(4),r=3}else{if(j(n)t){a.enqueue(b);break}}}})}(Number.MAX_SAFE_INTEGER,t.socket.binaryType),r=e.readable.pipeThrough(n).getReader(),i=q();i.readable.pipeTo(e.writable),t.writer=i.writable.getWriter();!function e(){r.read().then((function(n){var r=n.done,i=n.value;r||(t.onPacket(i),e())})).catch((function(t){}))}();var o={type:"open"};t.query.sid&&(o.data='{"sid":"'.concat(t.query.sid,'"}')),t.writer.write(o).then((function(){return t.onOpen()}))}))})))}},{key:"write",value:function(t){var e=this;this.writable=!1;for(var n=function(){var n=t[r],i=r===t.length-1;e.writer.write(n).then((function(){i&&ut((function(){e.writable=!0,e.emitReserved("drain")}),e.setTimeoutFn)}))},r=0;r1&&void 0!==arguments[1]?arguments[1]:{};return e(this,a),(r=s.call(this)).binaryType="arraybuffer",r.writeBuffer=[],n&&"object"===t(n)&&(o=n,n=null),n?(n=vt(n),o.hostname=n.host,o.secure="https"===n.protocol||"wss"===n.protocol,o.port=n.port,n.query&&(o.query=n.query)):o.host&&(o.hostname=vt(o.host).host),H(f(r),o),r.secure=null!=o.secure?o.secure:"undefined"!=typeof location&&"https:"===location.protocol,o.hostname&&!o.port&&(o.port=r.secure?"443":"80"),r.hostname=o.hostname||("undefined"!=typeof location?location.hostname:"localhost"),r.port=o.port||("undefined"!=typeof location&&location.port?location.port:r.secure?"443":"80"),r.transports=o.transports||["polling","websocket","webtransport"],r.writeBuffer=[],r.prevBufferLen=0,r.opts=i({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!1},o),r.opts.path=r.opts.path.replace(/\/$/,"")+(r.opts.addTrailingSlash?"/":""),"string"==typeof r.opts.query&&(r.opts.query=function(t){for(var e={},n=t.split("&"),r=0,i=n.length;r1))return this.writeBuffer;for(var t,e=1,n=0;n=57344?n+=3:(r++,n+=4);return n}(t):Math.ceil(1.33*(t.byteLength||t.size))),n>0&&e>this.maxPayload)return this.writeBuffer.slice(0,n);e+=2}return this.writeBuffer}},{key:"write",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"send",value:function(t,e,n){return this.sendPacket("message",t,e,n),this}},{key:"sendPacket",value:function(t,e,n,r){if("function"==typeof e&&(r=e,e=void 0),"function"==typeof n&&(r=n,n=null),"closing"!==this.readyState&&"closed"!==this.readyState){(n=n||{}).compress=!1!==n.compress;var i={type:t,data:e,options:n};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}}},{key:"close",value:function(){var t=this,e=function(){t.onClose("forced close"),t.transport.close()},n=function n(){t.off("upgrade",n),t.off("upgradeError",n),e()},r=function(){t.once("upgrade",n),t.once("upgradeError",n)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(function(){t.upgrading?r():e()})):this.upgrading?r():e()),this}},{key:"onError",value:function(t){a.priorWebsocketSuccess=!1,this.emitReserved("error",t),this.onClose("transport error",t)}},{key:"onClose",value:function(t,e){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&(removeEventListener("beforeunload",this.beforeunloadEventListener,!1),removeEventListener("offline",this.offlineEventListener,!1)),this.readyState="closed",this.id=null,this.emitReserved("close",t,e),this.writeBuffer=[],this.prevBufferLen=0)}},{key:"filterUpgrades",value:function(t){for(var e=[],n=0,r=t.length;n=0&&e.num1?e-1:0),r=1;r1?n-1:0),i=1;in._opts.retries&&(n._queue.shift(),e&&e(t));else if(n._queue.shift(),e){for(var i=arguments.length,o=new Array(i>1?i-1:0),s=1;s0&&void 0!==arguments[0]&&arguments[0];if(this.connected&&0!==this._queue.length){var e=this._queue[0];e.pending&&!t||(e.pending=!0,e.tryCount++,this.flags=e.flags,this.emit.apply(this,e.args))}}},{key:"packet",value:function(t){t.nsp=this.nsp,this.io._packet(t)}},{key:"onopen",value:function(){var t=this;"function"==typeof this.auth?this.auth((function(e){t._sendConnectPacket(e)})):this._sendConnectPacket(this.auth)}},{key:"_sendConnectPacket",value:function(t){this.packet({type:Bt.CONNECT,data:this._pid?i({pid:this._pid,offset:this._lastOffset},t):t})}},{key:"onerror",value:function(t){this.connected||this.emitReserved("connect_error",t)}},{key:"onclose",value:function(t,e){this.connected=!1,delete this.id,this.emitReserved("disconnect",t,e)}},{key:"onpacket",value:function(t){if(t.nsp===this.nsp)switch(t.type){case Bt.CONNECT:t.data&&t.data.sid?this.onconnect(t.data.sid,t.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case Bt.EVENT:case Bt.BINARY_EVENT:this.onevent(t);break;case Bt.ACK:case Bt.BINARY_ACK:this.onack(t);break;case Bt.DISCONNECT:this.ondisconnect();break;case Bt.CONNECT_ERROR:this.destroy();var e=new Error(t.data.message);e.data=t.data.data,this.emitReserved("connect_error",e)}}},{key:"onevent",value:function(t){var e=t.data||[];null!=t.id&&e.push(this.ack(t.id)),this.connected?this.emitEvent(e):this.receiveBuffer.push(Object.freeze(e))}},{key:"emitEvent",value:function(t){if(this._anyListeners&&this._anyListeners.length){var e,n=y(this._anyListeners.slice());try{for(n.s();!(e=n.n()).done;){e.value.apply(this,t)}}catch(t){n.e(t)}finally{n.f()}}p(s(a.prototype),"emit",this).apply(this,t),this._pid&&t.length&&"string"==typeof t[t.length-1]&&(this._lastOffset=t[t.length-1])}},{key:"ack",value:function(t){var e=this,n=!1;return function(){if(!n){n=!0;for(var r=arguments.length,i=new Array(r),o=0;o0&&t.jitter<=1?t.jitter:0,this.attempts=0}It.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},It.prototype.reset=function(){this.attempts=0},It.prototype.setMin=function(t){this.ms=t},It.prototype.setMax=function(t){this.max=t},It.prototype.setJitter=function(t){this.jitter=t};var Ft=function(n){o(s,n);var i=l(s);function s(n,r){var o,a;e(this,s),(o=i.call(this)).nsps={},o.subs=[],n&&"object"===t(n)&&(r=n,n=void 0),(r=r||{}).path=r.path||"/socket.io",o.opts=r,H(f(o),r),o.reconnection(!1!==r.reconnection),o.reconnectionAttempts(r.reconnectionAttempts||1/0),o.reconnectionDelay(r.reconnectionDelay||1e3),o.reconnectionDelayMax(r.reconnectionDelayMax||5e3),o.randomizationFactor(null!==(a=r.randomizationFactor)&&void 0!==a?a:.5),o.backoff=new It({min:o.reconnectionDelay(),max:o.reconnectionDelayMax(),jitter:o.randomizationFactor()}),o.timeout(null==r.timeout?2e4:r.timeout),o._readyState="closed",o.uri=n;var u=r.parser||qt;return o.encoder=new u.Encoder,o.decoder=new u.Decoder,o._autoConnect=!1!==r.autoConnect,o._autoConnect&&o.open(),o}return r(s,[{key:"reconnection",value:function(t){return arguments.length?(this._reconnection=!!t,this):this._reconnection}},{key:"reconnectionAttempts",value:function(t){return void 0===t?this._reconnectionAttempts:(this._reconnectionAttempts=t,this)}},{key:"reconnectionDelay",value:function(t){var e;return void 0===t?this._reconnectionDelay:(this._reconnectionDelay=t,null===(e=this.backoff)||void 0===e||e.setMin(t),this)}},{key:"randomizationFactor",value:function(t){var e;return void 0===t?this._randomizationFactor:(this._randomizationFactor=t,null===(e=this.backoff)||void 0===e||e.setJitter(t),this)}},{key:"reconnectionDelayMax",value:function(t){var e;return void 0===t?this._reconnectionDelayMax:(this._reconnectionDelayMax=t,null===(e=this.backoff)||void 0===e||e.setMax(t),this)}},{key:"timeout",value:function(t){return arguments.length?(this._timeout=t,this):this._timeout}},{key:"maybeReconnectOnOpen",value:function(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}},{key:"open",value:function(t){var e=this;if(~this._readyState.indexOf("open"))return this;this.engine=new gt(this.uri,this.opts);var n=this.engine,r=this;this._readyState="opening",this.skipReconnect=!1;var i=jt(n,"open",(function(){r.onopen(),t&&t()})),o=function(n){e.cleanup(),e._readyState="closed",e.emitReserved("error",n),t?t(n):e.maybeReconnectOnOpen()},s=jt(n,"error",o);if(!1!==this._timeout){var a=this._timeout,u=this.setTimeoutFn((function(){i(),o(new Error("timeout")),n.close()}),a);this.opts.autoUnref&&u.unref(),this.subs.push((function(){e.clearTimeoutFn(u)}))}return this.subs.push(i),this.subs.push(s),this}},{key:"connect",value:function(t){return this.open(t)}},{key:"onopen",value:function(){this.cleanup(),this._readyState="open",this.emitReserved("open");var t=this.engine;this.subs.push(jt(t,"ping",this.onping.bind(this)),jt(t,"data",this.ondata.bind(this)),jt(t,"error",this.onerror.bind(this)),jt(t,"close",this.onclose.bind(this)),jt(this.decoder,"decoded",this.ondecoded.bind(this)))}},{key:"onping",value:function(){this.emitReserved("ping")}},{key:"ondata",value:function(t){try{this.decoder.add(t)}catch(t){this.onclose("parse error",t)}}},{key:"ondecoded",value:function(t){var e=this;ut((function(){e.emitReserved("packet",t)}),this.setTimeoutFn)}},{key:"onerror",value:function(t){this.emitReserved("error",t)}},{key:"socket",value:function(t,e){var n=this.nsps[t];return n?this._autoConnect&&!n.active&&n.connect():(n=new Ut(this,t,e),this.nsps[t]=n),n}},{key:"_destroy",value:function(t){for(var e=0,n=Object.keys(this.nsps);e=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{var n=this.backoff.duration();this._reconnecting=!0;var r=this.setTimeoutFn((function(){e.skipReconnect||(t.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((function(n){n?(e._reconnecting=!1,e.reconnect(),t.emitReserved("reconnect_error",n)):e.onreconnect()})))}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){t.clearTimeoutFn(r)}))}}},{key:"onreconnect",value:function(){var t=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",t)}}]),s}(U),Mt={};function Vt(e,n){"object"===t(e)&&(n=e,e=void 0);var r,i=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,r=t;n=n||"undefined"!=typeof location&&location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==n?n.protocol+"//"+t:"https://"+t),r=vt(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+e,r.href=r.protocol+"://"+i+(n&&n.port===r.port?"":":"+r.port),r}(e,(n=n||{}).path||"/socket.io"),o=i.source,s=i.id,a=i.path,u=Mt[s]&&a in Mt[s].nsps;return n.forceNew||n["force new connection"]||!1===n.multiplex||u?r=new Ft(o,n):(Mt[s]||(Mt[s]=new Ft(o,n)),r=Mt[s]),i.query&&!n.query&&(n.query=i.queryKey),r.socket(i.path,n)}return i(Vt,{Manager:Ft,Socket:Ut,io:Vt,connect:Vt}),Vt})); diff --git a/result/views/stylesheets/style.css b/result/views/stylesheets/style.css new file mode 100644 index 0000000..6842773 --- /dev/null +++ b/result/views/stylesheets/style.css @@ -0,0 +1,112 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600); + +*{ + box-sizing:border-box; +} +html,body{ + margin:0; + padding:0; + height:100%; + font-family: 'Open Sans'; +} +body{ + opacity:0; + transition: all 1s linear; +} + +.divider{ + height: 150px; + width:2px; + background-color: #C0C9CE; + position: relative; + top: 50%; + float: left; + transform: translateY(-50%); +} + +#background-stats-1{ + background-color: #2196f3; +} + +#background-stats-2{ + background-color: #00cbca; +} + +#content-container{ + z-index:2; + position:relative; + margin:0 auto; + display:table; + padding:10px; + max-width:940px; + height:100%; +} +#content-container-center{ + display:table-cell; + text-align:center; + vertical-align:middle; +} +#result{ + z-index: 3; + position: absolute; + bottom: 40px; + right: 20px; + color: #fff; + opacity: 0.5; + font-size: 45px; + font-weight: 600; +} +#choice{ + transition: all 300ms linear; + line-height:1.3em; + background:#fff; + box-shadow: 10px 0 0 #fff, -10px 0 0 #fff; + vertical-align:middle; + font-size:40px; + font-weight: 600; + width: 450px; + height: 200px; +} +#choice a{ + text-decoration:none; +} +#choice a:hover, #choice a:focus{ + outline:0; + text-decoration:underline; +} + +#choice .choice{ + width: 49%; + position: relative; + top: 50%; + transform: translateY(-50%); + text-align: left; + padding-left: 50px; +} + +#choice .choice .label{ + text-transform: uppercase; +} + +#choice .choice.dogs{ + color: #00cbca; + float: right; +} + +#choice .choice.cats{ + color: #2196f3; + float: left; +} +#background-stats{ + z-index:1; + height:100%; + width:100%; + position:absolute; +} +#background-stats div{ + transition: width 400ms ease-in-out; + display:inline-block; + margin-bottom:-4px; + width:50%; + height:100%; +} diff --git a/seed-data/Dockerfile b/seed-data/Dockerfile new file mode 100644 index 0000000..f970e42 --- /dev/null +++ b/seed-data/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.9-slim + +# add apache bench (ab) tool +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + apache2-utils \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /seed + +COPY . . + +# create POST data files with ab friendly formats +RUN python make-data.py + +CMD ["/seed/generate-votes.sh"] diff --git a/seed-data/generate-votes.sh b/seed-data/generate-votes.sh new file mode 100755 index 0000000..2f82563 --- /dev/null +++ b/seed-data/generate-votes.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# create 3000 votes (2000 for option a, 1000 for option b) +ab -n 1000 -c 50 -p posta -T "application/x-www-form-urlencoded" http://vote/ +ab -n 1000 -c 50 -p postb -T "application/x-www-form-urlencoded" http://vote/ +ab -n 1000 -c 50 -p posta -T "application/x-www-form-urlencoded" http://vote/ diff --git a/seed-data/make-data.py b/seed-data/make-data.py new file mode 100644 index 0000000..def9f97 --- /dev/null +++ b/seed-data/make-data.py @@ -0,0 +1,13 @@ +# this creates urlencode-friendly files without EOL +import urllib.parse + +outfile = open('postb', 'w') +params = ({ 'vote': 'b' }) +encoded = urllib.parse.urlencode(params) +outfile.write(encoded) +outfile.close() +outfile = open('posta', 'w') +params = ({ 'vote': 'a' }) +encoded = urllib.parse.urlencode(params) +outfile.write(encoded) +outfile.close() diff --git a/vote/Dockerfile b/vote/Dockerfile new file mode 100644 index 0000000..2681083 --- /dev/null +++ b/vote/Dockerfile @@ -0,0 +1,32 @@ +# base defines a base stage that uses the official python runtime base image +FROM python:3.11-slim AS base + +# Add curl for healthcheck +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl && \ + rm -rf /var/lib/apt/lists/* + +# Set the application directory +WORKDIR /usr/local/app + +# Install our requirements.txt +COPY requirements.txt ./requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# dev defines a stage for development, where it'll watch for filesystem changes +FROM base AS dev +RUN pip install watchdog +ENV FLASK_ENV=development +CMD ["python", "app.py"] + +# final defines the stage that will bundle the application for production +FROM base AS final + +# Copy our code from the current folder to the working directory inside the container +COPY . . + +# Make port 80 available for links and/or publish +EXPOSE 80 + +# Define our command to be run when launching the container +CMD ["gunicorn", "app:app", "-b", "0.0.0.0:80", "--log-file", "-", "--access-logfile", "-", "--workers", "4", "--keep-alive", "0"] diff --git a/vote/app.py b/vote/app.py new file mode 100644 index 0000000..5965466 --- /dev/null +++ b/vote/app.py @@ -0,0 +1,51 @@ +from flask import Flask, render_template, request, make_response, g +from redis import Redis +import os +import socket +import random +import json +import logging + +option_a = os.getenv('OPTION_A', "Cats") +option_b = os.getenv('OPTION_B', "Dogs") +hostname = socket.gethostname() + +app = Flask(__name__) + +gunicorn_error_logger = logging.getLogger('gunicorn.error') +app.logger.handlers.extend(gunicorn_error_logger.handlers) +app.logger.setLevel(logging.INFO) + +def get_redis(): + if not hasattr(g, 'redis'): + g.redis = Redis(host="redis", db=0, socket_timeout=5) + return g.redis + +@app.route("/", methods=['POST','GET']) +def hello(): + voter_id = request.cookies.get('voter_id') + if not voter_id: + voter_id = hex(random.getrandbits(64))[2:-1] + + vote = None + + if request.method == 'POST': + redis = get_redis() + vote = request.form['vote'] + app.logger.info('Received vote for %s', vote) + data = json.dumps({'voter_id': voter_id, 'vote': vote}) + redis.rpush('votes', data) + + resp = make_response(render_template( + 'index.html', + option_a=option_a, + option_b=option_b, + hostname=hostname, + vote=vote, + )) + resp.set_cookie('voter_id', voter_id) + return resp + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=80, debug=True, threaded=True) diff --git a/vote/requirements.txt b/vote/requirements.txt new file mode 100644 index 0000000..430bfdc --- /dev/null +++ b/vote/requirements.txt @@ -0,0 +1,3 @@ +Flask +Redis +gunicorn diff --git a/vote/static/stylesheets/style.css b/vote/static/stylesheets/style.css new file mode 100644 index 0000000..53de543 --- /dev/null +++ b/vote/static/stylesheets/style.css @@ -0,0 +1,129 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:400,700,600); + +*{ + box-sizing:border-box; +} +html,body{ + margin: 0; + padding: 0; + background-color: #F7F8F9; + height: 100vh; + font-family: 'Open Sans'; +} + +button{ + border-radius: 0; + width: 100%; + height: 50%; +} + +button[type="submit"] { + -webkit-appearance:none; -webkit-border-radius:0; +} + +button i{ + float: right; + padding-right: 30px; + margin-top: 3px; +} + +button.a{ + background-color: #1aaaf8; +} + +button.b{ + background-color: #00cbca; +} + +#tip{ + text-align: left; + color: #c0c9ce; + font-size: 14px; +} + +#hostname{ + position: absolute; + bottom: 100px; + right: 0; + left: 0; + color: #8f9ea8; + font-size: 24px; +} + +#content-container{ + z-index: 2; + position: relative; + margin: 0 auto; + display: table; + padding: 10px; + max-width: 940px; + height: 100%; +} +#content-container-center{ + display: table-cell; + text-align: center; +} + +#content-container-center h3{ + color: #254356; +} + +#choice{ + transition: all 300ms linear; + line-height: 1.3em; + display: inline; + vertical-align: middle; + font-size: 3em; +} +#choice a{ + text-decoration:none; +} +#choice a:hover, #choice a:focus{ + outline:0; + text-decoration:underline; +} + +#choice button{ + display: block; + height: 80px; + width: 330px; + border: none; + color: white; + text-transform: uppercase; + font-size:18px; + font-weight: 700; + margin-top: 10px; + margin-bottom: 10px; + text-align: left; + padding-left: 50px; +} + +#choice button.a:hover{ + background-color: #1488c6; +} + +#choice button.b:hover{ + background-color: #00a2a1; +} + +#choice button.a:focus{ + background-color: #1488c6; +} + +#choice button.b:focus{ + background-color: #00a2a1; +} + +#background-stats{ + z-index:1; + height:100%; + width:100%; + position:absolute; +} +#background-stats div{ + transition: width 400ms ease-in-out; + display:inline-block; + margin-bottom:-4px; + width:50%; + height:100%; +} diff --git a/vote/templates/index.html b/vote/templates/index.html new file mode 100644 index 0000000..fb58924 --- /dev/null +++ b/vote/templates/index.html @@ -0,0 +1,49 @@ + + + + + {{option_a}} vs {{option_b}}! + + + + + + + + +
+
+

{{option_a}} vs {{option_b}}!

+
+ + +
+
+ (Tip: you can change your vote) +
+
+ Processed by container ID {{hostname}} +
+
+
+ + + + {% if vote %} + + {% endif %} + + diff --git a/worker/Dockerfile b/worker/Dockerfile new file mode 100644 index 0000000..a3f92d7 --- /dev/null +++ b/worker/Dockerfile @@ -0,0 +1,28 @@ +# because of dotnet, we always build on amd64, and target platforms in cli +# dotnet doesn't support QEMU for building or running. +# (errors common in arm/v7 32bit) https://github.com/dotnet/dotnet-docker/issues/1537 +# https://hub.docker.com/_/microsoft-dotnet +# hadolint ignore=DL3029 +# to build for a different platform than your host, use --platform= +# for example, if you were on Intel (amd64) and wanted to build for ARM, you would use: +# docker buildx build --platform "linux/arm64/v8" . + +# build compiles the program for the builder's local platform +FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/dotnet/sdk:7.0 AS build +ARG TARGETPLATFORM +ARG TARGETARCH +ARG BUILDPLATFORM +RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" + +WORKDIR /source +COPY *.csproj . +RUN dotnet restore -a $TARGETARCH + +COPY . . +RUN dotnet publish -c release -o /app -a $TARGETARCH --self-contained false --no-restore + +# app image +FROM mcr.microsoft.com/dotnet/runtime:7.0 +WORKDIR /app +COPY --from=build /app . +ENTRYPOINT ["dotnet", "Worker.dll"] diff --git a/worker/Program.cs b/worker/Program.cs new file mode 100644 index 0000000..9b5fb74 --- /dev/null +++ b/worker/Program.cs @@ -0,0 +1,154 @@ +using System; +using System.Data.Common; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Newtonsoft.Json; +using Npgsql; +using StackExchange.Redis; + +namespace Worker +{ + public class Program + { + public static int Main(string[] args) + { + try + { + var pgsql = OpenDbConnection("Server=db;Username=postgres;Password=postgres;"); + var redisConn = OpenRedisConnection("redis"); + var redis = redisConn.GetDatabase(); + + // Keep alive is not implemented in Npgsql yet. This workaround was recommended: + // https://github.com/npgsql/npgsql/issues/1214#issuecomment-235828359 + var keepAliveCommand = pgsql.CreateCommand(); + keepAliveCommand.CommandText = "SELECT 1"; + + var definition = new { vote = "", voter_id = "" }; + while (true) + { + // Slow down to prevent CPU spike, only query each 100ms + Thread.Sleep(100); + + // Reconnect redis if down + if (redisConn == null || !redisConn.IsConnected) { + Console.WriteLine("Reconnecting Redis"); + redisConn = OpenRedisConnection("redis"); + redis = redisConn.GetDatabase(); + } + string json = redis.ListLeftPopAsync("votes").Result; + if (json != null) + { + var vote = JsonConvert.DeserializeAnonymousType(json, definition); + Console.WriteLine($"Processing vote for '{vote.vote}' by '{vote.voter_id}'"); + // Reconnect DB if down + if (!pgsql.State.Equals(System.Data.ConnectionState.Open)) + { + Console.WriteLine("Reconnecting DB"); + pgsql = OpenDbConnection("Server=db;Username=postgres;Password=postgres;"); + } + else + { // Normal +1 vote requested + UpdateVote(pgsql, vote.voter_id, vote.vote); + } + } + else + { + keepAliveCommand.ExecuteNonQuery(); + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.ToString()); + return 1; + } + } + + private static NpgsqlConnection OpenDbConnection(string connectionString) + { + NpgsqlConnection connection; + + while (true) + { + try + { + connection = new NpgsqlConnection(connectionString); + connection.Open(); + break; + } + catch (SocketException) + { + Console.Error.WriteLine("Waiting for db"); + Thread.Sleep(1000); + } + catch (DbException) + { + Console.Error.WriteLine("Waiting for db"); + Thread.Sleep(1000); + } + } + + Console.Error.WriteLine("Connected to db"); + + var command = connection.CreateCommand(); + command.CommandText = @"CREATE TABLE IF NOT EXISTS votes ( + id VARCHAR(255) NOT NULL UNIQUE, + vote VARCHAR(255) NOT NULL + )"; + command.ExecuteNonQuery(); + + return connection; + } + + private static ConnectionMultiplexer OpenRedisConnection(string hostname) + { + // Use IP address to workaround https://github.com/StackExchange/StackExchange.Redis/issues/410 + var ipAddress = GetIp(hostname); + Console.WriteLine($"Found redis at {ipAddress}"); + + while (true) + { + try + { + Console.Error.WriteLine("Connecting to redis"); + return ConnectionMultiplexer.Connect(ipAddress); + } + catch (RedisConnectionException) + { + Console.Error.WriteLine("Waiting for redis"); + Thread.Sleep(1000); + } + } + } + + private static string GetIp(string hostname) + => Dns.GetHostEntryAsync(hostname) + .Result + .AddressList + .First(a => a.AddressFamily == AddressFamily.InterNetwork) + .ToString(); + + private static void UpdateVote(NpgsqlConnection connection, string voterId, string vote) + { + var command = connection.CreateCommand(); + try + { + command.CommandText = "INSERT INTO votes (id, vote) VALUES (@id, @vote)"; + command.Parameters.AddWithValue("@id", voterId); + command.Parameters.AddWithValue("@vote", vote); + command.ExecuteNonQuery(); + } + catch (DbException) + { + command.CommandText = "UPDATE votes SET vote = @vote WHERE id = @id"; + command.ExecuteNonQuery(); + } + finally + { + command.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/worker/Worker.csproj b/worker/Worker.csproj new file mode 100644 index 0000000..0084507 --- /dev/null +++ b/worker/Worker.csproj @@ -0,0 +1,14 @@ + + + + Exe + net7.0 + + + + + + + + + \ No newline at end of file