new: add custom url-test for each repository
This commit is contained in:
@@ -21,7 +21,7 @@ class AppDatabase extends _$AppDatabase with InfraLogger {
|
||||
AppDatabase.connect() : super(openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 3;
|
||||
int get schemaVersion => 4;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@@ -48,6 +48,9 @@ class AppDatabase extends _$AppDatabase with InfraLogger {
|
||||
await m.createTable(schema.geoAssetEntries);
|
||||
await _prePopulateGeoAssets();
|
||||
},
|
||||
from3To4: (m, schema) async {
|
||||
await m.addColumn(profileEntries, profileEntries.testUrl);
|
||||
},
|
||||
),
|
||||
beforeOpen: (details) async {
|
||||
if (kDebugMode) {
|
||||
|
||||
@@ -39,78 +39,38 @@ final class _S2 extends i0.VersionedSchema {
|
||||
|
||||
class Shape0 extends i0.VersionedTable {
|
||||
Shape0({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get type =>
|
||||
columnsByName['type']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<bool> get active =>
|
||||
columnsByName['active']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get url =>
|
||||
columnsByName['url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<DateTime> get lastUpdate =>
|
||||
columnsByName['last_update']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<int> get updateInterval =>
|
||||
columnsByName['update_interval']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get upload =>
|
||||
columnsByName['upload']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get download =>
|
||||
columnsByName['download']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get total =>
|
||||
columnsByName['total']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<DateTime> get expire =>
|
||||
columnsByName['expire']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<String> get webPageUrl =>
|
||||
columnsByName['web_page_url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get supportUrl =>
|
||||
columnsByName['support_url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get id => columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get type => columnsByName['type']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<bool> get active => columnsByName['active']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<String> get name => columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get url => columnsByName['url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<DateTime> get lastUpdate => columnsByName['last_update']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<int> get updateInterval => columnsByName['update_interval']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get upload => columnsByName['upload']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get download => columnsByName['download']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<int> get total => columnsByName['total']! as i1.GeneratedColumn<int>;
|
||||
i1.GeneratedColumn<DateTime> get expire => columnsByName['expire']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<String> get webPageUrl => columnsByName['web_page_url']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get supportUrl => columnsByName['support_url']! as i1.GeneratedColumn<String>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_0(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('id', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('type', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<bool> _column_2(String aliasedName) =>
|
||||
i1.GeneratedColumn<bool>('active', aliasedName, false,
|
||||
type: i1.DriftSqlType.bool,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("active" IN (0, 1))'));
|
||||
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 1,
|
||||
),
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('url', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<DateTime> _column_5(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_update', aliasedName, false,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<int> _column_6(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('update_interval', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_7(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('upload', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_8(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('download', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_9(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('total', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<DateTime> _column_10(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('expire', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<String> _column_11(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('web_page_url', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_12(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('support_url', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_0(String aliasedName) => i1.GeneratedColumn<String>('id', aliasedName, false, type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_1(String aliasedName) => i1.GeneratedColumn<String>('type', aliasedName, false, type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<bool> _column_2(String aliasedName) => i1.GeneratedColumn<bool>('active', aliasedName, false, type: i1.DriftSqlType.bool, defaultConstraints: i1.GeneratedColumn.constraintIsAlways('CHECK ("active" IN (0, 1))'));
|
||||
i1.GeneratedColumn<String> _column_3(String aliasedName) => i1.GeneratedColumn<String>('name', aliasedName, false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 1,
|
||||
),
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_4(String aliasedName) => i1.GeneratedColumn<String>('url', aliasedName, true, type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<DateTime> _column_5(String aliasedName) => i1.GeneratedColumn<DateTime>('last_update', aliasedName, false, type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<int> _column_6(String aliasedName) => i1.GeneratedColumn<int>('update_interval', aliasedName, true, type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_7(String aliasedName) => i1.GeneratedColumn<int>('upload', aliasedName, true, type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_8(String aliasedName) => i1.GeneratedColumn<int>('download', aliasedName, true, type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int> _column_9(String aliasedName) => i1.GeneratedColumn<int>('total', aliasedName, true, type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<DateTime> _column_10(String aliasedName) => i1.GeneratedColumn<DateTime>('expire', aliasedName, true, type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<String> _column_11(String aliasedName) => i1.GeneratedColumn<String>('web_page_url', aliasedName, true, type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_12(String aliasedName) => i1.GeneratedColumn<String>('support_url', aliasedName, true, type: i1.DriftSqlType.string);
|
||||
|
||||
final class _S3 extends i0.VersionedSchema {
|
||||
_S3({required super.database}) : super(version: 3);
|
||||
@@ -170,37 +130,26 @@ final class _S3 extends i0.VersionedSchema {
|
||||
|
||||
class Shape1 extends i0.VersionedTable {
|
||||
Shape1({required super.source, required super.alias}) : super.aliased();
|
||||
i1.GeneratedColumn<String> get id =>
|
||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get type =>
|
||||
columnsByName['type']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<bool> get active =>
|
||||
columnsByName['active']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<String> get name =>
|
||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get providerName =>
|
||||
columnsByName['provider_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get version =>
|
||||
columnsByName['version']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<DateTime> get lastCheck =>
|
||||
columnsByName['last_check']! as i1.GeneratedColumn<DateTime>;
|
||||
i1.GeneratedColumn<String> get id => columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get type => columnsByName['type']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<bool> get active => columnsByName['active']! as i1.GeneratedColumn<bool>;
|
||||
i1.GeneratedColumn<String> get name => columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get providerName => columnsByName['provider_name']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<String> get version => columnsByName['version']! as i1.GeneratedColumn<String>;
|
||||
i1.GeneratedColumn<DateTime> get lastCheck => columnsByName['last_check']! as i1.GeneratedColumn<DateTime>;
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<String> _column_13(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('provider_name', aliasedName, false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 1,
|
||||
),
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_14(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('version', aliasedName, true,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<DateTime> _column_15(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('last_check', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<String> _column_13(String aliasedName) => i1.GeneratedColumn<String>('provider_name', aliasedName, false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 1,
|
||||
),
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String> _column_14(String aliasedName) => i1.GeneratedColumn<String>('version', aliasedName, true, type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<DateTime> _column_15(String aliasedName) => i1.GeneratedColumn<DateTime>('last_check', aliasedName, true, type: i1.DriftSqlType.dateTime);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, _S2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, _S3 schema) from2To3,
|
||||
required Future<void> Function(i1.Migrator m, _S3 schema) from3To4,
|
||||
}) {
|
||||
return (currentVersion, database) async {
|
||||
switch (currentVersion) {
|
||||
@@ -214,6 +163,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from2To3(migrator, schema);
|
||||
return 3;
|
||||
case 3:
|
||||
final schema = _S3(database: database);
|
||||
final migrator = i1.Migrator(database, schema);
|
||||
await from3To4(migrator, schema);
|
||||
return 4;
|
||||
default:
|
||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||
}
|
||||
@@ -223,9 +177,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||
i1.OnUpgrade stepByStep({
|
||||
required Future<void> Function(i1.Migrator m, _S2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, _S3 schema) from2To3,
|
||||
required Future<void> Function(i1.Migrator m, _S3 schema) from3To4,
|
||||
}) =>
|
||||
i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
from2To3: from2To3,
|
||||
from3To4: from3To4,
|
||||
));
|
||||
|
||||
296
lib/core/database/schemas/drift_schema_v4.json
Normal file
296
lib/core/database/schemas/drift_schema_v4.json
Normal file
@@ -0,0 +1,296 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": true
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "profile_entries",
|
||||
"was_declared_in_moor": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"getter_name": "type",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumNameConverter<ProfileType>(ProfileType.values)",
|
||||
"dart_type_name": "ProfileType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"getter_name": "active",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"defaultConstraints": "CHECK (\"active\" IN (0, 1))",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"allowed-lengths": {
|
||||
"min": 1,
|
||||
"max": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_update",
|
||||
"getter_name": "lastUpdate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "update_interval",
|
||||
"getter_name": "updateInterval",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "DurationTypeConverter()",
|
||||
"dart_type_name": "Duration"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "upload",
|
||||
"getter_name": "upload",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "download",
|
||||
"getter_name": "download",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "total",
|
||||
"getter_name": "total",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "expire",
|
||||
"getter_name": "expire",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "web_page_url",
|
||||
"getter_name": "webPageUrl",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "support_url",
|
||||
"getter_name": "supportUrl",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "test_url",
|
||||
"getter_name": "testUrl",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [],
|
||||
"explicit_pk": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "geo_asset_entries",
|
||||
"was_declared_in_moor": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"getter_name": "type",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumNameConverter<GeoAssetType>(GeoAssetType.values)",
|
||||
"dart_type_name": "GeoAssetType"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"getter_name": "active",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"defaultConstraints": "CHECK (\"active\" IN (0, 1))",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"allowed-lengths": {
|
||||
"min": 1,
|
||||
"max": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "provider_name",
|
||||
"getter_name": "providerName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"allowed-lengths": {
|
||||
"min": 1,
|
||||
"max": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"getter_name": "version",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_check",
|
||||
"getter_name": "lastCheck",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [],
|
||||
"explicit_pk": [
|
||||
"id"
|
||||
],
|
||||
"unique_keys": [
|
||||
[
|
||||
"name",
|
||||
"provider_name"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -11,14 +11,14 @@ class ProfileEntries extends Table {
|
||||
TextColumn get name => text().withLength(min: 1)();
|
||||
TextColumn get url => text().nullable()();
|
||||
DateTimeColumn get lastUpdate => dateTime()();
|
||||
IntColumn get updateInterval =>
|
||||
integer().nullable().map(DurationTypeConverter())();
|
||||
IntColumn get updateInterval => integer().nullable().map(DurationTypeConverter())();
|
||||
IntColumn get upload => integer().nullable()();
|
||||
IntColumn get download => integer().nullable()();
|
||||
IntColumn get total => integer().nullable()();
|
||||
DateTimeColumn get expire => dateTime().nullable()();
|
||||
TextColumn get webPageUrl => text().nullable()();
|
||||
TextColumn get supportUrl => text().nullable()();
|
||||
TextColumn get testUrl => text().nullable()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
|
||||
@@ -22,12 +22,14 @@ abstract interface class ConnectionRepository {
|
||||
String fileName,
|
||||
String profileName,
|
||||
bool disableMemoryLimit,
|
||||
String? testUrl,
|
||||
);
|
||||
TaskEither<ConnectionFailure, Unit> disconnect();
|
||||
TaskEither<ConnectionFailure, Unit> reconnect(
|
||||
String fileName,
|
||||
String profileName,
|
||||
bool disableMemoryLimit,
|
||||
String? testUrl,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,11 +103,16 @@ class ConnectionRepositoryImpl with ExceptionHandler, InfraLogger implements Con
|
||||
@visibleForTesting
|
||||
TaskEither<ConnectionFailure, Unit> applyConfigOption(
|
||||
SingboxConfigOption options,
|
||||
String? testUrl,
|
||||
) {
|
||||
return exceptionHandler(
|
||||
() {
|
||||
_configOptionsSnapshot = options;
|
||||
return singbox.changeOptions(options).mapLeft(InvalidConfigOption.new).run();
|
||||
var newOptions = options;
|
||||
if (testUrl != null) {
|
||||
newOptions = options.copyWith(connectionTestUrl: testUrl);
|
||||
}
|
||||
return singbox.changeOptions(newOptions).mapLeft(InvalidConfigOption.new).run();
|
||||
},
|
||||
UnexpectedConnectionFailure.new,
|
||||
);
|
||||
@@ -138,10 +145,11 @@ class ConnectionRepositoryImpl with ExceptionHandler, InfraLogger implements Con
|
||||
String fileName,
|
||||
String profileName,
|
||||
bool disableMemoryLimit,
|
||||
String? testUrl,
|
||||
) {
|
||||
return TaskEither<ConnectionFailure, Unit>.Do(
|
||||
($) async {
|
||||
final options = await $(getConfigOption());
|
||||
var options = await $(getConfigOption());
|
||||
loggy.info(
|
||||
"config options: ${options.format()}\nMemory Limit: ${!disableMemoryLimit}",
|
||||
);
|
||||
@@ -159,7 +167,7 @@ class ConnectionRepositoryImpl with ExceptionHandler, InfraLogger implements Con
|
||||
}),
|
||||
);
|
||||
await $(setup());
|
||||
await $(applyConfigOption(options));
|
||||
await $(applyConfigOption(options, testUrl));
|
||||
return await $(
|
||||
singbox
|
||||
.start(
|
||||
@@ -203,23 +211,26 @@ class ConnectionRepositoryImpl with ExceptionHandler, InfraLogger implements Con
|
||||
String fileName,
|
||||
String profileName,
|
||||
bool disableMemoryLimit,
|
||||
String? testUrl,
|
||||
) {
|
||||
return exceptionHandler(
|
||||
() async {
|
||||
return getConfigOption()
|
||||
.flatMap((options) => applyConfigOption(options))
|
||||
.andThen(
|
||||
() => singbox
|
||||
.restart(
|
||||
profilePathResolver.file(fileName).path,
|
||||
profileName,
|
||||
disableMemoryLimit,
|
||||
)
|
||||
.mapLeft(UnexpectedConnectionFailure.new),
|
||||
)
|
||||
.run();
|
||||
return TaskEither<ConnectionFailure, Unit>.Do(
|
||||
($) async {
|
||||
var options = await $(getConfigOption());
|
||||
loggy.info(
|
||||
"config options: ${options.format()}\nMemory Limit: ${!disableMemoryLimit}",
|
||||
);
|
||||
|
||||
await $(applyConfigOption(options, testUrl));
|
||||
return await $(
|
||||
singbox
|
||||
.restart(
|
||||
profilePathResolver.file(fileName).path,
|
||||
profileName,
|
||||
disableMemoryLimit,
|
||||
)
|
||||
.mapLeft(UnexpectedConnectionFailure.new),
|
||||
);
|
||||
},
|
||||
UnexpectedConnectionFailure.new,
|
||||
);
|
||||
).handleExceptions(UnexpectedConnectionFailure.new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ class ConnectionNotifier extends _$ConnectionNotifier with AppLogger {
|
||||
profile.id,
|
||||
profile.name,
|
||||
ref.read(Preferences.disableMemoryLimit),
|
||||
profile.testUrl,
|
||||
)
|
||||
.mapLeft((err) {
|
||||
loggy.warning("error reconnecting", err);
|
||||
@@ -133,6 +134,7 @@ class ConnectionNotifier extends _$ConnectionNotifier with AppLogger {
|
||||
activeProfile.id,
|
||||
activeProfile.name,
|
||||
ref.read(Preferences.disableMemoryLimit),
|
||||
activeProfile.testUrl,
|
||||
)
|
||||
.mapLeft((err) async {
|
||||
loggy.warning("error connecting", err);
|
||||
|
||||
@@ -5,8 +5,7 @@ import 'package:hiddify/features/profile/model/profile_entity.dart';
|
||||
extension ProfileEntityMapper on ProfileEntity {
|
||||
ProfileEntriesCompanion toEntry() {
|
||||
return switch (this) {
|
||||
RemoteProfileEntity(:final url, :final options, :final subInfo) =>
|
||||
ProfileEntriesCompanion.insert(
|
||||
RemoteProfileEntity(:final url, :final options, :final subInfo) => ProfileEntriesCompanion.insert(
|
||||
id: id,
|
||||
type: ProfileType.remote,
|
||||
active: active,
|
||||
@@ -20,6 +19,7 @@ extension ProfileEntityMapper on ProfileEntity {
|
||||
expire: Value(subInfo?.expire),
|
||||
webPageUrl: Value(subInfo?.webPageUrl),
|
||||
supportUrl: Value(subInfo?.supportUrl),
|
||||
testUrl: Value(testUrl),
|
||||
),
|
||||
LocalProfileEntity() => ProfileEntriesCompanion.insert(
|
||||
id: id,
|
||||
@@ -41,6 +41,7 @@ extension RemoteProfileEntityMapper on RemoteProfileEntity {
|
||||
expire: Value(subInfo?.expire),
|
||||
webPageUrl: Value(subInfo?.webPageUrl),
|
||||
supportUrl: Value(subInfo?.supportUrl),
|
||||
testUrl: Value(testUrl),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -73,12 +74,14 @@ extension ProfileEntryMapper on ProfileEntry {
|
||||
lastUpdate: lastUpdate,
|
||||
options: options,
|
||||
subInfo: subInfo,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
ProfileType.local => LocalProfileEntity(
|
||||
id: id,
|
||||
active: active,
|
||||
name: name,
|
||||
lastUpdate: lastUpdate,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ abstract class ProfileParser {
|
||||
var name = '';
|
||||
if (headers['profile-title'] case [final titleHeader]) {
|
||||
if (titleHeader.startsWith("base64:")) {
|
||||
name =
|
||||
utf8.decode(base64.decode(titleHeader.replaceFirst("base64:", "")));
|
||||
name = utf8.decode(base64.decode(titleHeader.replaceFirst("base64:", "")));
|
||||
} else {
|
||||
name = titleHeader.trim();
|
||||
}
|
||||
}
|
||||
if (headers['content-disposition'] case [final contentDispositionHeader]
|
||||
when name.isEmpty) {
|
||||
if (headers['content-disposition'] case [final contentDispositionHeader] when name.isEmpty) {
|
||||
final regExp = RegExp('filename="([^"]*)"');
|
||||
final match = regExp.firstMatch(contentDispositionHeader);
|
||||
if (match != null && match.groupCount >= 1) {
|
||||
@@ -52,19 +50,20 @@ abstract class ProfileParser {
|
||||
final updateInterval = Duration(hours: int.parse(updateIntervalStr));
|
||||
options = ProfileOptions(updateInterval: updateInterval);
|
||||
}
|
||||
|
||||
String? testUrl;
|
||||
if (headers['test-url'] case [final testUrl_] when isUrl(testUrl_)) {
|
||||
testUrl = testUrl_;
|
||||
}
|
||||
SubscriptionInfo? subInfo;
|
||||
if (headers['subscription-userinfo'] case [final subInfoStr]) {
|
||||
subInfo = parseSubscriptionInfo(subInfoStr);
|
||||
}
|
||||
|
||||
if (subInfo != null) {
|
||||
if (headers['profile-web-page-url'] case [final profileWebPageUrl]
|
||||
when isUrl(profileWebPageUrl)) {
|
||||
if (headers['profile-web-page-url'] case [final profileWebPageUrl] when isUrl(profileWebPageUrl)) {
|
||||
subInfo = subInfo.copyWith(webPageUrl: profileWebPageUrl);
|
||||
}
|
||||
if (headers['support-url'] case [final profileSupportUrl]
|
||||
when isUrl(profileSupportUrl)) {
|
||||
if (headers['support-url'] case [final profileSupportUrl] when isUrl(profileSupportUrl)) {
|
||||
subInfo = subInfo.copyWith(supportUrl: profileSupportUrl);
|
||||
}
|
||||
}
|
||||
@@ -77,23 +76,16 @@ abstract class ProfileParser {
|
||||
lastUpdate: DateTime.now(),
|
||||
options: options,
|
||||
subInfo: subInfo,
|
||||
testUrl: testUrl,
|
||||
);
|
||||
}
|
||||
|
||||
static SubscriptionInfo? parseSubscriptionInfo(String subInfoStr) {
|
||||
final values = subInfoStr.split(';');
|
||||
final map = {
|
||||
for (final v in values)
|
||||
v.split('=').first.trim():
|
||||
num.tryParse(v.split('=').second.trim())?.toInt(),
|
||||
for (final v in values) v.split('=').first.trim(): num.tryParse(v.split('=').second.trim())?.toInt(),
|
||||
};
|
||||
if (map
|
||||
case {
|
||||
"upload": final upload?,
|
||||
"download": final download?,
|
||||
"total": var total,
|
||||
"expire": var expire
|
||||
}) {
|
||||
if (map case {"upload": final upload?, "download": final download?, "total": var total, "expire": var expire}) {
|
||||
total = (total == null || total == 0) ? infiniteTrafficThreshold : total;
|
||||
expire = (expire == null || expire == 0) ? infiniteTimeThreshold : expire;
|
||||
return SubscriptionInfo(
|
||||
|
||||
@@ -282,6 +282,7 @@ class ProfileRepositoryImpl with ExceptionHandler, InfraLogger implements Profil
|
||||
? profilePatch.copyWith(
|
||||
name: Value(baseProfile.name),
|
||||
url: Value(baseProfile.url),
|
||||
testUrl: Value(baseProfile.testUrl),
|
||||
updateInterval: Value(baseProfile.options?.updateInterval),
|
||||
)
|
||||
: profilePatch,
|
||||
@@ -349,6 +350,7 @@ class ProfileRepositoryImpl with ExceptionHandler, InfraLogger implements Profil
|
||||
'profile-update-interval',
|
||||
'support-url',
|
||||
'profile-web-page-url',
|
||||
'test-url',
|
||||
];
|
||||
|
||||
@visibleForTesting
|
||||
|
||||
@@ -15,6 +15,7 @@ sealed class ProfileEntity with _$ProfileEntity {
|
||||
required String name,
|
||||
required String url,
|
||||
required DateTime lastUpdate,
|
||||
String? testUrl,
|
||||
ProfileOptions? options,
|
||||
SubscriptionInfo? subInfo,
|
||||
}) = RemoteProfileEntity;
|
||||
@@ -24,6 +25,7 @@ sealed class ProfileEntity with _$ProfileEntity {
|
||||
required bool active,
|
||||
required String name,
|
||||
required DateTime lastUpdate,
|
||||
String? testUrl,
|
||||
}) = LocalProfileEntity;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,11 @@ class PlatformSingboxService with InfraLogger implements SingboxService {
|
||||
static const channelPrefix = "com.hiddify.app";
|
||||
|
||||
static const methodChannel = MethodChannel("$channelPrefix/method");
|
||||
static const statusChannel =
|
||||
EventChannel("$channelPrefix/service.status", JSONMethodCodec());
|
||||
static const alertsChannel =
|
||||
EventChannel("$channelPrefix/service.alerts", JSONMethodCodec());
|
||||
static const statsChannel =
|
||||
EventChannel("$channelPrefix/stats", JSONMethodCodec());
|
||||
static const statusChannel = EventChannel("$channelPrefix/service.status", JSONMethodCodec());
|
||||
static const alertsChannel = EventChannel("$channelPrefix/service.alerts", JSONMethodCodec());
|
||||
static const statsChannel = EventChannel("$channelPrefix/stats", JSONMethodCodec());
|
||||
static const groupsChannel = EventChannel("$channelPrefix/groups");
|
||||
static const activeGroupsChannel =
|
||||
EventChannel("$channelPrefix/active-groups");
|
||||
static const activeGroupsChannel = EventChannel("$channelPrefix/active-groups");
|
||||
static const logsChannel = EventChannel("$channelPrefix/service.logs");
|
||||
|
||||
late final ValueStream<SingboxStatus> _status;
|
||||
@@ -33,10 +29,8 @@ class PlatformSingboxService with InfraLogger implements SingboxService {
|
||||
@override
|
||||
Future<void> init() async {
|
||||
loggy.debug("initializing");
|
||||
final status =
|
||||
statusChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
|
||||
final alerts =
|
||||
alertsChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
|
||||
final status = statusChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
|
||||
final alerts = alertsChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
|
||||
|
||||
_status = ValueConnectableStream(Rx.merge([status, alerts])).autoConnect();
|
||||
await _status.first;
|
||||
@@ -250,9 +244,7 @@ class PlatformSingboxService with InfraLogger implements SingboxService {
|
||||
|
||||
@override
|
||||
Stream<List<String>> watchLogs(String path) async* {
|
||||
yield* logsChannel
|
||||
.receiveBroadcastStream()
|
||||
.map((event) => (event as List).map((e) => e as String).toList());
|
||||
yield* logsChannel.receiveBroadcastStream().map((event) => (event as List).map((e) => e as String).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// https://gist.github.com/dperini/729294
|
||||
final _urlRegex = RegExp(
|
||||
r"^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$",
|
||||
// r"^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$",
|
||||
r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+',
|
||||
);
|
||||
|
||||
/// https://stackoverflow.com/a/12968117
|
||||
|
||||
Reference in New Issue
Block a user