forked from KilleenCode/tauri-update-cloudflare
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhandler.ts
138 lines (107 loc) · 3.78 KB
/
handler.ts
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
136
137
138
import { testAsset } from './getPlatform'
import semverValid from 'semver/functions/valid'
import semverGt from 'semver/functions/gt'
import { AVAILABLE_ARCHITECTURES, AVAILABLE_PLATFORMS } from './constants'
import { handleLegacyRequest } from './legacy/handler'
import { findAssetSignature, getLatestRelease } from './services/github'
import { TauriUpdateResponse } from './types'
import { sanitizeVersion } from './utils/versioning'
declare global {
const GITHUB_ACCOUNT: string
const GITHUB_REPO: string
const GITHUB_TOKEN: string
}
const SendJSON = (data: Record<string, unknown>) => {
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json; charset=utf-8' },
})
}
const responses = {
NotFound: () => new Response('Not found', { status: 404 }),
NoContent: () => new Response(null, { status: 204 }),
SendUpdate: (data: TauriUpdateResponse) =>
SendJSON(data),
SendJSON
}
type RequestPathParts = [
string,
AVAILABLE_PLATFORMS,
AVAILABLE_ARCHITECTURES,
string,
]
const handleV1Request = async (request: Request) => {
const path = new URL(request.url).pathname
const [, target, arch, appVersion] = path
.slice(1)
.split('/') as RequestPathParts
if (!target || !arch || !appVersion || !semverValid(appVersion)) {
return responses.NotFound()
}
const release = await getLatestRelease(request)
const remoteVersion = sanitizeVersion(release.tag_name.toLowerCase())
if (!remoteVersion || !semverValid(remoteVersion)) {
return responses.NotFound()
}
const shouldUpdate = semverGt(remoteVersion, appVersion)
if (!shouldUpdate) {
return responses.NoContent()
}
const match = release.assets.find(({ name }) => {
const test = testAsset(target, arch, name)
return test
})
if (typeof match === 'undefined') {
return responses.NotFound()
}
const signature = await findAssetSignature(match.name, release.assets)
const proxy = GITHUB_TOKEN?.length;
const downloadURL = proxy ? createProxiedFileUrl(match.browser_download_url, request) : match.browser_download_url
const data: TauriUpdateResponse = {
url: downloadURL,
version: remoteVersion,
notes: release.body,
pub_date: release.published_at,
signature,
}
return responses.SendUpdate(data)
}
function lastInArray(array: ArrayLike<string>) {
const length = array.length;
return array[length - 1];
}
const createProxiedFileUrl = (downloadURL: string, request: Request) => {
const fileName = lastInArray(downloadURL.split('/'));
if (!fileName) { throw new Error('Could not get file name from download URL') }
const path = new URL(request.url)
const root = `${path.protocol}//${path.host}`
return new URL(`/latest/${fileName}`, root).toString()
}
const getLatestAssets = async (request: Request) => {
const fileName = lastInArray(request.url.split('/'));
if (!fileName) { throw new Error('Could not get file name from download URL') }
const release = await getLatestRelease(request)
const downloadPath = release.assets.find(({ name }) => name === fileName)?.browser_download_url
if (!downloadPath) { throw new Error('Could not get file path from download URL') }
const { readable, writable } = new TransformStream();
const file_response = await fetch(downloadPath, {
method: 'GET',
redirect: 'follow'
})
file_response?.body?.pipeTo(writable);
return new Response(readable, file_response);
}
export async function handleRequest(request: Request): Promise<Response> {
const path = new URL(request.url).pathname
if (path.includes('/latest')) {
return getLatestAssets(request)
}
const version = path.slice(1).split('/')[0]
if (version.includes('v')) {
switch (version) {
case 'v1':
default:
return handleV1Request(request)
}
}
return handleLegacyRequest(request)
}