diff --git a/packages/admin/src/src/dialogs/AdminUpdater.jsx b/packages/admin/src/src/dialogs/AdminUpdater.tsx similarity index 50% rename from packages/admin/src/src/dialogs/AdminUpdater.jsx rename to packages/admin/src/src/dialogs/AdminUpdater.tsx index 0f586a2a6..429cb5342 100644 --- a/packages/admin/src/src/dialogs/AdminUpdater.jsx +++ b/packages/admin/src/src/dialogs/AdminUpdater.tsx @@ -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 { + private updating: boolean; + + private interval: ReturnType; + + private startTimeout: ReturnType; + + private readonly textareaRef: React.RefObject; + + private readonly link: string; + + constructor(props: AdminUpdaterProps) { super(props); this.state = { @@ -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} */ - async getWebserverParams() { + async getWebserverParams(): Promise { const obj = await this.props.socket.getObject(`system.adapter.${this.props.adminInstance}`); return { @@ -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 { @@ -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)); @@ -174,40 +217,36 @@ class AdminUpdater extends Component { > {I18n.t('Updating %s...', 'admin')} - {(!this.state.response || this.state.response.running) && !this.state.error ? ( - - ) : null} - {this.state.response || this.state.error ? ( -