mirror of
https://github.com/SomboChea/ui
synced 2026-01-20 01:55:56 +07:00
chore: sync with 4.x webui
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
if (!__DEBUG__) {
|
||||
__webpack_public_path__ = window.VERDACCIO_API_URL.replace(/\/verdaccio\/$/, '/static/'); // eslint-disable-line
|
||||
__webpack_public_path__ = window.VERDACCIO_API_URL.replace(/\/verdaccio\/$/, '/static/') // eslint-disable-line
|
||||
}
|
||||
|
||||
@@ -2,48 +2,48 @@ import storage from './storage';
|
||||
|
||||
class API {
|
||||
request(url, method = 'GET', options = {}) {
|
||||
if (!window.VERDACCIO_API_URL) {
|
||||
throw new Error('VERDACCIO_API_URL is not defined!');
|
||||
}
|
||||
if (!window.VERDACCIO_API_URL) {
|
||||
throw new Error('VERDACCIO_API_URL is not defined!');
|
||||
}
|
||||
|
||||
const token = storage.getItem('token');
|
||||
if (token) {
|
||||
if (!options.headers) options.headers = {};
|
||||
const token = storage.getItem('token');
|
||||
if (token) {
|
||||
if (!options.headers) options.headers = {};
|
||||
|
||||
options.headers.authorization = `Bearer ${token}`;
|
||||
}
|
||||
options.headers.authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
if (!['http://', 'https://', '//'].some((prefix) => url.startsWith(prefix))) {
|
||||
url = window.VERDACCIO_API_URL + url;
|
||||
}
|
||||
if (!['http://', 'https://', '//'].some((prefix) => url.startsWith(prefix))) {
|
||||
url = window.VERDACCIO_API_URL + url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles response according to content type
|
||||
* @param {object} response
|
||||
* @returns {promise}
|
||||
*/
|
||||
function handleResponseType(response) {
|
||||
if (response.headers) {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType.includes('application/pdf')) {
|
||||
return Promise.all([response.ok, response.blob()]);
|
||||
}
|
||||
if (contentType.includes('application/json')) {
|
||||
return Promise.all([response.ok, response.json()]);
|
||||
}
|
||||
// it includes all text types
|
||||
if (contentType.includes('text/')) {
|
||||
return Promise.all([response.ok, response.text()]);
|
||||
/**
|
||||
* Handles response according to content type
|
||||
* @param {object} response
|
||||
* @returns {promise}
|
||||
*/
|
||||
function handleResponseType(response) {
|
||||
if (response.headers) {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType.includes('application/pdf')) {
|
||||
return Promise.all([response.ok, response.blob()]);
|
||||
}
|
||||
if (contentType.includes('application/json')) {
|
||||
return Promise.all([response.ok, response.json()]);
|
||||
}
|
||||
// it includes all text types
|
||||
if (contentType.includes('text/')) {
|
||||
return Promise.all([response.ok, response.text()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(url, {
|
||||
method,
|
||||
credentials: 'same-origin',
|
||||
...options,
|
||||
})
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(url, {
|
||||
method,
|
||||
credentials: 'same-origin',
|
||||
...options,
|
||||
})
|
||||
.then(handleResponseType)
|
||||
.then(([responseOk, body]) => {
|
||||
if (responseOk) {
|
||||
@@ -52,11 +52,11 @@ class API {
|
||||
reject(body);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new API();
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export function asyncComponent(getComponent) {
|
||||
return class AsyncComponent extends React.Component {
|
||||
static Component = null;
|
||||
state = {Component: AsyncComponent.Component};
|
||||
state = { Component: AsyncComponent.Component };
|
||||
|
||||
componentDidMount() {
|
||||
const {Component} = this.state;
|
||||
|
||||
const { Component } = this.state;
|
||||
if (!Component) {
|
||||
getComponent().then(({default: Component}) => {
|
||||
AsyncComponent.Component = Component;
|
||||
/* eslint react/no-did-mount-set-state:0 */
|
||||
this.setState({Component});
|
||||
});
|
||||
getComponent()
|
||||
.then(({ default: Component }) => {
|
||||
AsyncComponent.Component = Component;
|
||||
/* eslint react/no-did-mount-set-state:0 */
|
||||
this.setState({ Component });
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const {Component} = this.state;
|
||||
const { Component } = this.state;
|
||||
if (Component) {
|
||||
// eslint-disable-next-line verdaccio/jsx-spread
|
||||
return <Component {...this.props} />;
|
||||
}
|
||||
|
||||
|
||||
5
src/utils/file-size.js
Normal file
5
src/utils/file-size.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default function fileSizeSI(a, b, c, d, e) {
|
||||
return (b = Math, c = b.log, d = 1e3, e = c(a) / c(d) | 0, a / b.pow(d, e)).toFixed(2)
|
||||
+ ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes');
|
||||
};
|
||||
|
||||
@@ -1,76 +1,70 @@
|
||||
import isString from "lodash/isString";
|
||||
import isNumber from "lodash/isNumber";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { Base64 } from "js-base64";
|
||||
import API from "./api";
|
||||
|
||||
const HEADERS = {
|
||||
JSON: "application/json",
|
||||
JSON_CHARSET: "application/json; charset=utf-8",
|
||||
OCTET_STREAM: "application/octet-stream; charset=utf-8",
|
||||
TEXT_CHARSET: "text/plain; charset=utf-8",
|
||||
GZIP: "gzip"
|
||||
};
|
||||
import isString from 'lodash/isString';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import {Base64} from 'js-base64';
|
||||
import API from './api';
|
||||
import {HEADERS} from '../../lib/constants';
|
||||
|
||||
export function isTokenExpire(token) {
|
||||
if (!isString(token)) {
|
||||
return true;
|
||||
}
|
||||
if (!isString(token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let [, payload] = token.split(".");
|
||||
let [,payload] = token.split('.');
|
||||
|
||||
if (!payload) {
|
||||
return true;
|
||||
}
|
||||
if (!payload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
payload = JSON.parse(Base64.decode(payload));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line
|
||||
console.error("Invalid token:", error, token);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
payload = JSON.parse(Base64.decode(payload));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line
|
||||
console.error('Invalid token:', error, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!payload.exp || !isNumber(payload.exp)) {
|
||||
return true;
|
||||
}
|
||||
// Report as expire before (real expire time - 30s)
|
||||
const jsTimestamp = payload.exp * 1000 - 30000;
|
||||
const expired = Date.now() >= jsTimestamp;
|
||||
if (!payload.exp || !isNumber(payload.exp)) {
|
||||
return true;
|
||||
}
|
||||
// Report as expire before (real expire time - 30s)
|
||||
const jsTimestamp = (payload.exp * 1000) - 30000;
|
||||
const expired = Date.now() >= jsTimestamp;
|
||||
|
||||
return expired;
|
||||
return expired;
|
||||
}
|
||||
|
||||
|
||||
export async function makeLogin(username, password) {
|
||||
// checks isEmpty
|
||||
if (isEmpty(username) || isEmpty(password)) {
|
||||
const error = {
|
||||
title: "Unable to login",
|
||||
type: "error",
|
||||
description: "Username or password can't be empty!"
|
||||
};
|
||||
return { error };
|
||||
}
|
||||
// checks isEmpty
|
||||
if (isEmpty(username) || isEmpty(password)) {
|
||||
const error = {
|
||||
title: 'Unable to login',
|
||||
type: 'error',
|
||||
description: 'Username or password can\'t be empty!',
|
||||
};
|
||||
return {error};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await API.request("login", "POST", {
|
||||
body: JSON.stringify({ username, password }),
|
||||
headers: {
|
||||
Accept: HEADERS.JSON,
|
||||
"Content-Type": HEADERS.JSON
|
||||
}
|
||||
});
|
||||
const result = {
|
||||
username: response.username,
|
||||
token: response.token
|
||||
};
|
||||
return result;
|
||||
} catch (e) {
|
||||
const error = {
|
||||
title: "Unable to login",
|
||||
type: "error",
|
||||
description: e.error
|
||||
};
|
||||
return { error };
|
||||
}
|
||||
try {
|
||||
const response = await API.request('login', 'POST', {
|
||||
body: JSON.stringify({username, password}),
|
||||
headers: {
|
||||
Accept: HEADERS.JSON,
|
||||
'Content-Type': HEADERS.JSON,
|
||||
},
|
||||
});
|
||||
const result = {
|
||||
username: response.username,
|
||||
token: response.token,
|
||||
};
|
||||
return result;
|
||||
} catch (e) {
|
||||
const error = {
|
||||
title: 'Unable to login',
|
||||
type: 'error',
|
||||
description: e.error,
|
||||
};
|
||||
return {error};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import format from 'date-fns/format';
|
||||
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
|
||||
|
||||
export const TIMEFORMAT = 'DD.MM.YYYY, HH:mm:ss';
|
||||
export const DEFAULT_USER = 'Anonymous';
|
||||
|
||||
/**
|
||||
* Formats license field for webui.
|
||||
@@ -39,36 +38,6 @@ export function formatRepository(repository) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Formats author field for webui.
|
||||
* @see https://docs.npmjs.com/files/package.json#author
|
||||
*/
|
||||
export function formatAuthor(author) {
|
||||
let authorDetails = {
|
||||
name: DEFAULT_USER,
|
||||
email: '',
|
||||
avatar: '',
|
||||
};
|
||||
|
||||
if (isString(author)) {
|
||||
authorDetails = {
|
||||
...authorDetails,
|
||||
name: author ? author : authorDetails.name,
|
||||
};
|
||||
}
|
||||
|
||||
if (isObject(author)) {
|
||||
authorDetails = {
|
||||
...authorDetails,
|
||||
name: author.name ? author.name : authorDetails.name,
|
||||
email: author.email ? author.email : authorDetails.email,
|
||||
avatar: author.avatar ? author.avatar : authorDetails.avatar,
|
||||
};
|
||||
}
|
||||
|
||||
return authorDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* For <LastSync /> component
|
||||
* @param {array} uplinks
|
||||
|
||||
@@ -10,7 +10,10 @@ const colors = {
|
||||
grey: '#808080',
|
||||
greySuperLight: '#f5f5f5',
|
||||
greyLight: '#d3d3d3',
|
||||
greyLight2: '#908ba1',
|
||||
greyLight3: '#f3f4f240',
|
||||
greyDark: '#a9a9a9',
|
||||
greyDark2: '#586069',
|
||||
greyChateau: '#95989a',
|
||||
greyGainsboro: '#e3e3e3',
|
||||
greyAthens: '#d3dddd',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { css } from 'emotion';
|
||||
|
||||
const breakpoints = {
|
||||
export const breakpoints = {
|
||||
small: 576,
|
||||
medium: 768,
|
||||
large: 1024,
|
||||
|
||||
@@ -2,11 +2,3 @@ export function getRegistryURL() {
|
||||
// Don't add slash if it's not a sub directory
|
||||
return `${location.origin}${location.pathname === '/' ? '' : location.pathname}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specified package detail page url
|
||||
* @param {string} packageName
|
||||
*/
|
||||
export function getDetailPageURL(packageName) {
|
||||
return `${getRegistryURL()}/-/web/version/${packageName}`;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export function goToVerdaccioWebsite() {
|
||||
window.open('https://www.verdaccio.org/', '_blank');
|
||||
window.open('https://www.verdaccio.org/', '_blank');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user