Compare commits

...

2 Commits

Author SHA1 Message Date
7a672fc079 Merge branch 'main' of https://git.ntbx.io/boris/keywarden
Some checks failed
CI / Lint & Format (push) Successful in 4s
CI / Docker Build (push) Has been cancelled
CI / Tests (Pytest + Alembic + Postgres) (push) Has been cancelled
2025-09-23 18:55:10 +01:00
5aacf17ef2 Exposed docs endpoint, added default env 2025-09-23 18:55:07 +01:00
7 changed files with 69 additions and 25 deletions

10
.env.example Normal file
View File

@@ -0,0 +1,10 @@
DOCKERDIR=/opt/compose/keywarden
KEYWARDEN_SECRET_KEY=
# PostgreSQL Connection (These are default values, unneeded if matching environment)
KEYWARDEN_POSTGRES_USER="postgres"
KEYWARDEN_POSTGRES_PASSWORD="postgres"
KEYWARDEN_POSTGRES_HOST="keywarden-db"
KEYWARDEN_POSTGRES_PORT=5432
KEYWARDEN_POSTGRES_DB="keywarden"
KEYWARDEN_ACCESS_TOKEN_EXPIRE_MINUTES=60

View File

@@ -11,9 +11,12 @@ permissions:
env: env:
PYTHON_VERSION: "3.11" PYTHON_VERSION: "3.11"
# Used by tests / alembic; matches docker-compose-style DSN # Used by tests / alembic; matches docker compose environment
TEST_POSTGRES_DSN: postgresql+asyncpg://postgres:postgres@localhost:5432/keywarden KEYWARDEN_POSTGRES_USER: postgres
KEYWARDEN_POSTGRES_PASSWORD: postgres
KEYWARDEN_POSTGRES_HOST: localhost
KEYWARDEN_POSTGRES_PORT: 5432
KEYWARDEN_POSTGRES_DB: keywarden
jobs: jobs:
lint: lint:
name: Lint & Format name: Lint & Format

View File

@@ -25,11 +25,13 @@ if config.config_file_name is not None:
target_metadata = Base.metadata target_metadata = Base.metadata
# Get DB URL from env (prefer KEYWARDEN_ prefix, fall back to unprefixed, then a sane default for local) # Get DB URL from env (prefer KEYWARDEN_ prefix, fall back to unprefixed, then a sane default for local)
DB_URL = ( DB_USER = os.getenv("KEYWARDEN_POSTGRES_USER", "postgres")
os.getenv("KEYWARDEN_POSTGRES_DSN") DB_PASS = os.getenv("KEYWARDEN_POSTGRES_PASSWORD", "postgres")
or os.getenv("POSTGRES_DSN") DB_HOST = os.getenv("KEYWARDEN_POSTGRES_HOST", "localhost")
or "postgresql+asyncpg://postgres:postgres@localhost:5432/keywarden" DB_PORT = os.getenv("KEYWARDEN_POSTGRES_PORT", "5432")
) DB_NAME = os.getenv("KEYWARDEN_POSTGRES_DB", "keywarden")
DB_URL = f"postgresql+asyncpg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
def run_migrations_offline(): def run_migrations_offline():

View File

@@ -1,4 +1,4 @@
from pydantic import computed_field
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -8,13 +8,33 @@ class Settings(BaseSettings):
env_prefix="KEYWARDEN_", env_prefix="KEYWARDEN_",
extra="ignore", extra="ignore",
) )
PROJECT_NAME: str = "Keywarden" PROJECT_NAME: str = "Keywarden"
API_V1_STR: str = "/api/v1" API_V1_STR: str = "/api/v1"
SECRET_KEY: str
# Postgres split vars (with defaults)
POSTGRES_USER: str = "postgres"
POSTGRES_PASSWORD: str = "postgres"
POSTGRES_HOST: str = "keywarden-db"
POSTGRES_PORT: int = 5432
POSTGRES_DB: str = "keywarden"
SECRET_KEY: str = "insecure-dev-secret" # default for local dev only
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
POSTGRES_DSN: str
OIDC_ENABLED: bool = False
OIDC_ISSUER: str | None = None OIDC_ISSUER: str | None = None
OIDC_CLIENT_ID: str | None = None OIDC_CLIENT_ID: str | None = None
OIDC_CLIENT_SECRET: str | None = None OIDC_AUDIENCE: str | None = None # optional
OIDC_JWKS_URL: str | None = None # if not set, derive from issuer
@computed_field(return_type=str)
@property
def POSTGRES_DSN(self) -> str:
return (
f"postgresql+asyncpg://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
f"@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
)
settings = Settings() settings = Settings()

View File

@@ -3,7 +3,9 @@ from fastapi import FastAPI
from app.api.v1 import auth, keys from app.api.v1 import auth, keys
from app.core.config import settings from app.core.config import settings
app = FastAPI(title=settings.PROJECT_NAME) app = FastAPI(
title=settings.PROJECT_NAME
)
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"]) app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
app.include_router(keys.router, prefix=f"{settings.API_V1_STR}/keys", tags=["keys"]) app.include_router(keys.router, prefix=f"{settings.API_V1_STR}/keys", tags=["keys"])

View File

@@ -9,6 +9,7 @@ services:
- ${DOCKERDIR}/nginx/certs/:/certs/ - ${DOCKERDIR}/nginx/certs/:/certs/
- ${DOCKERDIR}/nginx/webdir/:/var/www/ - ${DOCKERDIR}/nginx/webdir/:/var/www/
- ${DOCKERDIR}/nginx/logs:/var/log/nginx/ - ${DOCKERDIR}/nginx/logs:/var/log/nginx/
# - "external:internal", change external to desired port
ports: ports:
- "443:443" - "443:443"
@@ -22,10 +23,13 @@ services:
db: db:
image: postgres:17-alpine image: postgres:17-alpine
environment: environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} POSTGRES_PASSWORD: ${KEYWARDEN_POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: keywarden POSTGRES_DB: ${KEYWARDEN_POSTGRES_DB:-keywarden}
POSTGRES_USER: postgres POSTGRES_USER: ${KEYWARDEN_POSTGRES_USER:-keywarden}
# ports: ["5432:5432"] POSTGRES_PORT: ${KEYWARDEN_POSTGRES_PORT:-5432}
# Do not enable unless debugging, not needed to be exposed outside of docker network
# ports:
# - "5432:5432"
volumes: volumes:
- "pgdata:/var/lib/postgresql/data" - "pgdata:/var/lib/postgresql/data"
@@ -33,12 +37,11 @@ services:
build: . build: .
depends_on: depends_on:
- db - db
environment: env_file:
- SECRET_KEY=[CREATE SECRET KEY] - .env
- POSTGRES_DSN=postgresql+asyncpg://postgres:${POSTGRES_PASSWORD:-postgres}@keywarden-postgres:5432/keywarden # API runs on port 8000, but is unneeded to be external unless using a custom reverse proxy on another machine
- ACCESS_TOKEN_EXPIRE_MINUTES=60 # ports:
ports: # - "8000:8000"
- "8000:8000"
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 command: uvicorn app.main:app --host 0.0.0.0 --port 8000
volumes: volumes:

View File

@@ -24,14 +24,18 @@ server {
location / { location / {
} }
location /docs {
proxy_pass http://api:8000;
}
location /openapi.json {
proxy_pass http://api:8000;
}
location /api/v1/ { location /api/v1/ {
proxy_pass http://api:8000; proxy_pass http://api:8000;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
} }
location /healthz { location /healthz {
proxy_pass http://api:8000; proxy_pass http://api:8000;