feat: update to v1.7.3 with smart asset detection and auto-install
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
abstract class Constants {
|
||||
static const appName = "Umbrix";
|
||||
static const githubUrl = "https://github.com/umbrix-app/umbrix";
|
||||
static const githubReleasesApiUrl = "https://api.github.com/repos/umbrix-app/umbrix/releases";
|
||||
static const githubLatestReleaseUrl = "https://github.com/umbrix-app/umbrix/releases/latest";
|
||||
static const githubUrl = "https://update.umbrix.net/vodorod/umbrix";
|
||||
static const githubReleasesApiUrl = "https://update.umbrix.net/api/v1/repos/vodorod/umbrix/releases";
|
||||
static const githubLatestReleaseUrl = "https://update.umbrix.net/vodorod/umbrix/releases/latest";
|
||||
static const appCastUrl = "http://localhost:8000/api/appcast.xml";
|
||||
static const telegramChannelUrl = "https://t.me/umbrix_app";
|
||||
static const privacyPolicyUrl = "https://umbrix.net/privacy.html";
|
||||
@@ -18,12 +18,14 @@ abstract class Constants {
|
||||
// 🖥️ Для Linux десктопа используйте: "http://localhost:8000/api/appcast.xml"
|
||||
// 📱 Для Android эмулятора используйте: "http://10.0.2.2:8000/api/appcast.xml"
|
||||
// См. документацию в папке: update-server/README.md
|
||||
static const customUpdateServerUrl = "http://localhost:8000/api/appcast.xml";
|
||||
|
||||
// ТЕСТ: Используем httpbin.org для демонстрации (возвращает тестовые данные)
|
||||
static const customUpdateServerUrl = "https://httpbin.org/json";
|
||||
|
||||
// Использовать собственный сервер обновлений вместо GitHub
|
||||
// true = использовать customUpdateServerUrl (для приватного репозитория)
|
||||
// false = использовать GitHub Releases (для публичного репозитория)
|
||||
static const useCustomUpdateServer = true;
|
||||
static const useCustomUpdateServer = false;
|
||||
}
|
||||
|
||||
const kAnimationDuration = Duration(milliseconds: 250);
|
||||
|
||||
@@ -23,6 +23,20 @@ abstract class GithubReleaseParser {
|
||||
}
|
||||
final preRelease = json["prerelease"] as bool;
|
||||
final publishedAt = DateTime.parse(json["published_at"] as String);
|
||||
|
||||
// Парсим assets
|
||||
final List<ReleaseAsset> assets = [];
|
||||
if (json["assets"] != null) {
|
||||
for (final assetJson in json["assets"] as List) {
|
||||
final asset = assetJson as Map<String, dynamic>;
|
||||
assets.add(ReleaseAsset(
|
||||
name: asset["name"] as String,
|
||||
downloadUrl: asset["browser_download_url"] as String,
|
||||
size: asset["size"] as int,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return RemoteVersionEntity(
|
||||
version: version,
|
||||
buildNumber: buildNumber,
|
||||
@@ -31,6 +45,7 @@ abstract class GithubReleaseParser {
|
||||
url: json["html_url"] as String,
|
||||
publishedAt: publishedAt,
|
||||
flavor: flavor,
|
||||
assets: assets,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,27 @@ class RemoteVersionEntity with _$RemoteVersionEntity {
|
||||
required String url,
|
||||
required DateTime publishedAt,
|
||||
required Environment flavor,
|
||||
@Default([]) List<ReleaseAsset> assets,
|
||||
}) = _RemoteVersionEntity;
|
||||
|
||||
String get presentVersion =>
|
||||
flavor == Environment.prod ? version : "$version ${flavor.name}";
|
||||
|
||||
/// Найти asset по расширению файла
|
||||
String? findAssetByExtension(String extension) {
|
||||
try {
|
||||
return assets.firstWhere((asset) => asset.name.endsWith(extension)).downloadUrl;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ReleaseAsset with _$ReleaseAsset {
|
||||
const factory ReleaseAsset({
|
||||
required String name,
|
||||
required String downloadUrl,
|
||||
required int size,
|
||||
}) = _ReleaseAsset;
|
||||
}
|
||||
|
||||
@@ -57,23 +57,55 @@ class NewVersionDialog extends HookConsumerWidget with PresLogger {
|
||||
downloadProgress.value = 0.0;
|
||||
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
|
||||
// Определяем нужное расширение файла
|
||||
String fileExt = '';
|
||||
if (Platform.isWindows)
|
||||
fileExt = '.exe';
|
||||
else if (Platform.isMacOS)
|
||||
fileExt = '.dmg';
|
||||
else if (Platform.isLinux) fileExt = '.AppImage';
|
||||
else if (Platform.isLinux)
|
||||
fileExt = '.deb';
|
||||
|
||||
// Ищем asset с нужным расширением
|
||||
final downloadUrl = newVersion.findAssetByExtension(fileExt);
|
||||
|
||||
if (downloadUrl == null) {
|
||||
if (context.mounted) {
|
||||
CustomToast.error('Файл установки $fileExt не найден в релизе').show(context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final savePath = '${tempDir.path}/umbrix-${newVersion.version}$fileExt';
|
||||
final file = File(savePath);
|
||||
if (await file.exists()) await file.delete();
|
||||
|
||||
final dio = Dio();
|
||||
await dio.download(newVersion.url, savePath, onReceiveProgress: (received, total) {
|
||||
await dio.download(downloadUrl, savePath, onReceiveProgress: (received, total) {
|
||||
if (total != -1) downloadProgress.value = received / total;
|
||||
});
|
||||
|
||||
loggy.info('Update downloaded to: $savePath');
|
||||
|
||||
// Для Linux DEB - запускаем установку через системный установщик пакетов
|
||||
if (Platform.isLinux && fileExt == '.deb') {
|
||||
try {
|
||||
// Пытаемся установить через pkexec apt install
|
||||
final result = await Process.run('pkexec', ['apt', 'install', '-y', savePath]);
|
||||
if (result.exitCode == 0) {
|
||||
if (context.mounted) {
|
||||
CustomToast.success('Обновление установлено! Перезапустите приложение.').show(context);
|
||||
context.pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
loggy.warning('Failed to install via pkexec apt: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Для других платформ или если apt не сработал - просто открываем файл
|
||||
final result = await OpenFile.open(savePath);
|
||||
|
||||
if (result.type != ResultType.done && context.mounted) {
|
||||
|
||||
Reference in New Issue
Block a user