mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-02 18:17:11 +09:00
* fix(security): honour the failed-login time window (#180) check_failed_logins computed a cutoff from since_hours and never used it, so the Security -> Events "Failed login attempts (24h)" card and its alert counted every failure still in /var/log/auth.log or /var/log/secure: up to a week of SSH attempts under the usual weekly log rotation. Each matching line is now dated from its stamp, either RFC 3339 (rsyslog's high-precision format) or the traditional BSD syslog stamp (no year, so a December line read in January is placed in the previous year), and lines older than the cutoff are skipped. A line whose stamp is not recognised is still counted rather than hidden. The log paths move to SecurityService.AUTH_LOGS so the tests can point at a temp file. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * refactor: separate backend startup phases and guard ordering * refactor: route file operations through capability-checked adapters * chore: bump version to 1.11.25 [skip ci] * refactor: split File Manager state and views with regression guards * test: enforce core boundaries and document extension rules * test: cover File Manager tree races and listing failures * fix: preserve File Manager deep links on initial mount * refactor: keep File Manager download operations in the action hook * fix: start agent maintenance only in the guarded runtime phase * test: capture nginx rendering contract before extraction * refactor: separate nginx configuration rendering from host operations * test: enforce pure nginx configuration boundaries * fix: require provisioned runtime before advertising previews * feat: expose value-free effective environment provenance through app access boundary * feat: add effective environment inspector with browser regression checks * feat: provision isolated generic previews with retryable resource cleanup * feat: queue preview automation and preserve cleanup ownership through app deletion * feat: add pinned preview deployment controls and queued lifecycle feedback * fix: preserve container port semantics for previews of slot deployments * fix: preserve search focus and ignore stale list responses * feat: run approved assistant deployments through persistent recipe operations * test: run search and assistant deployment regressions in CI * fix: refresh preview API and controller boundary inventories * docs: refresh controller migration count after preview extraction * fix: bind file editor previews to their storage target * chore: bump version to 1.11.26 [skip ci] --------- Co-authored-by: Max Freedom Pollard <272618364+MaxFreedomPollard@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
import os
|
|
from flask import Flask
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
from flask_jwt_extended import JWTManager
|
|
from flask_limiter import Limiter
|
|
from flask_limiter.util import get_remote_address
|
|
from flask_migrate import Migrate
|
|
|
|
from config import config
|
|
|
|
db = SQLAlchemy()
|
|
jwt = JWTManager()
|
|
migrate = Migrate()
|
|
|
|
# PyJWT 2.10+ enforces that 'sub' must be a string.
|
|
# Stringify the identity so integer user IDs work transparently.
|
|
@jwt.user_identity_loader
|
|
def _user_identity(user_id):
|
|
return str(user_id)
|
|
|
|
|
|
@jwt.additional_claims_loader
|
|
def _session_claims(user_id):
|
|
import time
|
|
import secrets
|
|
from app.models import User
|
|
user = db.session.get(User, user_id)
|
|
session_id = secrets.token_hex(16)
|
|
return {'auth_version': user.auth_version if user else None,
|
|
'session_id': session_id,
|
|
'auth_time': int(time.time())}
|
|
|
|
|
|
@jwt.token_in_blocklist_loader
|
|
def _session_revoked(_header, claims):
|
|
from app.middleware.session_auth import validate_session_claims
|
|
return validate_session_claims(claims, token_type=claims.get('type')) is None
|
|
|
|
|
|
limiter = Limiter(key_func=get_remote_address, default_limits=["100 per minute"])
|
|
# Note: key_func is updated to get_rate_limit_key after app init
|
|
socketio = None
|
|
|
|
# Path to frontend dist folder (relative to backend folder)
|
|
FRONTEND_DIST = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'frontend', 'dist')
|
|
|
|
|
|
from app.bootstrap.logging import configure_logging as _configure_logging
|
|
|
|
|
|
def create_app(config_name=None):
|
|
"""Assemble the panel in dependency order; retain public extension exports."""
|
|
global socketio
|
|
from app.bootstrap.configuration import configure_application
|
|
from app.bootstrap.persistence import prepare_persistence
|
|
from app.bootstrap.plugins import load_extensions
|
|
from app.bootstrap.runtime import start_runtime
|
|
from app.bootstrap.http import register_http_handlers
|
|
|
|
if config_name is None:
|
|
config_name = os.environ.get('FLASK_ENV', 'development')
|
|
app = Flask(__name__, static_folder=FRONTEND_DIST, static_url_path='')
|
|
app.config.from_object(config[config_name])
|
|
_configure_logging(app)
|
|
configure_application(app, db, migrate, jwt, limiter)
|
|
|
|
# Initialize SocketIO
|
|
from app.sockets import init_socketio
|
|
socketio = init_socketio(app)
|
|
|
|
# Initialize Agent Gateway
|
|
from app.agent_gateway import init_agent_gateway
|
|
init_agent_gateway(socketio)
|
|
|
|
# Core routes are an explicit, ordered manifest. Extension blueprints stay
|
|
# outside this registry and are loaded after migrations below.
|
|
from app.core_blueprints import register_core_blueprints
|
|
register_core_blueprints(app)
|
|
|
|
# Register the core restore handlers after their models and API modules
|
|
# have been imported by the blueprint registry.
|
|
from app.services import recycle_bin_service
|
|
recycle_bin_service.register_builtin_types()
|
|
|
|
# Restore adapters are process-local strategy registrations. They do not
|
|
# touch the database and must exist in testing/CLI app factories too.
|
|
from app.services.restore_point_adapters import register_builtin_restore_point_adapters
|
|
register_builtin_restore_point_adapters()
|
|
|
|
with app.app_context():
|
|
prepare_persistence(app)
|
|
load_extensions(app)
|
|
start_runtime(app)
|
|
|
|
register_http_handlers(app)
|
|
return app
|
|
|
|
|
|
def get_socketio():
|
|
"""Get the SocketIO instance."""
|
|
return socketio
|