aefc80a25c508a55fa585f5828e2ef8e2d81a23e
[openwrt-luci.git] /
1 'use strict';
2 'require baseclass';
3 'require fs';
4 'require rpc';
5 'require uci'
6 'require ui';
7
8
9 const callSystemBoard = rpc.declare({
10         object: 'system',
11         method: 'board'
12 });
13
14 function showUpgradeNotification(type, boardinfo, new_version, upgrade_info)
15 {
16         const table_rows = [
17                 // Title                Current                     Available
18                 [_('Firmware Version'), boardinfo.release.version,  upgrade_info.version_number        ],
19                 [_('Revision'),         boardinfo.release.revision, upgrade_info.version_code          ],
20                 [_('Kernel Version'),   boardinfo?.kernel,          upgrade_info.linux_kernel?.version ],
21         ];
22
23         const table = E('table', { 'class': 'table' });
24
25         table.appendChild(
26                 E('tr', { 'class': 'tr' }, [
27                         E('th', { 'class': 'th' }, [  ]),
28                         E('th', { 'class': 'th' }, [ _('Current') ]),
29                         E('th', { 'class': 'th' }, [ _('Available') ])
30                 ])
31         );
32
33         table_rows.forEach((cols) => {
34                 table.appendChild(E('tr', { 'class': 'tr' }, [
35                         E('td', { 'class': 'td left', 'width': '33%' }, [ cols[0] ]),
36                         E('td', { 'class': 'td left'                 }, [ cols[1] ?? '?' ]),
37                         E('td', { 'class': 'td left'                 }, [ cols[2] ?? '?' ]),
38                 ]));
39         });
40
41         ui.addTimeLimitedNotification(_('New Firmware Available'), [
42                 E('p', _('A new %s version of OpenWrt is available:').format(type)),
43                 table,
44                 E('p', [
45                         _('Check') + ' ',
46                         E('a', {href: `/cgi-bin/luci/admin/system/attendedsysupgrade`}, _('Attended Sysupgrade')),
47                         ' ' + _('and') + ' ',
48                         E('a', {href: `https://openwrt.org/releases/${new_version?.split('.').slice(0, 2).join('.')}/notes-${new_version}`}, _('release notes')),
49                 ]),
50         ], 60000, 'notice');
51 };
52
53 function shouldUpgrade(installed, available)
54 {
55         // If installed is any snapshot (release or main), don't upgrade.
56
57         if (! available) return false;
58         if (! installed || installed.includes('SNAPSHOT')) return false
59
60         const parse = (v) => v.split(/[-+]/)[0]?.split('.').map(Number);
61         const parseRC = (v) => v.split(/[-+]/)[1]?.split('').map(Number);
62         const isPrerelease = (v) => /-/.test(v);
63
64         const [aParts, bParts] = [parse(available), parse(installed)];
65
66         for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
67                 const numA = aParts[i] || 0;
68                 const numB = bParts[i] || 0;
69                 if (numA > numB) return true;
70                 if (numA < numB) return false;
71         }
72
73         const [aRC, bRC] = [parseRC(available), parseRC(installed)];
74
75         if (aRC > bRC) return true;
76         if (aRC < bRC) return false;
77
78         // If numeric parts are equal, handle release candidates
79         // if (isPrerelease(available) && !isPrerelease(installed)) return false;
80         if (!isPrerelease(available) && isPrerelease(installed)) return true;
81         return false;
82 }
83
84 async function checkDeviceAvailable(boardinfo, new_version)
85 {
86         const profile_url = `https://downloads.openwrt.org/releases/${new_version}/targets/${boardinfo?.release?.target}/profiles.json`;
87         return fetch(profile_url)
88                 .then(response => response.json())
89                 .then(data => {
90                         // special case for x86, armsr and loongarch
91                         if (Object.keys(data?.profiles).length == 1 && Object.keys(data?.profiles)[0] == "generic") {
92                                 return [true, data];
93                         }
94
95                         for (const profileName in data?.profiles) {
96                                 if (profileName === boardinfo?.board_name) {
97                                         return [true, data];
98                                 }
99                                 const profile = data?.profiles[profileName];
100                                 if (profile.supported_devices?.includes(boardinfo?.board_name)) {
101                                         return [true, data];
102                                 }
103                         }
104
105                         return [false, null];
106                 })
107                 .catch(error => {
108                         console.error('Failed to fetch firmware upgrade profile information:', error);
109                         return [false, null];
110                 });
111 };
112
113 return baseclass.extend({
114         title: '',
115
116         load: function() {
117                 return Promise.all([
118                         L.resolveDefault(callSystemBoard(), {}),
119                         uci.load('luci')
120                 ]);
121         },
122
123         handleSetUpgradeCheck: function(pref, ev) {
124                 ev.currentTarget.classList.add('spinning');
125                 ev.currentTarget.blur();
126
127                 uci.set('luci', 'main', 'check_for_newer_firmwares', pref);
128
129                 return uci.save()
130                         .then(L.bind(L.ui.changes.init, L.ui.changes))
131                         .then(L.bind(L.ui.changes.displayChanges, L.ui.changes));
132         },
133
134         oneshot: function(data) {
135                 var boardinfo = data[0];
136                 const check_upgrades = uci.get_bool('luci', 'main', 'check_for_newer_firmwares');
137
138                 if (check_upgrades) {
139                         fetch('https://downloads.openwrt.org/.versions.json')
140                         .then(response => response.json())
141                         .then(async (versions) => {
142                                 var label = null;
143                                 var new_version = null;
144
145                                 const installed_version = boardinfo?.release?.version;
146                                 const prev_version = versions?.oldstable_version;
147                                 const curr_version = versions?.stable_version;
148                                 const next_version = versions?.upcoming_version;  // Only available during "rc".
149
150                                 if (shouldUpgrade(installed_version, prev_version)) {
151                                         // On old branch, and a newer 'old stable' is available.
152                                         label = 'old stable';
153                                         new_version = prev_version;
154                                 } else if (shouldUpgrade(installed_version, curr_version)) {
155                                         // On old stable or current branch, and newer stable is available.
156                                         label = 'stable';
157                                         new_version = curr_version;
158                                 } else if (shouldUpgrade(installed_version, next_version)) {
159                                         // On current stable or rc branch, a newer rc is available.
160                                         label = 'release candidate';
161                                         new_version = next_version;
162                                 }
163
164                                 if (new_version) {
165                                         (async function(label, new_version) {
166                                                 const [available, upgrade_info] = await checkDeviceAvailable(boardinfo, new_version);
167                                                 if (available && upgrade_info)
168                                                         showUpgradeNotification(label, boardinfo, new_version, upgrade_info);
169                                         })(label, new_version);
170                                 }
171
172                         })
173                         .catch(error => {
174                                         console.error('Failed to fetch firmware upgrade version information:', error);
175                         });
176                 }
177         },
178
179         render: function(data) {
180                 const check_upgrades = uci.get_bool('luci', 'main', 'check_for_newer_firmwares') ?? false;
181                 const isReadonlyView = !L.hasViewPermission();
182
183                 let perform_check_pref = E('input', { type: 'checkbox', 'click': L.bind(this.handleSetUpgradeCheck, this, !check_upgrades), });
184                 perform_check_pref.checked = check_upgrades;
185
186                 let perform_check_pref_p = E('div', [_('Look online for upgrades upon status page load') + ' ', perform_check_pref]);
187
188                 return E('div', [!isReadonlyView ? perform_check_pref_p : '']);
189         }
190 });
git clone https://git.99rst.org/PROJECT