1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
import EventEmitter from 'eventemitter3'
const axios = require('axios')
import * as config from '../../config'
export class API extends EventEmitter {
token = null
initialized = false
installations = null
repositories = null
repoInstallationMap = null
async _request (options) {
if (typeof options === 'string') {
options = {
url: options
}
}
if (options.url.startsWith('/')) {
options.url = `${config.apiBaseUrl}${options.url}`
}
options.headers = Object.assign({}, options.headers)
if (this.token && !options.headers.Authorization) {
options.headers.Authorization = `Bearer ${this.token}`
}
try {
return await axios(options)
} catch (err) {
if (err.response?.status === 401) {
console.error('Authentication failed.')
this.emit('authentication-failed', err.response)
}
throw err
}
}
async init() {
if (this.initialized) {
return
}
const installationUrl = `${config.apiBaseUrl}/github/installation`
const param = new URLSearchParams(location.search).get('token')
if (!localStorage.auth_token && param) {
history.replaceState({}, null, location.pathname)
localStorage.auth_token = param
}
if (localStorage.auth_token) {
this.token = localStorage.auth_token
const { data } = await this._request(installationUrl)
this.emit('authenticated')
if (!data.installation) {
console.warn('No GitHub app installation found for authenticated user.')
this.emit('app-not-installed')
}
this.installations = data.installations
this.repositories = data.repositories
this.repoInstallationMap = data.repoInstallationMap
}
}
beginLoginFlow() {
localStorage.removeItem('auth_token')
location.href = `${config.apiBaseUrl}/github/authorize`
}
beginInstallAppFlow() {
location.href = `https://github.com/apps/${config.githubAppName}/installations/new`
}
isGitHubAuthorized() {
return !!this.token
}
isAppInstalled() {
return this.installations?.length && this.repositories?.length
}
async fetchRepoBranches(repo) {
const installation = encodeURIComponent(this.repoInstallationMap[repo.full_name])
const repository = encodeURIComponent(repo.full_name)
const { data } = await this._request(
`/github/installation/${installation}/${repository}/branches`
)
return data
}
async fetchLayoutAndKeymap(repo, branch) {
const installation = encodeURIComponent(this.repoInstallationMap[repo])
const repository = encodeURIComponent(repo)
const url = new URL(`${config.apiBaseUrl}/github/keyboard-files/${installation}/${repository}`)
if (branch) {
url.search = new URLSearchParams({ branch }).toString()
}
try {
const { data } = await this._request(url.toString())
const defaultLayout = data.info.layouts.default || data.info.layouts[Object.keys(data.info.layouts)[0]]
return {
layout: defaultLayout.layout,
keymap: data.keymap
}
} catch (err) {
if (err.response?.status === 400) {
console.error('Failed to load keymap and layout from github', err.response.data)
this.emit('repo-validation-error', err.response.data)
}
throw err
}
}
commitChanges(repo, branch, layout, keymap) {
const installation = encodeURIComponent(this.repoInstallationMap[repo])
const repository = encodeURIComponent(repo)
return this._request({
url: `/github/keyboard-files/${installation}/${repository}/${encodeURIComponent(branch)}`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: { layout, keymap }
})
}
}
export default new API()
|