You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
2.9 KiB
JavaScript
127 lines
2.9 KiB
JavaScript
export class Frontend {
|
|
contentTypes = {
|
|
html: 'text/html; charset=utf-8',
|
|
js: 'text/javascript',
|
|
css: 'text/css',
|
|
md: 'text/plain',
|
|
txt: 'text/plain',
|
|
png: 'image/png',
|
|
jpg: 'image/jpg',
|
|
webm: 'image/webm',
|
|
svg: 'image/svg',
|
|
}
|
|
|
|
constructor({apiBaseUrl, appBaseUrl}) {
|
|
this.apiBaseUrl = apiBaseUrl
|
|
this.appBaseUrl = appBaseUrl
|
|
this.files = {}
|
|
}
|
|
|
|
repoUrl(user, repo) {
|
|
return (
|
|
this.apiBaseUrl +
|
|
'/repos' +
|
|
'/' + encodeURIComponent(user) +
|
|
'/' + encodeURIComponent(repo)
|
|
)
|
|
}
|
|
|
|
pathString(path) {
|
|
return path.map(s => (
|
|
'/' + encodeURIComponent(s)
|
|
)).join('')
|
|
}
|
|
|
|
rawUrl(user, repo, commit, path) {
|
|
return (
|
|
this.appBaseUrl +
|
|
'/' + encodeURIComponent(user) +
|
|
'/' + encodeURIComponent(repo) +
|
|
'/raw/commit' +
|
|
'/' + encodeURIComponent(commit) +
|
|
this.pathString(path)
|
|
)
|
|
}
|
|
|
|
checkOk(resp) {
|
|
if (!resp.ok) {
|
|
throw new Error('not ok - ' + JSON.stringify(
|
|
{status: resp.status, url: resp.url}
|
|
))
|
|
}
|
|
}
|
|
|
|
async loadFile(user, repo, commit, src, dest) {
|
|
const url = this.rawUrl(user, repo, commit, src)
|
|
const resp = await fetch(url)
|
|
this.checkOk(resp)
|
|
const body = await resp.arrayBuffer()
|
|
const ext = dest.at('-1')?.match?.(/\.(\w+)$/)?.[1]
|
|
const contentType = this.contentTypes[ext]
|
|
this.files[this.pathString(dest)] = {
|
|
user,
|
|
repo,
|
|
commit,
|
|
body,
|
|
contentType: contentType ?? 'application/octet-stream',
|
|
}
|
|
}
|
|
|
|
contentsUrl(user, repo, path, ref) {
|
|
const url = new URL(
|
|
this.repoUrl(user, repo) +
|
|
'/contents' +
|
|
this.pathString(path)
|
|
)
|
|
if (ref !== undefined) {
|
|
const search = new URLSearchParams()
|
|
search.set('ref', ref)
|
|
url.search = search.toString()
|
|
}
|
|
return url.toString()
|
|
}
|
|
|
|
async loadRepo(user, repo, opts) {
|
|
const {
|
|
srcPath = [], destPath = [], ref
|
|
} = opts
|
|
const url = this.contentsUrl(
|
|
user, repo, srcPath, ref
|
|
)
|
|
const resp = await fetch(url)
|
|
this.checkOk(resp)
|
|
const contents = await resp.json()
|
|
await Promise.all(contents.map(item => (
|
|
item.type === 'dir' ?
|
|
this.loadRepo(user, repo, {
|
|
...opts,
|
|
srcPath: [...srcPath, item.name],
|
|
destPath: [...destPath, item.name],
|
|
}) :
|
|
this.loadFile(
|
|
user,
|
|
repo,
|
|
item.last_commit_sha,
|
|
[...srcPath, item.name],
|
|
[...destPath, item.name],
|
|
)
|
|
)))
|
|
}
|
|
|
|
async serve(event) {
|
|
const {pathname} = new URL(event.request.url)
|
|
if (pathname in this.files) {
|
|
const file = this.files[pathname]
|
|
event.respondWith(new Response(file.body, {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': file.contentType,
|
|
},
|
|
}))
|
|
} else {
|
|
event.respondWith(new Response(
|
|
'Not Found', {status: 404}
|
|
))
|
|
}
|
|
}
|
|
} |