6cc797f915
- Retain login state in remote browser profiles (don't delete on disconnect)
- Add GET /api/browser-sessions/{id}/clipboard for clipboard sync
- Add POST /api/browser-sessions/{id}/autofill-login for manual credential fill
- Add DELETE /api/browser-sessions/profiles/{custom_page_id} for login clear
- Add balance tracking with configurable divisor (balance_divisor)
- Health check on session reuse, idle TTL eviction, background cleanup
- Add first-frame watchdog (10s timeout) to prevent infinite loading
- Reconnect browser on active=true when session was closed
- UI: uniform text-only inline buttons (websites + upstreams pages)
- Fix page switch race with closingRemoteSessionPromise
90 lines
4.0 KiB
Python
90 lines
4.0 KiB
Python
from sqlalchemy import create_engine, inspect, text
|
|
from sqlalchemy.orm import sessionmaker, DeclarativeBase
|
|
from app.config import get_settings
|
|
|
|
settings = get_settings()
|
|
|
|
engine = create_engine(
|
|
settings.database_url,
|
|
connect_args={"check_same_thread": False},
|
|
)
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
pass
|
|
|
|
|
|
def get_db():
|
|
db = SessionLocal()
|
|
try:
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def init_db():
|
|
"""Create all tables."""
|
|
# import models so SQLAlchemy registers them
|
|
from app.models import admin_user, upstream, snapshot, webhook_config, notification_log, custom_page, website, revoked_token # noqa: F401
|
|
Base.metadata.create_all(bind=engine)
|
|
_migrate_custom_pages()
|
|
_migrate_upstreams()
|
|
|
|
|
|
def _migrate_custom_pages():
|
|
"""Apply small SQLite-safe migrations for deployments without Alembic."""
|
|
inspector = inspect(engine)
|
|
if "custom_pages" not in inspector.get_table_names():
|
|
return
|
|
columns = {col["name"] for col in inspector.get_columns("custom_pages")}
|
|
with engine.begin() as conn:
|
|
if "access_mode" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN access_mode VARCHAR(32) NOT NULL DEFAULT 'direct'"))
|
|
conn.execute(text("UPDATE custom_pages SET access_mode = CASE WHEN use_proxy = 1 THEN 'proxy' ELSE 'direct' END"))
|
|
if "login_username" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_username VARCHAR(255)"))
|
|
if "login_password" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_password TEXT"))
|
|
if "login_username_selector" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_username_selector VARCHAR(512)"))
|
|
if "login_password_selector" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_password_selector VARCHAR(512)"))
|
|
if "login_submit_selector" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_submit_selector VARCHAR(512)"))
|
|
if "login_autofill_enabled" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_autofill_enabled BOOLEAN NOT NULL DEFAULT 0"))
|
|
if "login_autofill_backfilled_at" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN login_autofill_backfilled_at DATETIME"))
|
|
conn.execute(
|
|
text(
|
|
"UPDATE custom_pages "
|
|
"SET login_autofill_enabled = 1, login_autofill_backfilled_at = CURRENT_TIMESTAMP "
|
|
"WHERE login_autofill_enabled = 0 "
|
|
"AND NULLIF(TRIM(login_username), '') IS NOT NULL "
|
|
"AND NULLIF(TRIM(login_password), '') IS NOT NULL"
|
|
)
|
|
)
|
|
if "linked_upstream_id" not in columns:
|
|
conn.execute(text("ALTER TABLE custom_pages ADD COLUMN linked_upstream_id INTEGER"))
|
|
|
|
|
|
def _migrate_upstreams():
|
|
"""Apply SQLite-safe migrations to the upstreams table."""
|
|
inspector = inspect(engine)
|
|
if "upstreams" not in inspector.get_table_names():
|
|
return
|
|
columns = {col["name"] for col in inspector.get_columns("upstreams")}
|
|
with engine.begin() as conn:
|
|
if "balance" not in columns:
|
|
conn.execute(text("ALTER TABLE upstreams ADD COLUMN balance FLOAT"))
|
|
if "balance_updated_at" not in columns:
|
|
conn.execute(text("ALTER TABLE upstreams ADD COLUMN balance_updated_at DATETIME"))
|
|
if "balance_endpoint" not in columns:
|
|
conn.execute(text("ALTER TABLE upstreams ADD COLUMN balance_endpoint VARCHAR(256) NOT NULL DEFAULT ''"))
|
|
if "balance_response_path" not in columns:
|
|
conn.execute(text("ALTER TABLE upstreams ADD COLUMN balance_response_path VARCHAR(256) NOT NULL DEFAULT ''"))
|
|
if "balance_divisor" not in columns:
|
|
conn.execute(text("ALTER TABLE upstreams ADD COLUMN balance_divisor FLOAT NOT NULL DEFAULT 1.0"))
|
|
|