From e4647950343f1a8a6c1f38d9001d81368a469b9c Mon Sep 17 00:00:00 2001 From: Mara-Li Date: Sat, 24 Feb 2024 01:51:43 +0100 Subject: [PATCH] feat: allow setting token for different repository (#319) Close #318 --- src/GitHub/branch.ts | 9 +++-- src/commands/callback.ts | 6 +-- src/commands/file_menu.ts | 10 ++--- src/i18n/locales/en.json | 5 +++ src/i18n/locales/fr.json | 5 +++ src/main.ts | 15 ++++--- src/settings/interface.ts | 1 + src/settings/migrate.ts | 41 ++++++++++++++++--- src/settings/modals/manage_repo.ts | 65 ++++++++++++++++++++++-------- src/styles.css | 10 +++++ src/utils/data_validation_test.ts | 5 +++ 11 files changed, 134 insertions(+), 38 deletions(-) diff --git a/src/GitHub/branch.ts b/src/GitHub/branch.ts index 43b5f46e..e89622ab 100644 --- a/src/GitHub/branch.ts +++ b/src/GitHub/branch.ts @@ -282,15 +282,15 @@ export class GithubBranch extends FilesManagement { //check the error code if (e.status === 404) { new Notice( - (i18next.t("commands.checkValidity.inRepo.error404", {repo: repo})) + (i18next.t("commands.checkValidity.inRepo.error404", {repo})) ); } else if (e.status === 403) { new Notice( - (i18next.t("commands.checkValidity.inRepo.error403", {repo: repo})) + (i18next.t("commands.checkValidity.inRepo.error403", {repo})) ); } else if (e.status === 301) { new Notice( - (i18next.t("commands.checkValidity.inRepo.error301", {repo:repo})) + (i18next.t("commands.checkValidity.inRepo.error301", {repo})) ); } }); @@ -323,6 +323,9 @@ export class GithubBranch extends FilesManagement { } } catch (e) { logs({settings: this.settings, e: true}, e); + new Notice( + (i18next.t("commands.checkValidity.error", {repo})) + ); break; } } diff --git a/src/commands/callback.ts b/src/commands/callback.ts index 282e6c85..130e88c1 100644 --- a/src/commands/callback.ts +++ b/src/commands/callback.ts @@ -83,7 +83,7 @@ export async function purgeNotesRemoteCallback(plugin: GithubPublisher, repo: Re repo, }; //@ts-ignore - const publisher = await plugin.reloadOctokit(); + const publisher = await plugin.reloadOctokit(repo?.smartKey); await purgeNotesRemote( publisher, branchName, @@ -106,7 +106,7 @@ export async function shareOneNoteCallback(repo: Repository|null, plugin: Github let name = i18next.t("commands.shareActiveFile"); const common = i18next.t("common.repository"); name = repo ? `${name} (${common} : ${repo.smartKey})` : name; - const octokit = await plugin.reloadOctokit(); + const octokit = await plugin.reloadOctokit(repo?.smartKey); //@ts-ignore return { id, @@ -232,7 +232,7 @@ export async function checkRepositoryValidityCallback(plugin: GithubPublisher, r let name = i18next.t("commands.checkValidity.title"); const common = i18next.t("common.repository"); name = repo ? `${name} (${common} : ${repo.smartKey})` : name; - const octokit = await plugin.reloadOctokit(); + const octokit = await plugin.reloadOctokit(repo?.smartKey); //@ts-ignore return { id, diff --git a/src/commands/file_menu.ts b/src/commands/file_menu.ts index d51df791..ebd017a3 100644 --- a/src/commands/file_menu.ts +++ b/src/commands/file_menu.ts @@ -16,7 +16,7 @@ import {ChooseRepoToRun} from "./suggest_other_repo_commands_modal"; * @param {Repository | null} repo - The data repository found in the file */ export async function shareFolderRepo(plugin: GithubPublisher, folder: TFolder, branchName: string, repo: Repository | null) { - const publisher = await plugin.reloadOctokit(); + const publisher = await plugin.reloadOctokit(repo?.smartKey); const statusBarItems = plugin.addStatusBarItem(); const repoFrontmatter = getRepoFrontmatter(plugin.settings, repo, null); const monoProperties: MonoRepoProperties = { @@ -146,12 +146,12 @@ export function addMenuFile(plugin: GithubPublisher, file: TFile, branchName: st item .setTitle(i18next.t("commands.shareViewFiles.multiple.on", { doc: fileName, - smartKey: getSharedKey?.smartKey.toUpperCase() || i18next.t("common.default").toUpperCase() + smartKey: getSharedKey?.smartKey?.toUpperCase() || i18next.t("common.default").toUpperCase() })) .setIcon("file-up") .onClick(async () => { await shareOneNote( - await plugin.reloadOctokit(), + await plugin.reloadOctokit(getSharedKey?.smartKey), file, getSharedKey, fileName @@ -214,7 +214,7 @@ export function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, fil .setIcon("file-up") .onClick(async () => { await shareOneNote( - await plugin.reloadOctokit(), + await plugin.reloadOctokit(otherRepo?.smartKey), file, otherRepo, fileName @@ -252,7 +252,7 @@ export function subMenuCommandsFile(plugin: GithubPublisher, item: MenuItem, fil .onClick(async () => { new ChooseRepoToRun(plugin.app, plugin, repo?.shareKey, branchName, "file", file.basename, async (item: Repository) => { await shareOneNote( - await plugin.reloadOctokit(), + await plugin.reloadOctokit(item.smartKey), file, item, fileName diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 7bd3e90c..57823986 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1,6 +1,7 @@ { "commands": { "checkValidity": { + "error": "Incorrect configuration", "inBranch": { "error403": "Error 403: {{- repo.owner}}/{{- repo.repo}} was moved permanently (from {{- repo.branch}}).", "error404": "Error 404: The branch {{- repo.branch}} was not found in {{- repo.owner}}/{{- repo.repo}}." @@ -143,6 +144,10 @@ "unpublished": "Cannot be published" } }, + "ommands": { + "checkValidity": { + } + }, "publish": { "branch": { "alreadyExists": "Branch already exists ({{- branchName}} on {{- repo.owner}}/{{- repo.repo}} - Using it.", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index e8223ccf..9696e3ef 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -1,6 +1,7 @@ { "commands": { "checkValidity": { + "error": "Configuration incorrecte", "inBranch": { "error403": "Erreur 403: {{- repo.owner}}/{{- repo.repo}} a été déplacé de manière permanente (depuis {{- repo.branch}}).", "error404": "Erreur 404 : La branche {{- repo.branch}} est introuvable depuis {{- repo.owner}}/{{- repo.repo}}." @@ -143,6 +144,10 @@ "unpublished": "Impossible à publier" } }, + "ommands": { + "checkValidity": { + } + }, "publish": { "branch": { "alreadyExists": "La branche {{- branchName}} sur {{- repo.owner}}/{{- repo.repo}} existe déjà - Utilisation de celle-ci.", diff --git a/src/main.ts b/src/main.ts index 40f33fef..a2fe96b2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -128,7 +128,7 @@ export default class GithubPublisher extends Plugin { * @returns {Promise} - The token of the plugin */ - async loadToken(): Promise { + async loadToken(repo?: string): Promise { const tokenPath = createTokenPath(this, this.settings.github.tokenPath); const tokenFileExists = await this.app.vault.adapter.exists(`${tokenPath}`); @@ -139,10 +139,15 @@ export default class GithubPublisher extends Plugin { const tokenFile = await this.app.vault.adapter.read(`${tokenPath}`); if (tokenPath.endsWith(".json")) { const tokenJSON = JSON.parse(tokenFile); - return tokenJSON.GITHUB_PUBLISHER_TOKEN; + const defaultToken = tokenJSON.GITHUB_PUBLISHER_TOKEN; + if (repo) + return tokenJSON.GITHUB_PUBLISHER_REPOS[repo] ?? defaultToken; + return defaultToken; } if (tokenFile) { - return tokenFile.split("=")[1]; + const defaultToken=tokenFile.split("\n").find((line) => line.startsWith("GITHUB_TOKEN"))?.split("=")[1] ?? ""; + if (repo) return tokenFile.split("\n").find((line) => line.startsWith(`${repo}_TOKEN`))?.split("=")[1] ?? defaultToken; + return defaultToken; } } catch (e) { notif({ settings: this.settings, e: true }, e); @@ -154,10 +159,10 @@ export default class GithubPublisher extends Plugin { /** * Create a new instance of Octokit to load a new instance of GithubBranch */ - async reloadOctokit() { + async reloadOctokit(repo?: string) { let octokit: Octokit; const apiSettings = this.settings.github.api; - const token = await this.loadToken(); + const token = await this.loadToken(repo); if (apiSettings.tiersForApi === GithubTiersVersion.entreprise && apiSettings.hostname.length > 0) { octokit = new Octokit( { diff --git a/src/settings/interface.ts b/src/settings/interface.ts index b5463d77..7a75b0ee 100644 --- a/src/settings/interface.ts +++ b/src/settings/interface.ts @@ -29,6 +29,7 @@ export interface Repository { branch: string; automaticallyMergePR: boolean; verifiedRepo?: boolean; + token?: string; api: { tiersForApi: GithubTiersVersion; hostname: string; diff --git a/src/settings/migrate.ts b/src/settings/migrate.ts index b100ee24..e8290fcb 100644 --- a/src/settings/migrate.ts +++ b/src/settings/migrate.ts @@ -129,7 +129,7 @@ async function migrateWorFlow(plugin: GithubPublisher) { await plugin.saveSettings(); } -export async function migrateToken(plugin: GithubPublisher, token?: string) { +export async function migrateToken(plugin: GithubPublisher, token?: string, repo?: string) { logs({ settings: plugin.settings }, "migrating token"); const tokenPath = createTokenPath(plugin, plugin.settings.github.tokenPath); //@ts-ignore @@ -144,18 +144,37 @@ export async function migrateToken(plugin: GithubPublisher, token?: string) { if (token === undefined) { return; } - logs({ settings: plugin.settings }, `Moving the GitHub Token in the file : ${tokenPath}`); + logs({ settings: plugin.settings }, `Moving the GitHub Token in the file : ${tokenPath} for ${repo ?? "default"} repository`); if (tokenPath.endsWith(".json")) { - const envToken = { + const envToken = repo ? { + "GITHUB_PUBLISHER_REPOS" : { + [repo] : token + } + } : { GITHUB_PUBLISHER_TOKEN: token }; if (!(await plugin.app.vault.adapter.exists(tokenPath))) { await plugin.app.vault.adapter.mkdir(normalizePath(tokenPath).split("/").slice(0, -1).join("/")); } - await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(envToken)); + const oldToken = JSON.parse(await plugin.app.vault.adapter.read(tokenPath)); + const newToken = { ...oldToken, ...envToken }; + await plugin.app.vault.adapter.write(tokenPath, JSON.stringify(newToken, null, 2)); + } else { - const envToken = `GITHUB_TOKEN=${token}`; - await plugin.app.vault.adapter.write(tokenPath, envToken); + const envToken = repo ? `${repo}_TOKEN=${token}` : `GITHUB_TOKEN=${token}`; + const oldToken = (await plugin.app.vault.adapter.read(tokenPath)).split("\n"); + //search if old token is already in the file, if yes, replace it + for (const key in oldToken) { + if (key.startsWith("GITHUB_TOKEN") && !repo) { + oldToken[key] = envToken; + await plugin.app.vault.adapter.write(tokenPath, oldToken.join("\n")); + return; + } else if (key.startsWith(`${repo}_TOKEN`) && repo) { + oldToken[key] = envToken; + await plugin.app.vault.adapter.write(tokenPath, oldToken.join("\n")); + return; + } + } } return; } @@ -193,6 +212,11 @@ async function migrateOtherRepository(plugin: GithubPublisher) { repo.copyLink = { links: "", removePart: [], + transform: { + toUri: false, + slugify: "disable", + applyRegex: [] + } }; await plugin.saveSettings(); } @@ -299,6 +323,11 @@ async function migrateOldSettings(plugin: GithubPublisher, old: OldSettings) { links: old.mainLink, removePart: old.linkRemover.split(/[,\n]\W*/).map((s) => s.trim()), addCmd: false, + transform: { + toUri: true, + slugify: "lower", + applyRegex: [], + } }, noticeError: old.logNotice, displayModalRepoEditing: false, diff --git a/src/settings/modals/manage_repo.ts b/src/settings/modals/manage_repo.ts index 994e0974..44db5211 100644 --- a/src/settings/modals/manage_repo.ts +++ b/src/settings/modals/manage_repo.ts @@ -4,6 +4,7 @@ import {App, Modal, Notice, Setting} from "obsidian"; import GithubPublisher from "../../main"; import {checkRepositoryValidity, verifyRateLimitAPI} from "../../utils/data_validation_test"; import {GitHubPublisherSettings, GithubTiersVersion, Repository} from "../interface"; +import { migrateToken } from "../migrate"; /** * @description This class is used to add a new repo to the settings in the "otherRepo" in the github setting section @@ -127,16 +128,17 @@ export class ModalAddingNewRepository extends Modal { .addButton((button) => { button .setButtonText(i18next.t("common.save")) + .setCta() .onClick(() => { const error = this.foundError(); const input = error.repo.length > 0 ? this.containerEl.querySelector(`[smartkey="${error.repo}"] input`) : contentEl.querySelector("[placeholder=\"smartKey\"] input"); if (error.type === "None") { //remove error - input?.classList.remove("error"); + input?.classList?.remove("error"); this.onSubmit(this.repository); this.close(); } - input?.classList.add("error"); + input?.classList?.add("error"); if (error.type === "duplicate") { new Notice(i18next.t("settings.github.smartRepo.modals.duplicate")); } else if (error.type === "default") { @@ -226,7 +228,26 @@ class ModalEditingRepository extends Modal { onOpen() { const {contentEl} = this; contentEl.empty(); - contentEl.addClasses(["github-publisher", "modals", "manage-repo"]); + contentEl.addClasses(["github-publisher", "modals", "manage-repo" , "edit"]); + new Setting(contentEl) + .setClass("no-display") + .addButton((button) => + button + .setCta() + .setButtonText(i18next.t("common.save")) + .onClick(async () => { + this.onSubmit(this.repository); + this.close(); + }) + ) + .addButton((button) => + button + .setWarning() + .setButtonText(i18next.t("common.cancel")) + .onClick(async () => { + this.close(); + }) + ); contentEl.createEl("h2", {text: i18next.t("common.edit", {things: this.repository.smartKey})}); new Setting(contentEl) @@ -293,6 +314,28 @@ class ModalEditingRepository extends Modal { this.repository.branch = value.trim(); }) ); + const desc_ghToken = document.createDocumentFragment(); + desc_ghToken.createEl("span", undefined, (span) => { + span.innerText = i18next.t("settings.github.ghToken.desc"); + span.createEl("a", undefined, (link) => { + link.innerText = `${i18next.t("common.here")}.`; + link.href = + "https://github.com/settings/tokens/new?scopes=repo,workflow"; + }); + }); + new Setting(contentEl) + .setName(i18next.t("common.ghToken")) + .setDesc(desc_ghToken) + .addText(async (text) => { + const decryptedToken: string = await this.plugin.loadToken(this.repository.smartKey); + text + .setPlaceholder("ghp_1234567890") + .setValue(this.repository.token ?? decryptedToken) + .onChange(async (value) => { + await migrateToken(this.plugin, value.trim(), this.repository.smartKey); + await this.plugin.saveSettings(); + }); + }); new Setting(contentEl) .setName(i18next.t("settings.github.automaticallyMergePR")) .addToggle((toggle) => @@ -303,13 +346,13 @@ class ModalEditingRepository extends Modal { }) ); new Setting(contentEl) - .setClass("github-publisher-no-display") + .setClass("no-display") .addButton((button) => button .setButtonText(i18next.t("settings.github.testConnection")) .setClass("connect") .onClick(async () => { - const octokit = await this.plugin.reloadOctokit(); + const octokit = await this.plugin.reloadOctokit(this.repository.smartKey); this.repository.verifiedRepo = await checkRepositoryValidity( octokit, this.repository, @@ -514,20 +557,10 @@ class ModalEditingRepository extends Modal { }); } } - - new Setting(contentEl) - .addButton((button) => - button - .setButtonText(i18next.t("common.save")) - .onClick(async () => { - this.onSubmit(this.repository); - this.close(); - }) - ); } onClose() { const {contentEl} = this; contentEl.empty(); - this.onSubmit(this.repository); + } } diff --git a/src/styles.css b/src/styles.css index 5522d30e..f0a36ae8 100644 --- a/src/styles.css +++ b/src/styles.css @@ -65,6 +65,16 @@ hr.github-publisher-hr { padding: 0; } +.modal:has(.github-publisher.modals.manage-repo.edit) { + width: 100%; +} + +.github-publisher.modals.manage-repo + .no-display + .setting-item-control:has(.mod-cta) { + margin: 0 100% 5px 0; +} + .github-publisher-import-input { width: 0.1px; height: 0.1px; diff --git a/src/utils/data_validation_test.ts b/src/utils/data_validation_test.ts index aba2cb3e..26aa25c2 100644 --- a/src/utils/data_validation_test.ts +++ b/src/utils/data_validation_test.ts @@ -397,6 +397,11 @@ export function defaultRepo(settings: GitHubPublisherSettings): Repository { copyLink: { links: settings.plugin.copyLink.links, removePart: settings.plugin.copyLink.removePart, + transform: { + toUri: settings.plugin.copyLink.transform.toUri, + slugify: settings.plugin.copyLink.transform.slugify, + applyRegex: settings.plugin.copyLink.transform.applyRegex, + }, }, }; }