diff --git a/.env.example b/.env.example index e69de29..46da2aa 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,5 @@ +# The client ID you received from GitHub for your GitHub App. +GITHUB_OAUTH_CLIENT_ID=GITHUB_OAUTH_CLIENT_ID + +# The client secret you received from GitHub for your GitHub App. +GITHUB_OAUTH_CLIENT_SECRET=GITHUB_OAUTH_CLIENT_SECRET diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 2b13f53..7f0f34d 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import Icon from 'components/Icon' import configHelper, { configKeys } from 'utils/configHelper' import keyHelper from 'utils/keyHelper' -import { friendlyFormatShortcut } from 'utils/general' +import { friendlyFormatShortcut, parseURLSearch, JSONRequest } from 'utils/general' import { version } from '../../package.json' const wikiLinks = { @@ -14,6 +14,11 @@ const wikiLinks = { 'https://github.com/EnixCoda/Gitako/wiki/How-to-create-access-token-for-Gitako%3F', } +const oauth = { + clientId: process.env.GITHUB_OAUTH_CLIENT_ID, + clientSecret: process.env.GITHUB_OAUTH_CLIENT_SECRET, +} + const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ type Props = { @@ -84,12 +89,33 @@ export default class SettingsBar extends React.PureComponent { ], } + componentDidMount() { + if (!this.props.accessToken) this.trySetUpAccessTokenWithCode() + } + componentWillReceiveProps({ toggleShowSideBarShortcut }: Props) { if (toggleShowSideBarShortcut !== this.props.toggleShowSideBarShortcut) { this.setState({ toggleShowSideBarShortcut }) } } + private async trySetUpAccessTokenWithCode() { + const search = parseURLSearch() + if ('code' in search) { + const res = await JSONRequest('https://github.com/login/oauth/access_token', { + code: search.code, + client_id: oauth.clientId, + client_secret: oauth.clientSecret, + }) + const { access_token: accessToken, scope } = res + if (scope !== 'repo' || !accessToken) { + throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) + } + window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) + this.setState({ accessToken }, () => this.saveToken()) + } + } + onInputAccessToken = (event: React.FormEvent) => { const { value } = event.currentTarget this.setState({ @@ -197,6 +223,13 @@ export default class SettingsBar extends React.PureComponent {

Access Token

+ + Create token + Why & how to create it? diff --git a/src/utils/general.ts b/src/utils/general.ts index 91056f3..742e08a 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -93,3 +93,38 @@ export function createStyleSheet(content: string) { export function setStyleSheetMedia(style: HTMLStyleElement, media: string) { style.setAttribute('media', media) } + +export function parseURLSearch(search: string = window.location.search) { + const parsed: any = {} + if (search.startsWith('?')) { + const pairs = search + .slice(1) + .split('&') + .map(rawPair => rawPair.split('=').map(raw => decodeURIComponent(raw))) // [key, value?][] + pairs.forEach(([key, value]) => { + if (Object.prototype.hasOwnProperty.call(parsed, key)) { + if (Array.isArray(parsed[key])) parsed[key].push(value) + else parsed[key] = [parsed[key], value] + } else { + parsed[key] = value + } + }) + } + return parsed +} + +export async function JSONRequest(url: string, data: any, method = 'post') { + return (await fetch(url, { + method, + mode: 'cors', + cache: 'no-cache', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + redirect: 'follow', + referrer: 'no-referrer', + body: JSON.stringify(data), + })).json() +}