Skip to content

Commit

Permalink
Corrected admin updater: #2437
Browse files Browse the repository at this point in the history
  • Loading branch information
GermanBluefox committed Apr 20, 2024
1 parent fb8a69e commit 71b091d
Showing 1 changed file with 112 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,51 @@ import {
Refresh as ReloadIcon,
} from '@mui/icons-material';

import { I18n } from '@iobroker/adapter-react-v5';
import { I18n, type AdminConnection } from '@iobroker/adapter-react-v5';

class AdminUpdater extends Component {
constructor(props) {
interface WebserverParameters {
useHttps: boolean;
port: number;
certPrivateName?: string;
certPublicName?: string;
}

interface ServerResponse {
running: boolean;
stderr: string[];
stdout: string[];
success?: boolean;
}

interface AdminUpdaterProps {
socket: AdminConnection;
host: string;
onClose: () => void;
version: string;
adminInstance: string;
onUpdating: (updating: boolean) => void;
themeType: string;
}

interface AdminUpdaterState {
response: any;
error: any;
starting: boolean;
upAgain: boolean;
}

class AdminUpdater extends Component<AdminUpdaterProps, AdminUpdaterState> {
private updating: boolean;

private interval: ReturnType<typeof setInterval>;

private startTimeout: ReturnType<typeof setTimeout>;

private readonly textareaRef: React.RefObject<HTMLTextAreaElement>;

private readonly link: string;

constructor(props: AdminUpdaterProps) {
super(props);

this.state = {
Expand All @@ -38,21 +79,17 @@ class AdminUpdater extends Component {
this.link = `${window.location.protocol}//${window.location.host}/`;
}

setUpdating(updating) {
setUpdating(updating: boolean) {
if (this.updating !== updating) {
this.updating = updating;
this.props.onUpdating(updating);
}
}

/** @typedef {{ useHttps: boolean, port: number, certPrivateName?: string, certPublicName?: string }} WebserverParameters */

/**
* Get the webserver configuration of the current admin instance
*
* @return {Promise<WebserverParameters>}
*/
async getWebserverParams() {
async getWebserverParams(): Promise<WebserverParameters> {
const obj = await this.props.socket.getObject(`system.adapter.${this.props.adminInstance}`);

return {
Expand Down Expand Up @@ -99,8 +136,6 @@ class AdminUpdater extends Component {
this.startTimeout = null;
}

/** @typedef {{ running: boolean; stderr: string[]; stdout: string[]; success?: boolean }} ServerResponse */

async checkStatus() {
console.log(`Request update status from: ${this.link}`);
try {
Expand All @@ -110,35 +145,43 @@ class AdminUpdater extends Component {
const plainBody = await res.text();
console.log(`Received status: ${plainBody}`);

/** @type {ServerResponse} */
const response = await res.json();
// sometimes stderr has only one empty string in it
if (response?.stderr) {
response.stderr = response.stderr.filter(line => line.trim());
}
if (response && !response.running && response.success && response.stdout) {
response.stdout.push('');
response.stdout.push('---------------------------------------------------');
response.stdout.push(I18n.t('%s was successfully updated to %s', 'admin', this.props.version));
} else if (response?.stdout) {
response.stdout.unshift('');
response.stdout.unshift('---------------------------------------------------');
response.stdout.unshift(I18n.t('updating %s to %s...', 'admin', this.props.version));
}
this.setState({ response, error: null }, async () => {
if (response && !response.running) {
this.interval && clearInterval(this.interval);
this.interval = null;
this.waitForAdapterStart();
} else if (response?.running) {
this.setUpdating(true);
}
if (plainBody.startsWith('{') && plainBody.endsWith('}')) {
try {
const response: ServerResponse = JSON.parse(plainBody) as ServerResponse;
// sometimes stderr has only one empty string in it
if (response?.stderr) {
response.stderr = response.stderr.filter(line => line.trim());
}
if (response && !response.running && response.success && response.stdout) {
response.stdout.push('');
response.stdout.push('---------------------------------------------------');
response.stdout.push(I18n.t('%s was successfully updated to %s', 'admin', this.props.version));
} else if (response?.stdout) {
response.stdout.unshift('');
response.stdout.unshift('---------------------------------------------------');
response.stdout.unshift(I18n.t('updating %s to %s...', 'admin', this.props.version));
}
this.setState({ response, error: null }, async () => {
if (response && !response.running) {
this.interval && clearInterval(this.interval);
this.interval = null;
this.waitForAdapterStart();
} else if (response?.running) {
this.setUpdating(true);
}

// scroll down
if (this.textareaRef.current) {
setTimeout(() => (this.textareaRef.current.scrollTop = this.textareaRef.current.scrollHeight), 100);
// scroll down
if (this.textareaRef.current) {
setTimeout(() => (this.textareaRef.current.scrollTop = this.textareaRef.current.scrollHeight), 100);
}
});
} catch (e) {
console.error(`Cannot parse response: ${e}`);
this.setState({ error: plainBody }, () => this.setUpdating(false));
}
});
} else {
console.error(`Response is not JSON: ${plainBody}`);
}
} catch (e) {
if (!this.state.starting) {
this.setState({ error: e.toString() }, () => this.setUpdating(false));
Expand Down Expand Up @@ -174,40 +217,36 @@ class AdminUpdater extends Component {
>
<DialogTitle>{I18n.t('Updating %s...', 'admin')}</DialogTitle>
<DialogContent style={{ height: 400, padding: '0 20px', overflow: 'hidden' }}>
{(!this.state.response || this.state.response.running) && !this.state.error ? (
<LinearProgress />
) : null}
{this.state.response || this.state.error ? (
<textarea
ref={this.textareaRef}
style={{
width: '100%',
height: '100%',
resize: 'none',
background: this.props.themeType === 'dark' ? '#000' : '#fff',
color: this.props.themeType === 'dark' ? '#EEE' : '#111',
boxSizing: 'border-box',
fontFamily:
'Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace',
border: this.state.response?.success
? '2px solid green'
: this.state.error ||
(this.state.response &&
!this.state.response.running &&
!this.state.response.success)
? '2px solid red'
: undefined,
}}
value={
this.state.error
? this.state.error
: this.state.response.stderr && this.state.response.stderr.length
? this.state.response.stderr.join('\n')
: this.state.response.stdout.join('\n')
}
readOnly
/>
) : null}
{(!this.state.response || this.state.response.running) && !this.state.error ? <LinearProgress /> : null}
{this.state.response || this.state.error ? <textarea
ref={this.textareaRef}
style={{
width: '100%',
height: '100%',
resize: 'none',
background: this.props.themeType === 'dark' ? '#000' : '#fff',
color: this.props.themeType === 'dark' ? '#EEE' : '#111',
boxSizing: 'border-box',
fontFamily:
'Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace',
border: this.state.response?.success
? '2px solid green'
: this.state.error ||
(this.state.response &&
!this.state.response.running &&
!this.state.response.success)
? '2px solid red'
: undefined,
}}
value={
this.state.error
? this.state.error
: this.state.response.stderr && this.state.response.stderr.length
? this.state.response.stderr.join('\n')
: this.state.response.stdout.join('\n')
}
readOnly
/> : null}
</DialogContent>
<DialogActions>
<Button
Expand All @@ -220,6 +259,7 @@ class AdminUpdater extends Component {
}
this.props.onClose();
}}
// @ts-expect-error grey is valid color
color={this.state.response?.success ? 'primary' : 'grey'}
startIcon={this.state.response?.success ? <ReloadIcon /> : <CloseIcon />}
>
Expand All @@ -231,14 +271,4 @@ class AdminUpdater extends Component {
}
}

AdminUpdater.propTypes = {
socket: PropTypes.object.isRequired,
host: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
version: PropTypes.string.isRequired,
adminInstance: PropTypes.string.isRequired,
onUpdating: PropTypes.func.isRequired,
themeType: PropTypes.string,
};

export default AdminUpdater;

0 comments on commit 71b091d

Please sign in to comment.