@emulators/github 0.4.0 → 0.5.0
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/README.md +178 -0
- package/dist/fonts/favicon.ico +0 -0
- package/dist/index.js +270 -155
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -37,7 +37,10 @@ function getGitHubStore(store) {
|
|
|
37
37
|
checkSuites: store.collection("github.check_suites", ["repo_id", "head_sha"]),
|
|
38
38
|
oauthApps: store.collection("github.oauth_apps", ["client_id"]),
|
|
39
39
|
apps: store.collection("github.apps", ["slug"]),
|
|
40
|
-
appInstallations: store.collection("github.app_installations", [
|
|
40
|
+
appInstallations: store.collection("github.app_installations", [
|
|
41
|
+
"app_id",
|
|
42
|
+
"installation_id"
|
|
43
|
+
]),
|
|
41
44
|
oauthGrants: store.collection("github.oauth_grants", ["user_id", "client_id"])
|
|
42
45
|
};
|
|
43
46
|
}
|
|
@@ -744,6 +747,7 @@ var FONTS = {
|
|
|
744
747
|
"geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
|
|
745
748
|
"GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
746
749
|
};
|
|
750
|
+
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
|
|
747
751
|
function parsePagination(c) {
|
|
748
752
|
const page = Math.max(1, parseInt(c.req.query("page") ?? "1", 10) || 1);
|
|
749
753
|
const per_page = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
|
|
@@ -921,6 +925,132 @@ body{
|
|
|
921
925
|
.app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
|
|
922
926
|
.app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
923
927
|
.empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
|
|
928
|
+
|
|
929
|
+
.inspector-layout{max-width:960px;margin:0 auto;padding:28px 20px;}
|
|
930
|
+
.inspector-tabs{display:flex;gap:4px;margin-bottom:20px;}
|
|
931
|
+
.inspector-tabs a{
|
|
932
|
+
padding:7px 16px;border-radius:6px;text-decoration:none;
|
|
933
|
+
font-size:.8125rem;color:#1a8c00;border:1px solid transparent;
|
|
934
|
+
transition:color .15s,border-color .15s;
|
|
935
|
+
}
|
|
936
|
+
.inspector-tabs a:hover{color:#33ff00;}
|
|
937
|
+
.inspector-tabs a.active{color:#33ff00;font-weight:600;border-color:#0a3300;background:#0a3300;}
|
|
938
|
+
.inspector-section{margin-bottom:24px;}
|
|
939
|
+
.inspector-section h2{
|
|
940
|
+
font-family:'Geist Pixel',monospace;
|
|
941
|
+
font-size:1rem;font-weight:600;color:#33ff00;margin-bottom:10px;
|
|
942
|
+
}
|
|
943
|
+
.inspector-section h3{
|
|
944
|
+
font-family:'Geist Pixel',monospace;
|
|
945
|
+
font-size:.875rem;font-weight:600;color:#1a8c00;margin:16px 0 8px;
|
|
946
|
+
}
|
|
947
|
+
.inspector-table{width:100%;border-collapse:collapse;margin-bottom:12px;}
|
|
948
|
+
.inspector-table th,.inspector-table td{
|
|
949
|
+
text-align:left;padding:8px 12px;border-bottom:1px solid #0a3300;
|
|
950
|
+
font-size:.8125rem;
|
|
951
|
+
}
|
|
952
|
+
.inspector-table th{color:#1a8c00;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;}
|
|
953
|
+
.inspector-table td{color:#33ff00;}
|
|
954
|
+
.inspector-table tbody tr{transition:background .1s;}
|
|
955
|
+
.inspector-table tbody tr:hover{background:#0a3300;}
|
|
956
|
+
.inspector-empty{color:#1a8c00;text-align:center;padding:20px 0;font-size:.8125rem;}
|
|
957
|
+
|
|
958
|
+
.checkout-layout{
|
|
959
|
+
display:flex;min-height:calc(100vh - 42px);
|
|
960
|
+
}
|
|
961
|
+
.checkout-summary{
|
|
962
|
+
flex:1;background:#020;padding:48px 40px 48px 10%;
|
|
963
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
964
|
+
border-right:1px solid #0a3300;
|
|
965
|
+
}
|
|
966
|
+
.checkout-form-side{
|
|
967
|
+
flex:1;background:#000;padding:48px 10% 48px 40px;
|
|
968
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
969
|
+
}
|
|
970
|
+
.checkout-merchant{
|
|
971
|
+
display:flex;align-items:center;gap:10px;margin-bottom:6px;
|
|
972
|
+
}
|
|
973
|
+
.checkout-merchant-name{
|
|
974
|
+
font-family:'Geist Pixel',monospace;
|
|
975
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
976
|
+
}
|
|
977
|
+
.checkout-test-badge{
|
|
978
|
+
font-size:.625rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
|
|
979
|
+
background:#0a3300;color:#1a8c00;padding:2px 8px;border-radius:4px;
|
|
980
|
+
}
|
|
981
|
+
.checkout-total{
|
|
982
|
+
font-family:'Geist Pixel',monospace;
|
|
983
|
+
font-size:2rem;font-weight:700;color:#33ff00;margin:8px 0 28px;
|
|
984
|
+
}
|
|
985
|
+
.checkout-line-item{
|
|
986
|
+
display:flex;align-items:center;gap:14px;padding:14px 0;
|
|
987
|
+
border-bottom:1px solid #0a3300;
|
|
988
|
+
}
|
|
989
|
+
.checkout-line-item:first-child{border-top:1px solid #0a3300;}
|
|
990
|
+
.checkout-item-icon{
|
|
991
|
+
width:42px;height:42px;border-radius:6px;background:#0a3300;
|
|
992
|
+
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
993
|
+
font-family:'Geist Pixel',monospace;font-size:.875rem;font-weight:700;color:#116600;
|
|
994
|
+
}
|
|
995
|
+
.checkout-item-details{flex:1;min-width:0;}
|
|
996
|
+
.checkout-item-name{font-size:.875rem;font-weight:600;color:#33ff00;}
|
|
997
|
+
.checkout-item-qty{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
998
|
+
.checkout-item-price{
|
|
999
|
+
font-size:.875rem;font-weight:600;color:#33ff00;text-align:right;white-space:nowrap;
|
|
1000
|
+
}
|
|
1001
|
+
.checkout-item-unit{font-size:.6875rem;color:#1a8c00;text-align:right;margin-top:2px;}
|
|
1002
|
+
.checkout-totals{margin-top:20px;}
|
|
1003
|
+
.checkout-totals-row{
|
|
1004
|
+
display:flex;justify-content:space-between;padding:6px 0;
|
|
1005
|
+
font-size:.8125rem;color:#1a8c00;
|
|
1006
|
+
}
|
|
1007
|
+
.checkout-totals-row.total{
|
|
1008
|
+
border-top:1px solid #0a3300;margin-top:8px;padding-top:14px;
|
|
1009
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
1010
|
+
}
|
|
1011
|
+
.checkout-form-section{margin-bottom:24px;}
|
|
1012
|
+
.checkout-form-label{
|
|
1013
|
+
font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:8px;display:block;
|
|
1014
|
+
}
|
|
1015
|
+
.checkout-input{
|
|
1016
|
+
width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
|
|
1017
|
+
background:#020;color:#33ff00;font:inherit;font-size:.875rem;
|
|
1018
|
+
transition:border-color .15s;outline:none;
|
|
1019
|
+
}
|
|
1020
|
+
.checkout-input:focus{border-color:#33ff00;}
|
|
1021
|
+
.checkout-input::placeholder{color:#116600;}
|
|
1022
|
+
.checkout-card-box{
|
|
1023
|
+
border:1px solid #0a3300;border-radius:6px;padding:14px;
|
|
1024
|
+
background:#020;
|
|
1025
|
+
}
|
|
1026
|
+
.checkout-card-row{
|
|
1027
|
+
display:flex;gap:12px;margin-top:10px;
|
|
1028
|
+
}
|
|
1029
|
+
.checkout-card-row .checkout-input{flex:1;}
|
|
1030
|
+
.checkout-sim-note{
|
|
1031
|
+
font-size:.6875rem;color:#1a8c00;margin-top:10px;text-align:center;
|
|
1032
|
+
font-style:italic;
|
|
1033
|
+
}
|
|
1034
|
+
.checkout-pay-btn{
|
|
1035
|
+
width:100%;padding:14px;border:none;border-radius:8px;
|
|
1036
|
+
background:#33ff00;color:#000;font:inherit;font-size:.9375rem;font-weight:700;
|
|
1037
|
+
cursor:pointer;transition:background .15s;
|
|
1038
|
+
font-family:'Geist Pixel',monospace;
|
|
1039
|
+
}
|
|
1040
|
+
.checkout-pay-btn:hover{background:#44ff22;}
|
|
1041
|
+
.checkout-cancel{
|
|
1042
|
+
text-align:center;margin-top:14px;
|
|
1043
|
+
}
|
|
1044
|
+
.checkout-cancel a{
|
|
1045
|
+
color:#1a8c00;text-decoration:none;font-size:.8125rem;
|
|
1046
|
+
transition:color .15s;
|
|
1047
|
+
}
|
|
1048
|
+
.checkout-cancel a:hover{color:#33ff00;}
|
|
1049
|
+
@media(max-width:768px){
|
|
1050
|
+
.checkout-layout{flex-direction:column;}
|
|
1051
|
+
.checkout-summary{padding:32px 20px;border-right:none;border-bottom:1px solid #0a3300;}
|
|
1052
|
+
.checkout-form-side{padding:32px 20px;}
|
|
1053
|
+
}
|
|
924
1054
|
`;
|
|
925
1055
|
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://emulate.dev" target="_blank" rel="noopener">emulate</a></div>`;
|
|
926
1056
|
function emuBar(service) {
|
|
@@ -940,6 +1070,7 @@ function head(title) {
|
|
|
940
1070
|
<head>
|
|
941
1071
|
<meta charset="utf-8"/>
|
|
942
1072
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
1073
|
+
<link rel="icon" href="/_emulate/favicon.ico"/>
|
|
943
1074
|
<title>${escapeHtml(title)} | emulate</title>
|
|
944
1075
|
<style>${CSS}</style>
|
|
945
1076
|
</head>`;
|
|
@@ -1049,9 +1180,7 @@ function canAccessRepo(gh, authUser, repo) {
|
|
|
1049
1180
|
if (!user) return false;
|
|
1050
1181
|
if (repo.owner_type === "User" && repo.owner_id === user.id) return true;
|
|
1051
1182
|
if (repo.owner_type === "Organization" && isOrgMember(gh, user.id, repo.owner_id)) return true;
|
|
1052
|
-
return Boolean(
|
|
1053
|
-
gh.collaborators.findBy("repo_id", repo.id).find((c) => c.user_id === user.id)
|
|
1054
|
-
);
|
|
1183
|
+
return Boolean(gh.collaborators.findBy("repo_id", repo.id).find((c) => c.user_id === user.id));
|
|
1055
1184
|
}
|
|
1056
1185
|
function assertRepoRead(gh, authUser, repo) {
|
|
1057
1186
|
if (canAccessRepo(gh, authUser, repo)) return;
|
|
@@ -1092,9 +1221,7 @@ function assertIssueWrite(gh, authUser, repo) {
|
|
|
1092
1221
|
|
|
1093
1222
|
// src/routes/users.ts
|
|
1094
1223
|
function listReposForUser(gh, user, type) {
|
|
1095
|
-
const owned = gh.repos.all().filter(
|
|
1096
|
-
(r) => r.owner_id === user.id && r.owner_type === "User"
|
|
1097
|
-
);
|
|
1224
|
+
const owned = gh.repos.all().filter((r) => r.owner_id === user.id && r.owner_type === "User");
|
|
1098
1225
|
const member = gh.collaborators.findBy("user_id", user.id).map((c) => gh.repos.get(c.repo_id)).filter((r) => Boolean(r)).filter((r) => !(r.owner_id === user.id && r.owner_type === "User"));
|
|
1099
1226
|
if (type === "owner") return owned;
|
|
1100
1227
|
if (type === "member") return member;
|
|
@@ -1222,10 +1349,7 @@ function usersRoutes({ app, store, baseUrl }) {
|
|
|
1222
1349
|
});
|
|
1223
1350
|
app.get("/users", (c) => {
|
|
1224
1351
|
const since = Math.max(0, parseInt(c.req.query("since") ?? "0", 10) || 0);
|
|
1225
|
-
const perPage = Math.min(
|
|
1226
|
-
100,
|
|
1227
|
-
Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30)
|
|
1228
|
-
);
|
|
1352
|
+
const perPage = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
|
|
1229
1353
|
const ordered = gh.users.all().filter((u) => u.id > since).sort((a, b) => a.id - b.id);
|
|
1230
1354
|
const page = ordered.slice(0, perPage);
|
|
1231
1355
|
if (page.length === perPage && ordered.length > perPage) {
|
|
@@ -1813,7 +1937,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
1813
1937
|
let ownerType = "User";
|
|
1814
1938
|
let ownerId = user.id;
|
|
1815
1939
|
let fullName = "";
|
|
1816
|
-
|
|
1940
|
+
const forkName = typeof body.name === "string" && body.name.trim() ? validateRepoName(body.name) : parent.name;
|
|
1817
1941
|
if (typeof body.organization === "string" && body.organization.trim()) {
|
|
1818
1942
|
const org = gh.orgs.findOneBy("login", body.organization.trim());
|
|
1819
1943
|
if (!org) throw notFound();
|
|
@@ -1896,9 +2020,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
1896
2020
|
const u = gh.users.get(col.user_id);
|
|
1897
2021
|
if (!u) return null;
|
|
1898
2022
|
return { user: u, permission: col.permission };
|
|
1899
|
-
}).filter(
|
|
1900
|
-
(x) => Boolean(x)
|
|
1901
|
-
).sort((a, b) => a.user.login.localeCompare(b.user.login));
|
|
2023
|
+
}).filter((x) => Boolean(x)).sort((a, b) => a.user.login.localeCompare(b.user.login));
|
|
1902
2024
|
const { page, per_page } = parsePagination(c);
|
|
1903
2025
|
const total = users.length;
|
|
1904
2026
|
const start = (page - 1) * per_page;
|
|
@@ -2019,9 +2141,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
2019
2141
|
const repo = lookupRepo(gh, owner, repoName);
|
|
2020
2142
|
if (!repo) throw notFound();
|
|
2021
2143
|
assertRepoRead(gh, c.get("authUser"), repo);
|
|
2022
|
-
const tags = [...gh.tags.findBy("repo_id", repo.id)].sort(
|
|
2023
|
-
(a, b) => a.tag.localeCompare(b.tag)
|
|
2024
|
-
);
|
|
2144
|
+
const tags = [...gh.tags.findBy("repo_id", repo.id)].sort((a, b) => a.tag.localeCompare(b.tag));
|
|
2025
2145
|
const { page, per_page } = parsePagination(c);
|
|
2026
2146
|
const total = tags.length;
|
|
2027
2147
|
const start = (page - 1) * per_page;
|
|
@@ -2931,9 +3051,7 @@ function checkMergeRequirements(gh, pr) {
|
|
|
2931
3051
|
if (checks && checks.contexts.length > 0) {
|
|
2932
3052
|
const runs = gh.checkRuns.findBy("repo_id", baseRepo.id).filter((r) => r.head_sha === pr.head_sha);
|
|
2933
3053
|
for (const ctx of checks.contexts) {
|
|
2934
|
-
const ok = runs.some(
|
|
2935
|
-
(r) => r.name === ctx && r.status === "completed" && r.conclusion === "success"
|
|
2936
|
-
);
|
|
3054
|
+
const ok = runs.some((r) => r.name === ctx && r.status === "completed" && r.conclusion === "success");
|
|
2937
3055
|
if (!ok) {
|
|
2938
3056
|
throw new ApiError(422, "Required status checks have not succeeded.");
|
|
2939
3057
|
}
|
|
@@ -3367,9 +3485,7 @@ ${commitMessage}` : commitTitle;
|
|
|
3367
3485
|
changes: 1,
|
|
3368
3486
|
blob_url: `${baseUrl}/${repo.full_name}/blob/${pr.head_sha}/${filename}`,
|
|
3369
3487
|
raw_url: `${baseUrl}/${repo.full_name}/raw/${pr.head_sha}/${filename}`,
|
|
3370
|
-
contents_url: `${baseUrl}/repos/${repo.full_name}/contents/${encodeURIComponent(
|
|
3371
|
-
filename
|
|
3372
|
-
)}?ref=${pr.head_ref}`,
|
|
3488
|
+
contents_url: `${baseUrl}/repos/${repo.full_name}/contents/${encodeURIComponent(filename)}?ref=${pr.head_ref}`,
|
|
3373
3489
|
patch: ""
|
|
3374
3490
|
}))
|
|
3375
3491
|
);
|
|
@@ -4106,7 +4222,7 @@ function reviewsRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
4106
4222
|
const pr = findPull3(gh, repo.id, pullNumber);
|
|
4107
4223
|
if (!pr) throw notFound();
|
|
4108
4224
|
const { page, per_page } = parsePagination(c);
|
|
4109
|
-
|
|
4225
|
+
const list = gh.reviews.findBy("repo_id", repo.id).filter((r) => r.pull_number === pullNumber);
|
|
4110
4226
|
list.sort((a, b) => a.id - b.id);
|
|
4111
4227
|
const total = list.length;
|
|
4112
4228
|
setLinkHeader(c, total, page, per_page);
|
|
@@ -4301,9 +4417,7 @@ function reviewsRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
4301
4417
|
if (!review) throw notFound();
|
|
4302
4418
|
const { page, per_page } = parsePagination(c);
|
|
4303
4419
|
const { sort, direction } = parseCommentSort2(c, "asc");
|
|
4304
|
-
let list = gh.comments.findBy("repo_id", repo.id).filter(
|
|
4305
|
-
(x) => x.comment_type === "review" && x.pull_number === pullNumber && x.review_id === reviewId
|
|
4306
|
-
);
|
|
4420
|
+
let list = gh.comments.findBy("repo_id", repo.id).filter((x) => x.comment_type === "review" && x.pull_number === pullNumber && x.review_id === reviewId);
|
|
4307
4421
|
list = sortComments2(list, sort, direction);
|
|
4308
4422
|
const total = list.length;
|
|
4309
4423
|
setLinkHeader(c, total, page, per_page);
|
|
@@ -4360,7 +4474,7 @@ function normalizeColor(raw) {
|
|
|
4360
4474
|
if (typeof raw !== "string" || !raw.trim()) {
|
|
4361
4475
|
throw new ApiError(422, "Validation failed");
|
|
4362
4476
|
}
|
|
4363
|
-
|
|
4477
|
+
const s = raw.trim().replace(/^#/, "");
|
|
4364
4478
|
if (!/^[0-9a-fA-F]{6}$/.test(s)) {
|
|
4365
4479
|
throw new ApiError(422, "Validation failed");
|
|
4366
4480
|
}
|
|
@@ -4446,7 +4560,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
4446
4560
|
if (!repo) throw notFound();
|
|
4447
4561
|
assertRepoRead(gh, c.get("authUser"), repo);
|
|
4448
4562
|
const { page, per_page } = parsePagination(c);
|
|
4449
|
-
|
|
4563
|
+
const list = gh.labels.findBy("repo_id", repo.id).slice();
|
|
4450
4564
|
list.sort((a, b) => a.name.localeCompare(b.name));
|
|
4451
4565
|
const total = list.length;
|
|
4452
4566
|
setLinkHeader(c, total, page, per_page);
|
|
@@ -4806,7 +4920,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
4806
4920
|
creator_id: actor.id
|
|
4807
4921
|
});
|
|
4808
4922
|
gh.milestones.update(row.id, { node_id: generateNodeId("Milestone", row.id) });
|
|
4809
|
-
|
|
4923
|
+
const m = recalcMilestoneIssueCounts(gh, repo.id, row.id);
|
|
4810
4924
|
const ownerLogin2 = ownerLoginOf(gh, repo);
|
|
4811
4925
|
webhooks.dispatch(
|
|
4812
4926
|
"milestone",
|
|
@@ -4963,7 +5077,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
4963
5077
|
if (i.milestone_id !== ms.id) continue;
|
|
4964
5078
|
for (const lid of i.label_ids) labelIdSet.add(lid);
|
|
4965
5079
|
}
|
|
4966
|
-
|
|
5080
|
+
const labels = [...labelIdSet].map((id) => gh.labels.get(id)).filter(Boolean);
|
|
4967
5081
|
labels.sort((a, b) => a.name.localeCompare(b.name));
|
|
4968
5082
|
const total = labels.length;
|
|
4969
5083
|
setLinkHeader(c, total, page, per_page);
|
|
@@ -5446,9 +5560,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
5446
5560
|
const repo = lookupRepo(gh, owner, repoName);
|
|
5447
5561
|
if (!repo) throw notFound();
|
|
5448
5562
|
assertRepoRead(gh, c.get("authUser"), repo);
|
|
5449
|
-
let list = [...gh.branches.findBy("repo_id", repo.id)].sort(
|
|
5450
|
-
(a, b) => a.name.localeCompare(b.name)
|
|
5451
|
-
);
|
|
5563
|
+
let list = [...gh.branches.findBy("repo_id", repo.id)].sort((a, b) => a.name.localeCompare(b.name));
|
|
5452
5564
|
const prot = c.req.query("protected");
|
|
5453
5565
|
if (prot === "true") list = list.filter((b) => b.protected);
|
|
5454
5566
|
else if (prot === "false") list = list.filter((b) => !b.protected);
|
|
@@ -5851,12 +5963,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
5851
5963
|
object: {
|
|
5852
5964
|
type: tag.object_type,
|
|
5853
5965
|
sha: tag.object_sha,
|
|
5854
|
-
url: objectApiUrl(
|
|
5855
|
-
repo,
|
|
5856
|
-
baseUrl,
|
|
5857
|
-
resolveGitObjectType(gh, repo.id, tag.object_sha),
|
|
5858
|
-
tag.object_sha
|
|
5859
|
-
)
|
|
5966
|
+
url: objectApiUrl(repo, baseUrl, resolveGitObjectType(gh, repo.id, tag.object_sha), tag.object_sha)
|
|
5860
5967
|
},
|
|
5861
5968
|
verification: { verified: false, reason: "unsigned", signature: null, payload: null, verified_at: null }
|
|
5862
5969
|
});
|
|
@@ -5912,12 +6019,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
5912
6019
|
object: {
|
|
5913
6020
|
type: saved.object_type,
|
|
5914
6021
|
sha: saved.object_sha,
|
|
5915
|
-
url: objectApiUrl(
|
|
5916
|
-
repo,
|
|
5917
|
-
baseUrl,
|
|
5918
|
-
resolveGitObjectType(gh, repo.id, saved.object_sha),
|
|
5919
|
-
saved.object_sha
|
|
5920
|
-
)
|
|
6022
|
+
url: objectApiUrl(repo, baseUrl, resolveGitObjectType(gh, repo.id, saved.object_sha), saved.object_sha)
|
|
5921
6023
|
},
|
|
5922
6024
|
verification: { verified: false, reason: "unsigned", signature: null, payload: null, verified_at: null }
|
|
5923
6025
|
},
|
|
@@ -6068,10 +6170,7 @@ function orgsAndTeamsRoutes({ app, store, baseUrl }) {
|
|
|
6068
6170
|
const gh = getGitHubStore(store);
|
|
6069
6171
|
app.get("/organizations", (c) => {
|
|
6070
6172
|
const since = Math.max(0, parseInt(c.req.query("since") ?? "0", 10) || 0);
|
|
6071
|
-
const perPage = Math.min(
|
|
6072
|
-
100,
|
|
6073
|
-
Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30)
|
|
6074
|
-
);
|
|
6173
|
+
const perPage = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
|
|
6075
6174
|
const ordered = gh.orgs.all().filter((o) => o.id > since).sort((a, b) => a.id - b.id);
|
|
6076
6175
|
const page = ordered.slice(0, perPage);
|
|
6077
6176
|
if (page.length === perPage && ordered.length > perPage) {
|
|
@@ -6575,9 +6674,7 @@ _Auto-generated release notes (stub)._
|
|
|
6575
6674
|
const repo = lookupRepo(gh, owner, repoName);
|
|
6576
6675
|
if (!repo) throw notFound();
|
|
6577
6676
|
assertRepoRead(gh, c.get("authUser"), repo);
|
|
6578
|
-
const candidates = releasesForRepo(gh, repo.id).filter(
|
|
6579
|
-
(r) => !r.draft && !r.prerelease && r.published_at
|
|
6580
|
-
);
|
|
6677
|
+
const candidates = releasesForRepo(gh, repo.id).filter((r) => !r.draft && !r.prerelease && r.published_at);
|
|
6581
6678
|
if (candidates.length === 0) throw notFound();
|
|
6582
6679
|
candidates.sort((a, b) => {
|
|
6583
6680
|
const pa = a.published_at ?? a.created_at;
|
|
@@ -7792,9 +7889,7 @@ function searchRoutes({ app, store, baseUrl }) {
|
|
|
7792
7889
|
(a, b) => order === "desc" ? b.stargazers_count - a.stargazers_count : a.stargazers_count - b.stargazers_count
|
|
7793
7890
|
);
|
|
7794
7891
|
} else if (sortRaw === "forks") {
|
|
7795
|
-
list.sort(
|
|
7796
|
-
(a, b) => order === "desc" ? b.forks_count - a.forks_count : a.forks_count - b.forks_count
|
|
7797
|
-
);
|
|
7892
|
+
list.sort((a, b) => order === "desc" ? b.forks_count - a.forks_count : a.forks_count - b.forks_count);
|
|
7798
7893
|
} else if (sortRaw === "updated") {
|
|
7799
7894
|
list.sort(
|
|
7800
7895
|
(a, b) => order === "desc" ? b.updated_at.localeCompare(a.updated_at) : a.updated_at.localeCompare(b.updated_at)
|
|
@@ -7846,7 +7941,7 @@ function searchRoutes({ app, store, baseUrl }) {
|
|
|
7846
7941
|
if (body.toLowerCase().includes(t)) s += 1;
|
|
7847
7942
|
return s;
|
|
7848
7943
|
}
|
|
7849
|
-
|
|
7944
|
+
const sorted = [...hits];
|
|
7850
7945
|
if (sortRaw === "created") {
|
|
7851
7946
|
sorted.sort((a, b) => {
|
|
7852
7947
|
const ca = a.kind === "issue" ? a.issue.created_at : a.pr.created_at;
|
|
@@ -7921,7 +8016,7 @@ function searchRoutes({ app, store, baseUrl }) {
|
|
|
7921
8016
|
if (h.o.name?.toLowerCase().includes(text)) s += 1;
|
|
7922
8017
|
return s;
|
|
7923
8018
|
}
|
|
7924
|
-
|
|
8019
|
+
const list = [...hits];
|
|
7925
8020
|
if (sortRaw === "followers") {
|
|
7926
8021
|
list.sort((a, b) => {
|
|
7927
8022
|
const fa = a.kind === "user" ? a.u.followers : a.o.followers;
|
|
@@ -7947,9 +8042,7 @@ function searchRoutes({ app, store, baseUrl }) {
|
|
|
7947
8042
|
const total = list.length;
|
|
7948
8043
|
const slice = list.slice((page - 1) * per_page, (page - 1) * per_page + per_page);
|
|
7949
8044
|
setLinkHeader(c, total, page, per_page);
|
|
7950
|
-
const items = slice.map(
|
|
7951
|
-
(h) => h.kind === "user" ? formatUser(h.u, baseUrl) : formatOrgBrief(h.o, baseUrl)
|
|
7952
|
-
);
|
|
8045
|
+
const items = slice.map((h) => h.kind === "user" ? formatUser(h.u, baseUrl) : formatOrgBrief(h.o, baseUrl));
|
|
7953
8046
|
return c.json({
|
|
7954
8047
|
total_count: total,
|
|
7955
8048
|
incomplete_results: false,
|
|
@@ -8400,7 +8493,13 @@ function formatArtifact(a, repo, gh, baseUrl) {
|
|
|
8400
8493
|
digest: null,
|
|
8401
8494
|
created_at: a.created_at,
|
|
8402
8495
|
expires_at: a.expires_at,
|
|
8403
|
-
workflow_run: run ? {
|
|
8496
|
+
workflow_run: run ? {
|
|
8497
|
+
id: run.id,
|
|
8498
|
+
repository_id: repo.id,
|
|
8499
|
+
head_repository_id: repo.id,
|
|
8500
|
+
head_branch: run.head_branch,
|
|
8501
|
+
head_sha: run.head_sha
|
|
8502
|
+
} : null
|
|
8404
8503
|
};
|
|
8405
8504
|
}
|
|
8406
8505
|
function filterRuns(gh, runs, q) {
|
|
@@ -8518,7 +8617,11 @@ function actionsRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
8518
8617
|
void webhooks.dispatch(
|
|
8519
8618
|
"workflow_run",
|
|
8520
8619
|
"requested",
|
|
8521
|
-
{
|
|
8620
|
+
{
|
|
8621
|
+
workflow_run: formatWorkflowRun(created, repo, gh, baseUrl),
|
|
8622
|
+
repository: formatRepo(repo, gh, baseUrl),
|
|
8623
|
+
sender: formatUser(actor, baseUrl)
|
|
8624
|
+
},
|
|
8522
8625
|
ownerLogin2,
|
|
8523
8626
|
repo.name
|
|
8524
8627
|
);
|
|
@@ -8674,13 +8777,11 @@ function actionsRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
8674
8777
|
const runId = parseInt(c.req.param("run_id"), 10);
|
|
8675
8778
|
const run = gh.workflowRuns.get(runId);
|
|
8676
8779
|
if (!run || run.repo_id !== repo.id) throw notFound();
|
|
8677
|
-
return c.text(
|
|
8678
|
-
`2025-01-01T00:00:00.0000000Z Workflow run ${run.id} logs (stub)
|
|
8780
|
+
return c.text(`2025-01-01T00:00:00.0000000Z Workflow run ${run.id} logs (stub)
|
|
8679
8781
|
${run.head_sha}
|
|
8680
|
-
`,
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
);
|
|
8782
|
+
`, 200, {
|
|
8783
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
8784
|
+
});
|
|
8684
8785
|
});
|
|
8685
8786
|
app.get("/repos/:owner/:repo/actions/runs/:run_id/jobs", (c) => {
|
|
8686
8787
|
const owner = c.req.param("owner");
|
|
@@ -9001,15 +9102,7 @@ function parseConclusion(raw) {
|
|
|
9001
9102
|
if (raw === void 0) return void 0;
|
|
9002
9103
|
if (raw === null) return null;
|
|
9003
9104
|
if (typeof raw !== "string") throw new ApiError(422, "Invalid conclusion");
|
|
9004
|
-
const allowed = /* @__PURE__ */ new Set([
|
|
9005
|
-
"success",
|
|
9006
|
-
"failure",
|
|
9007
|
-
"neutral",
|
|
9008
|
-
"cancelled",
|
|
9009
|
-
"skipped",
|
|
9010
|
-
"timed_out",
|
|
9011
|
-
"action_required"
|
|
9012
|
-
]);
|
|
9105
|
+
const allowed = /* @__PURE__ */ new Set(["success", "failure", "neutral", "cancelled", "skipped", "timed_out", "action_required"]);
|
|
9013
9106
|
if (!allowed.has(raw)) throw new ApiError(422, "Invalid conclusion");
|
|
9014
9107
|
return raw;
|
|
9015
9108
|
}
|
|
@@ -9393,7 +9486,7 @@ function checksRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
9393
9486
|
};
|
|
9394
9487
|
}
|
|
9395
9488
|
const nextStatus = patch.status ?? prev.status;
|
|
9396
|
-
|
|
9489
|
+
const nextConclusion = patch.conclusion !== void 0 ? patch.conclusion : prev.conclusion;
|
|
9397
9490
|
if (patch.head_sha && patch.head_sha !== prev.head_sha) {
|
|
9398
9491
|
const newSuite = getOrCreateCheckSuite(gh, repo, patch.head_sha, null);
|
|
9399
9492
|
patch.check_suite_id = newSuite.id;
|
|
@@ -9403,7 +9496,7 @@ function checksRoutes({ app, store, webhooks, baseUrl }) {
|
|
|
9403
9496
|
throw new ApiError(422, "conclusion is required when status is completed");
|
|
9404
9497
|
}
|
|
9405
9498
|
patch.conclusion = nextConclusion;
|
|
9406
|
-
|
|
9499
|
+
const nextCompleted = patch.completed_at !== void 0 ? patch.completed_at : prev.completed_at;
|
|
9407
9500
|
if (!nextCompleted) {
|
|
9408
9501
|
patch.completed_at = timestamp();
|
|
9409
9502
|
}
|
|
@@ -9542,7 +9635,13 @@ function rateLimitRoutes({ app }) {
|
|
|
9542
9635
|
integration_manifest: { limit: 5e3, remaining: 4999, reset, used: 1, resource: "integration_manifest" },
|
|
9543
9636
|
source_import: { limit: 100, remaining: 99, reset, used: 1, resource: "source_import" },
|
|
9544
9637
|
code_scanning_upload: { limit: 500, remaining: 499, reset, used: 1, resource: "code_scanning_upload" },
|
|
9545
|
-
actions_runner_registration: {
|
|
9638
|
+
actions_runner_registration: {
|
|
9639
|
+
limit: 1e4,
|
|
9640
|
+
remaining: 9999,
|
|
9641
|
+
reset,
|
|
9642
|
+
used: 1,
|
|
9643
|
+
resource: "actions_runner_registration"
|
|
9644
|
+
},
|
|
9546
9645
|
scim: { limit: 15e3, remaining: 14999, reset, used: 1, resource: "scim" }
|
|
9547
9646
|
},
|
|
9548
9647
|
rate: rateLimit
|
|
@@ -9620,13 +9719,13 @@ function metaRoutes({ app, baseUrl }) {
|
|
|
9620
9719
|
"+1": `${baseUrl}/emojis/+1.png`,
|
|
9621
9720
|
"-1": `${baseUrl}/emojis/-1.png`,
|
|
9622
9721
|
"100": `${baseUrl}/emojis/100.png`,
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9722
|
+
tada: `${baseUrl}/emojis/tada.png`,
|
|
9723
|
+
rocket: `${baseUrl}/emojis/rocket.png`,
|
|
9724
|
+
heart: `${baseUrl}/emojis/heart.png`,
|
|
9725
|
+
eyes: `${baseUrl}/emojis/eyes.png`,
|
|
9726
|
+
thinking: `${baseUrl}/emojis/thinking.png`,
|
|
9727
|
+
thumbsup: `${baseUrl}/emojis/thumbsup.png`,
|
|
9728
|
+
thumbsdown: `${baseUrl}/emojis/thumbsdown.png`
|
|
9630
9729
|
});
|
|
9631
9730
|
});
|
|
9632
9731
|
app.get("/zen", (c) => {
|
|
@@ -9726,9 +9825,15 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9726
9825
|
);
|
|
9727
9826
|
}
|
|
9728
9827
|
if (redirect_uri && !matchesRedirectUri(redirect_uri, oauthApp.redirect_uris)) {
|
|
9729
|
-
console.warn(
|
|
9828
|
+
console.warn(
|
|
9829
|
+
`[OAuth] redirect_uri mismatch: got "${redirect_uri}", registered: ${JSON.stringify(oauthApp.redirect_uris)}`
|
|
9830
|
+
);
|
|
9730
9831
|
return c.html(
|
|
9731
|
-
renderErrorPage(
|
|
9832
|
+
renderErrorPage(
|
|
9833
|
+
"Redirect URI mismatch",
|
|
9834
|
+
"The redirect_uri is not registered for this application.",
|
|
9835
|
+
SERVICE_LABEL
|
|
9836
|
+
),
|
|
9732
9837
|
400
|
|
9733
9838
|
);
|
|
9734
9839
|
}
|
|
@@ -9772,7 +9877,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9772
9877
|
clientId: client_id,
|
|
9773
9878
|
created_at: Date.now()
|
|
9774
9879
|
});
|
|
9775
|
-
debug(
|
|
9880
|
+
debug(
|
|
9881
|
+
"github.oauth",
|
|
9882
|
+
`[OAuth callback] generated code: ${code.slice(0, 8)}... for login=${login}, pendingCodes size: ${getPendingCodes(store).size}`
|
|
9883
|
+
);
|
|
9776
9884
|
const sessionId = randomBytes2(24).toString("base64url");
|
|
9777
9885
|
getSessionMap(store).set(sessionId, login);
|
|
9778
9886
|
c.header("Set-Cookie", `_emu_session=${sessionId}; Path=/; HttpOnly; SameSite=Lax`);
|
|
@@ -9787,7 +9895,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9787
9895
|
debug("github.oauth", `[OAuth token] Content-Type: ${contentType}`);
|
|
9788
9896
|
debug("github.oauth", `[OAuth token] Accept: ${accept}`);
|
|
9789
9897
|
debug("github.oauth", `[OAuth token] pendingCodes size: ${getPendingCodes(store).size}`);
|
|
9790
|
-
debug(
|
|
9898
|
+
debug(
|
|
9899
|
+
"github.oauth",
|
|
9900
|
+
`[OAuth token] pendingCodes keys: ${[...getPendingCodes(store).keys()].map((k) => k.slice(0, 8) + "...").join(", ")}`
|
|
9901
|
+
);
|
|
9791
9902
|
const rawText = await c.req.text();
|
|
9792
9903
|
debug("github.oauth", `[OAuth token] raw body: ${rawText.slice(0, 500)}`);
|
|
9793
9904
|
let raw;
|
|
@@ -9858,14 +9969,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9858
9969
|
}
|
|
9859
9970
|
const oauthApp = gh.oauthApps.findOneBy("client_id", pending.clientId);
|
|
9860
9971
|
if (oauthApp) {
|
|
9861
|
-
const existingGrant = gh.oauthGrants.all().find(
|
|
9862
|
-
(g) => g.user_id === user.id && g.client_id === pending.clientId
|
|
9863
|
-
);
|
|
9972
|
+
const existingGrant = gh.oauthGrants.all().find((g) => g.user_id === user.id && g.client_id === pending.clientId);
|
|
9864
9973
|
const orgAccess = {};
|
|
9865
9974
|
for (const org of gh.orgs.all()) {
|
|
9866
|
-
const isMember = gh.teamMembers.all().some(
|
|
9867
|
-
(tm) => tm.user_id === user.id && gh.teams.get(tm.team_id)?.org_id === org.id
|
|
9868
|
-
);
|
|
9975
|
+
const isMember = gh.teamMembers.all().some((tm) => tm.user_id === user.id && gh.teams.get(tm.team_id)?.org_id === org.id);
|
|
9869
9976
|
if (isMember) orgAccess[org.login] = "granted";
|
|
9870
9977
|
}
|
|
9871
9978
|
if (existingGrant) {
|
|
@@ -9915,19 +10022,19 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9915
10022
|
]);
|
|
9916
10023
|
});
|
|
9917
10024
|
const SCOPE_LABELS = {
|
|
9918
|
-
|
|
10025
|
+
repo: "Full control of private repositories",
|
|
9919
10026
|
"read:user": "Read all user profile data",
|
|
9920
10027
|
"user:email": "Access user email addresses (read-only)",
|
|
9921
|
-
|
|
9922
|
-
|
|
10028
|
+
user: "Full control of user profile",
|
|
10029
|
+
workflow: "Update GitHub action workflows",
|
|
9923
10030
|
"admin:org": "Full control of orgs and teams",
|
|
9924
10031
|
"admin:repo_hook": "Full control of repository hooks",
|
|
9925
10032
|
"read:org": "Read org and team membership",
|
|
9926
10033
|
"write:repo_hook": "Write repository hooks",
|
|
9927
10034
|
"read:repo_hook": "Read repository hooks",
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
10035
|
+
delete_repo: "Delete repositories",
|
|
10036
|
+
gist: "Create gists",
|
|
10037
|
+
notifications: "Access notifications",
|
|
9931
10038
|
"write:packages": "Upload packages",
|
|
9932
10039
|
"read:packages": "Download packages",
|
|
9933
10040
|
"admin:gpg_key": "Full control of GPG keys",
|
|
@@ -9941,7 +10048,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9941
10048
|
app.get("/settings/applications", (c) => {
|
|
9942
10049
|
const sessionUser = resolveSessionUser(c);
|
|
9943
10050
|
if (!sessionUser) {
|
|
9944
|
-
return c.html(
|
|
10051
|
+
return c.html(
|
|
10052
|
+
renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL),
|
|
10053
|
+
401
|
|
10054
|
+
);
|
|
9945
10055
|
}
|
|
9946
10056
|
const grants = gh.oauthGrants.findBy("user_id", sessionUser.id);
|
|
9947
10057
|
let bodyHtml;
|
|
@@ -9974,12 +10084,13 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9974
10084
|
app.get("/settings/connections/applications/:client_id", (c) => {
|
|
9975
10085
|
const sessionUser = resolveSessionUser(c);
|
|
9976
10086
|
if (!sessionUser) {
|
|
9977
|
-
return c.html(
|
|
10087
|
+
return c.html(
|
|
10088
|
+
renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL),
|
|
10089
|
+
401
|
|
10090
|
+
);
|
|
9978
10091
|
}
|
|
9979
10092
|
const clientId = c.req.param("client_id");
|
|
9980
|
-
const grant = gh.oauthGrants.all().find(
|
|
9981
|
-
(g) => g.user_id === sessionUser.id && g.client_id === clientId
|
|
9982
|
-
);
|
|
10093
|
+
const grant = gh.oauthGrants.all().find((g) => g.user_id === sessionUser.id && g.client_id === clientId);
|
|
9983
10094
|
if (!grant) {
|
|
9984
10095
|
return c.html(renderErrorPage("Not Found", "No authorization found for this application.", SERVICE_LABEL), 404);
|
|
9985
10096
|
}
|
|
@@ -9991,9 +10102,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
9991
10102
|
month: "long",
|
|
9992
10103
|
day: "numeric"
|
|
9993
10104
|
});
|
|
9994
|
-
const permRows = grant.scopes.map(
|
|
9995
|
-
(s) => `<li><span class="check">✓</span> ${escapeHtml(scopeLabel(s))}</li>`
|
|
9996
|
-
).join("\n");
|
|
10105
|
+
const permRows = grant.scopes.map((s) => `<li><span class="check">✓</span> ${escapeHtml(scopeLabel(s))}</li>`).join("\n");
|
|
9997
10106
|
const orgRows = Object.entries(grant.org_access).map(([org, status]) => {
|
|
9998
10107
|
const letter = escapeHtml((org[0] ?? "?").toUpperCase());
|
|
9999
10108
|
const badgeClass = status === "granted" ? "badge-granted" : status === "denied" ? "badge-denied" : "badge-requested";
|
|
@@ -10037,12 +10146,13 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10037
10146
|
app.post("/settings/connections/applications/:client_id/revoke", (c) => {
|
|
10038
10147
|
const sessionUser = resolveSessionUser(c);
|
|
10039
10148
|
if (!sessionUser) {
|
|
10040
|
-
return c.html(
|
|
10149
|
+
return c.html(
|
|
10150
|
+
renderErrorPage("Unauthorized", "You must be authenticated to perform this action.", SERVICE_LABEL),
|
|
10151
|
+
401
|
|
10152
|
+
);
|
|
10041
10153
|
}
|
|
10042
10154
|
const clientId = c.req.param("client_id");
|
|
10043
|
-
const grant = gh.oauthGrants.all().find(
|
|
10044
|
-
(g) => g.user_id === sessionUser.id && g.client_id === clientId
|
|
10045
|
-
);
|
|
10155
|
+
const grant = gh.oauthGrants.all().find((g) => g.user_id === sessionUser.id && g.client_id === clientId);
|
|
10046
10156
|
if (grant) {
|
|
10047
10157
|
gh.oauthGrants.delete(grant.id);
|
|
10048
10158
|
}
|
|
@@ -10073,10 +10183,13 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10073
10183
|
app.get("/app", (c) => {
|
|
10074
10184
|
const authApp = requireApp(c);
|
|
10075
10185
|
if (!authApp) {
|
|
10076
|
-
return c.json(
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10186
|
+
return c.json(
|
|
10187
|
+
{
|
|
10188
|
+
message: "A JSON web token could not be decoded",
|
|
10189
|
+
documentation_url: "https://docs.github.com/rest"
|
|
10190
|
+
},
|
|
10191
|
+
401
|
|
10192
|
+
);
|
|
10080
10193
|
}
|
|
10081
10194
|
const ghApp = gh.apps.all().find((a) => a.app_id === authApp.appId);
|
|
10082
10195
|
if (!ghApp) {
|
|
@@ -10102,29 +10215,31 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10102
10215
|
app.get("/app/installations", (c) => {
|
|
10103
10216
|
const authApp = requireApp(c);
|
|
10104
10217
|
if (!authApp) {
|
|
10105
|
-
return c.json(
|
|
10106
|
-
|
|
10107
|
-
|
|
10108
|
-
|
|
10218
|
+
return c.json(
|
|
10219
|
+
{
|
|
10220
|
+
message: "A JSON web token could not be decoded",
|
|
10221
|
+
documentation_url: "https://docs.github.com/rest"
|
|
10222
|
+
},
|
|
10223
|
+
401
|
|
10224
|
+
);
|
|
10109
10225
|
}
|
|
10110
10226
|
const installations = gh.appInstallations.findBy("app_id", authApp.appId);
|
|
10111
10227
|
const ghApp = gh.apps.all().find((a) => a.app_id === authApp.appId);
|
|
10112
|
-
return c.json(
|
|
10113
|
-
installations.map((inst) => formatInstallation(inst, ghApp, baseUrl))
|
|
10114
|
-
);
|
|
10228
|
+
return c.json(installations.map((inst) => formatInstallation(inst, ghApp, baseUrl)));
|
|
10115
10229
|
});
|
|
10116
10230
|
app.get("/app/installations/:installation_id", (c) => {
|
|
10117
10231
|
const authApp = requireApp(c);
|
|
10118
10232
|
if (!authApp) {
|
|
10119
|
-
return c.json(
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
|
|
10233
|
+
return c.json(
|
|
10234
|
+
{
|
|
10235
|
+
message: "A JSON web token could not be decoded",
|
|
10236
|
+
documentation_url: "https://docs.github.com/rest"
|
|
10237
|
+
},
|
|
10238
|
+
401
|
|
10239
|
+
);
|
|
10123
10240
|
}
|
|
10124
10241
|
const installationId = parseInt(c.req.param("installation_id"), 10);
|
|
10125
|
-
const inst = gh.appInstallations.all().find(
|
|
10126
|
-
(i) => i.installation_id === installationId && i.app_id === authApp.appId
|
|
10127
|
-
);
|
|
10242
|
+
const inst = gh.appInstallations.all().find((i) => i.installation_id === installationId && i.app_id === authApp.appId);
|
|
10128
10243
|
if (!inst) {
|
|
10129
10244
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10130
10245
|
}
|
|
@@ -10134,15 +10249,16 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10134
10249
|
app.post("/app/installations/:installation_id/access_tokens", async (c) => {
|
|
10135
10250
|
const authApp = requireApp(c);
|
|
10136
10251
|
if (!authApp) {
|
|
10137
|
-
return c.json(
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10252
|
+
return c.json(
|
|
10253
|
+
{
|
|
10254
|
+
message: "A JSON web token could not be decoded",
|
|
10255
|
+
documentation_url: "https://docs.github.com/rest"
|
|
10256
|
+
},
|
|
10257
|
+
401
|
|
10258
|
+
);
|
|
10141
10259
|
}
|
|
10142
10260
|
const installationId = parseInt(c.req.param("installation_id"), 10);
|
|
10143
|
-
const inst = gh.appInstallations.all().find(
|
|
10144
|
-
(i) => i.installation_id === installationId && i.app_id === authApp.appId
|
|
10145
|
-
);
|
|
10261
|
+
const inst = gh.appInstallations.all().find((i) => i.installation_id === installationId && i.app_id === authApp.appId);
|
|
10146
10262
|
if (!inst) {
|
|
10147
10263
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10148
10264
|
}
|
|
@@ -10176,13 +10292,16 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10176
10292
|
full_name: r.full_name,
|
|
10177
10293
|
private: r.private
|
|
10178
10294
|
}));
|
|
10179
|
-
return c.json(
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10295
|
+
return c.json(
|
|
10296
|
+
{
|
|
10297
|
+
token,
|
|
10298
|
+
expires_at: expiresAt,
|
|
10299
|
+
permissions: requestedPermissions,
|
|
10300
|
+
repository_selection: inst.repository_selection,
|
|
10301
|
+
...inst.repository_selection === "selected" ? { repositories: repos } : {}
|
|
10302
|
+
},
|
|
10303
|
+
201
|
|
10304
|
+
);
|
|
10186
10305
|
});
|
|
10187
10306
|
app.get("/repos/:owner/:repo/installation", (c) => {
|
|
10188
10307
|
const owner = c.req.param("owner");
|
|
@@ -10211,9 +10330,7 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10211
10330
|
if (!org) {
|
|
10212
10331
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10213
10332
|
}
|
|
10214
|
-
const inst = gh.appInstallations.all().find(
|
|
10215
|
-
(i) => i.account_id === org.id && i.account_type === "Organization"
|
|
10216
|
-
);
|
|
10333
|
+
const inst = gh.appInstallations.all().find((i) => i.account_id === org.id && i.account_type === "Organization");
|
|
10217
10334
|
if (!inst) {
|
|
10218
10335
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10219
10336
|
}
|
|
@@ -10226,9 +10343,7 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
|
|
|
10226
10343
|
if (!user) {
|
|
10227
10344
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10228
10345
|
}
|
|
10229
|
-
const inst = gh.appInstallations.all().find(
|
|
10230
|
-
(i) => i.account_id === user.id && i.account_type === "User"
|
|
10231
|
-
);
|
|
10346
|
+
const inst = gh.appInstallations.all().find((i) => i.account_id === user.id && i.account_type === "User");
|
|
10232
10347
|
if (!inst) {
|
|
10233
10348
|
return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
|
|
10234
10349
|
}
|