mirror of
https://github.com/immich-app/immich.git
synced 2025-12-16 09:13:13 +03:00
feat: sync status to web app
This commit is contained in:
3
mobile/openapi/README.md
generated
3
mobile/openapi/README.md
generated
@@ -159,6 +159,8 @@ Class | Method | HTTP request | Description
|
||||
*LibrariesApi* | [**scanLibrary**](doc//LibrariesApi.md#scanlibrary) | **POST** /libraries/{id}/scan | Scan a library
|
||||
*LibrariesApi* | [**updateLibrary**](doc//LibrariesApi.md#updatelibrary) | **PUT** /libraries/{id} | Update a library
|
||||
*LibrariesApi* | [**validate**](doc//LibrariesApi.md#validate) | **POST** /libraries/{id}/validate | Validate library settings
|
||||
*MaintenanceAdminApi* | [**deleteBackup**](doc//MaintenanceAdminApi.md#deletebackup) | **DELETE** /admin/maintenance/admin/maintenance/backups/{filename} | Delete backup
|
||||
*MaintenanceAdminApi* | [**listBackups**](doc//MaintenanceAdminApi.md#listbackups) | **GET** /admin/maintenance/admin/maintenance/backups/list | List backups
|
||||
*MaintenanceAdminApi* | [**maintenanceLogin**](doc//MaintenanceAdminApi.md#maintenancelogin) | **POST** /admin/maintenance/login | Log into maintenance mode
|
||||
*MaintenanceAdminApi* | [**maintenanceStatus**](doc//MaintenanceAdminApi.md#maintenancestatus) | **GET** /admin/maintenance/admin/maintenance/status | Get maintenance mode status
|
||||
*MaintenanceAdminApi* | [**setMaintenanceMode**](doc//MaintenanceAdminApi.md#setmaintenancemode) | **POST** /admin/maintenance | Set maintenance mode
|
||||
@@ -409,6 +411,7 @@ Class | Method | HTTP request | Description
|
||||
- [MachineLearningAvailabilityChecksDto](doc//MachineLearningAvailabilityChecksDto.md)
|
||||
- [MaintenanceAction](doc//MaintenanceAction.md)
|
||||
- [MaintenanceAuthDto](doc//MaintenanceAuthDto.md)
|
||||
- [MaintenanceListBackupsResponseDto](doc//MaintenanceListBackupsResponseDto.md)
|
||||
- [MaintenanceLoginDto](doc//MaintenanceLoginDto.md)
|
||||
- [MaintenanceStatusResponseDto](doc//MaintenanceStatusResponseDto.md)
|
||||
- [ManualJobName](doc//ManualJobName.md)
|
||||
|
||||
1
mobile/openapi/lib/api.dart
generated
1
mobile/openapi/lib/api.dart
generated
@@ -166,6 +166,7 @@ part 'model/logout_response_dto.dart';
|
||||
part 'model/machine_learning_availability_checks_dto.dart';
|
||||
part 'model/maintenance_action.dart';
|
||||
part 'model/maintenance_auth_dto.dart';
|
||||
part 'model/maintenance_list_backups_response_dto.dart';
|
||||
part 'model/maintenance_login_dto.dart';
|
||||
part 'model/maintenance_status_response_dto.dart';
|
||||
part 'model/manual_job_name.dart';
|
||||
|
||||
97
mobile/openapi/lib/api/maintenance_admin_api.dart
generated
97
mobile/openapi/lib/api/maintenance_admin_api.dart
generated
@@ -16,6 +16,103 @@ class MaintenanceAdminApi {
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Delete backup
|
||||
///
|
||||
/// Delete a backup by its filename
|
||||
///
|
||||
/// Note: This method returns the HTTP [Response].
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] filename (required):
|
||||
Future<Response> deleteBackupWithHttpInfo(String filename,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/admin/maintenance/admin/maintenance/backups/{filename}'
|
||||
.replaceAll('{filename}', filename);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
apiPath,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Delete backup
|
||||
///
|
||||
/// Delete a backup by its filename
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] filename (required):
|
||||
Future<void> deleteBackup(String filename,) async {
|
||||
final response = await deleteBackupWithHttpInfo(filename,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// List backups
|
||||
///
|
||||
/// Get the list of the successful and failed backups
|
||||
///
|
||||
/// Note: This method returns the HTTP [Response].
|
||||
Future<Response> listBackupsWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/admin/maintenance/admin/maintenance/backups/list';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
apiPath,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// List backups
|
||||
///
|
||||
/// Get the list of the successful and failed backups
|
||||
Future<MaintenanceListBackupsResponseDto?> listBackups() async {
|
||||
final response = await listBackupsWithHttpInfo();
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'MaintenanceListBackupsResponseDto',) as MaintenanceListBackupsResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Log into maintenance mode
|
||||
///
|
||||
/// Login with maintenance token or cookie to receive current information and perform further actions.
|
||||
|
||||
2
mobile/openapi/lib/api_client.dart
generated
2
mobile/openapi/lib/api_client.dart
generated
@@ -382,6 +382,8 @@ class ApiClient {
|
||||
return MaintenanceActionTypeTransformer().decode(value);
|
||||
case 'MaintenanceAuthDto':
|
||||
return MaintenanceAuthDto.fromJson(value);
|
||||
case 'MaintenanceListBackupsResponseDto':
|
||||
return MaintenanceListBackupsResponseDto.fromJson(value);
|
||||
case 'MaintenanceLoginDto':
|
||||
return MaintenanceLoginDto.fromJson(value);
|
||||
case 'MaintenanceStatusResponseDto':
|
||||
|
||||
111
mobile/openapi/lib/model/maintenance_list_backups_response_dto.dart
generated
Normal file
111
mobile/openapi/lib/model/maintenance_list_backups_response_dto.dart
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class MaintenanceListBackupsResponseDto {
|
||||
/// Returns a new [MaintenanceListBackupsResponseDto] instance.
|
||||
MaintenanceListBackupsResponseDto({
|
||||
this.backups = const [],
|
||||
this.failedBackups = const [],
|
||||
});
|
||||
|
||||
List<String> backups;
|
||||
|
||||
List<String> failedBackups;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is MaintenanceListBackupsResponseDto &&
|
||||
_deepEquality.equals(other.backups, backups) &&
|
||||
_deepEquality.equals(other.failedBackups, failedBackups);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(backups.hashCode) +
|
||||
(failedBackups.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'MaintenanceListBackupsResponseDto[backups=$backups, failedBackups=$failedBackups]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'backups'] = this.backups;
|
||||
json[r'failedBackups'] = this.failedBackups;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [MaintenanceListBackupsResponseDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static MaintenanceListBackupsResponseDto? fromJson(dynamic value) {
|
||||
upgradeDto(value, "MaintenanceListBackupsResponseDto");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceListBackupsResponseDto(
|
||||
backups: json[r'backups'] is Iterable
|
||||
? (json[r'backups'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
failedBackups: json[r'failedBackups'] is Iterable
|
||||
? (json[r'failedBackups'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<MaintenanceListBackupsResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <MaintenanceListBackupsResponseDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = MaintenanceListBackupsResponseDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, MaintenanceListBackupsResponseDto> mapFromJson(dynamic json) {
|
||||
final map = <String, MaintenanceListBackupsResponseDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = MaintenanceListBackupsResponseDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of MaintenanceListBackupsResponseDto-objects as value to a dart map
|
||||
static Map<String, List<MaintenanceListBackupsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<MaintenanceListBackupsResponseDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = MaintenanceListBackupsResponseDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'backups',
|
||||
'failedBackups',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ part of openapi.api;
|
||||
class MaintenanceStatusResponseDto {
|
||||
/// Returns a new [MaintenanceStatusResponseDto] instance.
|
||||
MaintenanceStatusResponseDto({
|
||||
this.action,
|
||||
required this.action,
|
||||
this.error,
|
||||
this.progress,
|
||||
this.task,
|
||||
});
|
||||
|
||||
MaintenanceStatusResponseDtoActionEnum? action;
|
||||
MaintenanceAction action;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -55,7 +55,7 @@ class MaintenanceStatusResponseDto {
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(action == null ? 0 : action!.hashCode) +
|
||||
(action.hashCode) +
|
||||
(error == null ? 0 : error!.hashCode) +
|
||||
(progress == null ? 0 : progress!.hashCode) +
|
||||
(task == null ? 0 : task!.hashCode);
|
||||
@@ -65,11 +65,7 @@ class MaintenanceStatusResponseDto {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.action != null) {
|
||||
json[r'action'] = this.action;
|
||||
} else {
|
||||
// json[r'action'] = null;
|
||||
}
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
@@ -97,7 +93,7 @@ class MaintenanceStatusResponseDto {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return MaintenanceStatusResponseDto(
|
||||
action: MaintenanceStatusResponseDtoActionEnum.fromJson(json[r'action']),
|
||||
action: MaintenanceAction.fromJson(json[r'action'])!,
|
||||
error: mapValueOfType<String>(json, r'error'),
|
||||
progress: num.parse('${json[r'progress']}'),
|
||||
task: mapValueOfType<String>(json, r'task'),
|
||||
@@ -148,83 +144,7 @@ class MaintenanceStatusResponseDto {
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'action',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class MaintenanceStatusResponseDtoActionEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const MaintenanceStatusResponseDtoActionEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const start = MaintenanceStatusResponseDtoActionEnum._(r'start');
|
||||
static const end = MaintenanceStatusResponseDtoActionEnum._(r'end');
|
||||
static const restoreDatabase = MaintenanceStatusResponseDtoActionEnum._(r'restore_database');
|
||||
|
||||
/// List of all possible values in this [enum][MaintenanceStatusResponseDtoActionEnum].
|
||||
static const values = <MaintenanceStatusResponseDtoActionEnum>[
|
||||
start,
|
||||
end,
|
||||
restoreDatabase,
|
||||
];
|
||||
|
||||
static MaintenanceStatusResponseDtoActionEnum? fromJson(dynamic value) => MaintenanceStatusResponseDtoActionEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<MaintenanceStatusResponseDtoActionEnum> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <MaintenanceStatusResponseDtoActionEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = MaintenanceStatusResponseDtoActionEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [MaintenanceStatusResponseDtoActionEnum] to String,
|
||||
/// and [decode] dynamic data back to [MaintenanceStatusResponseDtoActionEnum].
|
||||
class MaintenanceStatusResponseDtoActionEnumTypeTransformer {
|
||||
factory MaintenanceStatusResponseDtoActionEnumTypeTransformer() => _instance ??= const MaintenanceStatusResponseDtoActionEnumTypeTransformer._();
|
||||
|
||||
const MaintenanceStatusResponseDtoActionEnumTypeTransformer._();
|
||||
|
||||
String encode(MaintenanceStatusResponseDtoActionEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a MaintenanceStatusResponseDtoActionEnum.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
MaintenanceStatusResponseDtoActionEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'start': return MaintenanceStatusResponseDtoActionEnum.start;
|
||||
case r'end': return MaintenanceStatusResponseDtoActionEnum.end;
|
||||
case r'restore_database': return MaintenanceStatusResponseDtoActionEnum.restoreDatabase;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [MaintenanceStatusResponseDtoActionEnumTypeTransformer] instance.
|
||||
static MaintenanceStatusResponseDtoActionEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16705,12 +16705,11 @@
|
||||
"MaintenanceStatusResponseDto": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"enum": [
|
||||
"start",
|
||||
"end",
|
||||
"restore_database"
|
||||
],
|
||||
"type": "string"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/MaintenanceAction"
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
|
||||
@@ -44,8 +44,12 @@ export type SetMaintenanceModeDto = {
|
||||
action: MaintenanceAction;
|
||||
restoreBackupFilename?: string;
|
||||
};
|
||||
export type MaintenanceListBackupsResponseDto = {
|
||||
backups: string[];
|
||||
failedBackups: string[];
|
||||
};
|
||||
export type MaintenanceStatusResponseDto = {
|
||||
action?: Action;
|
||||
action: MaintenanceAction;
|
||||
error?: string;
|
||||
progress?: number;
|
||||
task?: string;
|
||||
@@ -521,7 +525,7 @@ export type AssetBulkUploadCheckDto = {
|
||||
assets: AssetBulkUploadCheckItem[];
|
||||
};
|
||||
export type AssetBulkUploadCheckResult = {
|
||||
action: Action2;
|
||||
action: Action;
|
||||
assetId?: string;
|
||||
id: string;
|
||||
isTrashed?: boolean;
|
||||
@@ -1851,6 +1855,28 @@ export function setMaintenanceMode({ setMaintenanceModeDto }: {
|
||||
body: setMaintenanceModeDto
|
||||
})));
|
||||
}
|
||||
/**
|
||||
* List backups
|
||||
*/
|
||||
export function listBackups(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: MaintenanceListBackupsResponseDto;
|
||||
}>("/admin/maintenance/admin/maintenance/backups/list", {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Delete backup
|
||||
*/
|
||||
export function deleteBackup({ filename }: {
|
||||
filename: string;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchText(`/admin/maintenance/admin/maintenance/backups/${encodeURIComponent(filename)}`, {
|
||||
...opts,
|
||||
method: "DELETE"
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Get maintenance mode status
|
||||
*/
|
||||
@@ -5074,11 +5100,6 @@ export enum MaintenanceAction {
|
||||
End = "end",
|
||||
RestoreDatabase = "restore_database"
|
||||
}
|
||||
export enum Action {
|
||||
Start = "start",
|
||||
End = "end",
|
||||
RestoreDatabase = "restore_database"
|
||||
}
|
||||
export enum NotificationLevel {
|
||||
Success = "success",
|
||||
Error = "error",
|
||||
@@ -5284,7 +5305,7 @@ export enum AssetMediaStatus {
|
||||
Replaced = "replaced",
|
||||
Duplicate = "duplicate"
|
||||
}
|
||||
export enum Action2 {
|
||||
export enum Action {
|
||||
Accept = "accept",
|
||||
Reject = "reject"
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class MaintenanceAuthDto {
|
||||
}
|
||||
|
||||
export class MaintenanceStatusResponseDto {
|
||||
@ValidateEnum({ enum: MaintenanceAction, name: 'MaintenanceAction' })
|
||||
action!: MaintenanceAction;
|
||||
|
||||
progress?: number;
|
||||
|
||||
@@ -22,6 +22,7 @@ export enum AppRoute {
|
||||
ADMIN_USERS = '/admin/users',
|
||||
ADMIN_LIBRARY_MANAGEMENT = '/admin/library-management',
|
||||
ADMIN_SETTINGS = '/admin/system-settings',
|
||||
ADMIN_MAINTENANCE = '/admin/maintenance',
|
||||
ADMIN_STATS = '/admin/server-status',
|
||||
ADMIN_JOBS = '/admin/jobs-status',
|
||||
ADMIN_REPAIR = '/admin/repair',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type MaintenanceAuthDto } from '@immich/sdk';
|
||||
import { type MaintenanceAuthDto, type MaintenanceStatusResponseDto } from '@immich/sdk';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const maintenanceStore = {
|
||||
auth: writable<MaintenanceAuthDto>(),
|
||||
status: writable<MaintenanceStatusResponseDto>(),
|
||||
};
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { page } from '$app/state';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { maintenanceStore } from '$lib/stores/maintenance.store';
|
||||
import { notificationManager } from '$lib/stores/notification-manager.svelte';
|
||||
import { createEventEmitter } from '$lib/utils/eventemitter';
|
||||
import { type AssetResponseDto, type NotificationDto, type ServerVersionResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
MaintenanceAction,
|
||||
type AssetResponseDto,
|
||||
type MaintenanceStatusResponseDto,
|
||||
type NotificationDto,
|
||||
type ServerVersionResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { io, type Socket } from 'socket.io-client';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import { user } from './user.store';
|
||||
@@ -37,6 +44,8 @@ export interface Events {
|
||||
on_notification: (notification: NotificationDto) => void;
|
||||
|
||||
AppRestartV1: (event: AppRestartEvent) => void;
|
||||
|
||||
MaintenanceStatusV1: (event: MaintenanceStatusResponseDto) => void;
|
||||
}
|
||||
|
||||
const websocket: Socket<Events> = io({
|
||||
@@ -61,6 +70,15 @@ websocket
|
||||
.on('disconnect', () => websocketStore.connected.set(false))
|
||||
.on('on_server_version', (serverVersion) => websocketStore.serverVersion.set(serverVersion))
|
||||
.on('AppRestartV1', (mode) => websocketStore.serverRestarting.set(mode))
|
||||
.on('MaintenanceStatusV1', (status) => {
|
||||
maintenanceStore.status.set(status);
|
||||
|
||||
if (status.action === MaintenanceAction.End) {
|
||||
websocketStore.serverRestarting.set({
|
||||
isMaintenanceMode: false,
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('on_new_release', (releaseVersion) => websocketStore.release.set(releaseVersion))
|
||||
.on('on_session_delete', () => authManager.logout())
|
||||
.on('on_notification', () => notificationManager.refresh())
|
||||
@@ -68,7 +86,7 @@ websocket
|
||||
|
||||
export const openWebsocketConnection = () => {
|
||||
try {
|
||||
if (get(user) || page.url.pathname.startsWith(AppRoute.MAINTENANCE)) {
|
||||
if (get(user) || get(websocketStore.serverRestarting) || page.url.pathname.startsWith(AppRoute.MAINTENANCE)) {
|
||||
websocket.connect();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { maintenanceStore } from '$lib/stores/maintenance.store';
|
||||
import { maintenanceLogin } from '@immich/sdk';
|
||||
import { websocketStore } from '$lib/stores/websocket';
|
||||
import { MaintenanceAction, maintenanceLogin, maintenanceStatus } from '@immich/sdk';
|
||||
|
||||
export function maintenanceCreateUrl(url: URL) {
|
||||
const target = new URL(AppRoute.MAINTENANCE, url.origin);
|
||||
@@ -31,3 +32,22 @@ export const loadMaintenanceAuth = async () => {
|
||||
// silently fail
|
||||
}
|
||||
};
|
||||
|
||||
export const loadMaintenanceStatus = async () => {
|
||||
try {
|
||||
const status = await maintenanceStatus();
|
||||
maintenanceStore.status.set(status);
|
||||
|
||||
if (status.action === MaintenanceAction.End) {
|
||||
websocketStore.serverRestarting.set({
|
||||
isMaintenanceMode: false,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
const status = (error as { status: number })?.status;
|
||||
if (status && status >= 500 && status < 600) {
|
||||
await new Promise((r) => setTimeout(r, 1e3));
|
||||
await loadMaintenanceStatus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { loadMaintenanceAuth } from '$lib/utils/maintenance';
|
||||
import { loadMaintenanceAuth, loadMaintenanceStatus } from '$lib/utils/maintenance';
|
||||
import type { PageLoad } from '../admin/$types';
|
||||
|
||||
export const load = (async () => {
|
||||
await loadMaintenanceAuth();
|
||||
await Promise.allSettled([loadMaintenanceAuth(), loadMaintenanceStatus()]);
|
||||
}) satisfies PageLoad;
|
||||
|
||||
Reference in New Issue
Block a user