Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor validator #182

Merged
merged 4 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions components/ServerInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { observer } from 'mobx-react';
import { useStores } from '../hooks/useStores';
import Colors from '../constants/Colors';
import { getIconName } from '../utils/Icons';
import JellyfinValidator from '../utils/JellyfinValidator';
import { parseUrl, validateServer } from '../utils/ServerValidator';

const sanitizeHost = (url = '') => url.trim();

Expand All @@ -38,7 +38,7 @@ const ServerInput = observer(({ onSuccess, ...props }) => {
// Parse the entered url
let url;
try {
url = JellyfinValidator.parseUrl(host);
url = parseUrl(host);
console.log('parsed url', url);
} catch (err) {
console.info(err);
Expand All @@ -49,7 +49,7 @@ const ServerInput = observer(({ onSuccess, ...props }) => {
}

// Validate the server is available
const validation = await JellyfinValidator.validate({ url });
const validation = await validateServer({ url });
console.log(`Server is ${validation.isValid ? '' : 'not '}valid`);
if (!validation.isValid) {
const message = validation.message || 'invalid';
Expand Down
6 changes: 3 additions & 3 deletions models/ServerModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { action, autorun, computed, decorate, observable } from 'mobx';
import { ignore } from 'mobx-sync';
import { task } from 'mobx-task';

import JellyfinValidator from '../utils/JellyfinValidator';
import { getServerUrl, fetchServerInfo } from '../utils/ServerValidator';

export default class ServerModel {
id
Expand All @@ -34,14 +34,14 @@ export default class ServerModel {

get parseUrlString() {
try {
return JellyfinValidator.getServerUrl(this);
return getServerUrl(this);
} catch (ex) {
return '';
}
}

fetchInfo = task(async () => {
return await JellyfinValidator.fetchServerInfo(this)
return await fetchServerInfo(this)
.then(action(info => {
this.online = true;
this.info = info;
Expand Down
124 changes: 0 additions & 124 deletions utils/JellyfinValidator.js

This file was deleted.

124 changes: 124 additions & 0 deletions utils/ServerValidator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* globals fetch, AbortController */
import Url from 'url';

const TIMEOUT_DURATION = 5000; // timeout request after 5s

export const parseUrl = (host = '', port = '') => {
if (!host) {
throw new Error('host cannot be blank');
}

// Default the protocol to http if not present
// Setting the protocol on the parsed url does not update the href
if (!host.startsWith('http://') && !host.startsWith('https://')) {
host = `http://${host}`;
}

// Parse the host as a url
const url = Url.parse(host);

if (!url.hostname) {
throw new Error(`Could not parse hostname for ${host}`);
}

// Override the port if provided
if (port) {
url.port = port;
}
return url;
};

export const fetchServerInfo = async (server = {}) => {
const serverUrl = server.urlString || getServerUrl(server);
const infoUrl = `${serverUrl}system/info/public`;
console.log('info url', infoUrl);

// Try to fetch the server's public info
const controller = new AbortController();
const { signal } = controller;

const request = fetch(infoUrl, { signal });

const timeoutId = setTimeout(() => {
console.log('request timed out, aborting');
controller.abort();
}, TIMEOUT_DURATION);

const responseJson = await request.then(response => {
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`Error response status [${response.status}] received from ${infoUrl}`);
}
return response.json();
});
console.log('response', responseJson);

return responseJson;
};

export const getServerUrl = (server = {}) => {
if (!server?.url?.href) {
throw new Error('Cannot get server url for invalid server', server);
}

// Strip the query string or hash if present
let serverUrl = server.url.href;
if (server.url.search || server.url.hash) {
const endUrl = server.url.search || server.url.hash;
serverUrl = serverUrl.substring(0, serverUrl.indexOf(endUrl));
}

// Ensure the url ends with /
if (!serverUrl.endsWith('/')) {
serverUrl += '/';
}

console.log('getServerUrl:', serverUrl);
return serverUrl;
};

export const validateServer = async(server = {}) => {
try {
// Does the server have a valid url?
getServerUrl(server);
} catch (err) {
return {
isValid: false,
message: 'invalid'
};
}

try {
const responseJson = await fetchServerInfo(server);

// Versions prior to 10.3.x do not include ProductName so return true if response
// includes Version < 10.3.x and has an Id
if (responseJson.Version) {
const versionNumber = responseJson.Version.split('.').map(num => Number.parseInt(num, 10));
if (versionNumber.length === 3 && versionNumber[0] === 10 && versionNumber[1] < 3) {
thornbill marked this conversation as resolved.
Show resolved Hide resolved
const isValid = responseJson.Id?.length > 0;
console.log('Is valid old version', isValid);
return { isValid };
}
}

const isValid = responseJson.ProductName === 'Jellyfin Server';
const answer = {
isValid
};
if (!isValid) {
answer.message = 'invalidProduct';
}
return answer;
} catch (err) {
return {
isValid: false,
message: 'noConnection'
};
}
};