mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-08 19:43:11 -04:00
15
.cfignore
15
.cfignore
@@ -1,5 +1,20 @@
|
|||||||
# This file is a copy of .gitignore except for file/folders created by the build
|
# This file is a copy of .gitignore except for file/folders created by the build
|
||||||
|
|
||||||
|
# from deploy-exclude.lst
|
||||||
|
*__pycache__*
|
||||||
|
.git/*
|
||||||
|
app/assets/*
|
||||||
|
bower_components/*
|
||||||
|
cache/*
|
||||||
|
.cache/*
|
||||||
|
node_modules/*
|
||||||
|
target/*
|
||||||
|
venv/*
|
||||||
|
.envrc
|
||||||
|
.cf/*
|
||||||
|
.pytest_cache/*
|
||||||
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|||||||
3
.github/actions/setup-project/action.yml
vendored
3
.github/actions/setup-project/action.yml
vendored
@@ -13,6 +13,9 @@ runs:
|
|||||||
uses: actions/setup-python@v3
|
uses: actions/setup-python@v3
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
- name: Install pipenv
|
||||||
|
shell: bash
|
||||||
|
run: pip install --upgrade pipenv
|
||||||
- name: Install application dependencies
|
- name: Install application dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make bootstrap
|
run: make bootstrap
|
||||||
|
|||||||
12
.github/workflows/checks.yml
vendored
12
.github/workflows/checks.yml
vendored
@@ -21,21 +21,23 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
- name: Run style checks
|
- name: Run style checks
|
||||||
run: flake8 .
|
run: pipenv run flake8 .
|
||||||
- name: Check imports alphabetized
|
- name: Check imports alphabetized
|
||||||
run: isort --check-only ./app ./tests
|
run: pipenv run isort --check-only ./app ./tests
|
||||||
- name: Run js lint
|
- name: Run js lint
|
||||||
run: npm run lint
|
run: npm run lint
|
||||||
- name: Run js tests
|
- name: Run js tests
|
||||||
run: npm test
|
run: npm test
|
||||||
- name: Run py tests
|
- name: Run py tests
|
||||||
run: pytest -n4 --maxfail=10
|
run: pipenv run pytest -n4 --maxfail=10
|
||||||
|
|
||||||
dependency-audits:
|
dependency-audits:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
|
- name: Create requirements.txt
|
||||||
|
run: pipenv requirements > requirements.txt
|
||||||
- uses: trailofbits/gh-action-pip-audit@v1.0.0
|
- uses: trailofbits/gh-action-pip-audit@v1.0.0
|
||||||
with:
|
with:
|
||||||
inputs: requirements.txt
|
inputs: requirements.txt
|
||||||
@@ -48,10 +50,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
- name: Install bandit
|
|
||||||
run: pip install bandit
|
|
||||||
- name: Run scan
|
- name: Run scan
|
||||||
run: bandit -r app/ --confidence-level medium
|
run: pipenv run bandit -r app/ --confidence-level medium
|
||||||
|
|
||||||
dynamic-scan:
|
dynamic-scan:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
6
.github/workflows/daily_checks.yml
vendored
6
.github/workflows/daily_checks.yml
vendored
@@ -24,6 +24,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
|
- name: Create requirements.txt
|
||||||
|
run: pipenv requirements > requirements.txt
|
||||||
- uses: trailofbits/gh-action-pip-audit@v1.0.0
|
- uses: trailofbits/gh-action-pip-audit@v1.0.0
|
||||||
with:
|
with:
|
||||||
inputs: requirements.txt
|
inputs: requirements.txt
|
||||||
@@ -36,10 +38,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
- name: Install bandit
|
|
||||||
run: pip install bandit
|
|
||||||
- name: Run scan
|
- name: Run scan
|
||||||
run: bandit -r app/ --confidence-level medium
|
run: pipenv run bandit -r app/ --confidence-level medium
|
||||||
|
|
||||||
dynamic-scan:
|
dynamic-scan:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
3
.github/workflows/deploy-demo.yml
vendored
3
.github/workflows/deploy-demo.yml
vendored
@@ -40,6 +40,9 @@ jobs:
|
|||||||
|
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
|
|
||||||
|
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||||
|
run: pipenv requirements > requirements.txt
|
||||||
|
|
||||||
- name: Deploy to cloud.gov
|
- name: Deploy to cloud.gov
|
||||||
uses: 18f/cg-deploy-action@main
|
uses: 18f/cg-deploy-action@main
|
||||||
env:
|
env:
|
||||||
|
|||||||
3
.github/workflows/deploy.yml
vendored
3
.github/workflows/deploy.yml
vendored
@@ -45,6 +45,9 @@ jobs:
|
|||||||
|
|
||||||
- uses: ./.github/actions/setup-project
|
- uses: ./.github/actions/setup-project
|
||||||
|
|
||||||
|
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
|
||||||
|
run: pipenv requirements > requirements.txt
|
||||||
|
|
||||||
- name: Deploy to cloud.gov
|
- name: Deploy to cloud.gov
|
||||||
uses: 18f/cg-deploy-action@main
|
uses: 18f/cg-deploy-action@main
|
||||||
env:
|
env:
|
||||||
|
|||||||
146
Makefile
146
Makefile
@@ -7,16 +7,6 @@ APP_VERSION_FILE = app/version.py
|
|||||||
GIT_BRANCH ?= $(shell git symbolic-ref --short HEAD 2> /dev/null || echo "detached")
|
GIT_BRANCH ?= $(shell git symbolic-ref --short HEAD 2> /dev/null || echo "detached")
|
||||||
GIT_COMMIT ?= $(shell git rev-parse HEAD 2> /dev/null || echo "")
|
GIT_COMMIT ?= $(shell git rev-parse HEAD 2> /dev/null || echo "")
|
||||||
|
|
||||||
CF_API ?= api.cloud.service.gov.uk
|
|
||||||
CF_ORG ?= govuk-notify
|
|
||||||
CF_SPACE ?= ${DEPLOY_ENV}
|
|
||||||
CF_HOME ?= ${HOME}
|
|
||||||
CF_APP ?= notify-admin
|
|
||||||
CF_MANIFEST_PATH ?= /tmp/manifest.yml
|
|
||||||
$(eval export CF_HOME)
|
|
||||||
|
|
||||||
NOTIFY_CREDENTIALS ?= ~/.notify-credentials
|
|
||||||
|
|
||||||
VIRTUALENV_ROOT := $(shell [ -z $$VIRTUAL_ENV ] && echo $$(pwd)/venv || echo $$VIRTUAL_ENV)
|
VIRTUALENV_ROOT := $(shell [ -z $$VIRTUAL_ENV ] && echo $$(pwd)/venv || echo $$VIRTUAL_ENV)
|
||||||
|
|
||||||
NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || echo "/usr/local/share/nvm/nvm.sh")
|
NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || echo "/usr/local/share/nvm/nvm.sh")
|
||||||
@@ -25,7 +15,7 @@ NVMSH := $(shell [ -f "$(HOME)/.nvm/nvm.sh" ] && echo "$(HOME)/.nvm/nvm.sh" || e
|
|||||||
|
|
||||||
.PHONY: bootstrap
|
.PHONY: bootstrap
|
||||||
bootstrap: generate-version-file ## Set up everything to run the app
|
bootstrap: generate-version-file ## Set up everything to run the app
|
||||||
pip3 install -r requirements_for_test.txt
|
pipenv install --dev
|
||||||
source $(NVMSH) --no-use && nvm install && npm ci --no-audit
|
source $(NVMSH) --no-use && nvm install && npm ci --no-audit
|
||||||
source $(NVMSH) && npm run build
|
source $(NVMSH) && npm run build
|
||||||
|
|
||||||
@@ -35,7 +25,7 @@ watch-frontend: ## Build frontend and watch for changes
|
|||||||
|
|
||||||
.PHONY: run-flask
|
.PHONY: run-flask
|
||||||
run-flask: ## Run flask
|
run-flask: ## Run flask
|
||||||
flask run -p 6012 --host=0.0.0.0
|
pipenv run flask run -p 6012 --host=0.0.0.0
|
||||||
|
|
||||||
.PHONY: npm-audit
|
.PHONY: npm-audit
|
||||||
npm-audit: ## Check for vulnerabilities in NPM packages
|
npm-audit: ## Check for vulnerabilities in NPM packages
|
||||||
@@ -45,14 +35,6 @@ npm-audit: ## Check for vulnerabilities in NPM packages
|
|||||||
help:
|
help:
|
||||||
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
.PHONY: virtualenv
|
|
||||||
virtualenv:
|
|
||||||
[ -z $$VIRTUAL_ENV ] && [ ! -d venv ] && python3 -m venv venv || true
|
|
||||||
|
|
||||||
.PHONY: upgrade-pip
|
|
||||||
upgrade-pip: virtualenv
|
|
||||||
${VIRTUALENV_ROOT}/bin/pip install --upgrade pip
|
|
||||||
|
|
||||||
.PHONY: generate-version-file
|
.PHONY: generate-version-file
|
||||||
generate-version-file: ## Generates the app version file
|
generate-version-file: ## Generates the app version file
|
||||||
@echo -e "__git_commit__ = \"${GIT_COMMIT}\"\n__time__ = \"${DATE}\"" > ${APP_VERSION_FILE}
|
@echo -e "__git_commit__ = \"${GIT_COMMIT}\"\n__time__ = \"${DATE}\"" > ${APP_VERSION_FILE}
|
||||||
@@ -62,12 +44,12 @@ test: py-lint py-test js-lint js-test ## Run tests
|
|||||||
|
|
||||||
.PHONY: py-lint
|
.PHONY: py-lint
|
||||||
py-lint: ## Run python linting scanners
|
py-lint: ## Run python linting scanners
|
||||||
flake8 .
|
pipenv run flake8 .
|
||||||
isort --check-only ./app ./tests
|
pipenv run isort --check-only ./app ./tests
|
||||||
|
|
||||||
.PHONY: py-test
|
.PHONY: py-test
|
||||||
py-test: ## Run python unit tests
|
py-test: ## Run python unit tests
|
||||||
py.test -n auto --maxfail=10 tests/
|
pipenv run py.test -n auto --maxfail=10 tests/
|
||||||
|
|
||||||
.PHONY: js-lint
|
.PHONY: js-lint
|
||||||
js-lint: ## Run javascript linting scanners
|
js-lint: ## Run javascript linting scanners
|
||||||
@@ -79,26 +61,23 @@ js-test: ## Run javascript unit tests
|
|||||||
|
|
||||||
.PHONY: fix-imports
|
.PHONY: fix-imports
|
||||||
fix-imports: ## Fix imports using isort
|
fix-imports: ## Fix imports using isort
|
||||||
isort ./app ./tests
|
pipenv run isort ./app ./tests
|
||||||
|
|
||||||
.PHONY: freeze-requirements
|
.PHONY: freeze-requirements
|
||||||
freeze-requirements: ## create static requirements.txt
|
freeze-requirements: ## create static requirements.txt
|
||||||
pip install --upgrade pip-tools
|
pipenv requirements > requirements.txt
|
||||||
pip-compile requirements.in
|
|
||||||
|
|
||||||
.PHONY: pip-audit
|
.PHONY: pip-audit
|
||||||
pip-audit:
|
pip-audit:
|
||||||
pip install --upgrade pip-audit
|
pipenv run pip-audit -r requirements.txt -l --ignore-vuln PYSEC-2022-237
|
||||||
pip-audit -r requirements.txt -l --ignore-vuln PYSEC-2022-237
|
-pipenv run pip-audit -r requirements_for_test.txt -l
|
||||||
-pip-audit -r requirements_for_test.txt -l
|
|
||||||
|
|
||||||
.PHONY: audit
|
.PHONY: audit
|
||||||
audit: npm-audit pip-audit
|
audit: npm-audit pip-audit
|
||||||
|
|
||||||
.PHONY: static-scan
|
.PHONY: static-scan
|
||||||
static-scan:
|
static-scan:
|
||||||
pip install bandit
|
pipenv run bandit -r app/
|
||||||
bandit -r app/
|
|
||||||
|
|
||||||
.PHONY: a11y-scan
|
.PHONY: a11y-scan
|
||||||
a11y-scan:
|
a11y-scan:
|
||||||
@@ -112,101 +91,22 @@ clean:
|
|||||||
|
|
||||||
## DEPLOYMENT
|
## DEPLOYMENT
|
||||||
|
|
||||||
.PHONY: check-env-vars
|
|
||||||
check-env-vars: ## Check mandatory environment variables
|
|
||||||
$(if ${DEPLOY_ENV},,$(error Must specify DEPLOY_ENV))
|
|
||||||
$(if ${DNS_NAME},,$(error Must specify DNS_NAME))
|
|
||||||
|
|
||||||
.PHONY: preview
|
|
||||||
preview: ## Set environment to preview
|
|
||||||
$(eval export DEPLOY_ENV=preview)
|
|
||||||
$(eval export DNS_NAME="notify.works")
|
|
||||||
@true
|
|
||||||
|
|
||||||
.PHONY: staging
|
|
||||||
staging: ## Set environment to staging
|
|
||||||
$(eval export DEPLOY_ENV=staging)
|
|
||||||
$(eval export DNS_NAME="staging-notify.works")
|
|
||||||
@true
|
|
||||||
|
|
||||||
.PHONY: production
|
|
||||||
production: ## Set environment to production
|
|
||||||
$(eval export DEPLOY_ENV=production)
|
|
||||||
$(eval export DNS_NAME="notifications.service.gov.uk")
|
|
||||||
@true
|
|
||||||
|
|
||||||
.PHONY: cf-login
|
|
||||||
cf-login: ## Log in to Cloud Foundry
|
|
||||||
$(if ${CF_USERNAME},,$(error Must specify CF_USERNAME))
|
|
||||||
$(if ${CF_PASSWORD},,$(error Must specify CF_PASSWORD))
|
|
||||||
$(if ${CF_SPACE},,$(error Must specify CF_SPACE))
|
|
||||||
@echo "Logging in to Cloud Foundry on ${CF_API}"
|
|
||||||
@cf login -a "${CF_API}" -u ${CF_USERNAME} -p "${CF_PASSWORD}" -o "${CF_ORG}" -s "${CF_SPACE}"
|
|
||||||
|
|
||||||
.PHONY: generate-manifest
|
|
||||||
generate-manifest:
|
|
||||||
$(if ${CF_APP},,$(error Must specify CF_APP))
|
|
||||||
$(if ${CF_SPACE},,$(error Must specify CF_SPACE))
|
|
||||||
$(if $(shell which gpg2), $(eval export GPG=gpg2), $(eval export GPG=gpg))
|
|
||||||
$(if ${GPG_PASSPHRASE_TXT}, $(eval export DECRYPT_CMD=echo -n $$$${GPG_PASSPHRASE_TXT} | ${GPG} --quiet --batch --passphrase-fd 0 --pinentry-mode loopback -d), $(eval export DECRYPT_CMD=${GPG} --quiet --batch -d))
|
|
||||||
|
|
||||||
@jinja2 --strict manifest.yml.j2 \
|
|
||||||
-D environment=${CF_SPACE} \
|
|
||||||
-D CF_APP=${CF_APP} \
|
|
||||||
--format=yaml \
|
|
||||||
<(${DECRYPT_CMD} ${NOTIFY_CREDENTIALS}/credentials/${CF_SPACE}/paas/environment-variables.gpg) 2>&1
|
|
||||||
|
|
||||||
.PHONY: upload-static ## Upload the static files to be served from S3
|
.PHONY: upload-static ## Upload the static files to be served from S3
|
||||||
upload-static:
|
upload-static:
|
||||||
aws s3 cp --region us-west-2 --recursive --cache-control max-age=315360000,immutable ./app/static s3://${DNS_NAME}-static
|
aws s3 cp --region us-west-2 --recursive --cache-control max-age=315360000,immutable ./app/static s3://${DNS_NAME}-static
|
||||||
|
|
||||||
.PHONY: cf-deploy
|
# .PHONY: cf-failwhale-deployed
|
||||||
cf-deploy: ## Deploys the app to Cloud Foundry
|
# cf-failwhale-deployed:
|
||||||
$(if ${CF_SPACE},,$(error Must specify CF_SPACE))
|
# @cf app notify-admin-failwhale --guid || (echo "notify-admin-failwhale is not deployed on ${CF_SPACE}" && exit 1)
|
||||||
@cf app --guid notify-admin || exit 1
|
|
||||||
# cancel any existing deploys to ensure we can apply manifest (if a deploy is in progress you'll see ScaleDisabledDuringDeployment)
|
|
||||||
cf cancel-deployment ${CF_APP} || true
|
|
||||||
|
|
||||||
# generate manifest (including secrets) and write it to CF_MANIFEST_PATH (in /tmp/)
|
# .PHONY: enable-failwhale
|
||||||
make -s CF_APP=${CF_APP} generate-manifest > ${CF_MANIFEST_PATH}
|
# enable-failwhale: cf-target cf-failwhale-deployed ## Enable the failwhale app and disable admin
|
||||||
# reads manifest from CF_MANIFEST_PATH
|
# @cf map-route notify-admin-failwhale ${DNS_NAME} --hostname www
|
||||||
CF_STARTUP_TIMEOUT=10 cf push ${CF_APP} --strategy=rolling -f ${CF_MANIFEST_PATH}
|
# @cf unmap-route notify-admin ${DNS_NAME} --hostname www
|
||||||
# delete old manifest file
|
# @echo "Failwhale is enabled"
|
||||||
rm -f ${CF_MANIFEST_PATH}
|
|
||||||
|
|
||||||
.PHONY: cf-deploy-prototype
|
# .PHONY: disable-failwhale
|
||||||
cf-deploy-prototype: cf-target ## Deploys the first prototype to Cloud Foundry
|
# disable-failwhale: cf-target cf-failwhale-deployed ## Disable the failwhale app and enable admin
|
||||||
make -s CF_APP=notify-admin-prototype generate-manifest > ${CF_MANIFEST_PATH}
|
# @cf map-route notify-admin ${DNS_NAME} --hostname www
|
||||||
cf push notify-admin-prototype --strategy=rolling -f ${CF_MANIFEST_PATH}
|
# @cf unmap-route notify-admin-failwhale ${DNS_NAME} --hostname www
|
||||||
rm -f ${CF_MANIFEST_PATH}
|
# @echo "Failwhale is disabled"
|
||||||
|
|
||||||
.PHONY: cf-deploy-prototype-2
|
|
||||||
cf-deploy-prototype-2: cf-target ## Deploys the second prototype to Cloud Foundry
|
|
||||||
make -s CF_APP=notify-admin-prototype-2 generate-manifest > ${CF_MANIFEST_PATH}
|
|
||||||
cf push notify-admin-prototype-2 --strategy=rolling -f ${CF_MANIFEST_PATH}
|
|
||||||
rm -f ${CF_MANIFEST_PATH}
|
|
||||||
|
|
||||||
.PHONY: cf-rollback
|
|
||||||
cf-rollback: cf-target ## Rollbacks the app to the previous release
|
|
||||||
cf cancel-deployment ${CF_APP}
|
|
||||||
rm -f ${CF_MANIFEST_PATH}
|
|
||||||
|
|
||||||
.PHONY: cf-target
|
|
||||||
cf-target: check-env-vars
|
|
||||||
@cf target -o ${CF_ORG} -s ${CF_SPACE}
|
|
||||||
|
|
||||||
.PHONY: cf-failwhale-deployed
|
|
||||||
cf-failwhale-deployed:
|
|
||||||
@cf app notify-admin-failwhale --guid || (echo "notify-admin-failwhale is not deployed on ${CF_SPACE}" && exit 1)
|
|
||||||
|
|
||||||
.PHONY: enable-failwhale
|
|
||||||
enable-failwhale: cf-target cf-failwhale-deployed ## Enable the failwhale app and disable admin
|
|
||||||
@cf map-route notify-admin-failwhale ${DNS_NAME} --hostname www
|
|
||||||
@cf unmap-route notify-admin ${DNS_NAME} --hostname www
|
|
||||||
@echo "Failwhale is enabled"
|
|
||||||
|
|
||||||
.PHONY: disable-failwhale
|
|
||||||
disable-failwhale: cf-target cf-failwhale-deployed ## Disable the failwhale app and enable admin
|
|
||||||
@cf map-route notify-admin ${DNS_NAME} --hostname www
|
|
||||||
@cf unmap-route notify-admin-failwhale ${DNS_NAME} --hostname www
|
|
||||||
@echo "Failwhale is disabled"
|
|
||||||
|
|||||||
56
Pipfile
Normal file
56
Pipfile
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
ago = "==0.0.93"
|
||||||
|
awscli-cwlogs = ">=1.4,<1.5"
|
||||||
|
blinker = "==1.4"
|
||||||
|
fido2 = "==0.9.3"
|
||||||
|
flask = "==2.1.2"
|
||||||
|
flask-basicauth = "==0.2.0"
|
||||||
|
flask-login = "==0.6.1"
|
||||||
|
flask-wtf = "==1.0.1"
|
||||||
|
gds-metrics = {version = "==0.2.4", ref = "6f1840a57b6fb1ee40b7e84f2f18ec229de8aa72", git = "https://github.com/alphagov/gds_metrics_python.git"}
|
||||||
|
govuk-bank-holidays = "==0.11"
|
||||||
|
govuk-frontend-jinja = {version = "==0.5.8-alpha", git = "https://github.com/alphagov/govuk-frontend-jinja.git"}
|
||||||
|
gunicorn = {version = "==20.1.0", extras = ["eventlet"], ref = "1299ea9e967a61ae2edebe191082fd169b864c64", git = "https://github.com/benoitc/gunicorn.git"}
|
||||||
|
humanize = "==4.1.0"
|
||||||
|
itsdangerous = "==2.1.2"
|
||||||
|
jinja2 = "==3.1.2"
|
||||||
|
notifications-python-client = "==6.3.0"
|
||||||
|
notifications-utils = {version = "==56.0.3", git = "https://github.com/GSA/notifications-utils.git"}
|
||||||
|
prometheus-client = "==0.14.1"
|
||||||
|
pyexcel = "==0.7.0"
|
||||||
|
pyexcel-io = "==0.6.6"
|
||||||
|
pyexcel-ods3 = "==0.6.1"
|
||||||
|
pyexcel-xls = "==0.7.0"
|
||||||
|
pyexcel-xlsx = "==0.6.0"
|
||||||
|
pyproj = "==3.3.1"
|
||||||
|
python-dotenv = "==0.20.0"
|
||||||
|
pytz = "==2022.1"
|
||||||
|
rtreelib = "==0.2.0"
|
||||||
|
werkzeug = "==2.1.2"
|
||||||
|
wtforms = "==3.0.1"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
isort = "==5.10.1"
|
||||||
|
pytest = "==7.1.2"
|
||||||
|
pytest-env = "==0.6.2"
|
||||||
|
pytest-mock = "==3.7.0"
|
||||||
|
pytest-xdist = "==2.5.0"
|
||||||
|
beautifulsoup4 = "==4.11.1"
|
||||||
|
freezegun = "==1.2.1"
|
||||||
|
flake8 = "==4.0.1"
|
||||||
|
flake8-bugbear = "==22.4.25"
|
||||||
|
flake8-print = "==5.0.0"
|
||||||
|
moto = "==3.1.7"
|
||||||
|
requests-mock = "==1.9.3"
|
||||||
|
# used for creating manifest file locally
|
||||||
|
jinja2-cli = {version = "==0.8.2", extras = ["yaml"]}
|
||||||
|
pip-audit = "*"
|
||||||
|
bandit = "*"
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.9"
|
||||||
1871
Pipfile.lock
generated
Normal file
1871
Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
78
README.md
78
README.md
@@ -1,43 +1,67 @@
|
|||||||
# US Notify Admin
|
# Notify UI
|
||||||
|
|
||||||
Cloned from the brilliant work of the team at [GOV.UK Notify](https://github.com/alphagov/notifications-admin), cheers!
|
This is the Notify front-end for government users and admins. To see it in action, check out [the demo site](https://notify-demo.app.cloud.gov) (contact team for credentials).
|
||||||
|
|
||||||
US Notify admin application - https://notify-demo.app.cloud.gov (contact team for access)
|
Through the interface, users can:
|
||||||
|
|
||||||
- Register and manage users
|
- Register and manage users
|
||||||
- Create and manage services
|
- Create and manage services
|
||||||
- Send batch emails and SMS by uploading a CSV
|
- Send batch SMS by uploading a CSV
|
||||||
- Show history of notifications
|
- View their history of notifications
|
||||||
|
|
||||||
## QUICKSTART
|
The [Notify API](https://github.com/GSA/notifications-api) provides the UI's backend and is required for most things to function. Set that up first!
|
||||||
|
|
||||||
---
|
## Local setup
|
||||||
**NOTE**: Set up the [notifications-api repo](https://github.com/18F/notifications-api) locally **FIRST**, you'll need both the docker network it provides and a functioning api to make use of the notifications-admin repo. It is expected as a byproduct of getting notifications-api running you will also be running VS Code and the Remote Containers extension, and that docker daemon is running and the API is as well.
|
|
||||||
|
|
||||||
Open the notifications-admin repo in VS Code (File->Open Folder, select notifications-admin folder)
|
### Direct setup
|
||||||
|
|
||||||
create a .env file as detailed in the .env Setup section below
|
1. Get the API running
|
||||||
|
|
||||||
Using VS Code's command pallette (cmd+shift+p), search "Remote Containers: Open folder in Container..."
|
1. Install [pipenv](https://pipenv.pypa.io/en/latest/)
|
||||||
|
|
||||||
choose devcontainer-admin folder (note: this is a subfolder of notifications-admin/). This will open a new window, closing the current one in the process. After the new window loads, hit "show logs" link in the bottom-right. If this is the first build it will take a few minutes to create the image. The process completes shortly after running gulp.js and compiling front-end files.
|
1. Install Python and Node dependencies
|
||||||
|
|
||||||
Select View->Open View..., then search/select “ports”. Await a green dot on the port view, then open a new terminal and run the web server:
|
`make bootstrap`
|
||||||
`make run-flask`
|
|
||||||
|
|
||||||
Visit [localhost:6012](http://localhost:6012)
|
1. Create the .env file
|
||||||
|
|
||||||
NOTE: any .py code changes you make should be picked up automatically in development. If you're developing JavaScript code, open another vscode terminal and run `npm run watch` to achieve the same.
|
```
|
||||||
|
cp sample.env .env
|
||||||
|
# follow the instructions in .env
|
||||||
|
```
|
||||||
|
|
||||||
---
|
1. Run the Flask server
|
||||||
## .env Setup
|
|
||||||
|
|
||||||
create a .env file using sample.env as a template
|
`make run-flask`
|
||||||
`cp sample.env .env` (or via VS Code file browser)
|
|
||||||
|
|
||||||
from the notifications-api checkout, copy the values in that repo's .env file for `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` into this repo's .env file.
|
1. Go to http://localhost:6012
|
||||||
|
|
||||||
Change `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` to what you'd like them to be for this deployment.
|
### VS Code && Docker installation
|
||||||
|
|
||||||
|
If you're working in VS Code, you can also leverage Docker for a containerized dev environment
|
||||||
|
|
||||||
|
1. Get the API running, including the Docker network
|
||||||
|
|
||||||
|
1. Create the .env file
|
||||||
|
|
||||||
|
```
|
||||||
|
cp sample.env .env
|
||||||
|
# follow the instructions in .env
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Install the Remote-Containers plug-in in VS Code
|
||||||
|
|
||||||
|
1. Using the command palette (shift+cmd+p) or green button thingy in the bottom left, search and select “Remote Containers: Open Folder in Container...” When prompted, choose **devcontainer-admin** folder (note: this is a *subfolder* of notifications-admin). This will start the container in a new window, replacing the current one.
|
||||||
|
|
||||||
|
1. Wait a few minutes while things happen 🍵
|
||||||
|
|
||||||
|
1. Open a VS Code terminal and run the Flask application:
|
||||||
|
|
||||||
|
`make run-flask`
|
||||||
|
|
||||||
|
1. Go to http://localhost:6012
|
||||||
|
|
||||||
|
NOTE: when you change .env in the future, you'll need to rebuild the devcontainer for the change to take effect. VS Code _should_ detect the change and prompt you with a toast notification during a cached build. If not, you can find a manual rebuild in command pallette or just `docker rm` the notifications-api container.
|
||||||
|
|
||||||
## To test the application
|
## To test the application
|
||||||
From a terminal within the running devcontainer:
|
From a terminal within the running devcontainer:
|
||||||
@@ -59,7 +83,7 @@ Unlike most of the tests and scans, pa11y-ci cannot currently be run from within
|
|||||||
1. Run `make run-flask` from within the devcontainer
|
1. Run `make run-flask` from within the devcontainer
|
||||||
2. Run `make a11y-scan` from your host computer.
|
2. Run `make a11y-scan` from your host computer.
|
||||||
|
|
||||||
## Further docs [STILL UK DOCS]
|
## Further docs from UK
|
||||||
|
|
||||||
- [Working with static assets](docs/static-assets.md)
|
- [Working with static assets](docs/static-assets.md)
|
||||||
- [JavaScript documentation](https://github.com/alphagov/notifications-manuals/wiki/JavaScript-Documentation)
|
- [JavaScript documentation](https://github.com/alphagov/notifications-manuals/wiki/JavaScript-Documentation)
|
||||||
@@ -72,3 +96,11 @@ Work through [commit `543be77`](https://github.com/GSA/notifications-admin/commi
|
|||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
As stated in [CONTRIBUTING.md](CONTRIBUTING.md), all contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.
|
As stated in [CONTRIBUTING.md](CONTRIBUTING.md), all contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.
|
||||||
|
|
||||||
|
## About the TTS Public Benefits Studio
|
||||||
|
|
||||||
|
The Public Benefits Studio is a team inside of [GSA’s Technology Transformation Services](https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services) (TTS), home to innovative programs like [18F](https://18f.gsa.gov/) and [Login.gov](https://login.gov). We collaborate with benefits programs to develop shared technology tools and best practices that reduce the burden of navigating government programs for low income individuals and families.
|
||||||
|
|
||||||
|
We’re a cross-functional team of technologists with specialized experience working across public benefits programs like Medicaid, SNAP, and unemployment insurance.
|
||||||
|
|
||||||
|
For more information on what we're working on, the Notify tool, and how to get involved with our team, [see our flyer.](https://github.com/GSA/notifications-admin/blob/main/docs/notify-pilot-flyer.md)
|
||||||
|
|||||||
@@ -145,11 +145,15 @@ class Production(Config):
|
|||||||
|
|
||||||
class Staging(Production):
|
class Staging(Production):
|
||||||
BASIC_AUTH_FORCE = True
|
BASIC_AUTH_FORCE = True
|
||||||
HEADER_COLOUR = '#6F72AF' # $mauve
|
HEADER_COLOUR = '#00ff00' # $green
|
||||||
|
|
||||||
|
|
||||||
class Demo(Staging):
|
class Demo(Staging):
|
||||||
pass
|
HEADER_COLOUR = '#6F72AF' # $mauve
|
||||||
|
|
||||||
|
|
||||||
|
class Sandbox(Staging):
|
||||||
|
HEADER_COLOUR = '#ff0000' # $red
|
||||||
|
|
||||||
|
|
||||||
class Scanning(Production):
|
class Scanning(Production):
|
||||||
@@ -167,5 +171,6 @@ configs = {
|
|||||||
'scanning': Scanning,
|
'scanning': Scanning,
|
||||||
'staging': Staging,
|
'staging': Staging,
|
||||||
'demo': Demo,
|
'demo': Demo,
|
||||||
|
'sandbox': Sandbox,
|
||||||
'production': Production
|
'production': Production
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
<h2 class="heading-medium">Links and URLs</h2>
|
<h2 class="heading-medium">Links and URLs</h2>
|
||||||
<p class="bottom-gutter-1-3">
|
<p class="bottom-gutter-1-3">
|
||||||
Always use full URLs, starting with https://
|
Always use full URLs, starting with https://. For example:
|
||||||
</p>
|
|
||||||
{{ govukInsetText({
|
{{ govukInsetText({
|
||||||
"text": "Apply now at https://www.gov.uk/example",
|
"text": "Apply now at https://www.usa.gov/example",
|
||||||
"classes": "govuk-!-margin-top-0"})
|
"classes": "govuk-!-margin-top-0"})
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{% block content_column_content %}
|
{% block content_column_content %}
|
||||||
|
|
||||||
<h1 class="heading heading-large">Text messages</h1>
|
<h1 class="heading heading-large">Text messages</h1>
|
||||||
<p class="govuk-body">Send thousands of free text messages to UK and international numbers with US Notify.</p>
|
<p class="govuk-body">Send thousands of free text messages to US phone numbers with Notify.</p>
|
||||||
{% if not current_user.is_authenticated %}
|
{% if not current_user.is_authenticated %}
|
||||||
<p class="govuk-body"><a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.register') }}">Create an account</a> and try Notify for yourself.</p>
|
<p class="govuk-body"><a class="govuk-link govuk-link--no-visited-state" href="{{ url_for('main.register') }}">Create an account</a> and try Notify for yourself.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="heading-small" id="international-numbers">Sending text messages to international numbers</h3>
|
<h3 class="heading-small" id="international-numbers">Sending text messages to international numbers</h3>
|
||||||
<p class="govuk-body">It might cost more to send text messages to international numbers than UK ones, depending on the country.</p>
|
<p class="govuk-body">It might cost more to send text messages to international numbers than US ones, depending on the country.</p>
|
||||||
{% set smsIntRates %}
|
{% set smsIntRates %}
|
||||||
{{ live_search(target_selector='#international-pricing .table-row', show=True, form=search_form, label='Search by country name or code') }}
|
{{ live_search(target_selector='#international-pricing .table-row', show=True, form=search_form, label='Search by country name or code') }}
|
||||||
|
|
||||||
|
|||||||
11
deploy-config/sandbox.yml
Normal file
11
deploy-config/sandbox.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
env: sandbox
|
||||||
|
instances: 1
|
||||||
|
memory: 1G
|
||||||
|
public_admin_route: notify-sandbox.app.cloud.gov
|
||||||
|
ADMIN_CLIENT_USERNAME: notify-admin
|
||||||
|
ADMIN_CLIENT_SECRET: dev-notify-secret-key
|
||||||
|
DANGEROUS_SALT: dev-notify-salt
|
||||||
|
SECRET_KEY: dev-notify-secret-key
|
||||||
|
BASIC_AUTH_USERNAME: sandbox
|
||||||
|
BASIC_AUTH_PASSWORD: sandbox
|
||||||
|
REDIS_ENABLED: 1
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
*__pycache__*
|
|
||||||
.git/*
|
|
||||||
app/assets/*
|
|
||||||
bower_components/*
|
|
||||||
cache/*
|
|
||||||
.cache/*
|
|
||||||
node_modules/*
|
|
||||||
target/*
|
|
||||||
venv/*
|
|
||||||
.envrc
|
|
||||||
.cf/*
|
|
||||||
.pytest_cache/*
|
|
||||||
99
docs/notify-pilot-info.md
Normal file
99
docs/notify-pilot-info.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
### The Public Benefits Studio
|
||||||
|
|
||||||
|
The Public Benefits Studio is a team inside of GSA’s Technology
|
||||||
|
Transformation Services (TTS), home to innovative programs like 18F and
|
||||||
|
Login.gov. We collaborate **with benefits programs to develop shared
|
||||||
|
technology tools and best practices that reduce the burden of navigating
|
||||||
|
government programs for low income individuals and families.**
|
||||||
|
|
||||||
|
|
||||||
|
We’re a cross-functional team of technologists with specialized
|
||||||
|
experience working across public benefits programs like Medicaid, SNAP,
|
||||||
|
and unemployment insurance.
|
||||||
|
|
||||||
|
### WHAT WE’RE CURRENTLY EXPLORING
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 40%" />
|
||||||
|
<col style="width: 59%" />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><p><strong>SMS & multichannel notifications</strong></p>
|
||||||
|
<p>Helping individuals and families stay better informed of actions they
|
||||||
|
need to take related to benefits, such as reminders for upcoming
|
||||||
|
appointments and enrolling in or recertifying their benefits.</p></td>
|
||||||
|
<td>According to Pew Research, 97% of US adults with an income of less
|
||||||
|
than $30,000 have a cellphone, and there have already been <a
|
||||||
|
href="https://bdtrust.org/nudging-benefits-access-in-the-right-direction/"><u>successful
|
||||||
|
pilots</u></a> to supplement traditional notification channels with SMS
|
||||||
|
that both decreased re-enrollment churn and saved the administering
|
||||||
|
agency money.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
OUR FIRST BET: US Notify
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 67%" />
|
||||||
|
<col style="width: 32%" />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><h3 id="section"><img src="https://user-images.githubusercontent.com/6556888/200647299-b991a5ed-ecf4-4d74-b238-f3def9bd1503.png"
|
||||||
|
style="width:4.63788in;height:3.75964in" /></h3></td>
|
||||||
|
<td><h3 id="create-custom-content">Create custom content</h3>
|
||||||
|
<p>No technical knowledge needed to create direct, actionable
|
||||||
|
messages.</p>
|
||||||
|
<h3 id="send-bulk-messages">Send bulk messages </h3>
|
||||||
|
<p>Upload a .csv file with necessary information and Notify sends
|
||||||
|
messages customized to each recipient</p>
|
||||||
|
<h3 id="see-how-messages-perform">See how messages perform</h3>
|
||||||
|
<p>Track how many messages you’ve sent and monitor successful delivery
|
||||||
|
rates</p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
**US Notify** will be a federally-run shared service allowing staff to
|
||||||
|
send thousands of customized-to-the-user text messages per year at
|
||||||
|
little-to-no-cost. The easy interface requires no technical expertise to
|
||||||
|
use and the setup process takes only ten minutes.
|
||||||
|
|
||||||
|
### WHERE WE’RE AT
|
||||||
|
|
||||||
|
**We’re in the early stages of assessing this product’s market fit and
|
||||||
|
targeting to pilot** this shared service with at least 3 partners in
|
||||||
|
spring of 2023. Following our first pilot, we intend to scale by adding
|
||||||
|
additional features based on partner needs.
|
||||||
|
|
||||||
|
| | | |
|
||||||
|
|-------------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
||||||
|
| **Pilot Launch Features** | **Priority Future Features** | **Possible Future Features** |
|
||||||
|
| Bulk, individually customizable SMS sending via web UI | SMS sending via API integration | Email sending via UI and API |
|
||||||
|
| Organization permissions settings for various team members to edit/send | Single-level decision bidirectionality (e.g. reply “YES” if, or “NO” if…) | Multiple-level decision bidirectionality (greater than one layer decision-tree) |
|
||||||
|
| Reusable message templates | Self-service account creation | Open-text reply bidirectionality (rather than reply yes or no, 1 or 2, etc.) |
|
||||||
|
| Message send/failure analytics | Application status page | Multilingual interface and content library options |
|
||||||
|
| 7-day records deletion | Scheduled send option | Recurring scheduled send |
|
||||||
|
|
||||||
|
### OPPORTUNITIES TO GET INVOLVED
|
||||||
|
|
||||||
|
To get involved, email us at [tts-benefits-studio@gsa.gov](mailto:tts-benefits-studio@gsa.gov) with the following in the subject line!
|
||||||
|
|
||||||
|
1. **Request a Studio introduction and/or a Notify demo:** Request an
|
||||||
|
introductory call to
|
||||||
|
talk about our product, near term goals, and ways to get involved.
|
||||||
|
|
||||||
|
2. **Provide feedback about Notify:** If you're a potential product
|
||||||
|
user, set up an individual feedback session to help us fortify our
|
||||||
|
tool and talk through potential set-up considerations from your
|
||||||
|
vantage point.
|
||||||
|
|
||||||
|
3. **Sign up to pilot Notify:** Already think this might be the tool
|
||||||
|
for you? We’re looking for benefit-administering partners to
|
||||||
|
co-design a pilot program to test US Notify for specific use cases.
|
||||||
|
Early adopters will have wrap-around set-up support from the Studio
|
||||||
|
and an opportunity to shape the future of this product.
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
{%- set apps = {
|
|
||||||
'notify-admin': {
|
|
||||||
'routes': {
|
|
||||||
'preview': ['www.notify.works', 'notify-admin-preview.apps.internal'],
|
|
||||||
'staging': ['www.staging-notify.works', 'notify-admin-staging.apps.internal'],
|
|
||||||
'production': ['www.notifications.service.gov.uk', 'notify-admin-production.apps.internal'],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'notify-admin-prototype': {},
|
|
||||||
'notify-admin-prototype-2': {}
|
|
||||||
} -%}
|
|
||||||
|
|
||||||
{%- set app = apps[CF_APP] -%}
|
|
||||||
|
|
||||||
---
|
|
||||||
applications:
|
|
||||||
- name: {{ CF_APP }}
|
|
||||||
buildpack: python_buildpack
|
|
||||||
|
|
||||||
memory: 1G
|
|
||||||
|
|
||||||
routes:
|
|
||||||
- route: {{ CF_APP }}-{{ environment }}.cloudapps.digital
|
|
||||||
{%- for route in app.get('routes', {}).get(environment, []) %}
|
|
||||||
- route: {{ route }}
|
|
||||||
{%- endfor %}
|
|
||||||
|
|
||||||
health-check-type: http
|
|
||||||
health-check-http-endpoint: '/_status?simple=true'
|
|
||||||
health-check-invocation-timeout: 10
|
|
||||||
|
|
||||||
services:
|
|
||||||
- logit-ssl-syslog-drain
|
|
||||||
- notify-prometheus
|
|
||||||
- notify-splunk
|
|
||||||
- notify-redis
|
|
||||||
|
|
||||||
env:
|
|
||||||
NOTIFY_APP_NAME: admin
|
|
||||||
NOTIFY_LOG_PATH: /home/vcap/logs/app.log
|
|
||||||
FLASK_APP: application.py
|
|
||||||
NOTIFY_ENVIRONMENT: {{ environment }}
|
|
||||||
|
|
||||||
# Credentials variables
|
|
||||||
ADMIN_CLIENT_SECRET: '{{ ADMIN_CLIENT_SECRET }}'
|
|
||||||
ADMIN_BASE_URL: '{{ ADMIN_BASE_URL }}'
|
|
||||||
API_HOST_NAME: '{{ API_HOST_NAME }}'
|
|
||||||
DANGEROUS_SALT: '{{ DANGEROUS_SALT }}'
|
|
||||||
SECRET_KEY: '{{ SECRET_KEY }}'
|
|
||||||
ROUTE_SECRET_KEY_1: '{{ ROUTE_SECRET_KEY_1 }}'
|
|
||||||
ROUTE_SECRET_KEY_2: '{{ ROUTE_SECRET_KEY_2 }}'
|
|
||||||
METRICS_BASIC_AUTH_TOKEN: {{ METRICS_BASIC_AUTH_TOKEN }}
|
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID: '{{ AWS_ACCESS_KEY_ID }}'
|
|
||||||
AWS_SECRET_ACCESS_KEY: '{{ AWS_SECRET_ACCESS_KEY }}'
|
|
||||||
|
|
||||||
ANTIVIRUS_API_HOST: '{{ ANTIVIRUS_API_HOST }}'
|
|
||||||
ANTIVIRUS_API_KEY: '{{ ANTIVIRUS_API_KEY }}'
|
|
||||||
|
|
||||||
ZENDESK_API_KEY: '{{ ZENDESK_API_KEY }}'
|
|
||||||
|
|
||||||
TEMPLATE_PREVIEW_API_HOST: '{{ TEMPLATE_PREVIEW_API_HOST }}'
|
|
||||||
TEMPLATE_PREVIEW_API_KEY: '{{ TEMPLATE_PREVIEW_API_KEY }}'
|
|
||||||
|
|
||||||
NOTIFY_BILLING_DETAILS: '{{ NOTIFY_BILLING_DETAILS | tojson }}'
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Run `make freeze-requirements` to update requirements.txt
|
|
||||||
# with package version changes made in requirements-app.txt
|
|
||||||
|
|
||||||
ago==0.0.93
|
|
||||||
govuk-bank-holidays==0.11
|
|
||||||
humanize==4.1.0
|
|
||||||
Flask==2.1.2
|
|
||||||
Flask-WTF==1.0.1
|
|
||||||
wtforms==3.0.1
|
|
||||||
Flask-Login==0.6.1
|
|
||||||
Werkzeug==2.1.2
|
|
||||||
jinja2==3.1.2
|
|
||||||
Flask-BasicAuth==0.2.0
|
|
||||||
|
|
||||||
blinker==1.4
|
|
||||||
pyexcel==0.7.0
|
|
||||||
pyexcel-io==0.6.6
|
|
||||||
pyexcel-xls==0.7.0
|
|
||||||
pyexcel-xlsx==0.6.0
|
|
||||||
pyexcel-ods3==0.6.1
|
|
||||||
pytz==2022.1
|
|
||||||
# Should be pinned until a new gunicorn release greater than 20.1.0 comes out. (Due to eventlet v0.33 compatibility issues)
|
|
||||||
git+https://github.com/benoitc/gunicorn.git@1299ea9e967a61ae2edebe191082fd169b864c64#egg=gunicorn[eventlet]==20.1.0
|
|
||||||
notifications-python-client==6.3.0
|
|
||||||
rtreelib==0.2.0
|
|
||||||
fido2==0.9.3
|
|
||||||
pyproj==3.3.1
|
|
||||||
python-dotenv==0.20.0
|
|
||||||
|
|
||||||
# PaaS
|
|
||||||
awscli-cwlogs>=1.4,<1.5
|
|
||||||
itsdangerous==2.1.2
|
|
||||||
notifications-utils @ git+https://github.com/GSA/notifications-utils.git@s3-credentials
|
|
||||||
govuk-frontend-jinja @ git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.8-alpha
|
|
||||||
|
|
||||||
# gds-metrics requires prometheseus 0.2.0, override that requirement as later versions bring significant performance gains
|
|
||||||
prometheus-client==0.14.1
|
|
||||||
git+https://github.com/alphagov/gds_metrics_python.git@6f1840a57b6fb1ee40b7e84f2f18ec229de8aa72
|
|
||||||
248
requirements.txt
248
requirements.txt
@@ -1,248 +0,0 @@
|
|||||||
#
|
|
||||||
# This file is autogenerated by pip-compile with python 3.9
|
|
||||||
# To update, run:
|
|
||||||
#
|
|
||||||
# pip-compile requirements.in
|
|
||||||
#
|
|
||||||
ago==0.0.93
|
|
||||||
# via -r requirements.in
|
|
||||||
async-timeout==4.0.2
|
|
||||||
# via redis
|
|
||||||
awscli==1.24.8
|
|
||||||
# via awscli-cwlogs
|
|
||||||
awscli-cwlogs==1.4.6
|
|
||||||
# via -r requirements.in
|
|
||||||
bleach==5.0.0
|
|
||||||
# via notifications-utils
|
|
||||||
blinker==1.4
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# gds-metrics
|
|
||||||
boto3==1.23.8
|
|
||||||
# via notifications-utils
|
|
||||||
botocore==1.26.8
|
|
||||||
# via
|
|
||||||
# awscli
|
|
||||||
# boto3
|
|
||||||
# s3transfer
|
|
||||||
cachetools==5.1.0
|
|
||||||
# via notifications-utils
|
|
||||||
certifi==2022.5.18.1
|
|
||||||
# via
|
|
||||||
# pyproj
|
|
||||||
# requests
|
|
||||||
cffi==1.15.0
|
|
||||||
# via cryptography
|
|
||||||
chardet==4.0.0
|
|
||||||
# via pyexcel
|
|
||||||
charset-normalizer==2.0.12
|
|
||||||
# via requests
|
|
||||||
click==8.1.3
|
|
||||||
# via flask
|
|
||||||
colorama==0.4.4
|
|
||||||
# via awscli
|
|
||||||
cryptography==38.0.3
|
|
||||||
# via fido2
|
|
||||||
deprecated==1.2.13
|
|
||||||
# via redis
|
|
||||||
dnspython==2.2.1
|
|
||||||
# via eventlet
|
|
||||||
docopt==0.6.2
|
|
||||||
# via notifications-python-client
|
|
||||||
docutils==0.16
|
|
||||||
# via awscli
|
|
||||||
et-xmlfile==1.1.0
|
|
||||||
# via openpyxl
|
|
||||||
eventlet==0.33.1
|
|
||||||
# via gunicorn
|
|
||||||
fido2==0.9.3
|
|
||||||
# via -r requirements.in
|
|
||||||
flask==2.1.2
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# flask-basicauth
|
|
||||||
# flask-login
|
|
||||||
# flask-redis
|
|
||||||
# flask-wtf
|
|
||||||
# gds-metrics
|
|
||||||
# notifications-utils
|
|
||||||
flask-basicauth==0.2.0
|
|
||||||
# via -r requirements.in
|
|
||||||
flask-login==0.6.1
|
|
||||||
# via -r requirements.in
|
|
||||||
flask-redis==0.4.0
|
|
||||||
# via notifications-utils
|
|
||||||
flask-wtf==1.0.1
|
|
||||||
# via -r requirements.in
|
|
||||||
gds-metrics @ git+https://github.com/alphagov/gds_metrics_python.git@6f1840a57b6fb1ee40b7e84f2f18ec229de8aa72
|
|
||||||
# via -r requirements.in
|
|
||||||
geojson==2.5.0
|
|
||||||
# via notifications-utils
|
|
||||||
govuk-bank-holidays==0.11
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# notifications-utils
|
|
||||||
govuk-frontend-jinja @ git+https://github.com/alphagov/govuk-frontend-jinja.git@v0.5.8-alpha
|
|
||||||
# via -r requirements.in
|
|
||||||
greenlet==1.1.2
|
|
||||||
# via eventlet
|
|
||||||
gunicorn @ git+https://github.com/benoitc/gunicorn.git@1299ea9e967a61ae2edebe191082fd169b864c64
|
|
||||||
# via -r requirements.in
|
|
||||||
humanize==4.1.0
|
|
||||||
# via -r requirements.in
|
|
||||||
idna==3.3
|
|
||||||
# via requests
|
|
||||||
importlib-metadata==4.12.0
|
|
||||||
# via flask
|
|
||||||
itsdangerous==2.1.2
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# flask
|
|
||||||
# flask-wtf
|
|
||||||
# notifications-utils
|
|
||||||
jinja2==3.1.2
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# flask
|
|
||||||
# govuk-frontend-jinja
|
|
||||||
# notifications-utils
|
|
||||||
jmespath==1.0.0
|
|
||||||
# via
|
|
||||||
# boto3
|
|
||||||
# botocore
|
|
||||||
lml==0.1.0
|
|
||||||
# via
|
|
||||||
# pyexcel
|
|
||||||
# pyexcel-io
|
|
||||||
lxml==4.9.1
|
|
||||||
# via
|
|
||||||
# pyexcel-ezodf
|
|
||||||
# pyexcel-ods3
|
|
||||||
markupsafe==2.1.1
|
|
||||||
# via
|
|
||||||
# jinja2
|
|
||||||
# wtforms
|
|
||||||
mistune==0.8.4
|
|
||||||
# via notifications-utils
|
|
||||||
notifications-python-client==6.3.0
|
|
||||||
# via -r requirements.in
|
|
||||||
notifications-utils @ git+https://github.com/GSA/notifications-utils.git@s3-credentials
|
|
||||||
# via -r requirements.in
|
|
||||||
openpyxl==3.0.10
|
|
||||||
# via pyexcel-xlsx
|
|
||||||
orderedset==2.0.3
|
|
||||||
# via notifications-utils
|
|
||||||
packaging==21.3
|
|
||||||
# via redis
|
|
||||||
phonenumbers==8.12.48
|
|
||||||
# via notifications-utils
|
|
||||||
prometheus-client==0.14.1
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# gds-metrics
|
|
||||||
pyasn1==0.4.8
|
|
||||||
# via rsa
|
|
||||||
pycparser==2.21
|
|
||||||
# via cffi
|
|
||||||
pyexcel==0.7.0
|
|
||||||
# via -r requirements.in
|
|
||||||
pyexcel-ezodf==0.3.4
|
|
||||||
# via pyexcel-ods3
|
|
||||||
pyexcel-io==0.6.6
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# pyexcel
|
|
||||||
# pyexcel-ods3
|
|
||||||
# pyexcel-xls
|
|
||||||
# pyexcel-xlsx
|
|
||||||
pyexcel-ods3==0.6.1
|
|
||||||
# via -r requirements.in
|
|
||||||
pyexcel-xls==0.7.0
|
|
||||||
# via -r requirements.in
|
|
||||||
pyexcel-xlsx==0.6.0
|
|
||||||
# via -r requirements.in
|
|
||||||
pyjwt==2.4.0
|
|
||||||
# via notifications-python-client
|
|
||||||
pyparsing==3.0.9
|
|
||||||
# via packaging
|
|
||||||
pypdf2==2.0.0
|
|
||||||
# via notifications-utils
|
|
||||||
pyproj==3.3.1
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# notifications-utils
|
|
||||||
python-dateutil==2.8.2
|
|
||||||
# via
|
|
||||||
# awscli-cwlogs
|
|
||||||
# botocore
|
|
||||||
python-dotenv==0.20.0
|
|
||||||
# via -r requirements.in
|
|
||||||
python-json-logger==2.0.2
|
|
||||||
# via notifications-utils
|
|
||||||
pytz==2022.1
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# notifications-utils
|
|
||||||
pyyaml==5.4.1
|
|
||||||
# via
|
|
||||||
# awscli
|
|
||||||
# notifications-utils
|
|
||||||
redis==4.3.1
|
|
||||||
# via flask-redis
|
|
||||||
requests==2.27.1
|
|
||||||
# via
|
|
||||||
# awscli-cwlogs
|
|
||||||
# govuk-bank-holidays
|
|
||||||
# notifications-python-client
|
|
||||||
# notifications-utils
|
|
||||||
rsa==4.7.2
|
|
||||||
# via awscli
|
|
||||||
rtreelib==0.2.0
|
|
||||||
# via -r requirements.in
|
|
||||||
s3transfer==0.5.2
|
|
||||||
# via
|
|
||||||
# awscli
|
|
||||||
# boto3
|
|
||||||
shapely==1.8.2
|
|
||||||
# via notifications-utils
|
|
||||||
six==1.16.0
|
|
||||||
# via
|
|
||||||
# awscli-cwlogs
|
|
||||||
# bleach
|
|
||||||
# eventlet
|
|
||||||
# fido2
|
|
||||||
# python-dateutil
|
|
||||||
smartypants==2.0.1
|
|
||||||
# via notifications-utils
|
|
||||||
statsd==3.3.0
|
|
||||||
# via notifications-utils
|
|
||||||
texttable==1.6.4
|
|
||||||
# via pyexcel
|
|
||||||
typing-extensions==4.3.0
|
|
||||||
# via pypdf2
|
|
||||||
urllib3==1.26.9
|
|
||||||
# via
|
|
||||||
# botocore
|
|
||||||
# requests
|
|
||||||
webencodings==0.5.1
|
|
||||||
# via bleach
|
|
||||||
werkzeug==2.1.2
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# flask
|
|
||||||
# flask-login
|
|
||||||
wrapt==1.14.1
|
|
||||||
# via deprecated
|
|
||||||
wtforms==3.0.1
|
|
||||||
# via
|
|
||||||
# -r requirements.in
|
|
||||||
# flask-wtf
|
|
||||||
xlrd==2.0.1
|
|
||||||
# via pyexcel-xls
|
|
||||||
xlwt==1.3.0
|
|
||||||
# via pyexcel-xls
|
|
||||||
zipp==3.8.1
|
|
||||||
# via importlib-metadata
|
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
|
||||||
# setuptools
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
-r requirements.txt
|
|
||||||
isort==5.10.1
|
|
||||||
pytest==7.1.2
|
|
||||||
pytest-env==0.6.2
|
|
||||||
pytest-mock==3.7.0
|
|
||||||
pytest-xdist==2.5.0
|
|
||||||
beautifulsoup4==4.11.1
|
|
||||||
freezegun==1.2.1
|
|
||||||
flake8==4.0.1
|
|
||||||
flake8-bugbear==22.4.25
|
|
||||||
flake8-print==5.0.0
|
|
||||||
moto==3.1.7
|
|
||||||
requests-mock==1.9.3
|
|
||||||
# used for creating manifest file locally
|
|
||||||
jinja2-cli[yaml]==0.8.2
|
|
||||||
34
sample.env
34
sample.env
@@ -1,5 +1,24 @@
|
|||||||
|
# STEPS TO SET UP
|
||||||
|
#
|
||||||
|
# 1. Pull down AWS creds from cloud.gov using `cf env`, then update AWS section
|
||||||
|
#
|
||||||
|
# 2. Uncomment either the Docker setup or the direct setup
|
||||||
|
#
|
||||||
|
# 3. Comment out the other setup
|
||||||
|
#
|
||||||
|
|
||||||
# ## REBUILD THE DEVCONTAINER WHEN YOU MODIFY .ENV ###
|
# ## REBUILD THE DEVCONTAINER WHEN YOU MODIFY .ENV ###
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
|
||||||
|
# AWS
|
||||||
|
AWS_REGION=us-west-2
|
||||||
|
AWS_ACCESS_KEY_ID="don't write secrets to the sample file"
|
||||||
|
AWS_SECRET_ACCESS_KEY="don't write secrets to the sample file"
|
||||||
|
|
||||||
|
#############################################################
|
||||||
|
|
||||||
|
# Application
|
||||||
NOTIFY_ENVIRONMENT=development
|
NOTIFY_ENVIRONMENT=development
|
||||||
FLASK_APP=application.py
|
FLASK_APP=application.py
|
||||||
FLASK_ENV=development
|
FLASK_ENV=development
|
||||||
@@ -8,11 +27,12 @@ WERKZEUG_DEBUG_PIN=off
|
|||||||
ANTIVIRUS_ENABLED=0
|
ANTIVIRUS_ENABLED=0
|
||||||
NODE_VERSION=16.15.1
|
NODE_VERSION=16.15.1
|
||||||
|
|
||||||
# URL of api app (on AWS this is the internal api endpoint)
|
#############################################################
|
||||||
API_HOST_NAME=http://dev:6011
|
|
||||||
REDIS_URL=redis://adminredis:6379/0
|
|
||||||
|
|
||||||
# AWS
|
# Local Docker setup
|
||||||
AWS_REGION=us-west-2
|
# API_HOST_NAME=http://dev:6011
|
||||||
AWS_ACCESS_KEY_ID="don't write secrets to the sample file"
|
# REDIS_URL=redis://adminredis:6379/0
|
||||||
AWS_SECRET_ACCESS_KEY="don't write secrets to the sample file"
|
|
||||||
|
# Local direct setup
|
||||||
|
API_HOST_NAME=http://localhost:6011
|
||||||
|
REDIS_URL=redis://localhost:6379/0
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ $0: Create a Service User Account for a given space
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
$0 -h
|
$0 -h
|
||||||
$0 -s <SPACE NAME> -u <USER NAME> [-r <ROLE NAME>] [-o <ORG NAME>]
|
$0 -s <SPACE NAME> -u <USER NAME> [-r <ROLE NAME>] [-o <ORG NAME>] [-m]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h: show help and exit
|
-h: show help and exit
|
||||||
-s <SPACE NAME>: configure the space to act on. Required
|
-s <SPACE NAME>: configure the space to act on. Required
|
||||||
-u <USER NAME>: set the service user name. Required
|
-u <USER NAME>: set the service user name. Required
|
||||||
-r <ROLE NAME>: set the service user's role to either space-deployer or space-auditor. Default: space-deployer
|
-r <ROLE NAME>: set the service user's role to either space-deployer or space-auditor. Default: space-deployer
|
||||||
|
-m: If provided, make the service user an OrgManager
|
||||||
-o <ORG NAME>: configure the organization to act on. Default: $org
|
-o <ORG NAME>: configure the organization to act on. Default: $org
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
OrgManager is required for terraform to create <env>-egress spaces
|
||||||
"
|
"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -23,8 +27,9 @@ set -o pipefail
|
|||||||
space=""
|
space=""
|
||||||
service=""
|
service=""
|
||||||
role="space-deployer"
|
role="space-deployer"
|
||||||
|
org_manager="false"
|
||||||
|
|
||||||
while getopts ":hs:u:r:o:" opt; do
|
while getopts ":hms:u:r:o:" opt; do
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
s)
|
s)
|
||||||
space=${OPTARG}
|
space=${OPTARG}
|
||||||
@@ -38,6 +43,9 @@ while getopts ":hs:u:r:o:" opt; do
|
|||||||
o)
|
o)
|
||||||
org=${OPTARG}
|
org=${OPTARG}
|
||||||
;;
|
;;
|
||||||
|
m)
|
||||||
|
org_manager="true"
|
||||||
|
;;
|
||||||
h)
|
h)
|
||||||
echo "$usage"
|
echo "$usage"
|
||||||
exit 0
|
exit 0
|
||||||
@@ -60,13 +68,17 @@ cf create-service-key $service service-account-key 1>&2
|
|||||||
|
|
||||||
# output service key to stdout in secrets.auto.tfvars format
|
# output service key to stdout in secrets.auto.tfvars format
|
||||||
creds=`cf service-key $service service-account-key | tail -n 4`
|
creds=`cf service-key $service service-account-key | tail -n 4`
|
||||||
username=`echo $creds | jq '.username'`
|
username=`echo $creds | jq -r '.username'`
|
||||||
password=`echo $creds | jq '.password'`
|
password=`echo $creds | jq -r '.password'`
|
||||||
|
|
||||||
|
if [[ $org_manager = "true" ]]; then
|
||||||
|
cf set-org-role $username $org OrgManager 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
cat << EOF
|
cat << EOF
|
||||||
# generated with $0 -s $space -u $service -r $role -o $org
|
# generated with $0 -s $space -u $service -r $role -o $org
|
||||||
# revoke with $(dirname $0)/destroy_service_account.sh -s $space -u $service -o $org
|
# revoke with $(dirname $0)/destroy_service_account.sh -s $space -u $service -o $org
|
||||||
|
|
||||||
cf_user = $username
|
cf_user = "$username"
|
||||||
cf_password = $password
|
cf_password = "$password"
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
48
terraform/sandbox/main.tf
Normal file
48
terraform/sandbox/main.tf
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
locals {
|
||||||
|
cf_org_name = "gsa-tts-benefits-studio-prototyping"
|
||||||
|
cf_space_name = "notify-sandbox"
|
||||||
|
env = "sandbox"
|
||||||
|
app_name = "notify-admin"
|
||||||
|
recursive_delete = true
|
||||||
|
}
|
||||||
|
|
||||||
|
module "redis" {
|
||||||
|
source = "github.com/18f/terraform-cloudgov//redis"
|
||||||
|
|
||||||
|
cf_user = var.cf_user
|
||||||
|
cf_password = var.cf_password
|
||||||
|
cf_org_name = local.cf_org_name
|
||||||
|
cf_space_name = local.cf_space_name
|
||||||
|
env = local.env
|
||||||
|
app_name = local.app_name
|
||||||
|
recursive_delete = local.recursive_delete
|
||||||
|
redis_plan_name = "redis-dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
module "logo_upload_bucket" {
|
||||||
|
source = "github.com/18f/terraform-cloudgov//s3"
|
||||||
|
|
||||||
|
cf_user = var.cf_user
|
||||||
|
cf_password = var.cf_password
|
||||||
|
cf_org_name = local.cf_org_name
|
||||||
|
cf_space_name = local.cf_space_name
|
||||||
|
recursive_delete = local.recursive_delete
|
||||||
|
s3_service_name = "${local.app_name}-logo-upload-bucket-${local.env}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ##########################################################################
|
||||||
|
# The following lines need to be commented out for the initial `terraform apply`
|
||||||
|
# It can be re-enabled after:
|
||||||
|
# 1) the api app has first been deployed
|
||||||
|
# 2) the admin app has first been deployed
|
||||||
|
###########################################################################
|
||||||
|
# module "api_network_route" {
|
||||||
|
# source = "../shared/container_networking"
|
||||||
|
|
||||||
|
# cf_user = var.cf_user
|
||||||
|
# cf_password = var.cf_password
|
||||||
|
# cf_org_name = local.cf_org_name
|
||||||
|
# cf_space_name = local.cf_space_name
|
||||||
|
# source_app_name = "${local.app_name}-${local.env}"
|
||||||
|
# destination_app_name = "notify-api-${local.env}"
|
||||||
|
# }
|
||||||
17
terraform/sandbox/providers.tf
Normal file
17
terraform/sandbox/providers.tf
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
terraform {
|
||||||
|
required_version = "~> 1.0"
|
||||||
|
required_providers {
|
||||||
|
cloudfoundry = {
|
||||||
|
source = "cloudfoundry-community/cloudfoundry"
|
||||||
|
version = "0.15.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend "s3" {
|
||||||
|
bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0"
|
||||||
|
key = "admin.tfstate.sandbox"
|
||||||
|
encrypt = "true"
|
||||||
|
region = "us-gov-west-1"
|
||||||
|
profile = "notify-terraform-backend"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
terraform/sandbox/variables.tf
Normal file
5
terraform/sandbox/variables.tf
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
variable "cf_password" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
variable "cf_user" {}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
ADMIN_CLIENT_SECRET: asdf
|
|
||||||
DANGEROUS_SALT: asdf
|
|
||||||
SECRET_KEY: asdf
|
|
||||||
ROUTE_SECRET_KEY_1: asdf
|
|
||||||
ROUTE_SECRET_KEY_2: asdf
|
|
||||||
AWS_ACCESS_KEY_ID: asdf
|
|
||||||
AWS_SECRET_ACCESS_KEY: asdf
|
|
||||||
BASIC_AUTH_USERNAME: asdf
|
|
||||||
BASIC_AUTH_PASSWORD: asdf
|
|
||||||
Reference in New Issue
Block a user