pinokiod 5.3.13 → 5.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/index.js +16 -222
- package/server/views/registry_checkin.ejs +24 -34
- package/server/views/checkpoints_registry.ejs +0 -153
- package/server/views/registry_link.ejs +0 -76
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -49,7 +49,6 @@ const REGISTRY_PING_PNG = Buffer.from(
|
|
|
49
49
|
'base64'
|
|
50
50
|
)
|
|
51
51
|
const DEFAULT_REGISTRY_URL = 'https://beta.pinokio.co'
|
|
52
|
-
const DEFAULT_REGISTRY_CONNECT_URL = `${DEFAULT_REGISTRY_URL}/connect/pinokio`
|
|
53
52
|
|
|
54
53
|
const ex = fn => (req, res, next) => {
|
|
55
54
|
Promise.resolve(fn(req, res, next)).catch(next);
|
|
@@ -1143,19 +1142,14 @@ class Server {
|
|
|
1143
1142
|
}
|
|
1144
1143
|
async getRegistryConfig() {
|
|
1145
1144
|
let url = null
|
|
1146
|
-
let apiKey = null
|
|
1147
1145
|
try {
|
|
1148
1146
|
const env = await Environment.get(this.kernel.homedir, this.kernel)
|
|
1149
1147
|
if (env && env.PINOKIO_REGISTRY_URL) {
|
|
1150
1148
|
const v = String(env.PINOKIO_REGISTRY_URL).trim()
|
|
1151
1149
|
if (v) url = v
|
|
1152
1150
|
}
|
|
1153
|
-
if (env && env.PINOKIO_REGISTRY_API_KEY) {
|
|
1154
|
-
const v = String(env.PINOKIO_REGISTRY_API_KEY).trim()
|
|
1155
|
-
if (v) apiKey = v
|
|
1156
|
-
}
|
|
1157
1151
|
} catch (_) {}
|
|
1158
|
-
if (
|
|
1152
|
+
if (url == null && this.kernel && this.kernel.store) {
|
|
1159
1153
|
const registry = this.kernel.store.get('registry') || {}
|
|
1160
1154
|
const updates = {}
|
|
1161
1155
|
if (url == null && registry.url) {
|
|
@@ -1165,13 +1159,6 @@ class Server {
|
|
|
1165
1159
|
updates.PINOKIO_REGISTRY_URL = v
|
|
1166
1160
|
}
|
|
1167
1161
|
}
|
|
1168
|
-
if (apiKey == null && registry.apiKey) {
|
|
1169
|
-
const v = String(registry.apiKey).trim()
|
|
1170
|
-
if (v) {
|
|
1171
|
-
apiKey = v
|
|
1172
|
-
updates.PINOKIO_REGISTRY_API_KEY = v
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
1162
|
if (Object.keys(updates).length) {
|
|
1176
1163
|
await this.updateEnvironmentVars(updates).catch(() => {})
|
|
1177
1164
|
}
|
|
@@ -1179,67 +1166,15 @@ class Server {
|
|
|
1179
1166
|
if (!url) {
|
|
1180
1167
|
url = DEFAULT_REGISTRY_URL
|
|
1181
1168
|
}
|
|
1182
|
-
return { url, apiKey }
|
|
1183
|
-
}
|
|
1184
|
-
async getRegistryConnectUrl() {
|
|
1185
|
-
let raw = null
|
|
1186
|
-
let source = null
|
|
1187
|
-
try {
|
|
1188
|
-
const env = await Environment.get(this.kernel.homedir, this.kernel)
|
|
1189
|
-
if (env && Object.prototype.hasOwnProperty.call(env, "PINOKIO_REGISTRY_CONNECT_URL")) {
|
|
1190
|
-
raw = env.PINOKIO_REGISTRY_CONNECT_URL
|
|
1191
|
-
source = "env-url"
|
|
1192
|
-
} else if (env && Object.prototype.hasOwnProperty.call(env, "PINOKIO_REGISTRY_CONNECT_ENDPOINT")) {
|
|
1193
|
-
raw = env.PINOKIO_REGISTRY_CONNECT_ENDPOINT
|
|
1194
|
-
source = "env-endpoint"
|
|
1195
|
-
}
|
|
1196
|
-
} catch (_) {}
|
|
1197
|
-
if (raw == null && process.env.PINOKIO_REGISTRY_CONNECT_URL != null) {
|
|
1198
|
-
raw = process.env.PINOKIO_REGISTRY_CONNECT_URL
|
|
1199
|
-
source = "process-url"
|
|
1200
|
-
} else if (raw == null && process.env.PINOKIO_REGISTRY_CONNECT_ENDPOINT != null) {
|
|
1201
|
-
raw = process.env.PINOKIO_REGISTRY_CONNECT_ENDPOINT
|
|
1202
|
-
source = "process-endpoint"
|
|
1203
|
-
}
|
|
1204
|
-
let value = raw != null ? String(raw).trim() : ""
|
|
1205
|
-
if (!value && this.kernel && this.kernel.store) {
|
|
1206
|
-
const registry = this.kernel.store.get('registry') || {}
|
|
1207
|
-
if (registry.connectUrl) {
|
|
1208
|
-
value = String(registry.connectUrl).trim()
|
|
1209
|
-
if (value) {
|
|
1210
|
-
await this.updateEnvironmentVars({
|
|
1211
|
-
PINOKIO_REGISTRY_CONNECT_URL: value,
|
|
1212
|
-
PINOKIO_REGISTRY_CONNECT_ENDPOINT: ""
|
|
1213
|
-
}).catch(() => {})
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
if (!value) {
|
|
1218
|
-
const registry = await this.getRegistryConfig().catch(() => ({ url: null }))
|
|
1219
|
-
const baseUrl = registry && registry.url ? String(registry.url).replace(/\/$/, "") : ""
|
|
1220
|
-
value = baseUrl ? `${baseUrl}/connect/pinokio` : DEFAULT_REGISTRY_CONNECT_URL
|
|
1221
|
-
}
|
|
1222
|
-
if (value && source === "env-endpoint") {
|
|
1223
|
-
await this.updateEnvironmentVars({
|
|
1224
|
-
PINOKIO_REGISTRY_CONNECT_URL: value,
|
|
1225
|
-
PINOKIO_REGISTRY_CONNECT_ENDPOINT: ""
|
|
1226
|
-
}).catch(() => {})
|
|
1227
|
-
}
|
|
1228
|
-
return value ? value : null
|
|
1169
|
+
return { url, apiKey: null }
|
|
1229
1170
|
}
|
|
1230
1171
|
async getRegistryViewUrl(hash) {
|
|
1231
1172
|
if (!hash) return null
|
|
1232
1173
|
const registry = await this.getRegistryConfig().catch(() => ({ url: null, apiKey: null }))
|
|
1233
1174
|
const baseUrlRaw = registry && registry.url ? String(registry.url) : ""
|
|
1234
1175
|
const baseUrl = baseUrlRaw ? baseUrlRaw.replace(/\/$/, "") : ""
|
|
1235
|
-
const connectUrl = await this.getRegistryConnectUrl().catch(() => null)
|
|
1236
1176
|
let uiOrigin = null
|
|
1237
|
-
if (
|
|
1238
|
-
try {
|
|
1239
|
-
uiOrigin = new URL(connectUrl).origin
|
|
1240
|
-
} catch (_) {}
|
|
1241
|
-
}
|
|
1242
|
-
if (!uiOrigin && baseUrlRaw) {
|
|
1177
|
+
if (baseUrlRaw) {
|
|
1243
1178
|
try {
|
|
1244
1179
|
uiOrigin = new URL(baseUrlRaw).origin
|
|
1245
1180
|
} catch (_) {}
|
|
@@ -4836,7 +4771,6 @@ class Server {
|
|
|
4836
4771
|
const successRaw = typeof req.query.success === 'string' ? req.query.success.trim() : ''
|
|
4837
4772
|
const appSlug = typeof req.query.app === 'string' ? req.query.app.trim() : ''
|
|
4838
4773
|
const registryRaw = typeof req.query.registry === 'string' ? req.query.registry.trim() : ''
|
|
4839
|
-
const connectRaw = typeof req.query.connectUrl === 'string' ? req.query.connectUrl.trim() : ''
|
|
4840
4774
|
const registryEnabled = await this.isRegistryEnabled().catch(() => false)
|
|
4841
4775
|
|
|
4842
4776
|
let returnUrl = null
|
|
@@ -4881,9 +4815,7 @@ class Server {
|
|
|
4881
4815
|
}
|
|
4882
4816
|
|
|
4883
4817
|
const registryRawPresent = !!registryRaw
|
|
4884
|
-
const connectRawPresent = !!connectRaw
|
|
4885
4818
|
let registryOverride = normalizeHttpUrl(registryRaw)
|
|
4886
|
-
let connectOverride = normalizeHttpUrl(connectRaw)
|
|
4887
4819
|
if (returnUrl) {
|
|
4888
4820
|
try {
|
|
4889
4821
|
const returnHost = new URL(returnUrl)
|
|
@@ -4894,24 +4826,14 @@ class Server {
|
|
|
4894
4826
|
|| sameBaseDomain(regHost.hostname, returnHost.hostname)
|
|
4895
4827
|
if (!ok) registryOverride = null
|
|
4896
4828
|
}
|
|
4897
|
-
if (connectOverride) {
|
|
4898
|
-
const conHost = new URL(connectOverride)
|
|
4899
|
-
const ok = hostMatches(conHost, returnHost)
|
|
4900
|
-
|| sameBaseDomain(conHost.hostname, returnHost.hostname)
|
|
4901
|
-
if (!ok) connectOverride = null
|
|
4902
|
-
}
|
|
4903
4829
|
if (!registryOverride && !registryRawPresent && returnOrigin) {
|
|
4904
4830
|
registryOverride = returnOrigin
|
|
4905
4831
|
}
|
|
4906
|
-
if (!connectOverride && !connectRawPresent && returnOrigin) {
|
|
4907
|
-
connectOverride = `${returnOrigin.replace(/\/$/, '')}/connect/pinokio`
|
|
4908
|
-
}
|
|
4909
4832
|
} catch (_) {}
|
|
4910
4833
|
} else {
|
|
4911
4834
|
registryOverride = null
|
|
4912
|
-
connectOverride = null
|
|
4913
4835
|
}
|
|
4914
|
-
console.log("[registry/checkin] repo=%s return=%s registryRaw=%s
|
|
4836
|
+
console.log("[registry/checkin] repo=%s return=%s registryRaw=%s registryOverride=%s", repoUrl || "", returnUrl || "", registryRaw || "", registryOverride || "")
|
|
4915
4837
|
|
|
4916
4838
|
let candidates = []
|
|
4917
4839
|
if (repoUrl && this.kernel && this.kernel.git) {
|
|
@@ -4960,7 +4882,6 @@ class Server {
|
|
|
4960
4882
|
successUrl,
|
|
4961
4883
|
registryEnabled: !!registryEnabled,
|
|
4962
4884
|
registryOverride,
|
|
4963
|
-
connectOverride,
|
|
4964
4885
|
candidates,
|
|
4965
4886
|
}
|
|
4966
4887
|
const dataJson = JSON.stringify(data).replace(/</g, '\\u003c')
|
|
@@ -4968,123 +4889,6 @@ class Server {
|
|
|
4968
4889
|
res.render("registry_checkin", { returnUrl, dataJson })
|
|
4969
4890
|
}))
|
|
4970
4891
|
|
|
4971
|
-
// Registry linking endpoint
|
|
4972
|
-
this.app.get("/registry/link", ex(async (req, res) => {
|
|
4973
|
-
const { token, registry, app: appOrigin, next: nextRaw, pinokioNext: pinokioNextRaw } = req.query
|
|
4974
|
-
|
|
4975
|
-
if (!token || !registry) {
|
|
4976
|
-
res.status(400).render("registry_link", {
|
|
4977
|
-
ok: false,
|
|
4978
|
-
message: "Missing token or registry parameters",
|
|
4979
|
-
registryUrl: registry ? String(registry) : null,
|
|
4980
|
-
})
|
|
4981
|
-
return
|
|
4982
|
-
}
|
|
4983
|
-
|
|
4984
|
-
const registryUrl = String(registry)
|
|
4985
|
-
let connectUrl = null
|
|
4986
|
-
let redirectUrl = null
|
|
4987
|
-
try {
|
|
4988
|
-
if (appOrigin) {
|
|
4989
|
-
const u = new URL(String(appOrigin))
|
|
4990
|
-
u.pathname = "/connect/pinokio"
|
|
4991
|
-
u.search = ""
|
|
4992
|
-
u.hash = ""
|
|
4993
|
-
connectUrl = u.toString().replace(/\/$/, "")
|
|
4994
|
-
}
|
|
4995
|
-
} catch (_) {}
|
|
4996
|
-
|
|
4997
|
-
if (connectUrl) {
|
|
4998
|
-
try {
|
|
4999
|
-
const u = new URL(connectUrl)
|
|
5000
|
-
u.searchParams.set("linked", "1")
|
|
5001
|
-
redirectUrl = u.toString()
|
|
5002
|
-
} catch (_) {}
|
|
5003
|
-
}
|
|
5004
|
-
|
|
5005
|
-
if (appOrigin && nextRaw) {
|
|
5006
|
-
try {
|
|
5007
|
-
const base = new URL(String(appOrigin))
|
|
5008
|
-
const nextVal = String(nextRaw)
|
|
5009
|
-
const candidate = new URL(nextVal, base.toString())
|
|
5010
|
-
if (candidate.origin === base.origin) redirectUrl = candidate.toString()
|
|
5011
|
-
} catch (_) {}
|
|
5012
|
-
}
|
|
5013
|
-
|
|
5014
|
-
if (pinokioNextRaw) {
|
|
5015
|
-
try {
|
|
5016
|
-
const nextVal = String(pinokioNextRaw)
|
|
5017
|
-
const protocol = (req.$source && req.$source.protocol) || req.get("X-Forwarded-Proto") || "http"
|
|
5018
|
-
const host = (req.$source && req.$source.host) || req.get("host")
|
|
5019
|
-
const base = new URL(`${protocol}://${host}`)
|
|
5020
|
-
let candidate = null
|
|
5021
|
-
if (nextVal.startsWith("/") && !nextVal.startsWith("//")) {
|
|
5022
|
-
if (nextVal.startsWith("/registry/checkin")) {
|
|
5023
|
-
candidate = new URL(nextVal, base.toString())
|
|
5024
|
-
}
|
|
5025
|
-
} else {
|
|
5026
|
-
const parsed = new URL(nextVal)
|
|
5027
|
-
if (parsed.origin === base.origin && parsed.pathname.startsWith("/registry/checkin")) {
|
|
5028
|
-
candidate = parsed
|
|
5029
|
-
}
|
|
5030
|
-
}
|
|
5031
|
-
if (candidate && candidate.origin === base.origin) redirectUrl = candidate.toString()
|
|
5032
|
-
} catch (_) {}
|
|
5033
|
-
}
|
|
5034
|
-
|
|
5035
|
-
try {
|
|
5036
|
-
// Exchange token for API key at the specified registry
|
|
5037
|
-
const response = await axios.post(
|
|
5038
|
-
`${registryUrl}/registry/exchange`,
|
|
5039
|
-
{ token: String(token) },
|
|
5040
|
-
{ headers: { 'Content-Type': 'application/json' } }
|
|
5041
|
-
)
|
|
5042
|
-
|
|
5043
|
-
const { apiKey, registryUrl: confirmedUrl } = response.data
|
|
5044
|
-
|
|
5045
|
-
const envUpdates = {
|
|
5046
|
-
PINOKIO_REGISTRY_API_KEY: apiKey,
|
|
5047
|
-
PINOKIO_REGISTRY_URL: confirmedUrl || registryUrl
|
|
5048
|
-
}
|
|
5049
|
-
if (connectUrl) {
|
|
5050
|
-
envUpdates.PINOKIO_REGISTRY_CONNECT_URL = connectUrl
|
|
5051
|
-
envUpdates.PINOKIO_REGISTRY_CONNECT_ENDPOINT = ""
|
|
5052
|
-
}
|
|
5053
|
-
await this.updateEnvironmentVars(envUpdates)
|
|
5054
|
-
|
|
5055
|
-
if (redirectUrl) {
|
|
5056
|
-
res.redirect(redirectUrl)
|
|
5057
|
-
return
|
|
5058
|
-
}
|
|
5059
|
-
|
|
5060
|
-
res.render("registry_link", { ok: true, registryUrl: confirmedUrl || registryUrl })
|
|
5061
|
-
} catch (error) {
|
|
5062
|
-
console.log("Error", error)
|
|
5063
|
-
if (connectUrl) {
|
|
5064
|
-
try {
|
|
5065
|
-
const u = new URL(connectUrl)
|
|
5066
|
-
u.searchParams.set("error", "1")
|
|
5067
|
-
const msg = error && error.message ? String(error.message) : String(error)
|
|
5068
|
-
u.searchParams.set("message", msg.slice(0, 240))
|
|
5069
|
-
res.redirect(u.toString())
|
|
5070
|
-
return
|
|
5071
|
-
} catch (_) {}
|
|
5072
|
-
}
|
|
5073
|
-
res.status(500).render("registry_link", {
|
|
5074
|
-
ok: false,
|
|
5075
|
-
message: error && error.message ? error.message : String(error),
|
|
5076
|
-
registryUrl,
|
|
5077
|
-
})
|
|
5078
|
-
}
|
|
5079
|
-
}))
|
|
5080
|
-
|
|
5081
|
-
this.app.get("/api/registry/status", ex(async (req, res) => {
|
|
5082
|
-
const registry = await this.getRegistryConfig()
|
|
5083
|
-
const baseUrl = registry && registry.url ? String(registry.url).replace(/\/$/, '') : null
|
|
5084
|
-
const apiKey = registry && registry.apiKey ? String(registry.apiKey) : null
|
|
5085
|
-
res.json({ linked: !!apiKey, url: baseUrl })
|
|
5086
|
-
}))
|
|
5087
|
-
|
|
5088
4892
|
this.app.post("/checkpoints/publish", ex(async (req, res) => {
|
|
5089
4893
|
res.status(404).json({ ok: false, error: "Not found" })
|
|
5090
4894
|
}))
|
|
@@ -5133,7 +4937,7 @@ class Server {
|
|
|
5133
4937
|
}
|
|
5134
4938
|
}
|
|
5135
4939
|
const registryOverride = normalizeHttpUrl(typeof req.query.registry === 'string' ? req.query.registry.trim() : '')
|
|
5136
|
-
const
|
|
4940
|
+
const registryToken = typeof req.get("x-registry-token") === "string" ? String(req.get("x-registry-token")).trim() : ""
|
|
5137
4941
|
const registryEnabled = await this.isRegistryEnabled().catch(() => false)
|
|
5138
4942
|
const publishRaw = typeof req.query.publish === 'string' ? req.query.publish.trim().toLowerCase() : ''
|
|
5139
4943
|
const wantPublish = registryEnabled && (publishRaw === '1' || publishRaw === 'true' || publishRaw === 'cloud')
|
|
@@ -5150,19 +4954,10 @@ class Server {
|
|
|
5150
4954
|
if (wantPublish) {
|
|
5151
4955
|
const registry = await this.getRegistryConfig()
|
|
5152
4956
|
const baseUrl = registryOverride || (registry && registry.url ? String(registry.url).replace(/\/$/, '') : null)
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
connectUrl = `${registryOverride.replace(/\/$/, '')}/connect/pinokio`
|
|
5158
|
-
} else {
|
|
5159
|
-
connectUrl = await this.getRegistryConnectUrl().catch(() => null)
|
|
5160
|
-
}
|
|
5161
|
-
}
|
|
5162
|
-
console.log("[checkpoints/snapshot] publish=1 registryOverride=%s baseUrl=%s connectUrl=%s hasApiKey=%s", registryOverride || "", baseUrl || "", connectUrl || "", apiKey ? "yes" : "no")
|
|
5163
|
-
if (!baseUrl || !apiKey) {
|
|
5164
|
-
await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "needs_link", at: Date.now() }).catch(() => {})
|
|
5165
|
-
res.json({ ok: true, created: created || null, publish: { ok: false, code: "not_linked", connectUrl } })
|
|
4957
|
+
console.log("[checkpoints/snapshot] publish=1 registryOverride=%s baseUrl=%s hasToken=%s", registryOverride || "", baseUrl || "", registryToken ? "yes" : "no")
|
|
4958
|
+
if (!baseUrl || !registryToken) {
|
|
4959
|
+
await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "needs_token", at: Date.now() }).catch(() => {})
|
|
4960
|
+
res.json({ ok: true, created: created || null, publish: { ok: false, code: "missing_token" } })
|
|
5166
4961
|
return
|
|
5167
4962
|
}
|
|
5168
4963
|
try {
|
|
@@ -5179,7 +4974,7 @@ class Server {
|
|
|
5179
4974
|
checkpoint,
|
|
5180
4975
|
system,
|
|
5181
4976
|
},
|
|
5182
|
-
{ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${
|
|
4977
|
+
{ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${registryToken}` } }
|
|
5183
4978
|
)
|
|
5184
4979
|
const remoteId = response && response.data
|
|
5185
4980
|
? (response.data.checkpoint && response.data.checkpoint.id ? response.data.checkpoint.id : (response.data.id ? response.data.id : null))
|
|
@@ -5192,12 +4987,11 @@ class Server {
|
|
|
5192
4987
|
} catch (error) {
|
|
5193
4988
|
const status = error && error.response && error.response.status ? Number(error.response.status) : null
|
|
5194
4989
|
const message = error && error.message ? error.message : String(error)
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
}
|
|
4990
|
+
if (status === 401 || status === 403) {
|
|
4991
|
+
await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "invalid_token", at: Date.now(), error: message }).catch(() => {})
|
|
4992
|
+
res.json({ ok: true, created: created || null, publish: { ok: false, code: "invalid_token" } })
|
|
4993
|
+
return
|
|
4994
|
+
}
|
|
5201
4995
|
await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "error", at: Date.now(), error: message }).catch(() => {})
|
|
5202
4996
|
res.json({ ok: true, created: created || null, publish: { ok: false, code: "error", error: message } })
|
|
5203
4997
|
return
|
|
@@ -9223,7 +9017,7 @@ class Server {
|
|
|
9223
9017
|
title: name,
|
|
9224
9018
|
url: gitRemote,
|
|
9225
9019
|
//redirect_uri: "http://localhost:3001/apps/redirect?git=" + gitRemote,
|
|
9226
|
-
redirect_uri:
|
|
9020
|
+
redirect_uri: `${this.portal}/resolve?url=${encodeURIComponent(gitRemote)}`,
|
|
9227
9021
|
platform: this.kernel.platform,
|
|
9228
9022
|
theme: this.theme,
|
|
9229
9023
|
agent: req.agent,
|
|
@@ -130,7 +130,6 @@
|
|
|
130
130
|
<script>
|
|
131
131
|
const data = <%- dataJson %>;
|
|
132
132
|
const registryOverride = data.registryOverride || null;
|
|
133
|
-
const connectOverride = data.connectOverride || null;
|
|
134
133
|
const repoEl = document.getElementById('repo');
|
|
135
134
|
const statusEl = document.getElementById('status');
|
|
136
135
|
const pickerEl = document.getElementById('picker');
|
|
@@ -172,36 +171,25 @@
|
|
|
172
171
|
return null;
|
|
173
172
|
};
|
|
174
173
|
|
|
175
|
-
const
|
|
176
|
-
|
|
174
|
+
const token = (() => {
|
|
175
|
+
const hash = window.location.hash ? window.location.hash.slice(1) : '';
|
|
176
|
+
if (!hash) return '';
|
|
177
|
+
const params = new URLSearchParams(hash);
|
|
178
|
+
return params.get('token') || '';
|
|
179
|
+
})();
|
|
180
|
+
if (token) {
|
|
177
181
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (pinokioNext.startsWith('/') && !pinokioNext.startsWith('//')) {
|
|
182
|
-
u.searchParams.set('pinokioNext', pinokioNext);
|
|
183
|
-
}
|
|
184
|
-
if (data.returnUrl) {
|
|
185
|
-
try {
|
|
186
|
-
const ret = new URL(data.returnUrl);
|
|
187
|
-
const nextPath = `${ret.pathname}${ret.search}${ret.hash || ''}`;
|
|
188
|
-
if (nextPath.startsWith('/') && !nextPath.startsWith('//')) {
|
|
189
|
-
u.searchParams.set('next', nextPath);
|
|
190
|
-
}
|
|
191
|
-
} catch (err) {}
|
|
192
|
-
}
|
|
193
|
-
u.searchParams.set('pinokioUrl', window.location.origin);
|
|
194
|
-
return u.toString();
|
|
195
|
-
} catch (err) {
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
};
|
|
182
|
+
window.history.replaceState(null, '', window.location.pathname + window.location.search);
|
|
183
|
+
} catch (err) {}
|
|
184
|
+
}
|
|
199
185
|
|
|
200
186
|
const candidates = Array.isArray(data.candidates) ? data.candidates : [];
|
|
201
187
|
const repoLabel = data.repoUrl ? `Repo: ${data.repoUrl}` : 'Missing repo URL';
|
|
202
188
|
if (repoEl) repoEl.textContent = repoLabel;
|
|
203
189
|
|
|
204
|
-
if (!
|
|
190
|
+
if (!token) {
|
|
191
|
+
setStatus('Missing check-in token. Return to the registry and try again.', 'error');
|
|
192
|
+
} else if (!data.repoUrl) {
|
|
205
193
|
setStatus('Missing repo URL. Return to the registry and try again.', 'error');
|
|
206
194
|
} else if (!data.returnUrl) {
|
|
207
195
|
setStatus('Missing return URL. Return to the registry and try again.', 'error');
|
|
@@ -255,16 +243,19 @@
|
|
|
255
243
|
setStatus('Missing folder selection.', 'error');
|
|
256
244
|
return;
|
|
257
245
|
}
|
|
246
|
+
if (!token) {
|
|
247
|
+
setStatus('Missing check-in token. Return to the registry and try again.', 'error');
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
258
250
|
setStatus('Creating checkpoint...', '');
|
|
259
251
|
if (submitBtn) submitBtn.disabled = true;
|
|
260
252
|
if (cancelBtn) cancelBtn.disabled = true;
|
|
261
253
|
try {
|
|
262
254
|
const qs = new URLSearchParams({ workspace: folder, publish: '1' });
|
|
263
255
|
if (registryOverride) qs.set('registry', registryOverride);
|
|
264
|
-
if (connectOverride) qs.set('connectUrl', connectOverride);
|
|
265
256
|
const res = await fetch(`/checkpoints/snapshot?${qs.toString()}`, {
|
|
266
257
|
method: 'POST',
|
|
267
|
-
headers: { 'Accept': 'application/json' }
|
|
258
|
+
headers: { 'Accept': 'application/json', 'X-Registry-Token': token }
|
|
268
259
|
});
|
|
269
260
|
const payload = res.ok ? await res.json().catch(() => null) : null;
|
|
270
261
|
if (!payload || !payload.ok) {
|
|
@@ -282,13 +273,12 @@
|
|
|
282
273
|
redirectToReturn({ ok: '1', hash });
|
|
283
274
|
return;
|
|
284
275
|
}
|
|
285
|
-
if (publish && publish.code === '
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
redirectToReturn({ error: 'not_linked', connectUrl: publish.connectUrl || '' });
|
|
276
|
+
if (publish && publish.code === 'missing_token') {
|
|
277
|
+
redirectToReturn({ error: 'missing_token' });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (publish && publish.code === 'invalid_token') {
|
|
281
|
+
redirectToReturn({ error: 'invalid_token' });
|
|
292
282
|
return;
|
|
293
283
|
}
|
|
294
284
|
if (publish && publish.code === 'error') {
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<style>
|
|
7
|
-
body {
|
|
8
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
9
|
-
display: flex;
|
|
10
|
-
align-items: center;
|
|
11
|
-
justify-content: center;
|
|
12
|
-
min-height: 100vh;
|
|
13
|
-
margin: 0;
|
|
14
|
-
background: #f5f5f5;
|
|
15
|
-
color: #0f172a;
|
|
16
|
-
}
|
|
17
|
-
.card {
|
|
18
|
-
background: white;
|
|
19
|
-
padding: 32px;
|
|
20
|
-
border-radius: 12px;
|
|
21
|
-
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.08);
|
|
22
|
-
max-width: 520px;
|
|
23
|
-
width: calc(100% - 32px);
|
|
24
|
-
}
|
|
25
|
-
h1 {
|
|
26
|
-
margin: 0 0 8px;
|
|
27
|
-
font-size: 22px;
|
|
28
|
-
}
|
|
29
|
-
.subtitle {
|
|
30
|
-
margin: 0 0 16px;
|
|
31
|
-
color: #64748b;
|
|
32
|
-
font-size: 14px;
|
|
33
|
-
}
|
|
34
|
-
label {
|
|
35
|
-
display: block;
|
|
36
|
-
font-weight: 600;
|
|
37
|
-
font-size: 13px;
|
|
38
|
-
margin-bottom: 6px;
|
|
39
|
-
}
|
|
40
|
-
input[type="url"] {
|
|
41
|
-
width: 100%;
|
|
42
|
-
padding: 10px 12px;
|
|
43
|
-
border-radius: 8px;
|
|
44
|
-
border: 1px solid #e2e8f0;
|
|
45
|
-
font-size: 14px;
|
|
46
|
-
box-sizing: border-box;
|
|
47
|
-
}
|
|
48
|
-
.hint {
|
|
49
|
-
margin-top: 6px;
|
|
50
|
-
font-size: 12px;
|
|
51
|
-
color: #94a3b8;
|
|
52
|
-
}
|
|
53
|
-
.actions {
|
|
54
|
-
margin-top: 16px;
|
|
55
|
-
display: flex;
|
|
56
|
-
gap: 10px;
|
|
57
|
-
flex-wrap: wrap;
|
|
58
|
-
}
|
|
59
|
-
.btn {
|
|
60
|
-
border: none;
|
|
61
|
-
border-radius: 8px;
|
|
62
|
-
padding: 10px 16px;
|
|
63
|
-
font-size: 14px;
|
|
64
|
-
cursor: pointer;
|
|
65
|
-
text-decoration: none;
|
|
66
|
-
display: inline-flex;
|
|
67
|
-
align-items: center;
|
|
68
|
-
justify-content: center;
|
|
69
|
-
}
|
|
70
|
-
.btn-primary {
|
|
71
|
-
background: #2563eb;
|
|
72
|
-
color: white;
|
|
73
|
-
}
|
|
74
|
-
.btn-secondary {
|
|
75
|
-
background: #e2e8f0;
|
|
76
|
-
color: #0f172a;
|
|
77
|
-
}
|
|
78
|
-
.message {
|
|
79
|
-
margin-top: 12px;
|
|
80
|
-
font-size: 13px;
|
|
81
|
-
min-height: 18px;
|
|
82
|
-
}
|
|
83
|
-
.message.success {
|
|
84
|
-
color: #166534;
|
|
85
|
-
}
|
|
86
|
-
.message.error {
|
|
87
|
-
color: #991b1b;
|
|
88
|
-
}
|
|
89
|
-
</style>
|
|
90
|
-
</head>
|
|
91
|
-
<body>
|
|
92
|
-
<div class="card">
|
|
93
|
-
<h1>Registry Settings</h1>
|
|
94
|
-
<div class="subtitle">Set the connect URL used for linking and published checkpoints.</div>
|
|
95
|
-
<form id="registry-form">
|
|
96
|
-
<label for="registry-connect-url">Registry connect URL</label>
|
|
97
|
-
<input
|
|
98
|
-
id="registry-connect-url"
|
|
99
|
-
type="url"
|
|
100
|
-
placeholder="https://your-registry/connect/pinokio"
|
|
101
|
-
value="<%= registryUrlPrefill || registryConnectUrl || '' %>"
|
|
102
|
-
autocomplete="off"
|
|
103
|
-
/>
|
|
104
|
-
<div class="hint">Example: https://your-registry/connect/pinokio</div>
|
|
105
|
-
<div class="actions">
|
|
106
|
-
<button class="btn btn-primary" type="submit">Save</button>
|
|
107
|
-
<a class="btn btn-secondary" href="/checkpoints">Back</a>
|
|
108
|
-
</div>
|
|
109
|
-
</form>
|
|
110
|
-
<div id="registry-message" class="message" aria-live="polite"></div>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<script>
|
|
114
|
-
const form = document.getElementById("registry-form");
|
|
115
|
-
const input = document.getElementById("registry-connect-url");
|
|
116
|
-
const message = document.getElementById("registry-message");
|
|
117
|
-
|
|
118
|
-
const setMessage = (text, isError) => {
|
|
119
|
-
if (!message) return;
|
|
120
|
-
message.textContent = text || "";
|
|
121
|
-
message.classList.toggle("error", !!isError);
|
|
122
|
-
message.classList.toggle("success", !isError && !!text);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
if (form) {
|
|
126
|
-
form.addEventListener("submit", async (event) => {
|
|
127
|
-
event.preventDefault();
|
|
128
|
-
setMessage("");
|
|
129
|
-
const connectUrl = input && input.value != null ? String(input.value).trim() : "";
|
|
130
|
-
const submitBtn = form.querySelector("button[type='submit']");
|
|
131
|
-
if (submitBtn) submitBtn.disabled = true;
|
|
132
|
-
try {
|
|
133
|
-
const res = await fetch("/checkpoints/registry", {
|
|
134
|
-
method: "POST",
|
|
135
|
-
headers: { "Content-Type": "application/json", "Accept": "application/json" },
|
|
136
|
-
body: JSON.stringify({ connectUrl })
|
|
137
|
-
});
|
|
138
|
-
const payload = res.ok ? await res.json().catch(() => null) : null;
|
|
139
|
-
if (!payload || !payload.ok) {
|
|
140
|
-
setMessage("Could not save registry settings.", true);
|
|
141
|
-
} else {
|
|
142
|
-
setMessage("Saved.", false);
|
|
143
|
-
}
|
|
144
|
-
} catch (_) {
|
|
145
|
-
setMessage("Could not save registry settings.", true);
|
|
146
|
-
} finally {
|
|
147
|
-
if (submitBtn) submitBtn.disabled = false;
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
</script>
|
|
152
|
-
</body>
|
|
153
|
-
</html>
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<html>
|
|
2
|
-
<head>
|
|
3
|
-
<meta charset="UTF-8" />
|
|
4
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
5
|
-
<style>
|
|
6
|
-
body {
|
|
7
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
8
|
-
display: flex;
|
|
9
|
-
align-items: center;
|
|
10
|
-
justify-content: center;
|
|
11
|
-
height: 100vh;
|
|
12
|
-
margin: 0;
|
|
13
|
-
background: #f5f5f5;
|
|
14
|
-
}
|
|
15
|
-
.card {
|
|
16
|
-
background: white;
|
|
17
|
-
padding: 40px;
|
|
18
|
-
border-radius: 12px;
|
|
19
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
20
|
-
text-align: center;
|
|
21
|
-
max-width: 500px;
|
|
22
|
-
}
|
|
23
|
-
h1 {
|
|
24
|
-
margin-top: 0;
|
|
25
|
-
}
|
|
26
|
-
h1.ok {
|
|
27
|
-
color: #16a34a;
|
|
28
|
-
}
|
|
29
|
-
h1.error {
|
|
30
|
-
color: #dc2626;
|
|
31
|
-
}
|
|
32
|
-
.meta {
|
|
33
|
-
color: #666;
|
|
34
|
-
font-size: 14px;
|
|
35
|
-
margin-top: 20px;
|
|
36
|
-
}
|
|
37
|
-
.close {
|
|
38
|
-
margin-top: 20px;
|
|
39
|
-
padding: 10px 20px;
|
|
40
|
-
background: #2563eb;
|
|
41
|
-
color: white;
|
|
42
|
-
border: none;
|
|
43
|
-
border-radius: 6px;
|
|
44
|
-
cursor: pointer;
|
|
45
|
-
}
|
|
46
|
-
.error {
|
|
47
|
-
color: #666;
|
|
48
|
-
font-size: 14px;
|
|
49
|
-
}
|
|
50
|
-
</style>
|
|
51
|
-
</head>
|
|
52
|
-
<body>
|
|
53
|
-
<div class="card">
|
|
54
|
-
<% if (ok) { %>
|
|
55
|
-
<h1 class="ok">✓ Linked Successfully!</h1>
|
|
56
|
-
<p>Pinokio is now connected to the registry.</p>
|
|
57
|
-
<p>You can now back up checkpoints to the registry.</p>
|
|
58
|
-
<div class="meta">Registry: <strong><%= registryUrl %></strong></div>
|
|
59
|
-
<button class="close" onclick="window.close()">Close</button>
|
|
60
|
-
<script>
|
|
61
|
-
setTimeout(() => window.close(), 5000);
|
|
62
|
-
</script>
|
|
63
|
-
<% } else { %>
|
|
64
|
-
<h1 class="error">❌ Linking Failed</h1>
|
|
65
|
-
<% if (message) { %>
|
|
66
|
-
<p class="error"><%= message %></p>
|
|
67
|
-
<% } %>
|
|
68
|
-
<% if (registryUrl) { %>
|
|
69
|
-
<p class="error">Registry: <%= registryUrl %></p>
|
|
70
|
-
<% } %>
|
|
71
|
-
<a href="/">Go back to Pinokio</a>
|
|
72
|
-
<% } %>
|
|
73
|
-
</div>
|
|
74
|
-
</body>
|
|
75
|
-
</html>
|
|
76
|
-
|