still fixing i40 vtl issue

This commit is contained in:
Warp Agent
2025-12-31 03:04:11 +07:00
parent 2de3c5f6ab
commit a558c97088
11 changed files with 3901 additions and 19 deletions

View File

@@ -70,6 +70,21 @@ export interface ListClientsParams {
search?: string
}
export interface PoolStats {
name: string
used_bytes: number
total_bytes: number
usage_percent: number
}
export interface DashboardStats {
director_status: string
director_uptime: string
last_job?: BackupJob
active_jobs_count: number
default_pool?: PoolStats
}
export const backupAPI = {
listJobs: async (params?: ListJobsParams): Promise<ListJobsResponse> => {
const queryParams = new URLSearchParams()
@@ -111,5 +126,132 @@ export const backupAPI = {
)
return response.data
},
getDashboardStats: async (): Promise<DashboardStats> => {
const response = await apiClient.get<DashboardStats>('/backup/dashboard/stats')
return response.data
},
listStoragePools: async (): Promise<{ pools: StoragePool[]; total: number }> => {
const response = await apiClient.get<{ pools: StoragePool[]; total: number }>('/backup/storage/pools')
return response.data
},
listStorageVolumes: async (poolName?: string): Promise<{ volumes: StorageVolume[]; total: number }> => {
const queryParams = new URLSearchParams()
if (poolName) queryParams.append('pool_name', poolName)
const response = await apiClient.get<{ volumes: StorageVolume[]; total: number }>(
`/backup/storage/volumes${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
)
return response.data
},
listStorageDaemons: async (): Promise<{ daemons: StorageDaemon[]; total: number }> => {
const response = await apiClient.get<{ daemons: StorageDaemon[]; total: number }>('/backup/storage/daemons')
return response.data
},
createStoragePool: async (data: CreateStoragePoolRequest): Promise<StoragePool> => {
const response = await apiClient.post<StoragePool>('/backup/storage/pools', data)
return response.data
},
deleteStoragePool: async (poolId: number): Promise<void> => {
await apiClient.delete(`/backup/storage/pools/${poolId}`)
},
createStorageVolume: async (data: CreateStorageVolumeRequest): Promise<StorageVolume> => {
const response = await apiClient.post<StorageVolume>('/backup/storage/volumes', data)
return response.data
},
updateStorageVolume: async (volumeId: number, data: UpdateStorageVolumeRequest): Promise<StorageVolume> => {
const response = await apiClient.put<StorageVolume>(`/backup/storage/volumes/${volumeId}`, data)
return response.data
},
deleteStorageVolume: async (volumeId: number): Promise<void> => {
await apiClient.delete(`/backup/storage/volumes/${volumeId}`)
},
listMedia: async (): Promise<{ media: Media[]; total: number }> => {
const response = await apiClient.get<{ media: Media[]; total: number }>('/backup/media')
return response.data
},
}
export interface CreateStoragePoolRequest {
name: string
pool_type?: string
label_format?: string
recycle?: boolean
auto_prune?: boolean
}
export interface CreateStorageVolumeRequest {
volume_name: string
pool_name: string
media_type?: string
max_vol_bytes?: number
vol_retention?: number
}
export interface UpdateStorageVolumeRequest {
max_vol_bytes?: number
vol_retention?: number
}
export interface Media {
media_id: number
volume_name: string
pool_name: string
media_type: string
status: string
vol_bytes: number
max_vol_bytes: number
vol_files: number
last_written?: string
recycle_count: number
slot?: number
in_changer?: number
library_name?: string
}
export interface StoragePool {
pool_id: number
name: string
pool_type: string
label_format?: string
recycle?: boolean
auto_prune?: boolean
volume_count: number
used_bytes: number
total_bytes: number
usage_percent: number
}
export interface StorageVolume {
volume_id: number
media_id: number
volume_name: string
pool_name: string
media_type: string
vol_status: string
vol_bytes: number
max_vol_bytes: number
vol_files: number
vol_retention?: string
last_written?: string
recycle_count: number
}
export interface StorageDaemon {
storage_id: number
name: string
address: string
port: number
device_name: string
media_type: string
status: string
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,21 @@ export default function LoginPage() {
}
return (
<>
<style>{`
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px #111a22 inset !important;
-webkit-text-fill-color: #ffffff !important;
box-shadow: 0 0 0 30px #111a22 inset !important;
caret-color: #ffffff !important;
}
input:-webkit-autofill::first-line {
color: #ffffff !important;
}
`}</style>
<div className="min-h-screen flex items-center justify-center bg-background-dark">
<div className="max-w-md w-full space-y-8 p-8 bg-card-dark border border-border-dark rounded-lg shadow-md">
<div className="flex flex-col items-center">
@@ -73,10 +88,11 @@ export default function LoginPage() {
name="username"
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-t-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-t-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm autofill:bg-[#111a22] autofill:text-white"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
autoComplete="username"
/>
</div>
<div>
@@ -88,10 +104,11 @@ export default function LoginPage() {
name="password"
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-b-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm"
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-border-dark bg-[#111a22] placeholder-text-secondary text-white rounded-b-md focus:outline-none focus:ring-primary focus:border-primary focus:z-10 sm:text-sm autofill:bg-[#111a22] autofill:text-white"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="current-password"
/>
</div>
</div>
@@ -121,6 +138,7 @@ export default function LoginPage() {
</form>
</div>
</div>
</>
)
}