Files
6e096fa286 Add real preview deployments and strengthen core boundaries (#184)
* 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>
2026-10-24 15:31:39 -04:00

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