cordova-plugin-hot-updates 1.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +239 -0
- package/docs/API.md +352 -0
- package/docs/README.md +187 -0
- package/docs/hot-updates-admin.html +467 -0
- package/package.json +5 -3
- package/src/ios/HotUpdates.h +23 -136
- package/src/ios/HotUpdates.m +761 -529
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to cordova-plugin-hot-updates will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [2.1.0] - 2025-11-04
|
|
9
|
+
|
|
10
|
+
### 🛠️ Fixed
|
|
11
|
+
|
|
12
|
+
- **getUpdate() duplicate downloads**: Added check for already installed version - `getUpdate()` now returns success without re-downloading if the requested version is already installed
|
|
13
|
+
- **Duplicate code**: Removed duplicate `setBool:YES forKey:kPendingUpdateReady` call in `saveDownloadedUpdate()`
|
|
14
|
+
|
|
15
|
+
### 🧹 Improved
|
|
16
|
+
|
|
17
|
+
- **Cleaner logs**: Removed all emoji and debug messages from native code - logs are now professional and suitable for production
|
|
18
|
+
- **Code cleanup**: Removed 3 unused internal methods (~80 lines of dead code):
|
|
19
|
+
- `removeVersionFromIgnoreList()`
|
|
20
|
+
- `isVersionIgnored()`
|
|
21
|
+
- `compareVersion:withVersion:`
|
|
22
|
+
- **Updated documentation**: Header comment now accurately reflects frontend-controlled architecture
|
|
23
|
+
|
|
24
|
+
### 📊 Technical Details
|
|
25
|
+
|
|
26
|
+
**Files changed**: 1 (HotUpdates.m)
|
|
27
|
+
- Added: ~15 lines
|
|
28
|
+
- Removed: ~80 lines
|
|
29
|
+
- Methods removed: 3
|
|
30
|
+
|
|
31
|
+
**No breaking changes** - API remains fully compatible with v2.0.0
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## [2.0.0] - 2025-10-30
|
|
36
|
+
|
|
37
|
+
### 🔥 Breaking Changes
|
|
38
|
+
|
|
39
|
+
- **Rollback behavior changed**: After successful rollback, `previousVersion` is now cleared to prevent infinite rollback loops. This means you can only rollback once without performing a new update first.
|
|
40
|
+
|
|
41
|
+
### ✨ Added
|
|
42
|
+
|
|
43
|
+
- **Loop Prevention**: Added protection against infinite update/rollback cycles
|
|
44
|
+
- `forceUpdate` now rejects attempts to install already installed version (error: `version_already_installed`)
|
|
45
|
+
- `rollback` validates that previous version differs from current (error: `same_version`)
|
|
46
|
+
- `rollback` clears `previousVersion` metadata after successful rollback
|
|
47
|
+
|
|
48
|
+
- **Enhanced Error Codes**: 13 detailed error codes for better error handling
|
|
49
|
+
- **forceUpdate**: 9 error codes (`version_already_installed`, `version_ignored`, `version_incompatible`, `download_failed`, `http_error`, `unzip_failed`, `invalid_package`, `install_failed`, `Download already in progress`)
|
|
50
|
+
- **rollback**: 4 error codes (`no_previous_version`, `previous_files_missing`, `same_version`, `file_operation_failed`)
|
|
51
|
+
|
|
52
|
+
- **Improved Version Checking**: `getVersionInfo` now properly validates all conditions for rollback availability
|
|
53
|
+
- Checks: folder exists + metadata exists + versions are different
|
|
54
|
+
- New fields: `rollbackAvailable` (folder exists), `rollbackReady` (all conditions met)
|
|
55
|
+
|
|
56
|
+
- **Enhanced API Responses**:
|
|
57
|
+
- `rollback` success response includes `canRollbackAgain: false` field
|
|
58
|
+
- `rollback` error response includes detailed error information: `error`, `message`, `currentVersion`, `previousVersion`, `canRollback`, `hasPreviousMetadata`
|
|
59
|
+
- `forceUpdate` error includes `currentVersion` and `requestedVersion` for `version_already_installed` error
|
|
60
|
+
|
|
61
|
+
### 🛠️ Fixed
|
|
62
|
+
|
|
63
|
+
- Fixed infinite rollback loop caused by version metadata swapping
|
|
64
|
+
- Fixed `canRollback` incorrectly returning `true` when versions are identical
|
|
65
|
+
- Fixed ability to install the same version multiple times
|
|
66
|
+
- Fixed rollback not detecting corrupted metadata
|
|
67
|
+
|
|
68
|
+
### 📝 Changed
|
|
69
|
+
|
|
70
|
+
- Reduced excessive debug logging, keeping only essential logs
|
|
71
|
+
- Improved log clarity and consistency across all methods
|
|
72
|
+
- Metadata management now uses strict version validation
|
|
73
|
+
|
|
74
|
+
### 🔒 Security
|
|
75
|
+
|
|
76
|
+
- Added validation to prevent rollback to same version (potential attack vector)
|
|
77
|
+
- Enhanced version comparison logic to prevent bypass attempts
|
|
78
|
+
|
|
79
|
+
## [1.0.0] - 2025-10-27
|
|
80
|
+
|
|
81
|
+
### ✨ Initial Release
|
|
82
|
+
|
|
83
|
+
- Frontend-controlled WebView Reload approach
|
|
84
|
+
- Automatic background update checking
|
|
85
|
+
- Force update capability with instant WebView reload
|
|
86
|
+
- Rollback mechanism with automatic backup
|
|
87
|
+
- IgnoreList for blocking problematic versions
|
|
88
|
+
- Canary system for version verification
|
|
89
|
+
- Semantic version comparison
|
|
90
|
+
- minAppVersion compatibility checking
|
|
91
|
+
- Auto-update toggle (disabled by default)
|
|
92
|
+
- First launch detection and handling
|
|
93
|
+
- Support for iOS 11.2+
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Migration Guide: 1.0.0 → 2.0.0
|
|
98
|
+
|
|
99
|
+
### Breaking Changes
|
|
100
|
+
|
|
101
|
+
**1. Rollback Behavior**
|
|
102
|
+
|
|
103
|
+
**Before (v1.0.0):**
|
|
104
|
+
```
|
|
105
|
+
Installed: 2.7.8, Previous: 2.7.7
|
|
106
|
+
rollback() → Installed: 2.7.7, Previous: 2.7.8 ⚠️ (could create infinite loop)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**After (v2.0.0):**
|
|
110
|
+
```
|
|
111
|
+
Installed: 2.7.8, Previous: 2.7.7
|
|
112
|
+
rollback() → Installed: 2.7.7, Previous: nil ✅ (loop prevented)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### What You Need to Update
|
|
116
|
+
|
|
117
|
+
**1. Error Handling for forceUpdate**
|
|
118
|
+
|
|
119
|
+
Add handler for new `version_already_installed` error:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
window.HotUpdates.forceUpdate(data, function(result) {
|
|
123
|
+
if (result.error === 'version_already_installed') {
|
|
124
|
+
// New in v2.0.0: Handle already installed version
|
|
125
|
+
showNotification('This version is already installed');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// ... rest of error handling
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**2. Error Handling for rollback**
|
|
133
|
+
|
|
134
|
+
Update rollback error handling with new detailed errors:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
window.HotUpdates.rollback(function(result) {
|
|
138
|
+
if (!result.success) {
|
|
139
|
+
// New in v2.0.0: Detailed error codes
|
|
140
|
+
switch(result.error) {
|
|
141
|
+
case 'no_previous_version':
|
|
142
|
+
showError('No previous version to rollback to');
|
|
143
|
+
break;
|
|
144
|
+
case 'previous_files_missing':
|
|
145
|
+
showError('Previous version files not found');
|
|
146
|
+
break;
|
|
147
|
+
case 'same_version':
|
|
148
|
+
showError('Cannot rollback to the same version');
|
|
149
|
+
break;
|
|
150
|
+
case 'file_operation_failed':
|
|
151
|
+
showError('Rollback failed');
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**3. UI Updates for Rollback**
|
|
159
|
+
|
|
160
|
+
Check `canRollbackAgain` field after rollback:
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
window.HotUpdates.rollback(function(result) {
|
|
164
|
+
if (result.success) {
|
|
165
|
+
console.log('Rollback successful');
|
|
166
|
+
console.log('Can rollback again:', result.canRollbackAgain); // Always false in v2.0.0
|
|
167
|
+
|
|
168
|
+
// Hide rollback button after successful rollback
|
|
169
|
+
if (!result.canRollbackAgain) {
|
|
170
|
+
hideRollbackButton();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**4. Version Info Checks**
|
|
177
|
+
|
|
178
|
+
Use improved `canRollback` field:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
window.HotUpdates.getVersionInfo(function(info) {
|
|
182
|
+
// v2.0.0: More accurate rollback availability check
|
|
183
|
+
if (info.canRollback) {
|
|
184
|
+
// All conditions met: folder + metadata + different versions
|
|
185
|
+
enableRollbackButton();
|
|
186
|
+
} else {
|
|
187
|
+
disableRollbackButton();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// New fields in v2.0.0:
|
|
191
|
+
console.log('Rollback folder exists:', info.rollbackAvailable);
|
|
192
|
+
console.log('Rollback ready:', info.rollbackReady);
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Testing After Migration
|
|
197
|
+
|
|
198
|
+
Run these tests to ensure v2.0.0 works correctly:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// Test 1: Prevent duplicate installation
|
|
202
|
+
window.HotUpdates.getCurrentVersion(function(current) {
|
|
203
|
+
window.HotUpdates.forceUpdate({
|
|
204
|
+
url: 'http://example.com/update.zip',
|
|
205
|
+
version: current // Same as current!
|
|
206
|
+
}, function(result) {
|
|
207
|
+
// Should return error: version_already_installed ✅
|
|
208
|
+
console.assert(result.error === 'version_already_installed');
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Test 2: Rollback loop prevention
|
|
213
|
+
window.HotUpdates.forceUpdate({url: '...', version: '2.7.8'}, function(r1) {
|
|
214
|
+
if (r1.status === 'installed') {
|
|
215
|
+
window.HotUpdates.rollback(function(r2) {
|
|
216
|
+
if (r2.success) {
|
|
217
|
+
// First rollback: should succeed ✅
|
|
218
|
+
window.HotUpdates.rollback(function(r3) {
|
|
219
|
+
// Second rollback: should fail with no_previous_version ✅
|
|
220
|
+
console.assert(!r3.success);
|
|
221
|
+
console.assert(r3.error === 'no_previous_version');
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Performance Impact
|
|
230
|
+
|
|
231
|
+
- **No performance degradation**: All changes are optimizations
|
|
232
|
+
- **Reduced log output**: Less console spam in production
|
|
233
|
+
- **Faster error detection**: Version checks happen earlier
|
|
234
|
+
|
|
235
|
+
### Support
|
|
236
|
+
|
|
237
|
+
- Documentation: [README.md](README.md)
|
|
238
|
+
- Issues: [GitHub Issues](https://github.com/vladimirDarksy/Cordova_hot_update/issues)
|
|
239
|
+
- Version comparison: [1.0.0...2.0.0](https://github.com/vladimirDarksy/Cordova_hot_update/compare/v1.0.0...v2.0.0)
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Hot Updates API v2.1.0
|
|
2
|
+
|
|
3
|
+
## 🎯 4 метода для управления обновлениями
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. getUpdate() - Скачать обновление
|
|
8
|
+
|
|
9
|
+
Скачивает обновление в фоновом режиме. **Не устанавливает!**
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
window.flasher.getUpdate({url, version}, callback)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Параметры:
|
|
16
|
+
|
|
17
|
+
| Параметр | Тип | Обязательный | Описание |
|
|
18
|
+
|----------|-----|--------------|----------|
|
|
19
|
+
| url | string | ✅ | URL архива обновления (.zip) |
|
|
20
|
+
| version | string | ⚠️ | Версия обновления (опционально) |
|
|
21
|
+
| callback | function | ✅ | Вызывается после завершения |
|
|
22
|
+
|
|
23
|
+
### Callback:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
function callback(error) {
|
|
27
|
+
if (error) {
|
|
28
|
+
// Ошибка: {error: {message: "текст ошибки"}}
|
|
29
|
+
console.error('Ошибка скачивания:', error.error.message);
|
|
30
|
+
} else {
|
|
31
|
+
// Успех: null или undefined
|
|
32
|
+
console.log('Обновление скачано');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Пример:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
window.flasher.getUpdate({
|
|
41
|
+
url: 'https://api.example.com/updates/2.7.8.zip',
|
|
42
|
+
version: '2.7.8'
|
|
43
|
+
}, function(error) {
|
|
44
|
+
if (error) {
|
|
45
|
+
alert('Не удалось скачать обновление');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Показать popup "Обновить приложение?"
|
|
49
|
+
showUpdatePopup();
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Возможные ошибки:
|
|
54
|
+
|
|
55
|
+
| Ошибка | Причина |
|
|
56
|
+
|--------|---------|
|
|
57
|
+
| `URL required` | Не передан URL |
|
|
58
|
+
| `Download failed` | Проблемы с сетью |
|
|
59
|
+
| `HTTP error` | Сервер вернул ошибку (не 200) |
|
|
60
|
+
| `Unzip failed` | Битый архив |
|
|
61
|
+
| `Invalid package` | В архиве нет папки www |
|
|
62
|
+
| `Download already in progress` | Уже идет загрузка |
|
|
63
|
+
|
|
64
|
+
### Важно:
|
|
65
|
+
|
|
66
|
+
- Если версия уже скачана - вернет **success** (не будет перезагружать)
|
|
67
|
+
- Обновление НЕ устанавливается автоматически
|
|
68
|
+
- Нужно вызвать `forceUpdate()` для установки
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 2. forceUpdate() - Установить обновление
|
|
73
|
+
|
|
74
|
+
Устанавливает скачанное обновление и перезагружает WebView.
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
window.flasher.forceUpdate(callback)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Параметры:
|
|
81
|
+
|
|
82
|
+
| Параметр | Тип | Обязательный | Описание |
|
|
83
|
+
|----------|-----|--------------|----------|
|
|
84
|
+
| callback | function | ✅ | Вызывается перед перезагрузкой |
|
|
85
|
+
|
|
86
|
+
### Callback:
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
function callback(error) {
|
|
90
|
+
if (error) {
|
|
91
|
+
// Ошибка: {error: {message: "текст ошибки"}}
|
|
92
|
+
console.error('Ошибка установки:', error.error.message);
|
|
93
|
+
} else {
|
|
94
|
+
// Успех: null или undefined
|
|
95
|
+
console.log('Обновление установлено, перезагрузка...');
|
|
96
|
+
// Через ~1 секунду WebView перезагрузится
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Пример:
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
window.flasher.forceUpdate(function(error) {
|
|
105
|
+
if (error) {
|
|
106
|
+
alert('Не удалось установить обновление');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// WebView сейчас перезагрузится с новой версией
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Возможные ошибки:
|
|
114
|
+
|
|
115
|
+
| Ошибка | Причина |
|
|
116
|
+
|--------|---------|
|
|
117
|
+
| `No update ready to install` | Не было вызвано `getUpdate()` |
|
|
118
|
+
| `Install failed` | Ошибка файловой системы |
|
|
119
|
+
|
|
120
|
+
### Важно:
|
|
121
|
+
|
|
122
|
+
- Можно вызвать только после успешного `getUpdate()`
|
|
123
|
+
- WebView перезагружается автоматически через ~1 секунду
|
|
124
|
+
- После перезагрузки **обязательно** вызовите `canary()`
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 3. canary() - Подтвердить успешную загрузку
|
|
129
|
+
|
|
130
|
+
Подтверждает, что обновление загрузилось без ошибок. **ОБЯЗАТЕЛЕН при каждом старте!**
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
window.flasher.canary(version, callback)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Параметры:
|
|
137
|
+
|
|
138
|
+
| Параметр | Тип | Обязательный | Описание |
|
|
139
|
+
|----------|-----|--------------|----------|
|
|
140
|
+
| version | string | ✅ | Текущая версия приложения |
|
|
141
|
+
| callback | function | ⚠️ | Не используется, можно передать null |
|
|
142
|
+
|
|
143
|
+
### Пример:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
document.addEventListener('deviceready', function() {
|
|
147
|
+
// ОБЯЗАТЕЛЬНО при каждом старте приложения!
|
|
148
|
+
window.flasher.canary('2.7.8', function() {
|
|
149
|
+
console.log('Canary confirmed');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Как получить версию:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
// Способ 1: Динамическая загрузка из native (рекомендуется)
|
|
158
|
+
cordova.exec(function(info) {
|
|
159
|
+
const currentVersion = info.installedVersion || info.appBundleVersion;
|
|
160
|
+
window.flasher.canary(currentVersion, null);
|
|
161
|
+
}, null, 'HotUpdates', 'getVersionInfo', []);
|
|
162
|
+
|
|
163
|
+
// Способ 2: Статическая (нужно обновлять вручную)
|
|
164
|
+
window.APP_VERSION = "2.7.8";
|
|
165
|
+
window.flasher.canary(window.APP_VERSION, null);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Важно:
|
|
169
|
+
|
|
170
|
+
- **ОБЯЗАТЕЛЬНО** вызывать при каждом старте приложения
|
|
171
|
+
- Если не вызвать в течение 20 секунд → автоматический откат на предыдущую версию
|
|
172
|
+
- Версия должна быть строкой (не числом!)
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 4. getIgnoreList() - Получить черный список версий
|
|
177
|
+
|
|
178
|
+
Возвращает список версий, которые вызвали проблемы и были откачены.
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
window.flasher.getIgnoreList(callback)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Параметры:
|
|
185
|
+
|
|
186
|
+
| Параметр | Тип | Обязательный | Описание |
|
|
187
|
+
|----------|-----|--------------|----------|
|
|
188
|
+
| callback | function | ✅ | Получает список версий |
|
|
189
|
+
|
|
190
|
+
### Callback:
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
function callback(result) {
|
|
194
|
+
const badVersions = result.versions; // ["2.7.5", "2.7.6"]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Пример:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
window.flasher.getIgnoreList(function(result) {
|
|
202
|
+
const ignoredVersions = result.versions;
|
|
203
|
+
|
|
204
|
+
// Проверяем новую версию перед скачиванием
|
|
205
|
+
if (ignoredVersions.includes(newVersion)) {
|
|
206
|
+
console.log('Эта версия в черном списке, не скачиваем');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Версия безопасна, можно скачивать
|
|
211
|
+
window.flasher.getUpdate({url: updateUrl}, ...);
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Важно:
|
|
216
|
+
|
|
217
|
+
- Список управляется **только native кодом**
|
|
218
|
+
- JavaScript может только **читать** список
|
|
219
|
+
- Версии добавляются автоматически при откате (rollback)
|
|
220
|
+
- Вы сами решаете, скачивать версию из списка или нет
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 📋 Полный сценарий работы
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
document.addEventListener('deviceready', function() {
|
|
228
|
+
// 1. ОБЯЗАТЕЛЬНО подтверждаем canary
|
|
229
|
+
loadCurrentVersion(function(currentVersion) {
|
|
230
|
+
window.flasher.canary(currentVersion, function() {
|
|
231
|
+
console.log('✅ Canary OK');
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// 2. Получаем черный список
|
|
236
|
+
window.flasher.getIgnoreList(function(result) {
|
|
237
|
+
const ignoredVersions = result.versions;
|
|
238
|
+
|
|
239
|
+
// 3. Проверяем обновления на сервере
|
|
240
|
+
checkServerForUpdates(function(updateInfo) {
|
|
241
|
+
if (!updateInfo.hasUpdate) {
|
|
242
|
+
console.log('Обновлений нет');
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 4. Проверяем черный список
|
|
247
|
+
if (ignoredVersions.includes(updateInfo.version)) {
|
|
248
|
+
console.log('Версия в черном списке, пропускаем');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 5. Скачиваем обновление
|
|
253
|
+
window.flasher.getUpdate({
|
|
254
|
+
url: updateInfo.downloadUrl,
|
|
255
|
+
version: updateInfo.version
|
|
256
|
+
}, function(error) {
|
|
257
|
+
if (error) {
|
|
258
|
+
console.error('Ошибка загрузки:', error.error.message);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// 6. Показываем popup
|
|
263
|
+
if (confirm('Обновить до версии ' + updateInfo.version + '?')) {
|
|
264
|
+
// 7. Устанавливаем
|
|
265
|
+
window.flasher.forceUpdate(function(error) {
|
|
266
|
+
if (error) {
|
|
267
|
+
console.error('Ошибка установки:', error.error.message);
|
|
268
|
+
}
|
|
269
|
+
// WebView перезагружается автоматически
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Если пользователь нажал "Отмена" - обновление установится
|
|
273
|
+
// автоматически при следующем запуске приложения
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Вспомогательная функция для получения версии
|
|
280
|
+
function loadCurrentVersion(callback) {
|
|
281
|
+
cordova.exec(
|
|
282
|
+
function(info) {
|
|
283
|
+
callback(info.installedVersion || info.appBundleVersion);
|
|
284
|
+
},
|
|
285
|
+
function(error) {
|
|
286
|
+
console.error('Не удалось получить версию');
|
|
287
|
+
callback('2.7.7'); // fallback
|
|
288
|
+
},
|
|
289
|
+
'HotUpdates',
|
|
290
|
+
'getVersionInfo',
|
|
291
|
+
[]
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Вспомогательная функция для проверки обновлений
|
|
296
|
+
function checkServerForUpdates(callback) {
|
|
297
|
+
fetch('https://api.example.com/updates/check?version=' + currentVersion)
|
|
298
|
+
.then(r => r.json())
|
|
299
|
+
.then(callback)
|
|
300
|
+
.catch(err => console.error('Ошибка проверки обновлений:', err));
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## ⚠️ Важные особенности
|
|
307
|
+
|
|
308
|
+
### Автоустановка
|
|
309
|
+
|
|
310
|
+
Если пользователь **проигнорировал popup** (закрыл приложение), обновление автоматически установится при следующем запуске.
|
|
311
|
+
|
|
312
|
+
### Откат (Rollback)
|
|
313
|
+
|
|
314
|
+
Если после обновления приложение крашится или не вызывается `canary()` в течение 20 секунд:
|
|
315
|
+
1. Автоматический откат на предыдущую версию
|
|
316
|
+
2. Сбойная версия добавляется в ignore list
|
|
317
|
+
3. Приложение продолжает работать на старой версии
|
|
318
|
+
|
|
319
|
+
### Черный список (Ignore List)
|
|
320
|
+
|
|
321
|
+
- **Не блокирует** установку (вы сами решаете)
|
|
322
|
+
- Служит для **информирования** о проблемных версиях
|
|
323
|
+
- Управляется только native кодом
|
|
324
|
+
- Можно установить версию из списка, если хотите (например, исправленную)
|
|
325
|
+
|
|
326
|
+
### Формат callback
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// ✅ Успех
|
|
330
|
+
callback() // null или undefined
|
|
331
|
+
callback(null) // null
|
|
332
|
+
|
|
333
|
+
// ❌ Ошибка
|
|
334
|
+
callback({
|
|
335
|
+
error: {
|
|
336
|
+
message: "текст ошибки" // может отсутствовать
|
|
337
|
+
}
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 🔗 Дополнительные файлы
|
|
344
|
+
|
|
345
|
+
- **README.md** - быстрый старт и примеры
|
|
346
|
+
- **hot-updates-admin.html** - тестовый интерфейс для отладки
|
|
347
|
+
- **BUGFIXES_2025-11-04.md** - список исправленных багов
|
|
348
|
+
- **QUICK_REFERENCE.md** - краткая справка
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
**Версия API:** 2.1.0 (Bugfix Update 2025-11-04)
|