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