* feat (Menu Features): Added a port forwarding button and optimized server storage logic Added the `portForward` option to the `ServerFuncBtn` enumeration to support port forwarding Added duplicate ID checks to `ServerStore` to prevent data conflicts * fix(server): Fixed the update logic for redirect IDs in server storage Prevents updates from being skipped when the ID remains unchanged, ensuring that redirect IDs are mapped correctly * fix(server): Fixed an issue where existing records were not skipped correctly when updating the redirect ID
190 lines
4.5 KiB
Dart
190 lines
4.5 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:fl_lib/fl_lib.dart';
|
|
|
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
|
import 'package:server_box/data/store/container.dart';
|
|
import 'package:server_box/data/store/setting.dart';
|
|
import 'package:server_box/data/store/snippet.dart';
|
|
|
|
class ServerStore extends HiveStore {
|
|
ServerStore._() : super('server');
|
|
|
|
static final instance = ServerStore._();
|
|
|
|
List<Spi>? _cache;
|
|
StreamSubscription<dynamic>? _boxWatchSub;
|
|
bool _suppressWatch = false;
|
|
|
|
@override
|
|
Future<void> init() async {
|
|
await super.init();
|
|
_boxWatchSub?.cancel();
|
|
_boxWatchSub = box.watch().listen((_) {
|
|
if (!_suppressWatch) {
|
|
_cache = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
@override
|
|
bool clear({bool? updateLastUpdateTsOnClear}) {
|
|
_suppressWatch = true;
|
|
try {
|
|
_cache = null;
|
|
return super.clear(updateLastUpdateTsOnClear: updateLastUpdateTsOnClear);
|
|
} finally {
|
|
_suppressWatch = false;
|
|
}
|
|
}
|
|
|
|
void invalidateCache() {
|
|
_cache = null;
|
|
}
|
|
|
|
void put(Spi info) {
|
|
_suppressWatch = true;
|
|
try {
|
|
set(info.id, info);
|
|
_cache = null;
|
|
} finally {
|
|
_suppressWatch = false;
|
|
}
|
|
}
|
|
|
|
void _putWithoutInvalidatingCache(Spi info) {
|
|
_suppressWatch = true;
|
|
try {
|
|
box.put(info.id, info);
|
|
} finally {
|
|
_suppressWatch = false;
|
|
}
|
|
}
|
|
|
|
List<Spi> fetch() {
|
|
return List<Spi>.from(_cache ??= _loadAll());
|
|
}
|
|
|
|
List<Spi> _loadAll() {
|
|
final List<Spi> ss = [];
|
|
for (final id in keys()) {
|
|
final s = get<Spi>(
|
|
id,
|
|
fromObj: (val) {
|
|
if (val is Spi) return val;
|
|
if (val is Map<dynamic, dynamic>) {
|
|
final map = val.toStrDynMap;
|
|
if (map == null) return null;
|
|
try {
|
|
final spi = Spi.fromJson(map as Map<String, dynamic>);
|
|
_putWithoutInvalidatingCache(spi);
|
|
return spi;
|
|
} catch (e) {
|
|
dprint('Parsing Spi from JSON', e);
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
);
|
|
if (s != null) {
|
|
ss.add(s);
|
|
}
|
|
}
|
|
return ss;
|
|
}
|
|
|
|
void delete(String id) {
|
|
_suppressWatch = true;
|
|
try {
|
|
remove(id);
|
|
_cache = null;
|
|
} finally {
|
|
_suppressWatch = false;
|
|
}
|
|
}
|
|
|
|
void update(Spi old, Spi newInfo) {
|
|
if (!have(old)) {
|
|
throw Exception('Old spi: $old not found');
|
|
}
|
|
_suppressWatch = true;
|
|
try {
|
|
remove(old.id);
|
|
set(newInfo.id, newInfo);
|
|
_cache = null;
|
|
} finally {
|
|
_suppressWatch = false;
|
|
}
|
|
}
|
|
|
|
bool have(Spi s) => get(s.id) != null;
|
|
|
|
void migrateIds() {
|
|
final ss = fetch();
|
|
final idMap = <String, String>{};
|
|
|
|
for (final s in ss) {
|
|
final newId = s.migrateId();
|
|
if (newId == null) continue;
|
|
idMap[s.oldId] = newId;
|
|
}
|
|
|
|
final srvOrder = SettingStore.instance.serverOrder.fetch();
|
|
final snippets = SnippetStore.instance.fetch();
|
|
final container = ContainerStore.instance;
|
|
|
|
bool srvOrderChanged = false;
|
|
for (final e in idMap.entries) {
|
|
final oldId = e.key;
|
|
final newId = e.value;
|
|
|
|
final srvIdx = srvOrder.indexOf(oldId);
|
|
if (srvIdx != -1) {
|
|
srvOrder[srvIdx] = newId;
|
|
srvOrderChanged = true;
|
|
}
|
|
|
|
final spi = get<Spi>(newId);
|
|
if (spi != null) {
|
|
final jumpId = spi.jumpId;
|
|
if (jumpId != null && idMap.containsKey(jumpId)) {
|
|
final newJumpId = idMap[jumpId];
|
|
if (spi.jumpId != newJumpId) {
|
|
final newSpi = spi.copyWith(jumpId: newJumpId);
|
|
update(spi, newSpi);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (final snippet in snippets) {
|
|
final autoRunsOn = snippet.autoRunOn;
|
|
final idx = autoRunsOn?.indexOf(oldId);
|
|
if (idx != null && idx != -1) {
|
|
final newAutoRunsOn = List<String>.from(autoRunsOn ?? []);
|
|
newAutoRunsOn[idx] = newId;
|
|
final newSnippet = snippet.copyWith(autoRunOn: newAutoRunsOn);
|
|
SnippetStore.instance.update(snippet, newSnippet);
|
|
}
|
|
}
|
|
|
|
final dockerHost = container.fetch(oldId);
|
|
if (dockerHost != null) {
|
|
container.remove(oldId);
|
|
container.set(newId, dockerHost);
|
|
}
|
|
}
|
|
|
|
for (final spi in ss) {
|
|
if (get(spi.id) == null) continue;
|
|
if (spi.jumpId != null && idMap.containsKey(spi.jumpId)) {
|
|
final newJumpId = idMap[spi.jumpId]!;
|
|
final newSpi = spi.copyWith(jumpId: newJumpId);
|
|
update(spi, newSpi);
|
|
}
|
|
}
|
|
|
|
if (srvOrderChanged) {
|
|
SettingStore.instance.serverOrder.put(srvOrder);
|
|
}
|
|
}
|
|
} |