2026-02-02 10:14:13 +08:00
|
|
|
part of 'database.dart';
|
|
|
|
|
|
|
|
|
|
@DataClassName('RawProxyGroup')
|
|
|
|
|
@TableIndex(
|
2026-10-08 17:58:43 +08:00
|
|
|
name: 'idx_proxy_groups_profile_order',
|
|
|
|
|
columns: {#profileId, #order},
|
2026-02-02 10:14:13 +08:00
|
|
|
)
|
|
|
|
|
class ProxyGroups extends Table {
|
|
|
|
|
@override
|
|
|
|
|
String get tableName => 'proxy_groups';
|
|
|
|
|
|
|
|
|
|
IntColumn get id => integer()();
|
|
|
|
|
|
2026-10-08 17:58:43 +08:00
|
|
|
IntColumn get profileId =>
|
|
|
|
|
integer().references(Profiles, #id, onDelete: KeyAction.cascade)();
|
2026-02-02 10:14:13 +08:00
|
|
|
|
|
|
|
|
TextColumn get name => text()();
|
|
|
|
|
|
|
|
|
|
TextColumn get type => text()();
|
|
|
|
|
|
|
|
|
|
TextColumn get proxies =>
|
|
|
|
|
text().map(const StringListConverter()).nullable()();
|
|
|
|
|
|
|
|
|
|
TextColumn get use => text().map(const StringListConverter()).nullable()();
|
|
|
|
|
|
2026-10-08 17:58:43 +08:00
|
|
|
TextColumn get definition => text().withDefault(const Constant('{}'))();
|
2026-02-02 10:14:13 +08:00
|
|
|
|
|
|
|
|
TextColumn get order => text().nullable()();
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Set<Column> get primaryKey => {id};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@DriftAccessor(tables: [ProxyGroups])
|
|
|
|
|
class ProxyGroupsDao extends DatabaseAccessor<Database>
|
|
|
|
|
with _$ProxyGroupsDaoMixin {
|
|
|
|
|
ProxyGroupsDao(super.attachedDatabase);
|
|
|
|
|
|
|
|
|
|
Selectable<ProxyGroup> query(int profileId) {
|
|
|
|
|
final stmt = proxyGroups.select();
|
|
|
|
|
stmt.where((row) => row.profileId.equals(profileId));
|
|
|
|
|
stmt.orderBy([
|
|
|
|
|
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
|
|
|
|
|
]);
|
|
|
|
|
return stmt.map((item) => item.toProxyGroup());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Selectable<int> count(int profileId) {
|
|
|
|
|
final stmt = proxyGroups.select();
|
|
|
|
|
stmt.where((row) => row.profileId.equals(profileId));
|
|
|
|
|
return stmt.count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<int> order(
|
|
|
|
|
int profileId, {
|
|
|
|
|
required ProxyGroup proxyGroup,
|
|
|
|
|
required String order,
|
|
|
|
|
}) async {
|
|
|
|
|
return proxyGroups.insertOnConflictUpdate(
|
|
|
|
|
proxyGroup.toCompanion(profileId, order),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-08 17:58:43 +08:00
|
|
|
/// In Dart, as a SQL replace on the JSON list breaks on quotes and commas.
|
|
|
|
|
Future<void> rewrite(
|
|
|
|
|
Iterable<ProxyGroup> groups,
|
|
|
|
|
ProxyGroup Function(ProxyGroup group) rewrite,
|
|
|
|
|
) async {
|
|
|
|
|
for (final group in groups) {
|
|
|
|
|
final next = rewrite(group);
|
|
|
|
|
if (next != group) {
|
|
|
|
|
await (proxyGroups.update()..where((t) => t.id.equals(group.id))).write(
|
|
|
|
|
next.toCompanion(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Selectable<String> names({int? except}) {
|
|
|
|
|
final query = selectOnly(proxyGroups, distinct: true)
|
|
|
|
|
..addColumns([proxyGroups.name]);
|
|
|
|
|
if (except != null) {
|
|
|
|
|
query.where(proxyGroups.profileId.equals(except).not());
|
|
|
|
|
}
|
|
|
|
|
return query.map((row) => row.read(proxyGroups.name)!);
|
2026-02-02 10:14:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-25 03:56:59 +08:00
|
|
|
Future<Set<int>> profileIdsUsing(String provider) async {
|
|
|
|
|
final query = selectOnly(proxyGroups)
|
|
|
|
|
..addColumns([proxyGroups.profileId, proxyGroups.use])
|
2026-10-08 17:58:43 +08:00
|
|
|
..where(proxyGroups.use.isNotNull());
|
2026-09-25 03:56:59 +08:00
|
|
|
return {
|
|
|
|
|
for (final row in await query.get())
|
|
|
|
|
if (row.readWithConverter(proxyGroups.use)!.contains(provider))
|
|
|
|
|
row.read(proxyGroups.profileId)!,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> renameUse(
|
|
|
|
|
Iterable<int> profileIds, {
|
|
|
|
|
required String oldName,
|
|
|
|
|
required String newName,
|
|
|
|
|
}) async {
|
|
|
|
|
if (profileIds.isEmpty) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
final rows =
|
|
|
|
|
await (proxyGroups.select()
|
|
|
|
|
..where((t) => t.profileId.isIn(profileIds) & t.use.isNotNull()))
|
|
|
|
|
.get();
|
|
|
|
|
for (final row in rows) {
|
|
|
|
|
final use = row.use!;
|
|
|
|
|
if (!use.contains(oldName)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-10-08 17:58:43 +08:00
|
|
|
final usePosition = row.toProxyGroup().usePosition;
|
2026-09-25 03:56:59 +08:00
|
|
|
await (proxyGroups.update()..where((t) => t.id.equals(row.id))).write(
|
|
|
|
|
ProxyGroupsCompanion(
|
|
|
|
|
use: Value([
|
|
|
|
|
for (final name in use) name == oldName ? newName : name,
|
|
|
|
|
]),
|
2026-10-08 17:58:43 +08:00
|
|
|
definition: usePosition?.containsKey(oldName) == true
|
|
|
|
|
? Value(
|
|
|
|
|
json.encode({
|
|
|
|
|
...json.decode(row.definition) as Map<String, Object?>,
|
|
|
|
|
'use-position': {
|
|
|
|
|
for (final MapEntry(:key, :value) in usePosition!.entries)
|
|
|
|
|
key == oldName ? newName : key: value,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
: const Value.absent(),
|
2026-09-25 03:56:59 +08:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-09-25 03:56:58 +08:00
|
|
|
}
|
|
|
|
|
|
2026-10-08 17:58:43 +08:00
|
|
|
void setAllWithBatch(Batch batch, Iterable<ProxyGroup> proxyGroups) {
|
2026-02-02 10:14:13 +08:00
|
|
|
final keys = indexing.generateNKeys(proxyGroups.length);
|
|
|
|
|
this.proxyGroups.setAll(
|
|
|
|
|
batch,
|
|
|
|
|
proxyGroups.mapIndexed(
|
2026-10-08 17:58:43 +08:00
|
|
|
(index, item) => item.toCompanion(null, keys[index]),
|
2026-02-02 10:14:13 +08:00
|
|
|
),
|
2026-10-08 17:58:43 +08:00
|
|
|
deleteFilter: (_) => const Constant(true),
|
|
|
|
|
preDelete: true,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setProfileGroupsWithBatch(
|
|
|
|
|
int profileId,
|
|
|
|
|
Batch batch,
|
|
|
|
|
Iterable<ProxyGroup> proxyGroups,
|
|
|
|
|
) {
|
|
|
|
|
this.proxyGroups.setAll(
|
|
|
|
|
batch,
|
|
|
|
|
proxyGroups.map((item) => item.toCompanion(profileId)),
|
|
|
|
|
deleteFilter: (t) => t.profileId.equals(profileId),
|
2026-02-02 10:14:13 +08:00
|
|
|
preDelete: true,
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-09-05 20:09:39 +08:00
|
|
|
|
2026-09-25 03:56:58 +08:00
|
|
|
Future<void> delAll(Iterable<int> ids) {
|
|
|
|
|
return batch((b) {
|
|
|
|
|
proxyGroups.deleteInChunks(b, ids, (t, chunk) => t.id.isIn(chunk));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-05 20:09:39 +08:00
|
|
|
void putAllWithBatch(Batch batch, Iterable<ProxyGroup> proxyGroups) {
|
|
|
|
|
final keys = indexing.generateNKeys(proxyGroups.length);
|
|
|
|
|
batch.insertAllOnConflictUpdate(
|
|
|
|
|
this.proxyGroups,
|
|
|
|
|
proxyGroups.mapIndexed(
|
|
|
|
|
(index, item) => item.toCompanion(null, keys[index]),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-02 10:14:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-10-08 17:58:43 +08:00
|
|
|
const _proxyGroupColumnKeys = {
|
|
|
|
|
'profileId',
|
|
|
|
|
'id',
|
|
|
|
|
'name',
|
|
|
|
|
'type',
|
|
|
|
|
'proxies',
|
|
|
|
|
'use',
|
|
|
|
|
'order',
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-02 10:14:13 +08:00
|
|
|
extension RawProxyGroupExt on RawProxyGroup {
|
|
|
|
|
ProxyGroup toProxyGroup() {
|
2026-10-08 17:58:43 +08:00
|
|
|
final options = decodeOrRestoreDefault(
|
|
|
|
|
'proxy group options',
|
|
|
|
|
() => Map<String, Object?>.from(json.decode(definition)),
|
|
|
|
|
() => const <String, Object?>{},
|
|
|
|
|
);
|
|
|
|
|
return decodeSalvaging(
|
|
|
|
|
'proxy group options',
|
|
|
|
|
options,
|
|
|
|
|
(options) => ProxyGroup.fromJson({
|
|
|
|
|
...options,
|
|
|
|
|
'profileId': profileId,
|
|
|
|
|
'id': id,
|
|
|
|
|
'name': name,
|
|
|
|
|
'type': type,
|
|
|
|
|
'proxies': proxies,
|
|
|
|
|
'use': use,
|
|
|
|
|
'order': order,
|
|
|
|
|
}),
|
2026-02-02 10:14:13 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension ProxyGroupsCompanionExt on ProxyGroup {
|
|
|
|
|
ProxyGroupsCompanion toCompanion([int? profileId, String? order]) {
|
|
|
|
|
return ProxyGroupsCompanion.insert(
|
|
|
|
|
id: Value(id),
|
2026-10-08 17:58:43 +08:00
|
|
|
profileId: (this.profileId ?? profileId)!,
|
2026-02-02 10:14:13 +08:00
|
|
|
name: name,
|
|
|
|
|
type: type.value,
|
|
|
|
|
proxies: Value(proxies),
|
|
|
|
|
use: Value(use),
|
2026-10-08 17:58:43 +08:00
|
|
|
definition: Value(
|
|
|
|
|
json.encode({
|
|
|
|
|
for (final MapEntry(:key, :value) in toJson().entries)
|
|
|
|
|
if (value != null && !_proxyGroupColumnKeys.contains(key))
|
|
|
|
|
key: value,
|
|
|
|
|
}),
|
|
|
|
|
),
|
2026-02-02 10:14:13 +08:00
|
|
|
order: Value(order ?? this.order),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|