Fail more softly on homeserver liveliness errors
This performs liveliness checks on the auth pages to try and show a friendlier error. Earlier checks in the app startup are expected to not block the app from loading on such failures. See https://github.com/vector-im/riot-web/issues/9828
This commit is contained in:
parent
b412103f21
commit
e2fdeec71a
8 changed files with 257 additions and 33 deletions
|
@ -22,7 +22,7 @@ import sdk from '../../../index';
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import PasswordReset from "../../../PasswordReset";
|
import PasswordReset from "../../../PasswordReset";
|
||||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||||
|
|
||||||
// Phases
|
// Phases
|
||||||
// Show controls to configure server details
|
// Show controls to configure server details
|
||||||
|
@ -53,9 +53,40 @@ module.exports = React.createClass({
|
||||||
password: "",
|
password: "",
|
||||||
password2: "",
|
password2: "",
|
||||||
errorText: null,
|
errorText: null,
|
||||||
|
|
||||||
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
|
// We also track the server dead errors independently of the regular errors so
|
||||||
|
// that we can render it differently, and override any other error the user may
|
||||||
|
// be seeing.
|
||||||
|
serverIsAlive: true,
|
||||||
|
serverDeadError: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this._checkServerLiveliness(this.props.serverConfig);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: async function(newProps) {
|
||||||
|
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||||
|
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||||
|
|
||||||
|
// Do a liveliness check on the new URLs
|
||||||
|
this._checkServerLiveliness(newProps.serverConfig);
|
||||||
|
},
|
||||||
|
|
||||||
|
_checkServerLiveliness: async function(serverConfig) {
|
||||||
|
try {
|
||||||
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||||
|
serverConfig.hsUrl,
|
||||||
|
serverConfig.isUrl,
|
||||||
|
);
|
||||||
|
this.setState({serverIsAlive: true});
|
||||||
|
} catch (e) {
|
||||||
|
this.setState(AutoDiscoveryUtils.authComponentStateForError(e));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
submitPasswordReset: function(email, password) {
|
submitPasswordReset: function(email, password) {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SENDING_EMAIL,
|
phase: PHASE_SENDING_EMAIL,
|
||||||
|
@ -89,6 +120,8 @@ module.exports = React.createClass({
|
||||||
onSubmitForm: function(ev) {
|
onSubmitForm: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if (!this.state.serverIsAlive) return;
|
||||||
|
|
||||||
if (!this.state.email) {
|
if (!this.state.email) {
|
||||||
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
||||||
} else if (!this.state.password || !this.state.password2) {
|
} else if (!this.state.password || !this.state.password2) {
|
||||||
|
@ -173,11 +206,21 @@ module.exports = React.createClass({
|
||||||
const Field = sdk.getComponent('elements.Field');
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
|
||||||
let errorText = null;
|
let errorText = null;
|
||||||
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
const err = this.state.errorText;
|
||||||
if (err) {
|
if (err) {
|
||||||
errorText = <div className="mx_Login_error">{ err }</div>;
|
errorText = <div className="mx_Login_error">{ err }</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let serverDeadSection;
|
||||||
|
if (!this.state.serverIsAlive) {
|
||||||
|
// TODO: TravisR - Design from Nad
|
||||||
|
serverDeadSection = (
|
||||||
|
<div className="mx_Login_error">
|
||||||
|
{this.state.serverDeadError}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
|
let yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
|
||||||
serverName: this.props.serverConfig.hsName,
|
serverName: this.props.serverConfig.hsName,
|
||||||
});
|
});
|
||||||
|
@ -207,11 +250,12 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
|
{errorText}
|
||||||
|
{serverDeadSection}
|
||||||
<h3>
|
<h3>
|
||||||
{yourMatrixAccountText}
|
{yourMatrixAccountText}
|
||||||
{editLink}
|
{editLink}
|
||||||
</h3>
|
</h3>
|
||||||
{errorText}
|
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<Field
|
<Field
|
||||||
|
@ -246,7 +290,12 @@ module.exports = React.createClass({
|
||||||
'A verification email will be sent to your inbox to confirm ' +
|
'A verification email will be sent to your inbox to confirm ' +
|
||||||
'setting your new password.',
|
'setting your new password.',
|
||||||
)}</span>
|
)}</span>
|
||||||
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
|
<input
|
||||||
|
className="mx_Login_submit"
|
||||||
|
type="submit"
|
||||||
|
value={_t('Send Reset Email')}
|
||||||
|
disabled={!this.state.serverIsAlive}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
||||||
{_t('Sign in instead')}
|
{_t('Sign in instead')}
|
||||||
|
|
|
@ -94,6 +94,13 @@ module.exports = React.createClass({
|
||||||
phase: PHASE_LOGIN,
|
phase: PHASE_LOGIN,
|
||||||
// The current login flow, such as password, SSO, etc.
|
// The current login flow, such as password, SSO, etc.
|
||||||
currentFlow: "m.login.password",
|
currentFlow: "m.login.password",
|
||||||
|
|
||||||
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
|
// We also track the server dead errors independently of the regular errors so
|
||||||
|
// that we can render it differently, and override any other error the user may
|
||||||
|
// be seeing.
|
||||||
|
serverIsAlive: true,
|
||||||
|
serverDeadError: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -233,7 +240,7 @@ module.exports = React.createClass({
|
||||||
username: username,
|
username: username,
|
||||||
busy: doWellknownLookup, // unset later by the result of onServerConfigChange
|
busy: doWellknownLookup, // unset later by the result of onServerConfigChange
|
||||||
errorText: null,
|
errorText: null,
|
||||||
canTryLogin: true,
|
canTryLogin: this.state.serverIsAlive,
|
||||||
});
|
});
|
||||||
if (doWellknownLookup) {
|
if (doWellknownLookup) {
|
||||||
const serverName = username.split(':').slice(1).join(':');
|
const serverName = username.split(':').slice(1).join(':');
|
||||||
|
@ -247,7 +254,19 @@ module.exports = React.createClass({
|
||||||
if (e.translatedMessage) {
|
if (e.translatedMessage) {
|
||||||
message = e.translatedMessage;
|
message = e.translatedMessage;
|
||||||
}
|
}
|
||||||
this.setState({errorText: message, busy: false, canTryLogin: false});
|
|
||||||
|
let errorText = message;
|
||||||
|
let discoveryState = {};
|
||||||
|
if (AutoDiscoveryUtils.isLivelinessError(e)) {
|
||||||
|
errorText = this.state.errorText;
|
||||||
|
discoveryState = this._stateForDiscoveryError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
errorText,
|
||||||
|
...discoveryState,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -272,7 +291,7 @@ module.exports = React.createClass({
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText: null,
|
errorText: null,
|
||||||
canTryLogin: true,
|
canTryLogin: this.state.serverIsAlive,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -297,13 +316,25 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_initLoginLogic: function(hsUrl, isUrl) {
|
_stateForDiscoveryError: function(err) {
|
||||||
const self = this;
|
return {
|
||||||
|
canTryLogin: false,
|
||||||
|
...AutoDiscoveryUtils.authComponentStateForError(err),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
_initLoginLogic: async function(hsUrl, isUrl) {
|
||||||
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
|
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
|
||||||
isUrl = isUrl || this.props.serverConfig.isUrl;
|
isUrl = isUrl || this.props.serverConfig.isUrl;
|
||||||
|
|
||||||
// TODO: TravisR - Only use this if the homeserver is the default homeserver
|
let isDefaultServer = false;
|
||||||
const fallbackHsUrl = this.props.fallbackHsUrl;
|
if (this.props.serverConfig.isDefault
|
||||||
|
&& hsUrl === this.props.serverConfig.hsUrl
|
||||||
|
&& isUrl === this.props.serverConfig.isUrl) {
|
||||||
|
isDefaultServer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackHsUrl = isDefaultServer ? this.props.fallbackHsUrl : null;
|
||||||
|
|
||||||
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
|
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
|
||||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||||
|
@ -315,6 +346,19 @@ module.exports = React.createClass({
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Do a quick liveliness check on the URLs
|
||||||
|
try {
|
||||||
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl);
|
||||||
|
this.setState({serverIsAlive: true, errorText: "", canTryLogin: true});
|
||||||
|
} catch (e) {
|
||||||
|
const discoveryState = this._stateForDiscoveryError(e);
|
||||||
|
this.setState({
|
||||||
|
busy: false,
|
||||||
|
...discoveryState,
|
||||||
|
});
|
||||||
|
return; // Server is dead - do not continue.
|
||||||
|
}
|
||||||
|
|
||||||
loginLogic.getFlows().then((flows) => {
|
loginLogic.getFlows().then((flows) => {
|
||||||
// look for a flow where we understand all of the steps.
|
// look for a flow where we understand all of the steps.
|
||||||
for (let i = 0; i < flows.length; i++ ) {
|
for (let i = 0; i < flows.length; i++ ) {
|
||||||
|
@ -339,14 +383,14 @@ module.exports = React.createClass({
|
||||||
"supported by this client.",
|
"supported by this client.",
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, (err) => {
|
||||||
self.setState({
|
this.setState({
|
||||||
errorText: self._errorTextFromError(err),
|
errorText: this._errorTextFromError(err),
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
canTryLogin: false,
|
canTryLogin: false,
|
||||||
});
|
});
|
||||||
}).finally(function() {
|
}).finally(() => {
|
||||||
self.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
}).done();
|
}).done();
|
||||||
|
@ -485,7 +529,7 @@ module.exports = React.createClass({
|
||||||
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
||||||
loginIncorrect={this.state.loginIncorrect}
|
loginIncorrect={this.state.loginIncorrect}
|
||||||
serverConfig={this.props.serverConfig}
|
serverConfig={this.props.serverConfig}
|
||||||
disableSubmit={this.isBusy()}
|
disableSubmit={this.isBusy() || !this.state.serverIsAlive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -522,6 +566,16 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let serverDeadSection;
|
||||||
|
if (!this.state.serverIsAlive) {
|
||||||
|
// TODO: TravisR - Design from Nad
|
||||||
|
serverDeadSection = (
|
||||||
|
<div className="mx_Login_error">
|
||||||
|
{this.state.serverDeadError}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
<AuthHeader />
|
<AuthHeader />
|
||||||
|
@ -531,6 +585,7 @@ module.exports = React.createClass({
|
||||||
{loader}
|
{loader}
|
||||||
</h2>
|
</h2>
|
||||||
{ errorTextSection }
|
{ errorTextSection }
|
||||||
|
{ serverDeadSection }
|
||||||
{ this.renderServerComponent() }
|
{ this.renderServerComponent() }
|
||||||
{ this.renderLoginComponentForStep() }
|
{ this.renderLoginComponentForStep() }
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onRegisterClick} href="#">
|
<a className="mx_AuthBody_changeFlow" onClick={this.onRegisterClick} href="#">
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { _t, _td } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||||
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
||||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||||
|
|
||||||
// Phases
|
// Phases
|
||||||
// Show controls to configure server details
|
// Show controls to configure server details
|
||||||
|
@ -79,6 +79,13 @@ module.exports = React.createClass({
|
||||||
// Phase of the overall registration dialog.
|
// Phase of the overall registration dialog.
|
||||||
phase: PHASE_REGISTRATION,
|
phase: PHASE_REGISTRATION,
|
||||||
flows: null,
|
flows: null,
|
||||||
|
|
||||||
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
|
// We also track the server dead errors independently of the regular errors so
|
||||||
|
// that we can render it differently, and override any other error the user may
|
||||||
|
// be seeing.
|
||||||
|
serverIsAlive: true,
|
||||||
|
serverDeadError: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -152,6 +159,19 @@ module.exports = React.createClass({
|
||||||
errorText: null,
|
errorText: null,
|
||||||
});
|
});
|
||||||
if (!serverConfig) serverConfig = this.props.serverConfig;
|
if (!serverConfig) serverConfig = this.props.serverConfig;
|
||||||
|
|
||||||
|
// Do a liveliness check on the URLs
|
||||||
|
try {
|
||||||
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||||
|
serverConfig.hsUrl,
|
||||||
|
serverConfig.isUrl,
|
||||||
|
);
|
||||||
|
this.setState({serverIsAlive: true});
|
||||||
|
} catch (e) {
|
||||||
|
this.setState(AutoDiscoveryUtils.authComponentStateForError(e));
|
||||||
|
return; // Server is dead - do not continue.
|
||||||
|
}
|
||||||
|
|
||||||
const {hsUrl, isUrl} = serverConfig;
|
const {hsUrl, isUrl} = serverConfig;
|
||||||
this._matrixClient = Matrix.createClient({
|
this._matrixClient = Matrix.createClient({
|
||||||
baseUrl: hsUrl,
|
baseUrl: hsUrl,
|
||||||
|
@ -447,6 +467,7 @@ module.exports = React.createClass({
|
||||||
onEditServerDetailsClick={onEditServerDetailsClick}
|
onEditServerDetailsClick={onEditServerDetailsClick}
|
||||||
flows={this.state.flows}
|
flows={this.state.flows}
|
||||||
serverConfig={this.props.serverConfig}
|
serverConfig={this.props.serverConfig}
|
||||||
|
canSubmit={this.state.serverIsAlive}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -462,6 +483,16 @@ module.exports = React.createClass({
|
||||||
errorText = <div className="mx_Login_error">{ err }</div>;
|
errorText = <div className="mx_Login_error">{ err }</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let serverDeadSection;
|
||||||
|
if (!this.state.serverIsAlive) {
|
||||||
|
// TODO: TravisR - Design from Nad
|
||||||
|
serverDeadSection = (
|
||||||
|
<div className="mx_Login_error">
|
||||||
|
{this.state.serverDeadError}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const signIn = <a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
const signIn = <a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
||||||
{ _t('Sign in instead') }
|
{ _t('Sign in instead') }
|
||||||
</a>;
|
</a>;
|
||||||
|
@ -480,6 +511,7 @@ module.exports = React.createClass({
|
||||||
<AuthBody>
|
<AuthBody>
|
||||||
<h2>{ _t('Create your account') }</h2>
|
<h2>{ _t('Create your account') }</h2>
|
||||||
{ errorText }
|
{ errorText }
|
||||||
|
{ serverDeadSection }
|
||||||
{ this.renderServerComponent() }
|
{ this.renderServerComponent() }
|
||||||
{ this.renderRegisterComponent() }
|
{ this.renderRegisterComponent() }
|
||||||
{ goBack }
|
{ goBack }
|
||||||
|
|
|
@ -108,6 +108,8 @@ export default class ModularServerConfig extends React.PureComponent {
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: message,
|
errorText: message,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +134,8 @@ export default class ModularServerConfig extends React.PureComponent {
|
||||||
onSubmit = async (ev) => {
|
onSubmit = async (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
await this.validateServer();
|
const result = await this.validateServer();
|
||||||
|
if (!result) return; // Do not continue.
|
||||||
|
|
||||||
if (this.props.onAfterSubmit) {
|
if (this.props.onAfterSubmit) {
|
||||||
this.props.onAfterSubmit();
|
this.props.onAfterSubmit();
|
||||||
|
|
|
@ -53,11 +53,13 @@ module.exports = React.createClass({
|
||||||
onEditServerDetailsClick: PropTypes.func,
|
onEditServerDetailsClick: PropTypes.func,
|
||||||
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
|
canSubmit: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
onValidationChange: console.error,
|
onValidationChange: console.error,
|
||||||
|
canSubmit: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -80,6 +82,8 @@ module.exports = React.createClass({
|
||||||
onSubmit: async function(ev) {
|
onSubmit: async function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
if (!this.props.canSubmit) return;
|
||||||
|
|
||||||
const allFieldsValid = await this.verifyFieldsBeforeSubmit();
|
const allFieldsValid = await this.verifyFieldsBeforeSubmit();
|
||||||
if (!allFieldsValid) {
|
if (!allFieldsValid) {
|
||||||
return;
|
return;
|
||||||
|
@ -540,7 +544,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerButton = (
|
const registerButton = (
|
||||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} />
|
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -109,6 +109,8 @@ export default class ServerConfig extends React.PureComponent {
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: message,
|
errorText: message,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +139,8 @@ export default class ServerConfig extends React.PureComponent {
|
||||||
onSubmit = async (ev) => {
|
onSubmit = async (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
await this.validateServer();
|
const result = await this.validateServer();
|
||||||
|
if (!result) return; // Do not continue.
|
||||||
|
|
||||||
if (this.props.onAfterSubmit) {
|
if (this.props.onAfterSubmit) {
|
||||||
this.props.onAfterSubmit();
|
this.props.onAfterSubmit();
|
||||||
|
|
|
@ -249,8 +249,11 @@
|
||||||
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
|
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
|
||||||
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
|
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
|
||||||
"%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …",
|
"%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …",
|
||||||
|
"Server failed liveliness check": "Server failed liveliness check",
|
||||||
|
"Server failed syntax check": "Server failed syntax check",
|
||||||
"No homeserver URL provided": "No homeserver URL provided",
|
"No homeserver URL provided": "No homeserver URL provided",
|
||||||
"Unexpected error resolving homeserver configuration": "Unexpected error resolving homeserver configuration",
|
"Unexpected error resolving homeserver configuration": "Unexpected error resolving homeserver configuration",
|
||||||
|
"Unexpected error resolving identity server configuration": "Unexpected error resolving identity server configuration",
|
||||||
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
|
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
|
||||||
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
|
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
|
||||||
"Please <a>contact your service administrator</a> to continue using the service.": "Please <a>contact your service administrator</a> to continue using the service.",
|
"Please <a>contact your service administrator</a> to continue using the service.": "Please <a>contact your service administrator</a> to continue using the service.",
|
||||||
|
@ -304,7 +307,6 @@
|
||||||
"Custom user status messages": "Custom user status messages",
|
"Custom user status messages": "Custom user status messages",
|
||||||
"Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)",
|
"Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)",
|
||||||
"Render simple counters in room header": "Render simple counters in room header",
|
"Render simple counters in room header": "Render simple counters in room header",
|
||||||
"Custom Notification Sounds": "Custom Notification Sounds",
|
|
||||||
"Edit messages after they have been sent (refresh to apply changes)": "Edit messages after they have been sent (refresh to apply changes)",
|
"Edit messages after they have been sent (refresh to apply changes)": "Edit messages after they have been sent (refresh to apply changes)",
|
||||||
"React to messages with emoji (refresh to apply changes)": "React to messages with emoji (refresh to apply changes)",
|
"React to messages with emoji (refresh to apply changes)": "React to messages with emoji (refresh to apply changes)",
|
||||||
"Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
|
"Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
|
||||||
|
|
|
@ -15,10 +15,15 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AutoDiscovery} from "matrix-js-sdk";
|
import {AutoDiscovery} from "matrix-js-sdk";
|
||||||
import {_td, newTranslatableError} from "../languageHandler";
|
import {_t, _td, newTranslatableError} from "../languageHandler";
|
||||||
import {makeType} from "./TypeUtils";
|
import {makeType} from "./TypeUtils";
|
||||||
import SdkConfig from "../SdkConfig";
|
import SdkConfig from "../SdkConfig";
|
||||||
|
|
||||||
|
const LIVLINESS_DISCOVERY_ERRORS = [
|
||||||
|
AutoDiscovery.ERROR_INVALID_HOMESERVER,
|
||||||
|
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER,
|
||||||
|
];
|
||||||
|
|
||||||
export class ValidatedServerConfig {
|
export class ValidatedServerConfig {
|
||||||
hsUrl: string;
|
hsUrl: string;
|
||||||
hsName: string;
|
hsName: string;
|
||||||
|
@ -31,7 +36,52 @@ export class ValidatedServerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AutoDiscoveryUtils {
|
export default class AutoDiscoveryUtils {
|
||||||
static async validateServerConfigWithStaticUrls(homeserverUrl: string, identityUrl: string): ValidatedServerConfig {
|
/**
|
||||||
|
* Checks if a given error or error message is considered an error
|
||||||
|
* relating to the liveliness of the server. Must be an error returned
|
||||||
|
* from this AutoDiscoveryUtils class.
|
||||||
|
* @param {string|Error} error The error to check
|
||||||
|
* @returns {boolean} True if the error is a liveliness error.
|
||||||
|
*/
|
||||||
|
static isLivelinessError(error: string|Error): boolean {
|
||||||
|
if (!error) return false;
|
||||||
|
return !!LIVLINESS_DISCOVERY_ERRORS.find(e => e === error || e === error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the common state for auth components (login, registration, forgot
|
||||||
|
* password) for a given validation error.
|
||||||
|
* @param {Error} err The error encountered.
|
||||||
|
* @returns {{serverDeadError: (string|*), serverIsAlive: boolean}} The state
|
||||||
|
* for the component, given the error.
|
||||||
|
*/
|
||||||
|
static authComponentStateForError(err: Error): {serverIsAlive: boolean, serverDeadError: string} {
|
||||||
|
if (AutoDiscoveryUtils.isLivelinessError(err)) {
|
||||||
|
// TODO: TravisR - Copy from Nad
|
||||||
|
return {
|
||||||
|
serverIsAlive: false,
|
||||||
|
serverDeadError: _t("Server failed liveliness check"),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// TODO: TravisR - Copy from Nad
|
||||||
|
return {
|
||||||
|
serverIsAlive: false,
|
||||||
|
serverDeadError: _t("Server failed syntax check"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a server configuration, using a pair of URLs as input.
|
||||||
|
* @param {string} homeserverUrl The homeserver URL.
|
||||||
|
* @param {string} identityUrl The identity server URL.
|
||||||
|
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
|
||||||
|
* not be raised.
|
||||||
|
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
||||||
|
*/
|
||||||
|
static async validateServerConfigWithStaticUrls(
|
||||||
|
homeserverUrl: string, identityUrl: string, syntaxOnly = false): ValidatedServerConfig {
|
||||||
|
|
||||||
if (!homeserverUrl) {
|
if (!homeserverUrl) {
|
||||||
throw newTranslatableError(_td("No homeserver URL provided"));
|
throw newTranslatableError(_td("No homeserver URL provided"));
|
||||||
}
|
}
|
||||||
|
@ -50,15 +100,33 @@ export default class AutoDiscoveryUtils {
|
||||||
const url = new URL(homeserverUrl);
|
const url = new URL(homeserverUrl);
|
||||||
const serverName = url.hostname;
|
const serverName = url.hostname;
|
||||||
|
|
||||||
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
|
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result, syntaxOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async validateServerName(serverName: string): ValidatedServerConfig {
|
/**
|
||||||
|
* Validates a server configuration, using a homeserver domain name as input.
|
||||||
|
* @param {string} serverName The homeserver domain name (eg: "matrix.org") to validate.
|
||||||
|
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
|
||||||
|
* not be raised.
|
||||||
|
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
||||||
|
*/
|
||||||
|
static async validateServerName(serverName: string, syntaxOnly=false): ValidatedServerConfig {
|
||||||
const result = await AutoDiscovery.findClientConfig(serverName);
|
const result = await AutoDiscovery.findClientConfig(serverName);
|
||||||
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
|
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildValidatedConfigFromDiscovery(serverName: string, discoveryResult): ValidatedServerConfig {
|
/**
|
||||||
|
* Validates a server configuration, using a pre-calculated AutoDiscovery result as
|
||||||
|
* input.
|
||||||
|
* @param {string} serverName The domain name the AutoDiscovery result is for.
|
||||||
|
* @param {*} discoveryResult The AutoDiscovery result.
|
||||||
|
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
|
||||||
|
* not be raised.
|
||||||
|
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
||||||
|
*/
|
||||||
|
static buildValidatedConfigFromDiscovery(
|
||||||
|
serverName: string, discoveryResult, syntaxOnly=false): ValidatedServerConfig {
|
||||||
|
|
||||||
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
|
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
|
||||||
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
|
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
|
||||||
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
|
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
|
||||||
|
@ -68,19 +136,27 @@ export default class AutoDiscoveryUtils {
|
||||||
|
|
||||||
const hsResult = discoveryResult['m.homeserver'];
|
const hsResult = discoveryResult['m.homeserver'];
|
||||||
if (hsResult.state !== AutoDiscovery.SUCCESS) {
|
if (hsResult.state !== AutoDiscovery.SUCCESS) {
|
||||||
if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) {
|
console.error("Error processing homeserver config:", hsResult);
|
||||||
throw newTranslatableError(hsResult.error);
|
if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(hsResult.error)) {
|
||||||
}
|
if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) {
|
||||||
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
|
throw newTranslatableError(hsResult.error);
|
||||||
|
}
|
||||||
|
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
|
||||||
|
} // else the error is not related to syntax - continue anyways.
|
||||||
}
|
}
|
||||||
|
|
||||||
const isResult = discoveryResult['m.identity_server'];
|
const isResult = discoveryResult['m.identity_server'];
|
||||||
let preferredIdentityUrl = "https://vector.im";
|
let preferredIdentityUrl = "https://vector.im"; // We already know this is an IS, so don't validate it.
|
||||||
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
|
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
|
||||||
preferredIdentityUrl = isResult["base_url"];
|
preferredIdentityUrl = isResult["base_url"];
|
||||||
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
|
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
|
||||||
console.error("Error determining preferred identity server URL:", isResult);
|
console.error("Error determining preferred identity server URL:", isResult);
|
||||||
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
|
if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(isResult.error)) {
|
||||||
|
if (AutoDiscovery.ALL_ERRORS.indexOf(isResult.error) !== -1) {
|
||||||
|
throw newTranslatableError(isResult.error);
|
||||||
|
}
|
||||||
|
throw newTranslatableError(_td("Unexpected error resolving identity server configuration"));
|
||||||
|
} // else the error is not related to syntax - continue anyways.
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferredHomeserverUrl = hsResult["base_url"];
|
const preferredHomeserverUrl = hsResult["base_url"];
|
||||||
|
|
Loading…
Reference in a new issue