still fixing i40 vtl issue
This commit is contained in:
@@ -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
@@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user