dashboard/store/github.js

254 lines
5.5 KiB
JavaScript

import dayjs from 'dayjs';
import { addParam, parseLinkHeader } from '@/utils/url';
import { addObjects, isArray } from '@/utils/array';
import { GITHUB_REPOS, GITHUB_SCOPES, _DATE } from '@/config/local-storage';
const API_BASE = 'https://api.github.com/';
export const BASE_SCOPES = ['read:org'];
export const EXTENDED_SCOPES = ['repo'];
export const DOCKERFILE = /^Dockerfile(\..*)?$/i;
export const YAML_FILE = /^.*\.ya?ml$/i;
function getFromStorage(key) {
if ( process.server ) {
return null;
}
const cached = window.localStorage.getItem(key);
if ( cached ) {
try {
const parsed = JSON.parse(cached);
return parsed;
} catch (e) {}
}
}
function getCachedRepos() {
const cached = getFromStorage(GITHUB_REPOS);
return cached || [];
}
function getCachedScopes() {
const cached = getFromStorage(GITHUB_SCOPES);
return cached;
}
function hasCached() {
if ( process.server ) {
return false;
}
const cached = window.localStorage.getItem(GITHUB_REPOS);
return !!cached;
}
function cacheExpired() {
if ( process.server ) {
return false;
}
const updated = window.localStorage.getItem(GITHUB_REPOS + _DATE);
if ( updated && dayjs().diff(updated) <= 60 * 60 * 1000 ) {
return false;
}
return true;
}
function setCache(repos, scopes) {
if ( repos ) {
window.localStorage.setItem(GITHUB_REPOS, JSON.stringify(repos));
window.localStorage.setItem(GITHUB_REPOS + _DATE, (new Date()).toISOString());
}
if ( scopes ) {
window.localStorage.setItem(GITHUB_SCOPES, JSON.stringify(scopes));
}
}
function forgetCache() {
window.localStorage.removeItem(GITHUB_REPOS);
window.localStorage.removeItem(GITHUB_REPOS + _DATE);
window.localStorage.removeItem(GITHUB_SCOPES);
}
function proxifyUrl(url) {
// Strip off absolute links to github API
if ( url.startsWith(API_BASE) ) {
url = url.substr(API_BASE.length);
}
// Add our proxy prefix
url = `/v1/github/${ url.replace(/^\/+/, '') }`;
// Less pages please
addParam(url, 'per_page', 100);
return url;
}
export const state = function() {
return {
repos: [],
scopes: []
};
};
export const actions = {
async apiList({ commit, dispatch }, {
url = null,
single = false,
depaginate = true,
onPageFn = null,
objectKey = 'items',
} = {}) {
const out = [];
url = proxifyUrl(url);
while ( true ) {
console.log('Github Request:', url); // eslint-disable-line no-console
const res = await dispatch('rancher/request', { url }, { root: true });
const links = parseLinkHeader(res._headers['link']);
const scopes = res._headers['x-oauth-scopes'];
if ( scopes ) {
commit('setScopes', scopes.split(/\s*,\s*/));
}
if ( single ) {
return res;
}
addObjects(out, isArray(res) ? res : res[objectKey]);
if ( onPageFn ) {
onPageFn(out);
}
if ( depaginate && links.next ) {
url = proxifyUrl(links.next);
} else {
break;
}
}
return out;
},
forgetCache() {
forgetCache();
},
async fetchRecentRepos({ commit, dispatch }, { allowCache = true } = {}) {
const cachedScopes = getCachedScopes();
if ( cachedScopes ) {
commit('setScopes', cachedScopes);
}
if ( allowCache && hasCached() ) {
const cached = getCachedRepos();
if ( cacheExpired() ) {
dispatch('fetchRecentRepos', { allowCache: false });
}
return cached;
}
const res = await dispatch('apiList', { url: '/user/repos?sort=updated', depaginate: false });
const more = await dispatch('apiList', { url: '/user/repos?affiliation=owner,collaborator' });
const out = [...res, ...more];
commit('setRepos', out);
return out;
},
async searchRepos({ state, dispatch }, { search }) {
if ( !search ) {
return state.repos.slice();
}
const res = await dispatch('apiList', {
url: `/search/repositories?q=${ escape(search) }`,
depaginate: false
});
return res;
},
async fetchRepoByUrl({ dispatch }, url) {
url = url.replace(/.git$/i, '');
url = url.replace(/https:\/\/github.com\//, '');
url = `/repos/${ url }`;
const res = await dispatch('apiList', { url, single: true });
return res;
},
async fetchBranches({ dispatch }, { repo }) {
const url = repo.branches_url.replace('{/branch}', '');
const res = await dispatch('apiList', { url });
return res;
},
async fetchBranch({ dispatch }, { repo, name }) {
name = name || 'master';
const url = repo.branches_url.replace('{/branch}', `/${ name }`);
const res = await dispatch('apiList', { url, single: true });
return res;
},
async fetchFiles({ dispatch }, { repo, branch, pattern = null }) {
let url = repo.trees_url.replace('{/sha}', `/${ branch.commit.sha }`);
url = addParam(url, 'recursive', 1);
const res = await dispatch('apiList', { url, objectKey: 'tree' });
if ( !pattern ) {
return res;
}
const out = res.filter(file => file.type === 'blob' && file.path.match(pattern));
return out;
},
async fetchFile({ dispatch }, { repo, branch, file }) {
let url = repo.contents_url.replace('{+path}', file);
url = addParam(url, 'ref', branch.commit.sha);
const res = await dispatch('apiList', { url, single: true });
return res;
},
};
export const mutations = {
setScopes(state, scopes) {
state.scopes = scopes;
setCache(null, scopes);
},
setRepos(state, repos) {
state.repos = repos;
setCache(repos);
},
};