Files
DockMV/web/src/types.ts
T
kawaandClaude Sonnet 5 8d20d9be84
Sync Gitea releases to GitHub / sync-releases (push) Canceled after 0s
Feature 1: Local accounts, replacing shared token
- Local accounts with bcrypt password hashing; first-run setup via POST /api/setup
- Personal API tokens (dmv_<48hex>, SHA-256 hashed at rest) for scripted access
- Server-side in-memory sessions with 32-byte secure cookie (dockmv_session, 7-day TTL, sliding renewal)
- Login rate limiting (exponential backoff 1s–30s cap) per IP
- Refuse to bind non-loopback while no account exists, unless DOCKMV_TRUST_ADDR=1 (for Docker's port mapping)
- Every account can manage every other account (no roles in v1)
- Auth middleware: public-path allowlist (/api/setup, /api/login, /api/logout, /api/me, /api/health) + session cookie check + API token (X-Auth-Token or Authorization: Bearer) check
- Frontend AuthGate gates app on GET /api/me; shows setup screen or login form or app tree as needed
- Account tab for personal token management; sign-out button in topbar
- Break: removed --token flag, DOCKMV_TOKEN env var, ?token= query param, /api/health no longer auto-responds when unauthenticated

Verified: go build/vet clean, frontend tsc+vite clean. Sandbox cannot execute binaries to test setup→login→session→token flow at runtime; recommend manual pass before merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 15:13:54 +02:00

314 lines
5.9 KiB
TypeScript

// Mirrors the Go types in internal/spec, internal/dkr and internal/job.
export type MountKind = 'volume' | 'anonymous' | 'bind' | 'tmpfs' | string
export interface Mount {
kind: MountKind
name?: string
source?: string
destination: string
readOnly: boolean
propagation?: string
tmpfsOpts?: string
sizeBytes: number
}
export interface Endpoint {
network: string
aliases?: string[]
ipv4Address?: string
ipv6Address?: string
macAddress?: string
}
export interface PortBinding {
containerPort: string
hostIp?: string
hostPort?: string
}
export interface Container {
id: string
name: string
state: string
image: string
imageId: string
imageDigest?: string
composeProject?: string
composeService?: string
env?: string[]
labels?: Record<string, string>
cmd?: string[]
entrypoint?: string[]
restartPolicy?: string
privileged?: boolean
networkMode?: string
endpoints?: Endpoint[]
ports?: PortBinding[]
mounts?: Mount[]
warnings?: string[]
}
export interface Volume {
name: string
driver: string
driverOpts?: Record<string, string>
labels?: Record<string, string>
}
export interface NetworkSpec {
name: string
driver: string
internal?: boolean
attachable?: boolean
}
export interface Inventory {
host: string
dockerVersion: string
containers: Container[]
volumes: Volume[]
networks: NetworkSpec[]
warnings?: string[]
}
export type ImageMode = 'auto' | 'pull' | 'stream' | 'skip'
export type MountAction = 'copy' | 'structure' | 'skip'
export type ConflictPolicy = 'fail' | 'skip' | 'replace' | 'rename'
export interface MountSelection {
action: MountAction
targetSource?: string
targetName?: string
}
export interface ItemSelection {
containerId: string
include: boolean
nameOverride?: string
migrateImage: boolean
imageMode: ImageMode
migrateNetworks: boolean
keepStaticIps: boolean
migratePorts: boolean
mounts: Record<string, MountSelection>
startAfter: boolean
stopSourceDuringCopy: boolean
stopSourceAfter: boolean
}
export interface Options {
conflict: ConflictPolicy
renameSuffix?: string
compress: boolean
compressLevel: number
dryRun: boolean
parallelism: number
verifyAfter: boolean
}
export interface Plan {
items: ItemSelection[]
options: Options
target?: string
packageName?: string
}
export interface SourceResponse {
inventory: Inventory
defaults: Record<string, ItemSelection>
options: Options
}
/** How a source daemon is reached. 'local' is the built-in, unremovable one. */
export type SourceKind = 'local' | 'docker' | 'ssh'
export interface Source {
id: string
name: string
kind: SourceKind
/** Daemon address for the 'docker' kind, e.g. tcp://10.0.0.5:2375. */
dockerHost?: string
/** Remote host for the 'ssh' kind; the same shape as a target connection. */
ssh?: Connection
}
export interface SourceStatus {
id: string
name: string
kind: SourceKind
endpoint: string
dockerVersion?: string
connected: boolean
error?: string
}
export interface SourcesResponse {
sources: Source[]
selected: string
current: SourceStatus | null
}
export type AuthMethod = 'password' | 'key' | 'agent'
export interface Connection {
id: string
name: string
host: string
port: number
user: string
auth: AuthMethod
password?: string
privateKey?: string
privateKeyPath?: string
passphrase?: string
sudo: boolean
dockerCmd?: string
saveSecrets: boolean
}
export interface Preflight {
dockerVersion: string
serverVersion: string
os: string
arch: string
hasGzip: boolean
diskFreeBytes: number
dockerRoot: string
problems?: string[]
}
export interface TargetContainer {
id: string
name: string
image: string
state: string
status: string
ports: string
}
export interface TargetInventory {
host: string
containers: TargetContainer[] | null
volumes: string[] | null
networks: string[] | null
preflight: Preflight
}
export interface HostKeyInfo {
host: string
keyType: string
fingerprint: string
trusted: boolean
changed: boolean
}
export type JobState = 'pending' | 'running' | 'succeeded' | 'failed' | 'skipped' | 'canceled'
export interface Step {
id: string
label: string
state: JobState
bytesDone: number
bytesTotal: number
error?: string
startedAt?: string
endedAt?: string
}
export interface JobItem {
id: string
name: string
state: JobState
error?: string
steps: Step[]
warnings?: string[]
}
export interface LogEntry {
seq: number
at: string
level: 'info' | 'warn' | 'error' | 'cmd'
item?: string
message: string
}
export interface JobSnapshot {
id: string
kind: 'ssh' | 'package' | 'restore'
title: string
state: JobState
dryRun: boolean
error?: string
createdAt: string
startedAt?: string
endedAt?: string
items: JobItem[]
log: LogEntry[]
bytesDone: number
bytesTotal: number
artifact?: string
artifactBytes?: number
revision: number
}
export interface PreviewItem {
containerId: string
name: string
targetName: string
image: string
commands: string[] | null
transfers: string[] | null
notes: string[] | null
warnings: string[] | null
totalBytes: number
}
export interface PreviewResponse {
networkCommands: string[] | null
items: PreviewItem[]
}
export interface PackageInfo {
name: string
path: string
bytes: number
isDir: boolean
createdAt: string
}
export interface Health {
ok: boolean
dockerHost: string
dockerVersion?: string
dockerError?: string
source?: SourceStatus
packageDir: string
dataDir: string
knownHosts: string
}
export interface User {
id: string
username: string
createdAt: string
lastLoginAt?: string
}
export interface APIToken {
id: string
userId: string
name: string
hint: string
createdAt: string
lastUsedAt?: string
}
/** The response from GET /api/me, /api/setup and /api/login. */
export interface Me {
id?: string
username?: string
authenticated: boolean
needsSetup?: boolean
}