+126
-2
@@ -269,16 +269,34 @@ jobs:
|
||||
ln -s /Applications dmg-staging/Applications
|
||||
hdiutil create -format ULMO -srcfolder dmg-staging -volname "Plezy" plezy-macos.dmg
|
||||
|
||||
- name: Sign DMG for Sparkle (EdDSA)
|
||||
if: ${{ secrets.SPARKLE_PRIVATE_KEY != '' }}
|
||||
env:
|
||||
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }}
|
||||
run: |
|
||||
SIGN_BIN="macos/Pods/Sparkle/bin/sign_update"
|
||||
SIGNATURE=$(echo "$SPARKLE_PRIVATE_KEY" | "$SIGN_BIN" plezy-macos.dmg --ed-key-file - -p)
|
||||
echo "MACOS_ED_SIGNATURE=$SIGNATURE" >> $GITHUB_ENV
|
||||
echo "MACOS_DMG_SIZE=$(stat -f%z plezy-macos.dmg)" >> $GITHUB_ENV
|
||||
|
||||
- name: Attest macOS DMG
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: plezy-macos.dmg
|
||||
|
||||
- name: Write macOS signature metadata
|
||||
run: |
|
||||
echo "${{ env.MACOS_ED_SIGNATURE }}" > macos-ed-signature.txt
|
||||
echo "${{ env.MACOS_DMG_SIZE }}" > macos-dmg-size.txt
|
||||
|
||||
- name: Upload macOS DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-dmg
|
||||
path: plezy-macos.dmg
|
||||
path: |
|
||||
plezy-macos.dmg
|
||||
macos-ed-signature.txt
|
||||
macos-dmg-size.txt
|
||||
|
||||
build-windows-x64:
|
||||
runs-on: windows-latest
|
||||
@@ -361,6 +379,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
cache: true
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Download x64 build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -376,6 +403,18 @@ jobs:
|
||||
- name: Build installer and portables
|
||||
run: .\windows\build-installer.ps1 -X64BuildDir "build-x64" -Arm64BuildDir "build-arm64"
|
||||
|
||||
- name: Sign installer for WinSparkle (DSA)
|
||||
if: ${{ secrets.WINSPARKLE_DSA_PRIVATE_KEY != '' }}
|
||||
env:
|
||||
WINSPARKLE_DSA_PRIVATE_KEY: ${{ secrets.WINSPARKLE_DSA_PRIVATE_KEY }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
$env:WINSPARKLE_DSA_PRIVATE_KEY | Out-File -Encoding ascii dsa_priv.pem
|
||||
$output = dart run auto_updater:sign_update plezy-windows-installer.exe
|
||||
Remove-Item dsa_priv.pem
|
||||
$output | Out-File -Encoding ascii win-dsa-signature.txt
|
||||
(Get-Item plezy-windows-installer.exe).Length | Out-File -Encoding ascii win-installer-size.txt
|
||||
|
||||
- name: Attest Windows artifacts
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
@@ -400,7 +439,10 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-installer
|
||||
path: plezy-windows-installer.exe
|
||||
path: |
|
||||
plezy-windows-installer.exe
|
||||
win-dsa-signature.txt
|
||||
win-installer-size.txt
|
||||
|
||||
build-linux-x64:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -634,6 +676,11 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: pubspec.yaml
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -642,6 +689,82 @@ jobs:
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R artifacts
|
||||
|
||||
- name: Read version from pubspec.yaml
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //' | sed 's/+.*//')
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate appcast.xml
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
|
||||
# Read macOS signature metadata (may be empty if secrets not configured)
|
||||
MACOS_ED_SIG=""
|
||||
MACOS_DMG_SIZE="0"
|
||||
if [ -f artifacts/macos-dmg/macos-ed-signature.txt ]; then
|
||||
MACOS_ED_SIG=$(cat artifacts/macos-dmg/macos-ed-signature.txt)
|
||||
fi
|
||||
if [ -f artifacts/macos-dmg/macos-dmg-size.txt ]; then
|
||||
MACOS_DMG_SIZE=$(cat artifacts/macos-dmg/macos-dmg-size.txt)
|
||||
fi
|
||||
|
||||
# Read Windows signature metadata (may be empty if secrets not configured)
|
||||
WIN_DSA_SIG=""
|
||||
WIN_INSTALLER_SIZE="0"
|
||||
if [ -f artifacts/windows-installer/win-dsa-signature.txt ]; then
|
||||
WIN_DSA_SIG=$(cat artifacts/windows-installer/win-dsa-signature.txt)
|
||||
fi
|
||||
if [ -f artifacts/windows-installer/win-installer-size.txt ]; then
|
||||
WIN_INSTALLER_SIZE=$(cat artifacts/windows-installer/win-installer-size.txt)
|
||||
fi
|
||||
|
||||
cat > appcast.xml << 'XMLEOF'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
|
||||
<channel>
|
||||
<title>Plezy Updates</title>
|
||||
XMLEOF
|
||||
|
||||
# macOS entry
|
||||
if [ -n "$MACOS_ED_SIG" ]; then
|
||||
cat >> appcast.xml << XMLEOF
|
||||
<item>
|
||||
<title>Version ${VERSION}</title>
|
||||
<sparkle:version>${VERSION}</sparkle:version>
|
||||
<sparkle:os>macos</sparkle:os>
|
||||
<enclosure url="https://github.com/edde746/plezy/releases/download/${VERSION}/plezy-macos.dmg"
|
||||
length="${MACOS_DMG_SIZE}" type="application/octet-stream"
|
||||
sparkle:edSignature="${MACOS_ED_SIG}" />
|
||||
</item>
|
||||
XMLEOF
|
||||
fi
|
||||
|
||||
# Windows entry
|
||||
if [ -n "$WIN_DSA_SIG" ]; then
|
||||
cat >> appcast.xml << XMLEOF
|
||||
<item>
|
||||
<title>Version ${VERSION}</title>
|
||||
<sparkle:version>${VERSION}</sparkle:version>
|
||||
<sparkle:os>windows-x64</sparkle:os>
|
||||
<enclosure url="https://github.com/edde746/plezy/releases/download/${VERSION}/plezy-windows-installer.exe"
|
||||
length="${WIN_INSTALLER_SIZE}" type="application/octet-stream"
|
||||
sparkle:dsaSignature="${WIN_DSA_SIG}" />
|
||||
</item>
|
||||
XMLEOF
|
||||
fi
|
||||
|
||||
cat >> appcast.xml << 'XMLEOF'
|
||||
</channel>
|
||||
</rss>
|
||||
XMLEOF
|
||||
|
||||
# Clean up leading whitespace from heredoc indentation
|
||||
sed -i 's/^ //' appcast.xml
|
||||
|
||||
echo "Generated appcast.xml:"
|
||||
cat appcast.xml
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
@@ -656,6 +779,7 @@ jobs:
|
||||
artifacts/windows-installer/plezy-windows-installer.exe
|
||||
artifacts/linux-x64/*
|
||||
artifacts/linux-arm64/*
|
||||
appcast.xml
|
||||
draft: true
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
|
||||
@@ -195,6 +195,12 @@ class _MainScreenState extends State<MainScreen> with RouteAware, WindowListener
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
// Native updater (Sparkle/WinSparkle) handles everything — skip Flutter dialog
|
||||
if (UpdateService.useNativeUpdater) {
|
||||
await UpdateService.checkForUpdatesNative(inBackground: true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final updateInfo = await UpdateService.checkForUpdatesOnStartup();
|
||||
|
||||
|
||||
@@ -931,6 +931,31 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab {
|
||||
}
|
||||
|
||||
Widget _buildUpdateSection() {
|
||||
// Native updater: simple tile that triggers Sparkle/WinSparkle native dialog
|
||||
if (UpdateService.useNativeUpdater) {
|
||||
return Card(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
t.settings.updates,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
focusNode: _focusTracker.get(_kCheckForUpdates),
|
||||
leading: const AppIcon(Symbols.system_update_rounded, fill: 1),
|
||||
title: Text(t.settings.checkForUpdates),
|
||||
trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1),
|
||||
onTap: () => UpdateService.checkForUpdatesNative(inBackground: false),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final hasUpdate = _updateInfo != null && _updateInfo!['hasUpdate'] == true;
|
||||
|
||||
return Card(
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:auto_updater/auto_updater.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
@@ -5,9 +8,14 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Service to check for new versions on GitHub
|
||||
/// Only enabled when ENABLE_UPDATE_CHECK build flag is set
|
||||
///
|
||||
/// On macOS (non-Homebrew) and installed Windows: delegates to Sparkle/WinSparkle
|
||||
/// via auto_updater for native update dialogs and in-app installs.
|
||||
/// On all other platforms: falls back to GitHub API check + browser link dialog.
|
||||
class UpdateService {
|
||||
static final Logger _logger = Logger();
|
||||
static const String _githubRepo = 'edde746/plezy';
|
||||
static const String _feedUrl = 'https://github.com/edde746/plezy/releases/latest/download/appcast.xml';
|
||||
|
||||
// SharedPreferences keys
|
||||
static const String _keySkippedVersion = 'update_skipped_version';
|
||||
@@ -16,11 +24,70 @@ class UpdateService {
|
||||
// Check cooldown: 6 hours
|
||||
static const Duration _checkCooldown = Duration(hours: 6);
|
||||
|
||||
static bool _nativeUpdaterInitialized = false;
|
||||
|
||||
/// Check if update checking is enabled via build flag
|
||||
static bool get isUpdateCheckEnabled {
|
||||
return const bool.fromEnvironment('ENABLE_UPDATE_CHECK', defaultValue: false);
|
||||
}
|
||||
|
||||
/// Whether the native auto_updater (Sparkle/WinSparkle) should be used.
|
||||
/// True on macOS (non-Homebrew) and installed Windows (has uninstaller).
|
||||
static bool get useNativeUpdater {
|
||||
if (!isUpdateCheckEnabled) return false;
|
||||
if (Platform.isMacOS) return !_isHomebrewInstall();
|
||||
if (Platform.isWindows) return _isInstalledApp();
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Initialize the native auto_updater (Sparkle/WinSparkle).
|
||||
/// Call once at startup if [useNativeUpdater] is true.
|
||||
static Future<void> initNativeUpdater() async {
|
||||
if (_nativeUpdaterInitialized) return;
|
||||
_nativeUpdaterInitialized = true;
|
||||
|
||||
try {
|
||||
await autoUpdater.setFeedURL(_feedUrl);
|
||||
// 6 hours in seconds
|
||||
await autoUpdater.setScheduledCheckInterval(_checkCooldown.inSeconds);
|
||||
} catch (e) {
|
||||
_logger.e('Failed to initialize native auto updater: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Trigger a background update check via Sparkle/WinSparkle.
|
||||
/// Only shows UI if an update is found.
|
||||
static Future<void> checkForUpdatesNative({bool inBackground = true}) async {
|
||||
if (!_nativeUpdaterInitialized) await initNativeUpdater();
|
||||
try {
|
||||
await autoUpdater.checkForUpdates(inBackground: inBackground);
|
||||
} catch (e) {
|
||||
_logger.e('Native update check failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the macOS app was installed via Homebrew.
|
||||
/// Homebrew casks live under /opt/homebrew/Caskroom/ or /usr/local/Caskroom/.
|
||||
static bool _isHomebrewInstall() {
|
||||
try {
|
||||
final execPath = Platform.resolvedExecutable;
|
||||
return execPath.contains('/Caskroom/') || execPath.contains('/homebrew/');
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the Windows app is an installed copy (not portable).
|
||||
/// The Inno Setup installer places unins000.exe next to the executable.
|
||||
static bool _isInstalledApp() {
|
||||
try {
|
||||
final exeDir = File(Platform.resolvedExecutable).parent.path;
|
||||
return File('$exeDir\\unins000.exe').existsSync();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip a specific version
|
||||
static Future<void> skipVersion(String version) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import auto_updater_macos
|
||||
import connectivity_plus
|
||||
import device_info_plus
|
||||
import file_picker
|
||||
@@ -23,6 +24,7 @@ import wakelock_plus
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AutoUpdaterMacosPlugin.register(with: registry.registrar(forPlugin: "AutoUpdaterMacosPlugin"))
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
PODS:
|
||||
- auto_updater_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- Sparkle
|
||||
- connectivity_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- device_info_plus (0.0.1):
|
||||
@@ -22,6 +25,7 @@ PODS:
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sparkle (2.9.0)
|
||||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -60,6 +64,7 @@ PODS:
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- auto_updater_macos (from `Flutter/ephemeral/.symlinks/plugins/auto_updater_macos/macos`)
|
||||
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
|
||||
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
|
||||
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
|
||||
@@ -80,9 +85,12 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Sparkle
|
||||
- sqlite3
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
auto_updater_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/auto_updater_macos/macos
|
||||
connectivity_plus:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
|
||||
device_info_plus:
|
||||
@@ -119,6 +127,7 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
auto_updater_macos: 3a42f1a06be6981f1a18be37e6e7bf86aa732118
|
||||
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
|
||||
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
|
||||
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
|
||||
@@ -130,6 +139,7 @@ SPEC CHECKSUMS:
|
||||
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
|
||||
screen_retriever_macos: 452e51764a9e1cdb74b3c541238795849f21557f
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
Sparkle: f4355f9ebbe9b7d932df4980d70f13922ac97b2a
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
|
||||
sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
|
||||
|
||||
@@ -30,5 +30,9 @@
|
||||
<string>NSApplication</string>
|
||||
<key>FLTEnableMergedPlatformUIThread</key>
|
||||
<false/>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://github.com/edde746/plezy/releases/latest/download/appcast.xml</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>q7t6eu1txRrqv1PeWDCRV95rFxnmD/D3GZWbUPMwUyU=</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -65,6 +65,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_updater:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: auto_updater
|
||||
sha256: "74fd008b021d15e4fc50fb5f1923870e6374ae831bb1168241c23bc35a4e898b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
auto_updater_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_updater_macos
|
||||
sha256: ef9de0daf38d782d2243fe131503a9404d62df762f5386e5360020e43c01867f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
auto_updater_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_updater_platform_interface
|
||||
sha256: ed5dff8cea18c58bc5ac787ff9122fedd1644272e02015d28c9b592f8078c5dc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
auto_updater_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_updater_windows
|
||||
sha256: "2bba20a71eee072f49b7267fedd5c4f1406c4b1b1e5b83932c634dbab75b80c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
background_downloader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -54,6 +54,7 @@ dependencies:
|
||||
mobile_scanner: ^6.0.2
|
||||
android_intent_plus: ^5.0.2
|
||||
background_downloader: ^9.5.2
|
||||
auto_updater: ^1.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <auto_updater_windows/auto_updater_windows_plugin_c_api.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <os_media_controls/os_media_controls_plugin_c_api.h>
|
||||
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
|
||||
@@ -15,6 +16,8 @@
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AutoUpdaterWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AutoUpdaterWindowsPluginCApi"));
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||
OsMediaControlsPluginCApiRegisterWithRegistrar(
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
auto_updater_windows
|
||||
connectivity_plus
|
||||
os_media_controls
|
||||
screen_retriever_windows
|
||||
|
||||
@@ -105,6 +105,12 @@ BEGIN
|
||||
END
|
||||
END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WinSparkle DSA public key for update signature verification
|
||||
//
|
||||
DSAPub DSAPEM "resources\\dsa_pub.pem"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIGQjCCBDUGByqGSM44BAEwggQoAoICAQCIYWApPkLWfPbST2OX3aX7CrNRC1Dy
|
||||
0YWG8matWNIjNoWCw4av5PS6k14/36MRuuDUzLspBM+h5r8Br/UNXuZSRtCgscmk
|
||||
EXw+OYoOIhHhUrmCofxm/r7s3Av73CrNSFTiUqZFBYbKuEtiZKZFk7djOFsT7omq
|
||||
yOSJz2qg5chxosfVMq3ePNzThTRL5t5y4l5kyBjhesXfOOfzegNvofAihi+SuRIB
|
||||
WW4S/RGIiZWV/QO40iTVBNtHd6T97A0VjGiy9ZYRWcmTudodeZTkfNb3Dn++NyZ9
|
||||
IPG+KiSfmFGAblF6lbJRHiIiCsF0lzm+v6szV4M/l5u52cUpRw3RVL8ZLxzODF9I
|
||||
cQ8ZrLlwOVlfFKgxQQvtsigBS3Z6A3bQii7+5bWLHjh8do4Vnc4Bts4s4mKXBFxN
|
||||
H7HAtj+jBiBFBBWesdltNNDRgITLfbShtT3R1tGcLLMz3G1o1he80Ps1MKxE4SYI
|
||||
D5Z8SV23m/uIDsTbPx/EiLpaVRko0KGvLvI7tXVZOgGuv32IjQE0Kja88MLTOHIf
|
||||
yImqxz/Wva/+TThmpyKOoCQ5ZuR+3BFl8pm+EMwYDpQFyVgruMEZ5hcRvPjGXh8i
|
||||
FNK9yfxPjfRHDeUL+N4u0UYH26cZCTKqocCY8tAuIwv/Z3vOagROChIImFpeeiSK
|
||||
ONI21p3DN3tUyQIdAIhC40EohYm1pa7z2s92INHSzotVxQ88yEy2zWsCggIAYX1z
|
||||
zRuIRbULECLxQUXIFN46K/d0kv/FsOo6x1OMr0KiS3+xTHgoVNsYBZiJPAC1wxdt
|
||||
qESc0lge5WAq2rmO+g8eBeO/RnmwwYbAOPfzAgnXQObRCymjjCHFn5cmdry5AELF
|
||||
TaQNe1c1DB5siCPUB9XZCDyIUxUrp7tpQDaAzHOkBTZ8QRA0+jL377I7FRCPvEto
|
||||
mxpYULBdq9FJ90tRAocDKKIbW0lM6+7aMdrZ5BA8+pcouhDYwm/+ac4PLZlxj4e1
|
||||
d/we96DNOqffgIxKCn870aLKx1fb3eWyq9vJzGim1KBV4jQZgmQpuuUps2EgJJtr
|
||||
u/bE9kA7I8ZXOIvedfR73behuL0stuFIPw86apy/1/AYROemGbQDuk5RVvGI2eY+
|
||||
ngFWTXObbS/kO8O3mnXMANdv0lL3Q4g681gTSzOiAmO2S8/rhnW1sRk8tJH8afm2
|
||||
fwKWuGwfgDV8oxdCro7UDNxT4rq3CIX+tEot3clxPv6urIx9L3pPBJenJb8f0K9p
|
||||
vaimP9mJxcRHTHTCdq4GEVb+kVgDZ3wkJgbXdH/CdHDaH2o8+KWH5sCL+ziAZ/HM
|
||||
1JhcG/RstIXtsi4zD3+GWZOJYIws+uk/5gsLAy6ZnUZRMkwm9s7Qh+DJmRVmaQ6G
|
||||
GmiU+jjlBzg1xT6+9PP6uyLAFWVSE+Sus1ezdCgDggIFAAKCAgBg7zX+v0j5B2W5
|
||||
k4jnie24aC/5wbMQ56ZV4GI6UZ9eZM0dqGKCC86XfLZZi1BacY50OMShmAkOc6k3
|
||||
1/ag2ddQgUO1jAlkjzd7xnlUXpwkL3yt2orGjamBu1rzbiZjaxbYpNlZd19r2+my
|
||||
UlbT+85OckJRZtq5rlFmscKQEWbPzC90zqbR/1C8NtjHK1iygdY/wlSRzp4a81gI
|
||||
GgCwATDLvQzPxHMictY05sCitnH4cf/acLtLED1ClgiS0DRrZ0DGglbz9UsqP5yB
|
||||
/UG7QoX8z3eT6hlicaoaLBkUIjZZcoOG1rb6ZshbtAm5fTRANiSlTek8TvYq6V85
|
||||
393lZafBCq2AvwDOtH7riSD7hJTL60XGeWVW7nhcTGNM24Y58Lkw0Op1Vi9CMPdL
|
||||
6XFbfW0mEic1w3I/oiLcYcqw+ktE2n5JcPNWsesoPZnJRb0nAY6T7eMsfTfbODMG
|
||||
qudB6F4lOetc/K+WAvfOVck2tAQhPRy0QvjlmaSbqIsVG1NG50pB0g8QL9VYOFsb
|
||||
TyrSY84vXaZmlM/Kk6I3fq7yRM1auOMOkSj6UcU1FenBXLdr1twiYU0/dmAlkU70
|
||||
+gESZBdvWa6SywB54rHBU5YIcoiwPQb2o70suh5PWDtAk47pazPSrMfjzWSvisyi
|
||||
IdoSn7v/pXbD9haYAkYFhXifYUUE/g==
|
||||
-----END PUBLIC KEY-----
|
||||
Reference in New Issue
Block a user