// ==UserScript== // @name kood.tech QOL // @description Buttons for managing auditor repository read access from intra // @match https://01.kood.tech/* // @grant none // @version 1.2 // @author spitko // ==/UserScript== const api = "/git/api/v1/"; function insertToggleAccessButton(auditorElement) { auditorElement.dataset.processed = true; let username = auditorElement.querySelector(".ellipsis-01").innerText; hasAccess(username).then((access) => { let button = document.createElement("button"); button.textContent = access ? "✅" : "❌"; button.onclick = function (e, newAccess) { if (e) { e.preventDefault(); } const shouldGrantAccess = newAccess !== undefined ? newAccess : button.textContent !== "✅"; if ((button.textContent === "✅") === shouldGrantAccess) { return; } if (shouldGrantAccess) { grantAccess(username).then(() => { button.textContent = "✅"; }); } else { revokeAccess(username).then(() => { button.textContent = "❌"; }); } }; button.className = "ml2-01 repo-access-button"; auditorElement.firstChild.insertAdjacentElement("beforeend", button); }); } function insertToggleAllButtons(auditRequired) { let div = document.createElement("div"); div.innerText = "Grant/revoke read access for all auditors: "; div.className = "mt3-01"; let grantButton = document.createElement("button"); grantButton.textContent = "✅"; grantButton.onclick = function () { document.querySelectorAll(".repo-access-button").forEach((button) => { button.onclick(false, true); }); }; div.appendChild(grantButton); let revokeButton = document.createElement("button"); revokeButton.textContent = "❌"; revokeButton.className = "ml1-01"; revokeButton.onclick = function () { document.querySelectorAll(".repo-access-button").forEach((button) => { button.onclick(false, false); }); }; div.appendChild(revokeButton); auditRequired.parentNode.insertAdjacentElement("beforeend", div); auditRequired.dataset.processed = true; } async function hasAccess(username) { const res = await fetch( api + `repos/${getRepo()}/collaborators/${username}`, { headers: { Authorization: `token ${getToken()}` } } ); return res.ok; } async function grantAccess(username) { return await fetch(api + `repos/${getRepo()}/collaborators/${username}`, { headers: { Authorization: `token ${getToken()}`, "Content-Type": "application/json", }, method: "PUT", body: `{"permission": "read"}`, }); } async function revokeAccess(username) { return await fetch(api + `repos/${getRepo()}/collaborators/${username}`, { headers: { Authorization: `token ${getToken()}` }, method: "DELETE", }); } function getToken() { return localStorage.getItem("token"); } function getRepo() { let repoURL = document.querySelector('a[data-test="repoLink"]').innerText; return repoURL.match(/\/git\/(.+)$/)[1]; } const observer = new MutationObserver(function (mutations) { mutations.forEach((mutation) => { if (mutation.type === "childList") { mutation.target .querySelectorAll('[data-test="audit"]:not([data-processed])') .forEach((elem) => { insertToggleAccessButton(elem); }); mutation.target .querySelectorAll('[data-test="auditRequired"]:not([data-processed])') .forEach((elem) => { insertToggleAllButtons(elem); }); mutation.target .querySelectorAll("#verificationCode-01:not([data-processed])") .forEach((elem) => { let input = elem.querySelector("input"); input.addEventListener("keyup", function (event) { if (event.keyCode === 13) { document .querySelectorAll(`input[value="true"]`) .forEach((elem) => elem.click()); document.querySelector("#submit-btn").click(); } }); elem.dataset.processed = true; }); } }); }); if (getToken()) { observer.observe(document.body, { childList: true, subtree: true }); } // Add button for saving token to localStorage // https://01.kood.tech/git/user/settings/applications if (location.href.match(/git\/user\/settings\/applications/)) { const tokenDiv = document.querySelector("div.ui.info.message.flash-info"); const button = document.createElement("button"); button.textContent = "Save to localStorage"; button.className = "ui button"; button.onclick = function () { localStorage.setItem("token", tokenDiv.firstElementChild.innerText); alert("Token saved!"); }; tokenDiv.insertAdjacentElement("beforeend", button); }