mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 05:24:49 +09:00
Introduce fallback audit logging and enrich audit tooling; add builtin extension discovery/install; and update default launcher ports and related docs/config. Key changes: - New middleware app.middleware.audit registers an after-request fallback that records authenticated mutating API requests when routes don't emit explicit audit logs. - Audit model/action constants extended and AuditService enhanced (request context handling, redaction for sensitive settings, optional commit flag, sets g.audit_logged, emits events). - Routes/services updated to create explicit audit entries for marketplace and plugin operations (create/update/publish/install/uninstall/enable/disable) and SSO/settings flows switched to use AuditService. - Added tests for audit logging behavior and redaction (backend/tests/test_audit_log.py). - Plugin service: added BUILTIN_EXTENSIONS_DIR, list_builtin_extensions and install_builtin_extension; plugin API endpoints to list/install builtin extensions. - Contribution system extended to support layouts and route layout metadata; frontend wiring updated to surface standalone/custom layouts and extension routes. - Launcher port defaults changed (backend -> 47927, frontend -> 41921). Updated backend .env.example, config CORS defaults, Dockerfiles and docker-compose to use SERVERKIT_BACKEND_PORT, and many docs/README files and frontend envs to reflect new ports. - Minor frontend cleanups and new builtin extension example (serverkit-git) plus associated manifest and frontend re-export. These changes improve observability/security for mutating APIs, enable opt-in builtin plugins, and align local dev/run ports with the launcher defaults.
103 lines
3.9 KiB
Python
103 lines
3.9 KiB
Python
import os
|
|
import sys
|
|
import warnings
|
|
from datetime import timedelta
|
|
|
|
# Default insecure keys that must be changed in production
|
|
INSECURE_SECRET_KEYS = [
|
|
'dev-secret-key-change-in-production',
|
|
'jwt-secret-key-change-in-production',
|
|
'change-this-to-a-secure-random-string',
|
|
'change-this-to-another-secure-random-string',
|
|
]
|
|
|
|
|
|
class Config:
|
|
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')
|
|
|
|
# Database - use instance folder for Flask convention
|
|
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', 'sqlite:////app/instance/serverkit.db')
|
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
|
|
# JWT
|
|
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY', 'jwt-secret-key-change-in-production')
|
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=15)
|
|
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
|
|
|
|
# CORS - allow the launcher defaults plus legacy local dev ports.
|
|
DEFAULT_CORS_ORIGINS = ','.join([
|
|
'http://localhost:41921',
|
|
'http://127.0.0.1:41921',
|
|
'http://localhost:47927',
|
|
'http://127.0.0.1:47927',
|
|
'http://localhost:5173',
|
|
'http://127.0.0.1:5173',
|
|
'http://localhost:5000',
|
|
'http://127.0.0.1:5000',
|
|
])
|
|
CORS_ORIGINS = os.environ.get('CORS_ORIGINS', DEFAULT_CORS_ORIGINS).split(',')
|
|
|
|
|
|
class DevelopmentConfig(Config):
|
|
DEBUG = True
|
|
|
|
@classmethod
|
|
def init_app(cls, app):
|
|
if app.config.get('SECRET_KEY') == 'dev-secret-key-change-in-production':
|
|
warnings.warn('WARNING: Using default SECRET_KEY. Change before deploying.')
|
|
if app.config.get('JWT_SECRET_KEY') == 'jwt-secret-key-change-in-production':
|
|
warnings.warn('WARNING: Using default JWT_SECRET_KEY. Change before deploying.')
|
|
|
|
|
|
class TestingConfig(Config):
|
|
"""Config for pytest and other automated tests."""
|
|
TESTING = True
|
|
DEBUG = True
|
|
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL', 'sqlite:///:memory:')
|
|
# Reduce noise during tests
|
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(minutes=5)
|
|
|
|
|
|
class ProductionConfig(Config):
|
|
DEBUG = False
|
|
|
|
# Secure session cookies in production
|
|
SESSION_COOKIE_SECURE = True
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
|
|
|
def __init__(self):
|
|
# Validate that secret keys are not default values in production
|
|
if self.SECRET_KEY in INSECURE_SECRET_KEYS:
|
|
print("FATAL: SECRET_KEY is set to a default insecure value in production mode!", file=sys.stderr)
|
|
print("Generate a secure key with: python -c \"import secrets; print(secrets.token_hex(32))\"", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if self.JWT_SECRET_KEY in INSECURE_SECRET_KEYS:
|
|
print("FATAL: JWT_SECRET_KEY is set to a default insecure value in production mode!", file=sys.stderr)
|
|
print("Generate a secure key with: python -c \"import secrets; print(secrets.token_hex(32))\"", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
@classmethod
|
|
def init_app(cls, app):
|
|
"""Validate production configuration."""
|
|
insecure_keys = ['dev-secret-key-change-in-production', 'jwt-secret-key-change-in-production']
|
|
if app.config['SECRET_KEY'] in insecure_keys:
|
|
raise ValueError('CRITICAL: SECRET_KEY must be changed for production deployment')
|
|
if app.config['JWT_SECRET_KEY'] in insecure_keys:
|
|
raise ValueError('CRITICAL: JWT_SECRET_KEY must be changed for production deployment')
|
|
# Validate CORS origins
|
|
cors_raw = os.environ.get('CORS_ORIGINS', '')
|
|
cors_origins = [o.strip() for o in cors_raw.split(',') if o.strip()]
|
|
if not cors_origins:
|
|
raise ValueError('CORS_ORIGINS must be explicitly set in production')
|
|
app.config['CORS_ORIGINS'] = cors_origins
|
|
|
|
|
|
config = {
|
|
'development': DevelopmentConfig,
|
|
'testing': TestingConfig,
|
|
'production': ProductionConfig,
|
|
'default': DevelopmentConfig,
|
|
}
|