diff --git a/app/main.py b/app/main.py index 65d539b..2d6af14 100644 --- a/app/main.py +++ b/app/main.py @@ -197,6 +197,8 @@ class Service(Base): type: Mapped[ServiceType] = mapped_column(Enum(ServiceType), index=True) target: Mapped[str] = mapped_column(Text) comment: Mapped[str] = mapped_column(Text, default="") + svc_login: Mapped[str] = mapped_column(String(256), default="") + svc_password: Mapped[str] = mapped_column(String(256), default="") icon_path: Mapped[str] = mapped_column(Text, default="") active: Mapped[bool] = mapped_column(Boolean, default=True) warm_pool_size: Mapped[int] = mapped_column(Integer, default=0) @@ -1255,6 +1257,8 @@ def ensure_schema_compatibility() -> None: with engine.begin() as conn: conn.execute(text("ALTER TABLE services ADD COLUMN IF NOT EXISTS warm_pool_size INTEGER NOT NULL DEFAULT 0")) conn.execute(text("ALTER TABLE services ADD COLUMN IF NOT EXISTS comment TEXT NOT NULL DEFAULT ''")) + conn.execute(text("ALTER TABLE services ADD COLUMN IF NOT EXISTS svc_login VARCHAR(256) NOT NULL DEFAULT ''")) + conn.execute(text("ALTER TABLE services ADD COLUMN IF NOT EXISTS svc_password VARCHAR(256) NOT NULL DEFAULT ''")) conn.execute(text("ALTER TABLE services ADD COLUMN IF NOT EXISTS icon_path TEXT NOT NULL DEFAULT ''")) conn.execute( text( @@ -2677,6 +2681,8 @@ def create_service(payload: dict, request: Request, _: User = Depends(require_ad type=service_type, target=target, comment=payload.get("comment", ""), + svc_login=payload.get("svc_login", ""), + svc_password=payload.get("svc_password", ""), active=payload.get("active", True), warm_pool_size=max(0, int(payload.get("warm_pool_size", 0))), ) @@ -2741,7 +2747,7 @@ def edit_service(service_id: int, payload: dict, request: Request, _: User = Dep service = db.get(Service, service_id) if not service: raise HTTPException(status_code=404, detail="Service not found") - for key in ["name", "slug", "target", "active", "comment"]: + for key in ["name", "slug", "target", "active", "comment", "svc_login", "svc_password"]: if key in payload: setattr(service, key, payload[key]) if "type" in payload: diff --git a/app/static/style.css b/app/static/style.css index 1e587d0..d4ddcfe 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -344,6 +344,72 @@ button { border-color: #0f5b94; color: #fff; } +.tile-wrap { + display: flex; + flex-direction: column; + gap: 0; +} +.svc-credentials { + background: linear-gradient(135deg, #f0f6fc 0%, #e8f2f9 100%); + border: 1px solid #c7d9ea; + border-top: none; + border-radius: 0 0 12px 12px; + padding: 0.55rem 0.85rem 0.65rem; + display: flex; + flex-direction: column; + gap: 0.35rem; +} +.svc-cred-row { + display: flex; + align-items: center; + gap: 0.4rem; + min-width: 0; +} +.svc-cred-label { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .04em; + color: #5a7d9a; + min-width: 3.8rem; + flex-shrink: 0; +} +.svc-cred-value { + font-size: 0.84rem; + font-family: monospace; + color: #1a3a52; + font-weight: 600; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} +.svc-cred-masked { + letter-spacing: .12em; + font-size: 0.9rem; +} +.svc-cred-copy { + flex-shrink: 0; + width: 26px; + height: 26px; + border: 1px solid #b0c8de; + border-radius: 6px; + background: #fff; + color: #3a6d96; + cursor: pointer; + display: grid; + place-items: center; + padding: 0; + font-size: 0.78rem; + transition: background .15s, color .15s, border-color .15s; + font-family: sans-serif; +} +.svc-cred-copy::before { content: "\2398"; font-size: 0.9rem; } +.svc-cred-copy.copied { background: #1a8a4a; border-color: #1a8a4a; color: #fff; } +.svc-cred-copy.copied::before { content: "\2713"; } +.svc-cred-copy:hover:not(.copied) { background: #e6f0f9; border-color: #7aabcf; } + .tile { display: flex; flex-direction: column; @@ -355,6 +421,7 @@ button { box-shadow: 0 8px 22px rgba(0, 0, 0, 0.06); border: 1px solid transparent; transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease; + flex: 1; } .tile:hover { transform: translateY(-2px); @@ -546,6 +613,11 @@ button { border: 1px solid rgba(255, 255, 255, 0.45); backdrop-filter: blur(3px); } +.dashboard-page .svc-credentials { + background: rgba(224, 240, 252, 0.7); + border-color: rgba(180, 210, 235, 0.6); + backdrop-filter: blur(3px); +} .made-by-wrap { @@ -643,6 +715,11 @@ button { background: rgba(255, 255, 255, 0.9) !important; border: 1px solid rgba(198, 218, 235, 0.9) !important; } +.dashboard-page .svc-credentials { + backdrop-filter: none !important; + background: rgba(232, 244, 253, 0.95) !important; + border-color: rgba(180, 210, 235, 0.9) !important; +} .dashboard-page .panel { width: 100%; min-width: 0; @@ -812,3 +889,7 @@ button { .tile-comment th { background: #eaf2fb; font-weight: 700; } .tile-comment del { text-decoration: line-through; color: #7a9aaf; } .tile-comment input[type=checkbox] { margin-right: 0.3em; } + +.tile-wrap:has(.svc-credentials) .tile { + border-radius: 12px 12px 0 0; +} diff --git a/app/templates/admin.html b/app/templates/admin.html index dc16d84..da75bb2 100644 --- a/app/templates/admin.html +++ b/app/templates/admin.html @@ -123,7 +123,7 @@