Bit of a settings fixup to make it work with t.s

This commit is contained in:
Andrew Godwin 2022-11-26 11:33:33 -07:00
parent 849c221aee
commit a3f45a001b
5 changed files with 53 additions and 34 deletions

View file

@ -4,3 +4,4 @@ notes.md
.git
.venv
.pre-commit-config.yaml
.env

View file

@ -1,8 +1,8 @@
TAKAHE_DATABASE_URL="postgres://postgres:insecure_password@db/takahe"
TAKAHE_DATABASE_SERVER="sqlite://takahe.db"
TAKAHE_DEBUG=true
TAKAHE_SECRET_KEY="insecure_secret"
TAKAHE_CSRF_TRUSTED_ORIGINS=["http://127.0.0.1:8000", "https://127.0.0.1:8000"]
TAKAHE_USE_PROXY_HEADERS=true
TAKAHE_EMAIL_BACKEND="console://console"
TAKAHE_EMAIL_SERVER="console://console"
TAKAHE_MAIN_DOMAIN="example.com"
TAKAHE_ENVIRONMENT="development"

View file

@ -11,7 +11,7 @@ COPY . /takahe
WORKDIR /takahe
RUN TAKAHE_DATABASE_URL="postgres://dummy:dummy@localhost/postgres" python3 manage.py collectstatic
RUN TAKAHE_DATABASE_SERVER="postgres://x@example.com/x" python3 manage.py collectstatic
EXPOSE 8000

View file

@ -61,36 +61,33 @@ Environment Variables
All of these variables are *required* for a working installation, and should
be provided from the first boot.
* ``PGHOST``, ``PGPORT``, ``PGUSER``, ``PGDATABASE``, and ``PGPASSWORD`` are the
standard PostgreSQL environment variables for configuring your database.
* ``TAKAHE_DATABASE_SERVER`` should be a database DSN for your database (you can use
the standard ``PG*`` variables too if you want)
* ``TAKAHE_SECRET_KEY`` must be a fixed, random value (it's used for internal
cryptography). Don't change this unless you want to invalidate all sessions.
* ``TAKAHE_MEDIA_BACKEND`` must be one of ``local``, ``s3`` or ``gcs``.
* ``TAKAHE_MEDIA_BACKEND`` must be a URI starting with ``local://``, ``s3://`` or ``gcs://``.
* If it is set to ``local``, you must also provide ``TAKAHE_MEDIA_ROOT``,
* If it is set to ``local://``, you must also provide ``TAKAHE_MEDIA_ROOT``,
the path to the local media directory, and ``TAKAHE_MEDIA_URL``, a
fully-qualified URL prefix that serves that directory.
* If it is set to ``gcs``, you must also provide ``TAKAHE_MEDIA_BUCKET``,
the name of the bucket to store files in. The bucket must be publicly
readable and have "uniform access control" enabled.
* If it is set to ``gcs://``, it must be in the form ``gcs://bucket-name``
(note the two slashes if you just want a bucket name)
* If it is set to ``s3``, you must also provide ``TAKAHE_MEDIA_BUCKET``,
the name of the bucket to store files in.
* If it is set to ``s3://``, it must be in the form ``s3://access-key:secret-key@endpoint-url/bucket-name``
* ``TAKAHE_MAIN_DOMAIN`` should be the domain name (without ``https://``) that
will be used for default links (such as in emails). It does *not* need to be
the same as any domain you are hosting user accounts on.
* ``TAKAHE_EMAIL_HOST`` and ``TAKAHE_EMAIL_PORT`` (along with
``TAKAHE_EMAIL_USER`` and ``TAKAHE_EMAIL_PASSWORD``, if needed) should point
to an SMTP server Takahe can use for sending email. Email is *required*, to
allow account creation and password resets.
* ``TAKAHE_EMAIL_SERVER`` should be set to an ``smtp://`` or ``sendgrid://`` URI
* If you are using SendGrid, you can just set an API key in
``TAKAHE_EMAIL_SENDGRID_KEY`` instead.
* If you are using SMTP, it is ``smtp://username:password@host:port/``. You
can also put ``?tls=true`` or ``?ssl=true`` on the end to enable encryption.
* If you are using SendGrid, you should set the URI to ``sendgrid://api-key``
* ``TAKAHE_EMAIL_FROM`` is the email address that emails from the system will
appear to come from.
@ -99,12 +96,13 @@ be provided from the first boot.
be automatically promoted to administrator when it signs up. You only need
this for initial setup, and can unset it after that if you like.
* ``TAKAHE_STATOR_TOKEN`` should be a random string that you are using to
protect the stator (task runner) endpoint. You'll use this value later.
* If you don't want to run Stator as a background process but as a view,
set ``TAKAHE_STATOR_TOKEN`` to a random string that you are using to
protect it; you'll use this when setting up the URL to be called.
* If your installation is behind a HTTPS endpoint that is proxying it, set
``TAKAHE_SECURE_HEADER`` to the header name used to signify that HTTPS is
being used (usually ``X-Forwarded-Proto``)
``TAKAHE_USE_PROXY_HEADERS`` to ``true``. (The HTTPS proxy header must be called
``X-Forwarded-Proto``).
* If you want to receive emails about internal site errors, set
``TAKAHE_ERROR_EMAILS`` to a comma-separated list of email addresses that

View file

@ -31,25 +31,32 @@ class Settings(BaseSettings):
"""
#: The default database.
DATABASE_URL: Optional[PostgresDsn]
DATABASE_SERVER: Optional[PostgresDsn]
#: The currently running environment, used for things such as sentry
#: error reporting.
ENVIRONMENT: Environments = "development"
#: Should django run in debug mode?
DEBUG: bool = False
#: Set a secret key used for signing values such as sessions. Randomized
#: by default, so you'll logout everytime the process restarts.
SECRET_KEY: str = Field(default_factory=lambda: secrets.token_hex(128))
#: Set a secret key used to protect the stator. Randomized by default.
STATOR_TOKEN: str = Field(default_factory=lambda: secrets.token_hex(128))
#: If set, a list of allowed values for the HOST header. The default value
#: of '*' means any host will be accepted.
ALLOWED_HOSTS: List[str] = Field(default_factory=lambda: ["*"])
#: If set, a list of hosts to accept for CORS.
CORS_HOSTS: List[str] = Field(default_factory=list)
#: If set, a list of hosts to accept for CSRF.
CSRF_HOSTS: List[str] = Field(default_factory=list)
#: If enabled, trust the HTTP_X_FORWARDED_FOR header.
USE_PROXY_HEADERS: bool = False
@ -59,25 +66,25 @@ class Settings(BaseSettings):
#: Fallback domain for links.
MAIN_DOMAIN: str = "example.com"
EMAIL_DSN: AnyUrl = "console://localhost"
EMAIL_SERVER: AnyUrl = "console://localhost"
EMAIL_FROM: EmailStr = "test@example.com"
AUTO_ADMIN_EMAIL: Optional[EmailStr] = None
ERROR_EMAILS: Optional[List[EmailStr]] = None
MEDIA_URL: str = "/media/"
MEDIA_ROOT: str = str(BASE_DIR / "MEDIA")
MEDIA_ROOT: str = str(BASE_DIR / "media")
MEDIA_BACKEND: Optional[AnyUrl] = None
PGHOST: Optional[str] = None
PGPORT: int = 5432
PGPORT: Optional[int] = 5432
PGNAME: str = "takahe"
PGUSER: str = "postgres"
PGPASSWORD: Optional[str] = None
@validator("PGHOST", always=True)
def validate_db(cls, PGHOST, values): # noqa
if not values.get("DATABASE_URL") and not PGHOST:
raise ValueError("Either DATABASE_URL or PGHOST are required.")
if not values.get("DATABASE_SERVER") and not PGHOST:
raise ValueError("Either DATABASE_SERVER or PGHOST are required.")
return PGHOST
class Config:
@ -154,8 +161,10 @@ TEMPLATES = [
WSGI_APPLICATION = "takahe.wsgi.application"
if SETUP.DATABASE_URL:
DATABASES = {"default": dj_database_url.parse(SETUP.DATABASE_URL, conn_max_age=600)}
if SETUP.DATABASE_SERVER:
DATABASES = {
"default": dj_database_url.parse(SETUP.DATABASE_SERVER, conn_max_age=600)
}
else:
DATABASES = {
"default": {
@ -243,11 +252,17 @@ if SETUP.SENTRY_DSN:
)
SERVER_EMAIL = SETUP.EMAIL_FROM
if SETUP.EMAIL_DSN:
parsed = urllib.parse.urlparse(SETUP.EMAIL_DSN)
if SETUP.EMAIL_SERVER:
parsed = urllib.parse.urlparse(SETUP.EMAIL_SERVER)
query = urllib.parse.parse_qs(parsed.query)
if parsed.scheme == "console":
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
elif parsed.scheme == "sendgrid":
EMAIL_HOST = "smtp.sendgrid.net"
EMAIL_PORT = 587
EMAIL_HOST_USER = "apikey"
EMAIL_HOST_PASSWORD = parsed.hostname
EMAIL_USE_TLS = True
elif parsed.scheme == "smtp":
EMAIL_HOST = parsed.hostname
EMAIL_PORT = parsed.port
@ -256,7 +271,7 @@ if SETUP.EMAIL_DSN:
EMAIL_USE_TLS = as_bool(query.get("tls"))
EMAIL_USE_SSL = as_bool(query.get("ssl"))
else:
raise ValueError("Unknown schema for EMAIL_DSN.")
raise ValueError("Unknown schema for EMAIL_SERVER.")
if SETUP.MEDIA_BACKEND:
@ -264,7 +279,10 @@ if SETUP.MEDIA_BACKEND:
query = urllib.parse.parse_qs(parsed.query)
if parsed.scheme == "gcs":
DEFAULT_FILE_STORAGE = "storages.backends.gcloud.GoogleCloudStorage"
GS_BUCKET_NAME = parsed.path.lstrip("/")
if parsed.path.lstrip("/"):
GS_BUCKET_NAME = parsed.path.lstrip("/")
else:
GS_BUCKET_NAME = parsed.hostname
GS_QUERYSTRING_AUTH = False
elif parsed.scheme == "s3":
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
@ -273,6 +291,8 @@ if SETUP.MEDIA_BACKEND:
AWS_SECRET_ACCESS_KEY = parsed.password
port = parsed.port or 443
AWS_S3_ENDPOINT_URL = f"{parsed.hostname}:{port}"
else:
raise ValueError(f"Unsupported media backend {parsed.scheme}")
if SETUP.ERROR_EMAILS:
ADMINS = [("Admin", e) for e in SETUP.ERROR_EMAILS]