Files
notifications-admin/app/config.py

174 lines
6.2 KiB
Python
Raw Normal View History

import json
import os
2022-11-29 12:16:29 -05:00
2022-11-29 10:58:59 -05:00
import pytz
from app.cloudfoundry_config import cloud_config
2016-12-08 16:50:37 +00:00
2015-11-24 09:40:14 +00:00
class Config(object):
NOTIFY_APP_NAME = 'admin'
NOTIFY_ENVIRONMENT = os.environ.get('NOTIFY_ENVIRONMENT', 'development')
API_HOST_NAME = os.environ.get('API_HOST_NAME', 'localhost')
ADMIN_BASE_URL = os.environ.get('ADMIN_BASE_URL', 'http://localhost:6012')
HEADER_COLOUR = '#81878b' # mix(govuk-colour("dark-grey"), govuk-colour("mid-grey"))
LOGO_CDN_DOMAIN = 'static-logos.notifications.service.gov.uk' # TODO use our own CDN
ASSETS_DEBUG = False
TIMEZONE = os.environ.get('TIMEZONE', 'America/New_York')
2022-11-29 10:58:59 -05:00
PY_TIMEZONE = pytz.timezone(TIMEZONE)
# Credentials
ADMIN_CLIENT_SECRET = os.environ.get('ADMIN_CLIENT_SECRET')
2022-07-19 18:23:48 -07:00
ADMIN_CLIENT_USER_NAME = os.environ.get('ADMIN_CLIENT_USERNAME')
SECRET_KEY = os.environ.get('SECRET_KEY')
DANGEROUS_SALT = os.environ.get('DANGEROUS_SALT')
# ZENDESK_API_KEY = os.environ.get('ZENDESK_API_KEY')
ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', 'dev-route-secret-key-1')
ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', 'dev-route-secret-key-2')
BASIC_AUTH_USERNAME = os.environ.get('BASIC_AUTH_USERNAME')
BASIC_AUTH_PASSWORD = os.environ.get('BASIC_AUTH_PASSWORD')
TEMPLATE_PREVIEW_API_HOST = os.environ.get('TEMPLATE_PREVIEW_API_HOST', 'http://localhost:9999')
TEMPLATE_PREVIEW_API_KEY = os.environ.get('TEMPLATE_PREVIEW_API_KEY', 'my-secret-key')
# Logging
NOTIFY_LOG_LEVEL = os.environ.get('NOTIFY_LOG_LEVEL', 'INFO')
2016-12-08 16:50:37 +00:00
DEFAULT_SERVICE_LIMIT = 50
EMAIL_EXPIRY_SECONDS = 3600 # 1 hour
INVITATION_EXPIRY_SECONDS = 3600 * 24 * 2 # 2 days - also set on api
EMAIL_2FA_EXPIRY_SECONDS = 1800 # 30 Minutes
PERMANENT_SESSION_LIFETIME = 20 * 60 * 60 # 20 hours
SEND_FILE_MAX_AGE_DEFAULT = 365 * 24 * 60 * 60 # 1 year
REPLY_TO_EMAIL_ADDRESS_VALIDATION_TIMEOUT = 45
ACTIVITY_STATS_LIMIT_DAYS = 7
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_NAME = 'notify_admin_session'
SESSION_COOKIE_SECURE = True
2019-11-28 14:39:30 +00:00
# don't send back the cookie if it hasn't been modified by the request. this means that the expiry time won't be
# updated unless the session is changed - but it's generally refreshed by `save_service_or_org_after_request`
# every time anyway, except for specific endpoints (png/pdfs generally) where we've disabled that handler.
SESSION_REFRESH_EACH_REQUEST = False
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = None
CHECK_PROXY_HEADER = False
2022-08-05 00:25:03 -07:00
AWS_REGION = os.environ.get('AWS_REGION')
Add Redis cache between admin and API Most of the time spent by the admin app to generate a page is spent waiting for the API. This is slow for three reasons: 1. Talking to the API means going out to the internet, then through nginx, the Flask app, SQLAlchemy, down to the database, and then serialising the result to JSON and making it into a HTTP response 2. Each call to the API is synchronous, therefore if a page needs 3 API calls to render then the second API call won’t be made until the first has finished, and the third won’t start until the second has finished 3. Every request for a service page in the admin app makes a minimum of two requests to the API (`GET /service/…` and `GET /user/…`) Hitting the database will always be the slowest part of an app like Notify. But this slowness is exacerbated by 2. and 3. Conversely every speedup made to 1. is multiplied by 2. and 3. So this pull request aims to make 1. a _lot_ faster by taking nginx, Flask, SQLAlchemy and the database out of the equation. It replaces them with Redis, which as an in-memory key/value store is a lot faster than Postgres. There is still the overhead of going across the network to talk to Redis, but the net improvement is vast. This commit only caches the `GET /service` response, but is written in such a way that we can easily expand to caching other responses down the line. The tradeoff here is that our code is more complex, and we risk introducing edge cases where a cache becomes stale. The mitigations against this are: - invalidating all caches after 24h so a stale cache doesn’t remain around indefinitely - being careful when we add new stuff to the service response --- Some indicative numbers, based on: - `GET http://localhost:6012/services/<service_id>/template/<template_id>` - with the admin app running locally - talking to Redis running locally - also talking to the API running locally, itself talking to a local Postgres instance - times measured with Chrome web inspector, average of 10 requests ╲ | No cache | Cache service | Cache service and user | Cache service, user and template -- | -- | -- | -- | -- **Request time** | 136ms | 97ms | 73ms | 37ms **Improvement** | 0% | 41% | 88% | 265% --- Estimates of how much storage this requires: - Services: 1,942 on production × 2kb = 4Mb - Users: 4,534 on production × 2kb = 9Mb - Templates: 7,079 on production × 4kb = 28Mb
2018-04-06 13:37:49 +01:00
REDIS_URL = cloud_config.redis_url
REDIS_ENABLED = os.environ.get('REDIS_ENABLED', '1') == '1'
2022-08-05 00:25:03 -07:00
# TODO: reassign this
2019-05-13 10:17:20 +01:00
NOTIFY_SERVICE_ID = 'd6aa2c68-a2d9-4437-ab19-3ae8eb202553'
NOTIFY_BILLING_DETAILS = json.loads(
os.environ.get('NOTIFY_BILLING_DETAILS') or 'null'
) or {
'account_number': '98765432',
'sort_code': '01-23-45',
'IBAN': 'GB33BUKB20201555555555',
'swift': 'ABCDEF12',
'notify_billing_email_addresses': [
'generic@digital.cabinet-office.gov.uk',
'first.last@digital.cabinet-office.gov.uk',
]
}
2015-11-24 09:40:14 +00:00
def _default_s3_credentials(bucket_name):
return {
'bucket': bucket_name,
'access_key_id': os.environ.get('AWS_ACCESS_KEY_ID'),
'secret_access_key': os.environ.get('AWS_SECRET_ACCESS_KEY'),
'region': os.environ.get('AWS_REGION')
}
2015-11-24 09:40:14 +00:00
class Development(Config):
2022-08-30 10:31:48 -04:00
BASIC_AUTH_FORCE = False
2015-11-24 09:40:14 +00:00
DEBUG = True
SESSION_COOKIE_SECURE = False
2016-05-04 13:01:55 +01:00
SESSION_PROTECTION = None
HTTP_PROTOCOL = 'http'
ASSET_DOMAIN = ''
ASSET_PATH = '/static/'
2022-08-05 00:25:03 -07:00
2022-07-25 15:18:39 -07:00
# Buckets
CSV_UPLOAD_BUCKET = _default_s3_credentials('local-notifications-csv-upload')
CONTACT_LIST_BUCKET = _default_s3_credentials('local-contact-list')
LOGO_UPLOAD_BUCKET = _default_s3_credentials('local-public-logos-tools')
2022-08-05 00:25:03 -07:00
# credential overrides
DANGEROUS_SALT = 'development-notify-salt'
2022-08-26 16:04:30 +00:00
SECRET_KEY = 'dev-notify-secret-key' # nosec B105 - only used in development
# ADMIN_CLIENT_USER_NAME is called ADMIN_CLIENT_ID in api repo, they should match
ADMIN_CLIENT_USER_NAME = 'notify-admin'
ADMIN_CLIENT_SECRET = 'dev-notify-secret-key' # nosec B105 - only used in development
2015-11-24 09:40:14 +00:00
class Test(Development):
TESTING = True
WTF_CSRF_ENABLED = False
ASSET_DOMAIN = 'static.example.com'
ASSET_PATH = 'https://static.example.com/'
API_HOST_NAME = 'http://you-forgot-to-mock-an-api-call-to'
2022-09-12 17:38:53 -07:00
REDIS_URL = 'redis://you-forgot-to-mock-a-redis-call-to'
LOGO_CDN_DOMAIN = 'static-logos.test.com'
# Buckets
CSV_UPLOAD_BUCKET = _default_s3_credentials('test-csv-upload')
CONTACT_LIST_BUCKET = _default_s3_credentials('test-contact-list')
LOGO_UPLOAD_BUCKET = _default_s3_credentials('test-logo-upload')
class Production(Config):
HEADER_COLOUR = '#005EA5' # $govuk-blue
2015-11-30 14:32:58 +00:00
HTTP_PROTOCOL = 'https'
BASIC_AUTH_FORCE = True
ASSET_DOMAIN = '' # TODO use a CDN
ASSET_PATH = '/static/' # TODO use a CDN
DEBUG = False
2022-07-25 15:18:39 -07:00
# buckets
CSV_UPLOAD_BUCKET = cloud_config.s3_credentials(
f"notify-api-csv-upload-bucket-{os.environ['NOTIFY_ENVIRONMENT']}")
CONTACT_LIST_BUCKET = cloud_config.s3_credentials(
f"notify-api-contact-list-bucket-{os.environ['NOTIFY_ENVIRONMENT']}")
LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials(
f"notify-admin-logo-upload-bucket-{os.environ['NOTIFY_ENVIRONMENT']}")
2022-08-05 00:25:03 -07:00
class Staging(Production):
BASIC_AUTH_FORCE = True
HEADER_COLOUR = '#00ff00' # $green
2016-12-08 16:50:37 +00:00
2022-11-02 16:05:53 -04:00
class Demo(Staging):
HEADER_COLOUR = '#6F72AF' # $mauve
class Sandbox(Staging):
HEADER_COLOUR = '#ff0000' # $red
2022-11-02 16:05:53 -04:00
class Scanning(Production):
BASIC_AUTH_FORCE = False
HTTP_PROTOCOL = 'http'
API_HOST_NAME = 'https://notify-api-demo.app.cloud.gov/'
SECRET_KEY = 'dev-notify-secret-key' # nosec B105 - only used in development
ADMIN_CLIENT_USER_NAME = 'notify-admin'
ADMIN_CLIENT_SECRET = 'dev-notify-secret-key' # nosec B105 - only used in development
2016-12-08 16:50:37 +00:00
2015-11-24 09:40:14 +00:00
configs = {
'development': Development,
'test': Test,
'scanning': Scanning,
'staging': Staging,
2022-11-02 16:05:53 -04:00
'demo': Demo,
'sandbox': Sandbox,
'production': Production
2015-11-24 09:40:14 +00:00
}