diff --git a/.eslintrc.js b/.eslintrc.js index a7ebce2c..8da368a2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,5 +30,6 @@ module.exports = { "node/no-missing-require": ["warn"], "node/no-extraneous-require": ["off"], "node/no-unsupported-features/node-builtins": ["off"], + "node/no-callback-literal": ["off"], }, } diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 2fa382be..a7e9b519 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -13,8 +13,4 @@ ## Reporting a Vulnerability -- Please report security vulnerabilitys in email at: - -## Release cycle - -- Standard updates coming on Tuesdays if there is something to update and hotfix updates can come on Tuesdays, Thursdays or Saturdays if there is a problem that can not wait until the next Tuesday. +- Please report security vulnerability in email at: diff --git a/.tailwindrc.js b/.tailwindrc.js index b5d1ae30..cfea3098 100644 --- a/.tailwindrc.js +++ b/.tailwindrc.js @@ -2,7 +2,7 @@ module.exports = { mode: "jit", purge: { enabled: true, - content: ["./app/**/*.html"], + content: ["./app/**/*.html", "./app/**/*.js"], }, theme: { extend: { @@ -37,6 +37,10 @@ module.exports = { borderRadius: { "2xl": "30px", }, + + screens: { + lg: "1400px", + }, }, }, corePlugins: { diff --git a/README.md b/README.md index 676374aa..0c346179 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ ## Features -- 🔒 Secure: Your codes is secured by AES 256bit encrypton and your own password. +- 🔒 Secure: Your codes is secured by AES 256bit encryption and your own password. - 🗄️ Import: You can directly import from Google Authenticator or from any 2FA QR code you screenshot. - ⌨️ Shortcuts: Easily open Authme with custom shortcuts and it can also start when your system starts. @@ -40,7 +40,7 @@ ## Importing -- From QR codes: You screenshot all the QR codes from your favourite websites, and import all of them to Authme +- From QR codes: You screenshot all the QR codes from your favorite websites, and import all of them to Authme - From Google Authenticator: Import all of your saved QR codes from your Google Authenticator app. ## Contributing and development diff --git a/app/application/index.html b/app/application/index.html index c3805af6..c3663432 100644 --- a/app/application/index.html +++ b/app/application/index.html @@ -10,7 +10,6 @@ - @@ -128,65 +127,59 @@ -
+

Authme

Please choose your import file!

If you don't have an import file please create one!

-
-
- - -
+
+ +

Getting started

Useful options

-
-
- - -
+
+ +

Importing files

-
-
- - -
+
+ +

diff --git a/app/application/src/css/index.css b/app/application/src/css/index.css index b3b689fb..2c82b256 100644 --- a/app/application/src/css/index.css +++ b/app/application/src/css/index.css @@ -8,12 +8,6 @@ h4 { font-size: 1.5rem; } -.center { - top: -70px; - width: 1000px; - height: auto; -} - .button11 { color: black; background: white; @@ -86,6 +80,12 @@ h4 { height: 270px; } +@media (max-width: 1199.98px) { + .grid { + width: 90%; + } +} + .grid:last-child { margin-bottom: 50px; } @@ -113,8 +113,12 @@ h4 { } .input1 { - width: 90px; - font-size: 1.3rem; + width: 128px !important; + font-size: 20px; + display: flex; + justify-content: center; + align-items: center; + user-select: all !important; } .input2 { @@ -216,3 +220,7 @@ a { user-select: all; margin-bottom: 10px; } + +.text2 { + font-size: 26px !important; +} diff --git a/app/application/src/js/convert.js b/app/application/src/js/convert.js deleted file mode 100644 index 630ecd85..00000000 --- a/app/application/src/js/convert.js +++ /dev/null @@ -1,45 +0,0 @@ -let data = [] -let save_text - -const loadFile = () => { - dialog - .showOpenDialog({ - title: "Import from Authme Import Text file", - properties: ["openFile"], - filters: [{ name: "Text file", extensions: ["txt"] }], - }) - .then((result) => { - canceled = result.canceled - filepath = result.filePaths - - if (canceled === false) { - const text = fs.readFileSync(filepath.toString(), "utf-8") - save_text = text - - processdata(text) - } - }) -} - -const processdata = (text) => { - // remove double qoutes - const pre_data1 = text.replace(/"/g, "") - - // new line - const pre_data2 = pre_data1.replace(/,/g, "\n") - - // make the array - const pre_data3 = pre_data2.split(/\n/) - while (pre_data3.length) { - data.push(pre_data3.shift()) - } - - // remove first blank - data.splice(0, 1) - - data = data.filter((_, i) => { - return (i + 1) % 5 - }) - - separation() -} diff --git a/app/application/src/js/index.js b/app/application/src/js/index.js index 03e163b6..5b90d485 100644 --- a/app/application/src/js/index.js +++ b/app/application/src/js/index.js @@ -1,6 +1,7 @@ +const { app, shell, dialog, clipboard, Notification } = require("@electron/remote") +const logger = require("@levminer/lib/logger/renderer") +const { aes, convert } = require("@levminer/lib") const speakeasy = require("@levminer/speakeasy") -const { app, shell, dialog } = require("@electron/remote") -const { typedef, aes } = require("@levminer/lib") const fs = require("fs") const path = require("path") const electron = require("electron") @@ -12,6 +13,9 @@ window.onerror = (error) => { ipc.send("rendererError", { renderer: "application", error: error }) } +// ? logger +logger.getWindow("application") + // ? if development let dev = false @@ -39,14 +43,8 @@ if (!fs.existsSync(path.join(file_path, "hash.authme"))) { // eslint-disable-next-line let prev = false - -let names = [] -let secret = [] -const issuer = [] -const type = [] - +let save_text const query = [] - let clear /** @@ -62,10 +60,8 @@ const settings_refresher = setInterval(() => { if (file.security.require_password !== null || file.security.password !== null) { clearInterval(settings_refresher) - console.warn("Authme - Settings refresh completed") + logger.log("Settings refresh completed") } - - console.warn("Authme - Settings refreshed") }, 100) const name_state = file.settings.show_2fa_names @@ -73,87 +69,76 @@ const copy_state = file.settings.reset_after_copy const reveal_state = file.settings.click_to_reveal const search_state = file.settings.save_search_results -const offset_number = file.experimental.offset const sort_number = file.experimental.sort -// ? separate values -const separation = () => { - let c0 = 0 - let c1 = 1 - let c2 = 2 - let c3 = 3 - - for (let i = 0; i < data.length; i++) { - if (i == c0) { - const names_before = data[i] - const names_after = names_before.slice(8).trim() - names.push(names_after) - c0 = c0 + 4 - } - - if (i == c1) { - const secret_before = data[i] - const secret_after = secret_before.slice(8).trim() - secret.push(secret_after) - c1 = c1 + 4 - } - - if (i == c2) { - const issuer_before = data[i] - const issuer_after = issuer_before.slice(8).trim() - issuer.push(issuer_after) - c2 = c2 + 4 - } - - if (i == c3) { - type.push(data[i]) - c3 = c3 + 4 - } - } - - const issuer_original = [...issuer] - - const sort = () => { - const names_new = [] - const secret_new = [] - - issuer.forEach((element) => { - for (let i = 0; i < issuer_original.length; i++) { - if (element === issuer_original[i]) { - names_new.push(names[i]) - secret_new.push(secret[i]) - } - } +/** + * Load file first time from dialog + */ +const loadFile = () => { + dialog + .showOpenDialog({ + title: "Import from Authme Import Text file", + properties: ["openFile"], + filters: [{ name: "Text file", extensions: ["txt"] }], }) + .then((result) => { + canceled = result.canceled + filepath = result.filePaths - names = names_new - secret = secret_new - } - - if (sort_number === 1) { - issuer.sort((a, b) => { - return a.localeCompare(b) - }) + if (canceled === false) { + const text = fs.readFileSync(filepath.toString(), "utf-8") + save_text = text - sort() - } else if (sort_number === 2) { - issuer.sort((a, b) => { - return b.localeCompare(a) + processdata(text) + } }) +} - sort() - } +/** + * Process data from saved source + * @param {String} text + */ +const processdata = (text) => { + const data = convert.fromText(text, sort_number) - go() + go(data) } -const go = () => { +/** + * Start creating 2FA elements + * @param {LibImportFile} data + */ +const go = (data) => { document.querySelector("#search").style.display = "grid" document.querySelector(".h1").style.marginBottom = "0px" - document.querySelector(".center").style.top = "80px" + document.querySelector(".content").style.top = "80px" document.querySelector("#choose").style.display = "none" document.querySelector("#starting").style.display = "none" + const names = data.names + const secrets = data.secrets + const issuers = data.issuers + + /** + * Load storage + * @type {LibStorage} + */ + let storage + + if (dev === false) { + storage = JSON.parse(localStorage.getItem("storage")) + + storage.issuers = issuers + + localStorage.setItem("storage", JSON.stringify(storage)) + } else { + storage = JSON.parse(localStorage.getItem("dev_storage")) + + storage.issuers = issuers + + localStorage.setItem("dev_storage", JSON.stringify(storage)) + } + const generate = () => { // counter let counter = 0 @@ -169,15 +154,15 @@ const go = () => {

Name

-

Code

+

Code

Code

- +

Code

Time

-

Time

+

Time

Name

@@ -195,15 +180,15 @@ const go = () => {

Name

-

Code

+

Code

Code

- +

Code

Time

-

Time

+

Time

Name

@@ -223,15 +208,15 @@ const go = () => {

Name

-

Code

+

Code

Code

- +

Code

Time

-

Time

+

Time

-
+

Authme

Welcome back!

@@ -47,22 +47,24 @@

Please enter your password to continue!

-
+
More options -
-

Forgot password?

-

If you have a backup code you can recover your password. If you don't have one then try to think harder.

- -
+
diff --git a/app/confirm/src/css/index.css b/app/confirm/src/css/index.css index 2d5734f6..372c5956 100644 --- a/app/confirm/src/css/index.css +++ b/app/confirm/src/css/index.css @@ -1,9 +1,3 @@ -.center { - top: 50px; - width: 1000px; - height: auto; -} - .input1 { width: 226px; } @@ -21,3 +15,16 @@ details { font-size: 24px; } + +.animation { + animation: fadeEffect 1s; +} + +@keyframes fadeEffect { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/app/confirm/src/js/index.js b/app/confirm/src/js/index.js index 8968b6db..2ca2bc09 100644 --- a/app/confirm/src/js/index.js +++ b/app/confirm/src/js/index.js @@ -1,4 +1,5 @@ const { app, dialog, clipboard } = require("@electron/remote") +const logger = require("@levminer/lib/logger/renderer") const bcrypt = require("bcryptjs") const fs = require("fs") const electron = require("electron") @@ -11,6 +12,9 @@ window.onerror = (error) => { ipc.send("rendererError", { renderer: "confirm", error: error }) } +// ? logger +logger.getWindow("confirm") + // ? if development let dev = false let integrity = false @@ -64,9 +68,9 @@ const check_integrity = () => { const file = JSON.parse( fs.readFileSync(path.join(file_path, "settings.json"), "utf-8", async (err, data) => { if (err) { - return console.warn(`Authme - Error reading settings.json - ${err}`) + return logger.warn(`Error reading settings.json - ${err}`) } else { - return console.warn("Authme - Successfully readed settings.json") + return logger.warn("Successfully readed settings.json") } }) ) @@ -76,7 +80,7 @@ const check_integrity = () => { if (integrity == false) { try { - console.log(storage) + logger.log(storage) if (file.security.password !== storage.password || file.security.require_password !== storage.require_password) { dialog @@ -96,7 +100,7 @@ const check_integrity = () => { }) } } catch (error) { - console.warn("Authme - Local storage not found") + logger.error("Local storage not found") dialog .showMessageBox({ @@ -128,9 +132,9 @@ const unhashPassword = async () => { const file = JSON.parse( fs.readFileSync(path.join(file_path, "settings.json"), "utf-8", async (err, data) => { if (err) { - return console.warn(`Authme - Error reading settings.json - ${err}`) + return logger.warn(`Error reading settings.json - ${err}`) } else { - return console.warn("Authme - Successfully readed settings.json") + return logger.log("Successfully readed settings.json") } }) ) @@ -138,7 +142,7 @@ const unhashPassword = async () => { // compare const password_input = Buffer.from(document.querySelector("#password_input").value) - const compare = await bcrypt.compare(password_input.toString(), file.security.password).then(console.warn("Authme - Passwords compared!")) + const compare = await bcrypt.compare(password_input.toString(), file.security.password).then(logger.log("Passwords compared!")) if (compare == true) { if (file.security.new_encryption === true) { @@ -156,7 +160,7 @@ const unhashPassword = async () => { location.reload() }, 1000) } else { - console.warn("Authme - Passwords dont match!") + logger.warn("Passwords dont match!") text.style.color = "#A30015" text.textContent = "Passwords don't match! Try again!" @@ -182,10 +186,6 @@ const forgotPassword = () => { const hash = Buffer.from(sha.generateHash(text.toString("base64"))) - console.log(hash.toString()) - console.log("-") - console.log(storage.hash) - if (hash.toString() === storage.hash) { const encrypted = Buffer.from(rsa.decrypt(text.toString(), Buffer.from(storage.backup_string, "base64")), "base64") @@ -244,3 +244,26 @@ document.querySelector("#show_pass_01").addEventListener("click", () => { document.querySelector("#show_pass_0").style.display = "flex" document.querySelector("#show_pass_01").style.display = "none" }) + +let more_options_shown = false + +/** + * Show more options div + */ +const showMoreOptions = () => { + const more_options = document.querySelector("#more_options") + + if (more_options_shown === false) { + more_options.style.visibility = "visible" + + setTimeout(() => { + more_options.style.display = "block" + }, 10) + + more_options_shown = true + } else { + more_options.style.display = "none" + + more_options_shown = false + } +} diff --git a/app/edit/index.html b/app/edit/index.html index 4e5164d9..eb3e58cb 100644 --- a/app/edit/index.html +++ b/app/edit/index.html @@ -4,7 +4,6 @@ Authme - @@ -25,34 +24,37 @@
- -
- -

Authme

-

Edit

-

You can edit, delete or add more codes.

- +
+ + +
-
-

Rollback

+
+

Rollback

You can rollback to the latest save. If you load your codes below it will overwrite the current rollback save!

-
-
-

Load saved code(s)

+
+

Load saved code(s)

You can load the code(s) you saved. After that you can edit them or add more.

-
-
-

Edit mode

+
+

Edit mode

Now you can modify existing codes or add more.

- - +
+ + +
+
+
+ More options +
+ +
+
+
-
+
diff --git a/app/edit/src/css/index.css b/app/edit/src/css/index.css index 7c0ca26d..e1c034b4 100644 --- a/app/edit/src/css/index.css +++ b/app/edit/src/css/index.css @@ -1,10 +1,3 @@ -.center { - top: -50px; - width: 1000px; - height: auto; - padding-bottom: 50px !important; -} - .input1 { color: black; background-color: white; @@ -44,7 +37,8 @@ margin-top: 0px; border-radius: 30px; margin: 0 auto; - margin-top: 20px; + margin-top: 5rem; + margin-bottom: 5rem; width: 66.666667%; height: 300px; } diff --git a/app/edit/src/js/convert.js b/app/edit/src/js/convert.js deleted file mode 100644 index fd9dfcb4..00000000 --- a/app/edit/src/js/convert.js +++ /dev/null @@ -1,24 +0,0 @@ -let data = [] - -const processdata = (text) => { - // remove double qoutes - const pre_data1 = text.replace(/"/g, "") - - // new line - const pre_data2 = pre_data1.replace(/,/g, "\n") - - // make the array - const pre_data3 = pre_data2.split(/\n/) - while (pre_data3.length) { - data.push(pre_data3.shift()) - } - - // remove first blank - data.splice(0, 1) - - data = data.filter((_, i) => { - return (i + 1) % 5 - }) - - separation() -} diff --git a/app/edit/src/js/index.js b/app/edit/src/js/index.js index 78e1ff7d..adc3abec 100644 --- a/app/edit/src/js/index.js +++ b/app/edit/src/js/index.js @@ -1,5 +1,6 @@ -const { shell, app, dialog } = require("@electron/remote") -const { aes } = require("@levminer/lib") +const { app, dialog } = require("@electron/remote") +const { aes, convert } = require("@levminer/lib") +const logger = require("@levminer/lib/logger/renderer") const fs = require("fs") const electron = require("electron") const ipc = electron.ipcRenderer @@ -10,6 +11,9 @@ window.onerror = (error) => { ipc.send("rendererError", { renderer: "edit", error: error }) } +// ? logger +logger.getWindow("edit") + // ? if development let dev = false @@ -50,22 +54,21 @@ const settings_refresher = setInterval(() => { if (file.security.require_password !== null || file.security.password !== null) { clearInterval(settings_refresher) - console.warn("Authme - Settings refresh completed") + logger.log("Settings refresh completed") } - - console.warn("Authme - Settings refreshed") }, 100) // ? rollback const cache_path = path.join(file_path, "cache") const rollback_con = document.querySelector(".rollback") const rollback_text = document.querySelector("#rollbackBut") +let cache = true fs.readFile(path.join(cache_path, "latest.authmecache"), "utf-8", (err, data) => { if (err) { - console.warn("Authme - Cache file don't exist") + logger.warn("Cache file don't exist") } else { - console.log("Authme - Cache file exists") + logger.log("Cache file exists") rollback_con.style.display = "block" @@ -89,25 +92,26 @@ const rollback = () => { noLink: true, message: `Are you sure you want to rollback to the latest save? - This requires a restart and will overwrite your saved codes!`, + This will overwrite your saved codes!`, }) .then((result) => { if (result.response === 0) { fs.readFile(path.join(cache_path, "latest.authmecache"), "utf-8", (err, data) => { if (err) { - console.error("Authme - Error reading hash file", err) + logger.error("Error reading hash file", err) } else { fs.writeFile(path.join(file_path, "hash.authme"), data, (err) => { if (err) { - console.error("Authme - Failed to create cache folder", err) + logger.error("Failed to create cache folder", err) } else { - console.log("Authme - Hash file created") + logger.log("Hash file created") } }) } }) - restart() + reloadApplication() + reloadSettings() } }) } @@ -116,74 +120,63 @@ const rollback = () => { const names = [] const secrets = [] const issuers = [] -const types = [] - -const separation = () => { - rollback_con.style.display = "none" - - let c0 = 0 - let c1 = 1 - let c2 = 2 - let c3 = 3 - - for (let i = 0; i < data.length; i++) { - if (i == c0) { - const name_before = data[i] - const name_after = name_before.slice(8) - names.push(name_after.trim()) - c0 = c0 + 4 - } - - if (i == c1) { - const secret_before = data[i] - const secret_after = secret_before.slice(8) - secrets.push(secret_after.trim()) - c1 = c1 + 4 - } +const ids = [] - if (i == c2) { - const issuer_before = data[i] - const issuer_after = issuer_before.slice(8) - issuers.push(issuer_after.trim()) - c2 = c2 + 4 - } +/** + * Process data from saved file + * @param {String} text + */ +const processdata = (text) => { + const data = convert.fromText(text, 0) - if (i == c3) { - types.push(data[i]) - c3 = c3 + 4 - } + for (let i = 0; i < data.names.length; i++) { + names.push(data.names[i]) + secrets.push(data.secrets[i]) + issuers.push(data.issuers[i]) } go() } +// block counter +let counter = 0 + +/** + * Start creating edit elements + */ const go = () => { - createCache() + document.querySelector(".codes_container").innerHTML = "" + + if (cache === true) { + createCache() + cache = false + } document.querySelector(".beforeLoad").style.display = "none" + document.querySelector(".rollback").style.display = "none" document.querySelector(".afterLoad").style.display = "block" - for (let i = 0; i < names.length; i++) { + for (let j = 0; j < names.length; j++) { const codes_container = document.querySelector(".codes_container") const div = document.createElement("div") div.innerHTML = ` -
+
-

${issuers[i]}

+

${issuers[counter]}

- +
-
` + div.setAttribute("id", counter) codes_container.appendChild(div) + + counter++ } + + save_text = "" } // ? edit @@ -231,6 +229,8 @@ const del = (number) => { del_but.style.color = "red" del_but.style.borderColor = "red" + counter = 0 + dialog .showMessageBox({ title: "Authme", @@ -245,18 +245,14 @@ const del = (number) => { del_but.style.color = "" del_but.style.borderColor = "white" - const input = document.querySelector(`#edit_inp_${number}`).value - const div = document.querySelector(`#grid${number}`) div.remove() - const query = (element) => element === input - - const index = names.findIndex(query) + names.splice(number, 1) + secrets.splice(number, 1) + issuers.splice(number, 1) - names.splice(index, 1) - secrets.splice(index, 1) - issuers.splice(index, 1) + go() } else { del_but.style.color = "" del_but.style.borderColor = "white" @@ -278,7 +274,7 @@ const createSave = () => { noLink: true, message: `Are you sure you want to save the modified code(s)? - This requires a restart and will overwrite your saved codes!`, + This will overwrite your saved codes!`, }) .then((result) => { if (result.response === 0) { @@ -293,7 +289,28 @@ const createSave = () => { saveCodes() } - restart() + /** + * Load storage + * @type {LibStorage} + */ + let storage + + if (dev === false) { + storage = JSON.parse(localStorage.getItem("storage")) + + storage.issuers = issuers + + localStorage.setItem("storage", JSON.stringify(storage)) + } else { + storage = JSON.parse(localStorage.getItem("dev_storage")) + + storage.issuers = issuers + + localStorage.setItem("dev_storage", JSON.stringify(storage)) + } + + reloadApplication() + reloadSettings() } }) } @@ -364,14 +381,26 @@ const addMore = () => { for (let i = 0; i < files.length; i++) { fs.readFile(files[i], (err, input) => { if (err) { - console.log("Authme - Error loading file") + logger.error("Error loading file") } else { + logger.log("File readed") + data = [] const container = document.querySelector(".codes_container") container.innerHTML = "" - processdata(input.toString()) + counter = 0 + + const /** @type{LibImportFile} */ imported = convert.fromText(input.toString(), 0) + + for (let i = 0; i < imported.names.length; i++) { + names.push(imported.names[i]) + secrets.push(imported.secrets[i]) + issuers.push(imported.issuers[i]) + } + + go() } }) } @@ -384,7 +413,7 @@ const createCache = () => { if (file.security.new_encryption === true) { fs.readFile(path.join(file_path, "codes", "codes.authme"), "utf-8", (err, data) => { if (err) { - console.error("Authme - Error reading hash file", err) + logger.error("Error reading hash file", err) } else { if (!fs.existsSync(cache_path)) { fs.mkdirSync(cache_path) @@ -392,9 +421,9 @@ const createCache = () => { fs.writeFile(path.join(cache_path, "latest.authmecache"), data, (err) => { if (err) { - console.error("Authme - Failed to create cache folder", err) + logger.error("Failed to create cache folder", err) } else { - console.log("Authme - Cache file created") + logger.log("Cache file created") } }) } @@ -402,7 +431,7 @@ const createCache = () => { } else { fs.readFile(path.join(file_path, "hash.authme"), "utf-8", (err, data) => { if (err) { - console.error("Authme - Error reading hash file", err) + logger.error("Error reading hash file", err) } else { if (!fs.existsSync(cache_path)) { fs.mkdirSync(cache_path) @@ -410,9 +439,9 @@ const createCache = () => { fs.writeFile(path.join(cache_path, "latest.authmecache"), data, (err) => { if (err) { - console.error("Authme - Failed to create cache folder", err) + logger.error("Failed to create cache folder", err) } else { - console.log("Authme - Cache file created") + logger.log("Cache file created") } }) } @@ -439,7 +468,20 @@ const loadError = () => { // ? new encryption method const loadChooser = () => { if (file.security.new_encryption === true) { - newLoad() + fs.readFile(path.join(file_path, "codes", "codes.authme"), "utf-8", (err, data) => { + if (err) { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "error", + message: `No save file found. + + Go back to the main page and save your codes!`, + }) + } else { + newLoad() + } + }) } else { loadCodes() } @@ -471,7 +513,7 @@ const newLoad = () => { fs.readFile(path.join(file_path, "codes", "codes.authme"), (err, content) => { if (err) { - console.warn("Authme - The file codes.authme don't exists") + logger.warn("The file codes.authme don't exists") password.fill(0) key.fill(0) @@ -494,9 +536,34 @@ const hide = () => { ipc.send("hide_edit") } -const restart = () => { - setTimeout(() => { - app.relaunch() - app.exit() - }, 500) +// ? reloads +const reloadApplication = () => { + ipc.send("reload_application") +} + +const reloadSettings = () => { + ipc.send("reload_settings") +} + +/** + * Revert all current changes + */ +const revertChanges = () => { + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Yes", "Cancel"], + defaultId: 1, + cancelId: 1, + type: "warning", + noLink: true, + message: `Are you sure you want to revert all current change(s)? + + You will lose all current changes!`, + }) + .then((result) => { + if (result.response === 0) { + location.reload() + } + }) } diff --git a/app/export/index.html b/app/export/index.html index 7bf843eb..713c381c 100644 --- a/app/export/index.html +++ b/app/export/index.html @@ -4,13 +4,11 @@ Authme - - @@ -24,22 +22,26 @@
- -
-

Authme

-

Export

-

You can export everything form your saved config.

- +
+ + +
-
-

Export code(s)

+
+

Export code(s)

If you saved your config you can export the secrets and the QR code(s).

-
-
-
-

Save exported code(s)

+ diff --git a/app/export/src/css/index.css b/app/export/src/css/index.css deleted file mode 100644 index fe268c29..00000000 --- a/app/export/src/css/index.css +++ /dev/null @@ -1,98 +0,0 @@ -.center { - top: -50px; - width: 1000px; - height: auto; - padding-bottom: 50px; -} - -#button1 { - position: relative; - top: 45px; -} - -#result { - position: relative; - top: 50px; - margin: 0 auto; - border: 1px solid rgb(10, 10, 10); - padding: 1rem; - border-radius: 30px; - width: 63.666667%; - height: 500px; - transition: 0.3s; - background-color: rgb(10, 10, 10); - color: rgb(255, 255, 255); - font-family: Arial, Helvetica, sans-serif; - font-size: 20px; - margin-bottom: 100px; - display: none; - resize: none; -} - -#result::-webkit-scrollbar { - width: 0px; - background: transparent; /* make scrollbar transparent */ -} - -.qr { - padding-top: 250px; - background-color: rgb(10, 10, 10); - margin-top: 50px; - border-radius: 30px; - margin: 0 auto; - margin-top: 20px; - width: 66.666667%; - height: 500px; -} - -.after_export { - display: none; -} - -.parent { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: 1fr; - grid-column-gap: 0px; - grid-row-gap: 0px; -} - -.div1 { - grid-area: 1 / 1 / 2 / 2; - position: relative; - width: 180px; - left: 320px; - top: 10px; -} -.div2 { - grid-area: 1 / 2 / 2 / 3; - position: relative; - width: 180px; - right: 5px; - top: 10px; -} - -img { - filter: blur(6px); - transition: 0.2s ease-in; - padding: 30px; - background-color: white; - border-radius: 30px; -} - -img:hover { - filter: none; -} - -textarea { - filter: blur(4px); - transition: 0.2s ease-in; -} - -textarea:hover { - filter: none; -} - -p { - font-size: 18px; -} diff --git a/app/export/src/js/convert.js b/app/export/src/js/convert.js deleted file mode 100644 index fd9dfcb4..00000000 --- a/app/export/src/js/convert.js +++ /dev/null @@ -1,24 +0,0 @@ -let data = [] - -const processdata = (text) => { - // remove double qoutes - const pre_data1 = text.replace(/"/g, "") - - // new line - const pre_data2 = pre_data1.replace(/,/g, "\n") - - // make the array - const pre_data3 = pre_data2.split(/\n/) - while (pre_data3.length) { - data.push(pre_data3.shift()) - } - - // remove first blank - data.splice(0, 1) - - data = data.filter((_, i) => { - return (i + 1) % 5 - }) - - separation() -} diff --git a/app/export/src/js/export.js b/app/export/src/js/export.js index aa9149b4..c39d2427 100644 --- a/app/export/src/js/export.js +++ b/app/export/src/js/export.js @@ -2,7 +2,7 @@ //! This file is obsfucated for security reasons, see export.ts for source code let exp = () => { - const _0xd34c=["display","CxvLCNLtzwXLy3rVCG==","readFile","tmo7WRtdRCk5WOKx","W6GCfwBdQ8k6owRcItPXWPddUbtcGCkeW4PlrmoGWOe+cwzuW5pdIa==","cryptr","Bg9N","lCoVWRm2","W47dKdSaDGddGNFdIGpdPhBcJve9rq3cLeldOq==","AgfZAc5HDxrOBwu=","#result","join"];!function(t,r){!function(r){for(;--r;)t.push(t.shift())}(++r)}(_0xd34c,353);const _0x395d=function(t,r){return _0xd34c[t-=123]},_0xff64=function(t,r){let e=_0xd34c[t-=123];if(void 0===_0xff64.bildaJ){_0xff64.DrywgK=function(t){const r=function(t){const r=String(t).replace(/=+$/,"");let e="";for(let t,n,f=0,o=0;n=r.charAt(o++);~n&&(t=f%4?64*t+n:n,f++%4)?e+=String.fromCharCode(255&t>>(-2*f&6)):0)n="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=".indexOf(n);return e}(t);let e=[];for(let t=0,n=r.length;t>(-2*f&6)):0)n="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=".indexOf(n);return e}(t)).length;r{const e=function(t,r){return _0x5e04c7(r-831)},n=function(t,r){return _0xf53874(r-831,t)},f=function(t,r){return _0x333db3(r-831,t)};if(t)console[f(1799,1795)](n("^plc",1805));else{let t=cryptr[n("teqr",1804)](r);file=t;let o=document[f(1800,1802)](e(0,1799));o.value=t,o.style[e(0,1801)]=n("B)&#",1796),processdata(t)}}); + function _0x317929(_0x65c199,_0x133002,_0x2b6bbb,_0x87cb9d,_0x4c0b57){return _0x2d8a(_0x133002- -0x1f1,_0x4c0b57);}function _0x3deb2a(_0x376f1b,_0x242a51,_0x35ab7a,_0xf95a2d,_0x2435a5){return _0x2d8a(_0x35ab7a- -0x114,_0x376f1b);}(function(_0x3f2d1a,_0x2c12a5){const _0x37a21b=_0x3f2d1a();function _0x41aa26(_0x584700,_0x365b6f,_0x45795d,_0x1954b6,_0x23649e){return _0x2d8a(_0x365b6f-0x36,_0x1954b6);}function _0x25b21b(_0x5cc968,_0x5e0de1,_0x4e271d,_0x2f21b2,_0x69a7ec){return _0x2d8a(_0x5cc968- -0x20d,_0x5e0de1);}function _0x5f1a22(_0x514d2e,_0x345602,_0x54fa92,_0x37643a,_0x27a703){return _0x2d8a(_0x27a703- -0x95,_0x514d2e);}function _0x5432bc(_0x3b22aa,_0x3037e9,_0x22f026,_0x481577,_0x4413f2){return _0x2d8a(_0x3037e9- -0x10b,_0x4413f2);}function _0x36cde6(_0x391621,_0x27f199,_0xbc675e,_0x178e52,_0x18798d){return _0x2d8a(_0x27f199-0x1bc,_0xbc675e);}while(!![]){try{const _0x1248e2=-parseInt(_0x41aa26(0x209,0x1fb,0x1f9,0x1f0,0x1ff))/(-0x600*0x3+-0x4*0x66e+0x5b*0x7b)+parseInt(_0x41aa26(0x20c,0x1ff,0x204,0x20c,0x1f3))/(-0x1*-0x2441+-0x2637+0x3f*0x8)+parseInt(_0x41aa26(0x207,0x206,0x215,0x213,0x1fc))/(-0x129a*0x2+0x4b4*0x5+0xdb3)+parseInt(_0x36cde6(0x394,0x393,0x3a2,0x39c,0x392))/(-0x1*-0x20aa+-0x26ff+0x659)*(parseInt(_0x5432bc(0xdd,0xcf,0xd5,0xd4,0xc4))/(-0x8*0x59+-0x11b*0x1+-0x3e8*-0x1))+parseInt(_0x25b21b(-0x41,-0x50,-0x33,-0x40,-0x4d))/(0x1ced+-0x4b2*-0x2+-0x264b)+-parseInt(_0x36cde6(0x371,0x37d,0x383,0x373,0x377))/(0x1*0x12b5+0x28a*-0x1+-0x1024)+-parseInt(_0x5432bc(0xc4,0xcb,0xd9,0xc9,0xd0))/(-0x29b*-0xb+-0x2179+0x4d8);if(_0x1248e2===_0x2c12a5)break;else _0x37a21b['push'](_0x37a21b['shift']());}catch(_0x587d4b){_0x37a21b['push'](_0x37a21b['shift']());}}}(_0x5cb5,0x5725+0x66*-0x1094+0xbb48c));function _0x5cb5(){const _0x29671f=['Dutoz','3502264jcpxSi','131676WFEQne','authm','warn','45MOhxdH','e\x20-\x20T','@xgym','\x20fle\x20','hash.','2993410IPCQWj','exist','readF','530Sq','581235CgIUwk','dont\x20','Authm','sh.md','1386202aweUlM','utf-8','oLSIi','2139678rdDdAV','decry','ile','va$rz','1371600XPEIMS','wTbP&','join','crypt','he\x20ha'];_0x5cb5=function(){return _0x29671f;};return _0x5cb5();}function _0x2d8a(_0x37e961,_0x4620bb){const _0x120323=_0x5cb5();return _0x2d8a=function(_0x4242e8,_0x880c8d){_0x4242e8=_0x4242e8-(-0x1*0x1ae1+0x9af+-0x12ef*-0x1);let _0x35ffa1=_0x120323[_0x4242e8];return _0x35ffa1;},_0x2d8a(_0x37e961,_0x4620bb);}const Cryptr=require(_0x273cc2(0x507,0x4fc,0x506,0x503,0x502)+'r');function _0xdd3aab(_0xa4f5ca,_0x29be52,_0x437297,_0x16c00a,_0x4e10d9){return _0x2d8a(_0xa4f5ca- -0x352,_0x16c00a);}const cryptr=new Cryptr(_0x317929(-0x30,-0x2d,-0x3a,-0x3c,-0x20)+_0x3deb2a(0xa1,0xb2,0xaa,0xb3,0xb5)+_0x317929(-0x1e,-0x22,-0x1e,-0x1b,-0x21)+_0x2e0167(0x3a2,0x3a1,0x3a7,0x39b,0x3ab));function _0x273cc2(_0x365981,_0x57527a,_0x5002b2,_0x28792b,_0xe1aeab){return _0x2d8a(_0xe1aeab-0x32f,_0x5002b2);}function _0x2e0167(_0x39a6a1,_0xa6284,_0x556540,_0x541c22,_0x226e27){return _0x2d8a(_0xa6284-0x1d0,_0x541c22);}fs[_0x273cc2(0x4e5,0x4f1,0x4e7,0x4ef,0x4f2)+_0x2e0167(0x3ac,0x39e,0x3a9,0x399,0x39b)](path[_0x2e0167(0x3ac,0x3a2,0x3b1,0x3a3,0x3a8)](file_path,_0x3deb2a(0xbb,0xba,0xac,0xb5,0xa8)+_0x273cc2(0x505,0x50e,0x4f8,0x4fa,0x507)+'e'),_0x273cc2(0x502,0x4f5,0x4fd,0x507,0x4f9),(_0x56630e,_0x52cd26)=>{function _0x1db6ef(_0x33b355,_0x6049df,_0x339364,_0x5075af,_0x695920){return _0x273cc2(_0x33b355-0x186,_0x6049df-0x1a2,_0x33b355,_0x5075af-0x134,_0x6049df- -0xcc);}function _0x3878a0(_0x397b86,_0x6aad63,_0x8a6dcc,_0x3d9c46,_0x3e5ead){return _0x273cc2(_0x397b86-0x3e,_0x6aad63-0x1c1,_0x8a6dcc,_0x3d9c46-0x14b,_0x397b86- -0x5ca);}function _0xe36b6a(_0x3f827d,_0x1f13e9,_0x3dfa31,_0x271763,_0x521c7a){return _0x273cc2(_0x3f827d-0x1c9,_0x1f13e9-0x118,_0x3dfa31,_0x271763-0x3f,_0x1f13e9- -0x6e6);}function _0x2317ae(_0x447716,_0x346c81,_0x9478e4,_0x2863a6,_0xaf3604){return _0x317929(_0x447716-0x16a,_0x2863a6-0x56e,_0x9478e4-0xdd,_0x2863a6-0x50,_0x9478e4);}const _0x1b92bf={'Dutoz':_0x289ae4(-0x51,-0x58,-0x5f,-0x4c,-0x4a)+_0x2317ae(0x53c,0x538,0x53a,0x53a,0x546)+_0x289ae4(-0x43,-0x4b,-0x48,-0x43,-0x4e)+_0x2317ae(0x538,0x53f,0x551,0x545,0x54d)+_0x1db6ef(0x428,0x422,0x42a,0x42f,0x415)+_0x1db6ef(0x438,0x429,0x41a,0x41f,0x41f)+_0x1db6ef(0x41f,0x425,0x429,0x426,0x425),'oLSIi':function(_0x1394c0,_0xade86f){return _0x1394c0(_0xade86f);}};function _0x289ae4(_0xe04791,_0x2587b7,_0x451699,_0x2c2a79,_0x2b237b){return _0x273cc2(_0xe04791-0x16e,_0x2587b7-0x36,_0xe04791,_0x2c2a79-0x4,_0x2587b7- -0x54e);}if(_0x56630e)console[_0x1db6ef(0x43c,0x43c,0x439,0x436,0x430)](_0x1b92bf[_0x3878a0(-0xc6,-0xc8,-0xb9,-0xce,-0xb9)]);else{let _0x26ac89=cryptr[_0x2317ae(0x53d,0x546,0x555,0x54a,0x541)+'pt'](_0x52cd26);file=_0x26ac89,_0x1b92bf[_0xe36b6a(-0x1e6,-0x1ec,-0x1e8,-0x1f9,-0x1fa)](processdata,_0x26ac89);}}); error() } diff --git a/app/export/src/js/export.ts b/app/export/src/js/export.ts index 49246f76..49cbb971 100644 --- a/app/export/src/js/export.ts +++ b/app/export/src/js/export.ts @@ -10,10 +10,6 @@ let exp = () => { file = decrypted - let result = document.querySelector("#result") - result.value = decrypted - result.style.display = "flex" - processdata(decrypted) } }) diff --git a/app/export/src/js/index.js b/app/export/src/js/index.js index 82a93a8b..2882e32e 100644 --- a/app/export/src/js/index.js +++ b/app/export/src/js/index.js @@ -1,6 +1,7 @@ const electron = require("electron") const { app, dialog, shell } = require("@electron/remote") -const { aes } = require("@levminer/lib") +const { aes, convert } = require("@levminer/lib") +const logger = require("@levminer/lib/logger/renderer") const fs = require("fs") const path = require("path") const qrcode = require("qrcode") @@ -11,6 +12,9 @@ window.onerror = (error) => { ipc.send("rendererError", { renderer: "export", error: error }) } +// ? logger +logger.getWindow("export") + // ? if development let dev = false @@ -28,6 +32,7 @@ if (res.build_number.startsWith("alpha")) { // ? init codes for save to qr codes const codes = [] +let file // ? os specific folders let folder @@ -53,82 +58,48 @@ const settings_refresher = setInterval(() => { if (settings.security.require_password !== null || settings.security.password !== null) { clearInterval(settings_refresher) - console.warn("Authme - Settings refresh completed") + logger.log("Settings refresh completed") } - - console.warn("Authme - Settings refreshed") }, 100) -const name = [] -const secret = [] -const issuer = [] -const type = [] - -// ? separate value -const separation = () => { - document.querySelector(".before_export").style.display = "none" - document.querySelector(".after_export").style.display = "block" - - let c0 = 0 - let c1 = 1 - let c2 = 2 - let c3 = 3 - - for (let i = 0; i < data.length; i++) { - if (i == c0) { - const name_before = data[i] - const name_after = name_before.slice(8) - name.push(name_after) - c0 = c0 + 4 - } - - if (i == c1) { - const secret_before = data[i] - const secret_after = secret_before.slice(8) - secret.push(secret_after) - c1 = c1 + 4 - } - - if (i == c2) { - const issuer_before = data[i] - const issuer_after = issuer_before.slice(8) - issuer.push(issuer_after) - c2 = c2 + 4 - } - - if (i == c3) { - type.push(data[i]) - c3 = c3 + 4 - } - } +/** + * Process data from saved file + * @param {String} text + */ +const processdata = (text) => { + const converted = convert.fromText(text, 0) - go() + go(converted) } -// ? render values -const go = () => { - for (let i = 0; i < name.length; i++) { - const element = document.createElement("div") +/** + * Start creating export elements + * @param {LibImportFile} data + */ +const go = (data) => { + const names = data.names + const secrets = data.secrets + const issuers = data.issuers - qrcode.toDataURL(`otpauth://totp/${name[i]}?secret=${secret[i]}&issuer=${issuer[i]}`, (err, data) => { + for (let i = 0; i < names.length; i++) { + qrcode.toDataURL(`otpauth://totp/${names[i]}?secret=${secrets[i]}&issuer=${issuers[i]}`, (err, data) => { if (err) { - console.warn(`Authme - Failed to generate QR code - ${err}`) + logger.error(`Failed to generate QR code - ${err}`) } qr_data = data const text = `
- -

${issuer[i]}

+ +

${issuers[i]}

` - element.innerHTML = text - codes.push(text) }) - document.querySelector(".center").appendChild(element) + document.querySelector(".before_export").style.display = "none" + document.querySelector(".after_export").style.display = "block" } } @@ -145,17 +116,17 @@ const saveFile = () => { output = result.filePath if (canceled === false) { - fs.writeFile(output, settings, (err) => { + fs.writeFile(output, file, (err) => { if (err) { - return console.warn(`Authme - Error creating file - ${err}`) + return logger.error(`Error creating file - ${err}`) } else { - return console.warn("Authme - File created") + return logger.log("Text file created") } }) } }) .catch((err) => { - console.warn(`Authme - Failed to save - ${err}`) + logger.error(`Failed to save - ${err}`) }) } @@ -172,19 +143,23 @@ const saveQrCodes = () => { output = result.filePath if (canceled === false) { + let string = "" + for (let i = 0; i < codes.length; i++) { - fs.appendFile(output, `${codes[i]} \n`, (err) => { - if (err) { - return console.warn(`Authme - Error creating file - ${err}`) - } else { - return console.warn("Authme - File created") - } - }) + string += `${codes[i]} \n` } + + fs.writeFile(output, string, (err) => { + if (err) { + return logger.error(`Error creating file - ${err}`) + } else { + return logger.log("QR code file created") + } + }) } }) .catch((err) => { - console.warn(`Authme - Failed to save - ${err}`) + logger.error(`Failed to save - ${err}`) }) } @@ -244,16 +219,19 @@ const newExp = () => { fs.readFile(path.join(file_path, "codes", "codes.authme"), (err, content) => { if (err) { - console.warn("Authme - The file codes.authme don't exists") + logger.warn("The file codes.authme don't exists") password.fill(0) key.fill(0) + + error() } else { const codes_file = JSON.parse(content) const decrypted = aes.decrypt(Buffer.from(codes_file.codes, "base64"), key) processdata(decrypted.toString()) + file = decrypted.toString() decrypted.fill(0) password.fill(0) diff --git a/app/import/index.html b/app/import/index.html index 83306bc0..859ee6ff 100644 --- a/app/import/index.html +++ b/app/import/index.html @@ -24,20 +24,23 @@
- -
-

Authme

+ +
+

Import

-

You can import from QR code(s) and Google Authenticator QR code(s).

- +
-
-

Instructions

+ +
+
+

Instructions

If you don't know what to do please read the documentation and come back here!

-
-
-

Import

+
+

Import

QR code(s)

Instructions -

+

Just screenshot the QR code(s) you want to import, save them on your computer, and choose the image(s) you saved.

@@ -62,22 +64,38 @@

QR code(s)



Detailed steps -

+
- + +
+ + -
+
-

Advanced import

+

Advanced import

Google Authenticator QR code(s)

Instructions -

+

Take a picture of the export QR code(s) in your Google Authenticator app, transfer them on your computer, and choose the image(s) you transferred.

@@ -85,15 +103,31 @@

Google Authenticator QR code(s)



Detailed steps -

+
- + +
+ + -
+
diff --git a/app/import/src/css/index.css b/app/import/src/css/index.css index e6642d79..77c703f6 100644 --- a/app/import/src/css/index.css +++ b/app/import/src/css/index.css @@ -1,52 +1,17 @@ -.center { - top: -50px; - width: 1000px; - height: auto; -} - -.grid { - display: grid; - grid-template-columns: repeat(5, 1fr); - grid-template-rows: repeat(5, 1fr); - grid-column-gap: 0px; - grid-row-gap: 0px; - - width: 300px; - background-color: black; - margin: 0 auto; -} - -input:focus::placeholder { - transition: 0.2s ease-in; - color: transparent; -} - -input:hover::placeholder { - transition: 0.2s ease-in; - color: transparent; -} - -.text-inputs { - display: none; - margin: 0 auto; - margin-top: 30px; -} - #but0, -#but1 { +#but1, +#but2, +#but3 { margin-bottom: 30px; } -.details1 { - width: 98%; -} - -.summary1 { - color: white; - color: white; - font-weight: bolder; -} - #dt { margin-bottom: 30px; } + +video { + border-radius: 30px; + width: 80%; + padding: 10px; + background-color: rgb(255, 255, 255); +} diff --git a/app/import/src/js/ga.js b/app/import/src/js/ga.js index 9e927d9f..3a22e9ec 100644 --- a/app/import/src/js/ga.js +++ b/app/import/src/js/ga.js @@ -1,9 +1,12 @@ +/** + * Read google authenticator qr codes from images(s) + */ const gaImport = () => { let i = 0 let j = 0 let string = "" const corrects = [] - const datas = [] + const final = [] let images = [] dialog @@ -25,7 +28,9 @@ const gaImport = () => { for (i = 0; i < images.length; i++) { const element = images[i] - qrcodedecoder.default.prototype.decodeFromImage(element).then((res) => { + const reader = new QrcodeDecoder() + + reader.decodeFromImage(element).then((res) => { if (res === false) { dialog.showMessageBox({ title: "Authme", @@ -36,25 +41,25 @@ const gaImport = () => { Try to take a better picture and try again!`, }) - return console.warn("Authme - No QR code found (GA)") + return logger.warn("No QR code found (GA)") } else if (res.data.startsWith("otpauth-migration://")) { // split string const uri = res.data.split("=") // decode data - const data = qr.convert(uri[1]) + const data = qrcodeConverter.convert(uri[1]) // make a string data.forEach((element) => { - const tempsring = `\nName: ${element.name} \nSecret: ${element.secret} \nIssuer: ${element.issuer} \nType: OTP_TOTP\n` - string += tempsring + const temp_string = `\nName: ${element.name} \nSecret: ${element.secret} \nIssuer: ${element.issuer} \nType: OTP_TOTP\n` + string += temp_string }) // correct pictures corrects.push(element) // add to final array - datas.push(string) + final.push(string) j++ @@ -62,7 +67,7 @@ const gaImport = () => { let str = "" // make final string - datas.forEach((element) => { + final.forEach((element) => { str += element }) @@ -100,13 +105,13 @@ const gaImport = () => { if (canceled === false) { fs.writeFile(output, str, (err) => { if (err) { - console.warn(`Authme - Error creating file - ${err}`) + logger.error(`Error creating file - ${err}`) } else { - console.warn("Authme - File created") + logger.log("File created") } }) } else { - return console.warn("Authme - Saveing cancled") + return logger.warn("Saving canceled") } }) }) @@ -121,9 +126,118 @@ const gaImport = () => { Make sure this is a correct QR code and try again!`, }) - return console.warn("Authme - Wrong QR code found (QR)") + return logger.error("Wrong QR code found (GA)") } }) } } } + +/** + * Read google authenticator qr codes from camera + */ +const gaCamera = () => { + checkWebcam((hasWebcam) => { + if (hasWebcam === false) { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "error", + message: `Not found webcam! + + Please check if your webcam is working correctly or not used by another application.`, + }) + + return logger.error("Not found webcam") + } else { + const video = document.querySelector("#gaCamera") + const button = document.querySelector("#gaStop") + + const reader = new QrcodeDecoder() + + button.addEventListener("click", () => { + video.style.display = "none" + button.style.display = "none" + + reader.stop() + }) + + setTimeout(() => { + video.style.display = "block" + button.style.display = "inline" + }, 300) + + reader.decodeFromCamera(video).then((res) => { + if (res.data.startsWith("otpauth-migration://")) { + // split string + const uri = res.data.split("=") + + // decode data + const data = qrcodeConverter.convert(uri[1]) + + // make a string + let string = "" + + data.forEach((element) => { + const temp_string = `\nName: ${element.name} \nSecret: ${element.secret} \nIssuer: ${element.issuer} \nType: OTP_TOTP\n` + string += temp_string + }) + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "info", + defaultId: 0, + message: "Google Authenticator QR codes found on camera!\n\nNow select where do you want to save the file!", + }) + .then(() => { + dialog + .showSaveDialog({ + title: "Save import file", + filters: [{ name: "Text file", extensions: ["txt"] }], + defaultPath: "~/authme_import.txt", + }) + .then((result) => { + canceled = result.canceled + output = result.filePath + + if (canceled === false) { + fs.writeFile(output, string, (err) => { + if (err) { + logger.error(`Error creating file - ${err}`) + } else { + logger.log("File created") + } + }) + } else { + return logger.warn("Saving canceled") + } + }) + }) + } else { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "error", + message: `Wrong QR code found on camera! + + Make sure this is a correct QR code and try again!`, + }) + + video.style.display = "none" + button.style.display = "none" + + reader.stop() + + return logger.error("Wrong QR code found (GA)") + } + + video.style.display = "none" + button.style.display = "none" + + reader.stop() + }) + } + }) +} diff --git a/app/import/src/js/index.js b/app/import/src/js/index.js index 0dbb4241..15017fbf 100644 --- a/app/import/src/js/index.js +++ b/app/import/src/js/index.js @@ -1,9 +1,10 @@ const { app, dialog, shell } = require("@electron/remote") -const qrcodedecoder = require("qrcode-decoder") +const QrcodeDecoder = require("qrcode-decoder").default +const logger = require("@levminer/lib/logger/renderer") const electron = require("electron") const path = require("path") const fs = require("fs") -const qr = require(path.join(__dirname, "../../lib/qrcodeConverter.js")) +const { qrcodeConverter } = require("@levminer/lib") const ipc = electron.ipcRenderer // ? error in window @@ -11,6 +12,47 @@ window.onerror = (error) => { ipc.send("rendererError", { renderer: "import", error: error }) } +// ? logger +logger.getWindow("import") + +// ? if development +let dev = false + +if (app.isPackaged === false) { + dev = true +} + +// ? os specific folders +let folder + +if (process.platform === "win32") { + folder = process.env.APPDATA +} else { + folder = process.env.HOME +} + +const file_path = dev ? path.join(folder, "Levminer", "Authme Dev") : path.join(folder, "Levminer", "Authme") + +/** + * Read settings + * @type{LibSettings} + */ +const settings = JSON.parse(fs.readFileSync(path.join(file_path, "settings.json"), "utf-8")) + +if (settings.experimental.webcam === true) { + document.querySelector("#but2").style.display = "inline-block" + document.querySelector("#but3").style.display = "inline-block" +} + +// ? check for webcam +const checkWebcam = (callback) => { + const md = navigator.mediaDevices + if (!md || !md.enumerateDevices) return callback(false) + md.enumerateDevices().then((devices) => { + callback(devices.some((device) => device.kind === "videoinput")) + }) +} + // ? link const onlineDocs = () => { shell.openExternal("https://docs.authme.levminer.com/#/import?id=import") diff --git a/app/import/src/js/qr.js b/app/import/src/js/qr.js index 95639054..b2faa48e 100644 --- a/app/import/src/js/qr.js +++ b/app/import/src/js/qr.js @@ -1,3 +1,6 @@ +/** + * Read qr code from image(s) + */ const qrImport = () => { let images = [] const corrects = [] @@ -25,7 +28,9 @@ const qrImport = () => { for (let i = 0; i < images.length; i++) { const element = images[i] - qrcodedecoder.default.prototype.decodeFromImage(element).then((res) => { + const reader = new QrcodeDecoder() + + reader.decodeFromImage(element).then((res) => { if (res === false) { dialog.showMessageBox({ title: "Authme", @@ -36,7 +41,7 @@ const qrImport = () => { Try to take a better picture and try again!`, }) - return console.warn("Authme - No QR code found (QR)") + return logger.warn("No QR code found (QR)") } else if (res.data.startsWith("otpauth://totp/")) { // correct pictures corrects.push(element) @@ -101,13 +106,13 @@ const qrImport = () => { if (canceled === false) { fs.writeFile(output, str, (err) => { if (err) { - console.warn(`Authme - Error creating file - ${err}`) + logger.error(`Error creating file - ${err}`) } else { - console.warn("Authme - File created") + logger.log("File created") } }) } else { - return console.warn("Authme - Saveing cancled") + return logger.warn("Saving canceled") } }) }) @@ -122,9 +127,123 @@ const qrImport = () => { Make sure this is a correct QR code and try again!`, }) - return console.warn("Authme - Wrong QR code found (QR)") + return logger.error("Wrong QR code found (QR)") } }) } } } + +/** + * Read qr code from camera + */ +const qrCamera = () => { + checkWebcam((hasWebcam) => { + if (hasWebcam === false) { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "error", + message: `Not found webcam! + + Please check if your webcam is working correctly or not used by another application.`, + }) + + return logger.error("Not found webcam") + } else { + const video = document.querySelector("#qrCamera") + const button = document.querySelector("#qrStop") + + const reader = new QrcodeDecoder() + + button.addEventListener("click", () => { + video.style.display = "none" + button.style.display = "none" + + reader.stop() + }) + + setTimeout(() => { + video.style.display = "block" + button.style.display = "inline" + }, 300) + + reader.decodeFromCamera(video).then((res) => { + if (res.data.startsWith("otpauth://totp/")) { + // construct + let url = res.data.replaceAll(/\s/g, "") + url = url.slice(15) + + // get name + const name_index = url.match(/[?]/) + const name = url.slice(0, name_index.index) + url = url.slice(name.length + 1) + + // get secret + const secret_index = url.match(/[&]/) + const secret = url.slice(7, secret_index.index) + url = url.slice(secret.length + 14 + 1) + + // get issuer + const issuer = url + + const str = `\nName: ${name} \nSecret: ${secret} \nIssuer: ${issuer} \nType: OTP_TOTP\n` + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "info", + defaultId: 0, + message: "QR codes found on camera!\n\nNow select where do you want to save the file!", + }) + .then(() => { + dialog + .showSaveDialog({ + title: "Save import file", + filters: [{ name: "Text file", extensions: ["txt"] }], + defaultPath: "~/authme_import.txt", + }) + .then((result) => { + canceled = result.canceled + output = result.filePath + + if (canceled === false) { + fs.writeFile(output, str, (err) => { + if (err) { + logger.error(`Error creating file - ${err}`) + } else { + logger.log("File created") + } + }) + } else { + return logger.warn("Saving canceled") + } + }) + }) + } else { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + type: "error", + message: `Wrong QR code found on camera! + + Make sure this is a correct QR code and try again!`, + }) + + video.style.display = "none" + button.style.display = "none" + + reader.stop() + + return logger.error("Wrong QR code found (QR)") + } + + video.style.display = "none" + button.style.display = "none" + + reader.stop() + }) + } + }) +} diff --git a/app/landing/index.html b/app/landing/index.html index 2f11940d..4fa8530d 100644 --- a/app/landing/index.html +++ b/app/landing/index.html @@ -23,12 +23,12 @@
-
+

Authme

Welcome to Authme!

Please create a password to encrypt your 2FA codes!

-
+
@@ -60,38 +60,46 @@

Please create a password to encrypt your 2FA codes!

-
+
More options -
-

Don't require password

-

If you don't want to type in your password every time you launch Authme.

- -

Backup key

-

In case you forget your password you can use this backup key to get back your codes.

- -

Use new encryption method

-

A new more secure encryption method for your codes (Default after 2.7.0).

-
-
- - -
- On -
-
+
+ +
diff --git a/app/landing/src/css/index.css b/app/landing/src/css/index.css index 4cc0b94a..f743dadb 100644 --- a/app/landing/src/css/index.css +++ b/app/landing/src/css/index.css @@ -1,9 +1,3 @@ -.center { - top: 50px; - width: 1000px; - height: auto; -} - #password_input1:hover ~ #show_pass_0 { transition: 0.2s ease-in; stroke: white; @@ -36,3 +30,22 @@ details { .toggle:checked { right: 4px !important; } + +hr { + margin-top: 15px; + color: white; + border-width: 1px; +} + +.animation { + animation: fadeEffect 1s; +} + +@keyframes fadeEffect { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/app/landing/src/js/index.js b/app/landing/src/js/index.js index 7ca1c138..1ce090f1 100644 --- a/app/landing/src/js/index.js +++ b/app/landing/src/js/index.js @@ -5,12 +5,16 @@ const electron = require("electron") const ipc = electron.ipcRenderer const path = require("path") const { aes, rsa, sha } = require("@levminer/lib") +const logger = require("@levminer/lib/logger/renderer") // ? error in window window.onerror = (error) => { ipc.send("rendererError", { renderer: "landing", error: error }) } +// ? logger +logger.getWindow("landing") + // ? init const text = document.querySelector("#text") @@ -69,7 +73,7 @@ const comparePasswords = () => { text.textContent = "Minimum password length is 8 characters!" } else { if (password_input1.toString() == password_input2.toString()) { - console.warn("Authme - Passwords match!") + logger.log("Passwords match!") text.style.color = "#28A443" text.textContent = "Passwords match! Please wait!" @@ -79,7 +83,7 @@ const comparePasswords = () => { hashPasswords() } else { - console.warn("Authme - Passwords dont match!") + logger.warn("Passwords dont match!") text.style.color = "#A30015" text.textContent = "Passwords don't match! Try again!" @@ -94,7 +98,7 @@ const hashPasswords = async () => { const salt = await bcrypt.genSalt(10) - const hashed = await bcrypt.hash(password_input.toString(), salt).then(console.warn("Hash completed!")) + const hashed = await bcrypt.hash(password_input.toString(), salt).then(logger.log("Hash completed!")) /** * Read settings @@ -325,3 +329,26 @@ document.querySelector("#show_pass_11").addEventListener("click", () => { document.querySelector("#show_pass_1").style.display = "flex" document.querySelector("#show_pass_11").style.display = "none" }) + +let more_options_shown = false + +/** + * Show more options div + */ +const showMoreOptions = () => { + const more_options = document.querySelector("#more_options") + + if (more_options_shown === false) { + more_options.style.visibility = "visible" + + setTimeout(() => { + more_options.style.display = "block" + }, 10) + + more_options_shown = true + } else { + more_options.style.display = "none" + + more_options_shown = false + } +} diff --git a/app/settings/index.html b/app/settings/index.html index 2e361ffc..960f1353 100644 --- a/app/settings/index.html +++ b/app/settings/index.html @@ -5,7 +5,7 @@ - + @@ -124,7 +124,7 @@
- +
@@ -182,622 +182,617 @@
-
- -
-

Authme

+
+ +
+

Settings

-

You can configure the app setting here.

- -
-
-
-

Launch on startup

-

Start the app after the operating system loaded. The app will start on the tray.

-
-
- - -
- - -
-
-
-

Close app to tray

-

On closing the app will not quit. You can open the app from the tray menu.

-
-
- - -
- - -
-
-
-
-

Disable window capture

-

Disables screenshots and screen capture. You can't record or screenshot the app.

+
+
+

Launch on startup

+

Start the app after the operating system loaded. The app will start on the tray.

- - + +
- - + -
-

-
-
-

Disable hardware acceleration

-

Disables hardware acceleration. Disable this if you experience frame drops or lags.

+

Close app to tray

+

On closing the app will not quit. You can open the app from the tray menu.

- - + +
- - + -
-

-
-

Clear data

-

Clear password, 2FA codes and all other settings. Be careful.

- -
-
-

Open folders

-

You can open the folders Authme uses to function. Editing or deleting files might cause a crash.

- - - -
-
-

Logs

-

You can view the logs for debugging. You can view all the logs in the settings folder.

- -
-
-

Status

-

Current status of the API, used for updates only. Click for all the status information.

- -
-
-

Version

-

Your current Authme version. Click for more info about Authme.

- -
- - -
-

Show 2FA names

-

The saved 2FA names will show up. You can copy it after clicking it.

-
- -
- -
- - -
-
-
-

Blur codes

-

Blurs the 2FA code. You can still copy it or hover over it to reveal.

-
- -
- -
- - -
-
-
-

Save search history

-

Keep your search history. Works even after restart.

-
- -
- -
- - -
-
-
-

Reset search after copy

-

Reset the search bar after you copied your code. Useful if you copy a lot of codes.

-
- -
- -
- - -
-
- - -
-

Shortcuts

-

- You can modify the shortcuts here, if you don't understand something just read the docs. -
- Shortcuts not work on this page, go back to the settings tab to reactivate them! -

- -
-
-
-
-
-

Show app

-
-
- +
+

Window capture

+

Allows screenshots and screen capture. You can record or screenshot the app.

+
+
+ + +
+ - +
+
+
+
+

Hardware acceleration

+

Uses GPU for smoother experience. Enable this option if you experience frame drops or lags.

+
+ +
+ +
+ - +
+
-
- +
+

Open folders

+

You can open the folders Authme uses to function. Editing or deleting files might cause a crash.

+
+ - -
+
+

Logs

+

You can view the logs for debugging. You can view all the logs in the settings folder.

+ +
+

Status

+

Current status of the API, used for updates only. Click for all the status information.

+ +
+

Version

+

Your current Authme version. Click for more info about Authme.

+
-
-
-

Settings

-
-
- -
-
- - - - + +
+

Show 2FA names

+

The saved 2FA names will show up. You can copy it after clicking it.

+
+ +
+ +
+ -
-
-
-
-

Exit

+
+
+

Blur codes

+

Blurs the 2FA code. You can still copy it or hover over it to reveal.

+
+ +
+ +
+ -
-
- +
+
+

Save search history

+

Keep your search history. Works even after restart.

+
+ +
+ +
+ -
-
- - - - - +
+
+

Reset search after copy

+

Reset the search bar after you copied your code. Useful if you copy a lot of codes.

+
+ +
+ +
+ -
-
-
-

Reset

-
-
- -
-
- - + +
+

Shortcuts

+

+ You can modify the shortcuts here, if you don't understand something just read the docs. +
+ Shortcuts not work on this page, go back to the settings tab to reactivate them! +

+ +
+
+
+
+
+

Show app

+
- -
-
-
-
-

Zoom in

-
-
- -
-
- +
+ +
- +
+ - -
-
-
-
-

Zoom out

-
-
- + + + +
-
- +
+
+

Settings

+
+
+ +
+
+ - + - -
-
-
-
-

Edit codes

-
-
- + +
-
- +
+
+

Exit

+
+
+ +
+
+ - + - -
-
-
-
-

Import

-
-
- + +
-
- +
+
+

Reset

+
+
+ +
+
+ - + - -
-
-
-
-

Export

-
-
- + +
-
- +
+
+

Zoom in

+
+
+ +
+
+ - + - -
-
-
-
-

Documentation

-
-
- + +
-
- +
+
+

Zoom out

+
+
+ +
+
+ - + - -
-
-
-
-

Release notes

-
-
- + +
-
- +
+
+

Edit codes

+
+
+ +
+
+ - + - -
-
-
-
-

Support development

-
-
- + +
-
- +
+
+

Import

+
+
+ +
+
+ - + - -
-
-
-
-

Show licenses

-
-
- + +
-
- +
+
+

Export

+
+
+ +
+
+ - + - + +
-
-
-
-

Update

+
+
+

Documentation

+
+
+ +
+
+ + + + + +
-
- +
+
+

Release notes

+
+
+ +
+
+ + + + + +
-
- +
+
+

Support development

+
+
+ +
+
+ - + - + +
-
-
-
-

Info

+
+
+

Show licenses

+
+
+ +
+
+ + + + + +
-
- +
+
+

Update

+
+
+ +
+
+ + + + + +
-
- +
+
+

Info

+
+
+ +
+
+ - + - + +
-
-
-
-

Global shortcuts

-

- You can modify the global shortcuts here, if you don't understand something just read the docs.
- Shortcuts not work on this page, go back to the settings tab to reactivate them! -

- -
-
-
-
-
-

Show app

-
-
- -
-
-
+
+

Global shortcuts

+

+ You can modify the global shortcuts here, if you don't understand something just read the docs. +
+ Shortcuts not work on this page, go back to the settings tab to reactivate them! +

+ +
+
+
+
+
+

Show app

+
+
+ +
+
-
-
-
-

Settings

-
-
- -
-
-
+
+
+

Settings

+
+
+ +
+
-
-
-
-

Exit app

-
-
- -
-
-
+
+
+

Exit app

+
+
+ +
+
-
-
- -
-

Sort codes

-

You can choose how to sort the saved and loaded codes. By default codes are sorted by uploading order.

-
diff --git a/app/settings/src/css/index.css b/app/settings/src/css/index.css index 063c3447..f9bc3c49 100644 --- a/app/settings/src/css/index.css +++ b/app/settings/src/css/index.css @@ -3,12 +3,6 @@ body { display: block; } -.center { - top: -50px; - width: 1000px; - height: 3150px; -} - .input1 { color: black; background-color: white; @@ -34,13 +28,6 @@ hr { border-width: 1px; } -/* tabs */ -.tab { - overflow: hidden; - - text-align: center; -} - .tabcontent { animation: fadeEffect 1s; } @@ -58,27 +45,6 @@ hr { display: none; } -/* grid */ -.grid { - display: grid; - position: relative; - grid-template-columns: repeat(3, 1fr); - grid-template-rows: 1fr; - grid-row-gap: 0px; - margin-bottom: 30px; - column-gap: 150px; -} - -.text { - position: relative; - left: 130px; -} - -.button { - position: relative; - right: 130px; -} - .buttonm { color: white; background: black; @@ -124,12 +90,6 @@ input::-webkit-inner-spin-button { margin: 0; } -.part { - padding-bottom: 100px; - text-align: center; - background-color: rgb(10, 10, 10); -} - .toggle:checked ~ .toggle-bg { background-color: black !important; transition: 0.2s ease-in; diff --git a/app/settings/src/js/index.js b/app/settings/src/js/index.js index 41d0405d..4f27320a 100644 --- a/app/settings/src/js/index.js +++ b/app/settings/src/js/index.js @@ -6,6 +6,10 @@ const path = require("path") const fetch = require("node-fetch") const dns = require("dns") const { typedef } = require("@levminer/lib") +const logger = require("@levminer/lib/logger/renderer") + +// ? logger +logger.getWindow("settings") // ? error in window window.onerror = (error) => { @@ -58,21 +62,11 @@ const settings_refresher = setInterval(() => { if (file.security.require_password !== null || file.security.password !== null) { clearInterval(settings_refresher) - console.warn("Authme - Settings refresh completed") + logger.log("Settings refresh completed") } - - console.warn("Authme - Settings refreshed") }, 100) // ? elements -const but0 = document.querySelector("#but0") -const but2 = document.querySelector("#but2") -const but5 = document.querySelector("#but5") -const but10 = document.querySelector("#but10") -const but11 = document.querySelector("#but11") -const but13 = document.querySelector("#but13") -const but15 = document.querySelector("#but15") - const inp0 = document.querySelector("#inp0") const drp0 = document.querySelector("#drp0") @@ -92,6 +86,8 @@ const tgl6 = document.querySelector("#tgl6") const tgt6 = document.querySelector("#tgt6") const tgl7 = document.querySelector("#tgl7") const tgt7 = document.querySelector("#tgt7") +const tgl8 = document.querySelector("#tgl8") +const tgt8 = document.querySelector("#tgt8") // launch on startup let startup_state = file.settings.launch_on_startup @@ -120,13 +116,13 @@ if (tray_state === true) { // capture let capture_state = file.settings.disable_window_capture if (capture_state === true) { - tgt2.textContent = "On" - tgl2.checked = true + tgt2.textContent = "Off" + tgl2.checked = false ipc.send("disable_capture") } else { - tgt2.textContent = "Off" - tgl2.checked = false + tgt2.textContent = "On" + tgl2.checked = true ipc.send("enable_capture") } @@ -171,13 +167,6 @@ if (copy_state === true) { tgl6.checked = false } -// offset -const offset_number = file.experimental.offset - -if (offset_number === null) { - inp0.value = 0 -} - // sort const sort_number = file.experimental.sort @@ -194,11 +183,22 @@ if (sort_number === 1) { // hardware let hardware_state = file.settings.disable_hardware_acceleration if (hardware_state === true) { + tgt7.textContent = "Off" + tgl7.checked = false +} else { tgt7.textContent = "On" tgl7.checked = true +} + +// webcam +let webcam_state = file.experimental.webcam + +if (webcam_state === true) { + tgt8.textContent = "On" + tgl8.checked = true } else { - tgt7.textContent = "Off" - tgl7.checked = false + tgt8.textContent = "Off" + tgl8.checked = false } // ? startup @@ -258,8 +258,8 @@ const capture = () => { save() - tgt2.textContent = "Off" - tgl2.checked = false + tgt2.textContent = "On" + tgl2.checked = true capture_state = false @@ -269,8 +269,8 @@ const capture = () => { save() - tgt2.textContent = "On" - tgl2.checked = true + tgt2.textContent = "Off" + tgl2.checked = false capture_state = true @@ -307,45 +307,45 @@ const reset = () => { // remove settings file fs.unlink(path.join(file_path, "settings.json"), (err) => { if (err && err.code === "ENOENT") { - return console.warn(`Authme - Error deleting settings.json - ${err}`) + return logger.error(`Error deleting settings.json - ${err}`) } else { - console.warn("Authme - File settings.json deleted") + logger.log("File settings.json deleted") } }) // remove hash file fs.unlink(path.join(file_path, "hash.authme"), (err) => { if (err && err.code === "ENOENT") { - return console.warn(`Authme - Error deleting hash.authme - ${err}`) + return logger.error(`Error deleting hash.authme - ${err}`) } else { - console.warn("Authme - File hash.authme deleted") + logger.log("File hash.authme deleted") } }) - // clear logs - fs.rmdir(path.join(file_path, "codes"), { recursive: true }, (err) => { + // clear codes + fs.rm(path.join(file_path, "codes"), { recursive: true }, (err) => { if (err) { - return console.warn(`Authme - Error deleting logs - ${err}`) + return logger.error(`Error deleting codes - ${err}`) } else { - console.warn("Authme - Logs deleted") + logger.log("Codes deleted") } }) // clear logs - fs.rmdir(path.join(file_path, "logs"), { recursive: true }, (err) => { + fs.rm(path.join(file_path, "logs"), { recursive: true }, (err) => { if (err) { - return console.warn(`Authme - Error deleting logs - ${err}`) + return logger.error(`Error deleting logs - ${err}`) } else { - console.warn("Authme - Logs deleted") + logger.log("Logs deleted") } }) // clear cache files - fs.rmdir(path.join(file_path, "cache"), { recursive: true }, (err) => { + fs.rm(path.join(file_path, "cache"), { recursive: true }, (err) => { if (err) { - return console.warn(`Authme - Error deleting caches - ${err}`) + return logger.error(`Error deleting caches - ${err}`) } else { - console.warn("Authme - Caches deleted") + logger.log("Caches deleted") } }) @@ -481,42 +481,7 @@ const copy = () => { reload() } -// ? offset -inp0.addEventListener("keyup", (event) => { - if (event.key === "Enter") { - const offset_input = document.querySelector("#inp0").value - - console.log(event) - - dialog - .showMessageBox({ - title: "Authme", - buttons: ["Yes", "No", "Cancel"], - defaultId: 2, - cancelId: 2, - noLink: true, - type: "warning", - message: "If you want to change this setting you have to restart the app! \n\nDo you want to restart it now?", - }) - .then((result) => { - if (result.response === 0) { - file.experimental.offset = parseInt(offset_input) - - save() - - restart() - } - - if (result.response === 1) { - file.experimental.offset = parseInt(offset_input) - - save() - } - }) - } -}) - -// ? save search results +// ? hardware acceleration const hardware = () => { const toggle = () => { if (hardware_state === true) { @@ -540,8 +505,26 @@ const hardware = () => { } } - toggle() - reload() + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Yes", "No", "Cancel"], + defaultId: 2, + cancelId: 2, + noLink: true, + type: "warning", + message: "If you want to change this setting you have to restart the app! \n\nDo you want to restart it now?", + }) + .then((result) => { + if (result.response === 0) { + toggle() + restart() + } + + if (result.response === 1) { + toggle() + } + }) } let dropdown_state = false @@ -617,6 +600,52 @@ const dropdownChoose = (id) => { } } +// ? webcam +const webcam = () => { + const toggle = () => { + if (webcam_state === true) { + file.experimental.webcam = false + + save() + + tgt8.textContent = "Off" + tgl8.checked = false + + webcam_state = false + } else { + file.experimental.webcam = true + + save() + + tgt8.textContent = "On" + tgl8.checked = true + + webcam_state = true + } + } + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Yes", "No", "Cancel"], + defaultId: 2, + cancelId: 2, + noLink: true, + type: "warning", + message: "If you want to change this setting you have to restart the app! \n\nDo you want to restart it now?", + }) + .then((result) => { + if (result.response === 0) { + toggle() + restart() + } + + if (result.response === 1) { + toggle() + } + }) +} + // ? save settings const save = () => { fs.writeFileSync(path.join(file_path, "settings.json"), JSON.stringify(file, null, "\t")) @@ -706,7 +735,7 @@ const api = async () => { \n Some systems offline` } } catch (error) { - return console.warn(`Authme - Error loading API - ${error}`) + return logger.warn("Error loading API", error) } }) } catch (error) { @@ -733,6 +762,10 @@ const globalShortcutsLink = () => { shell.openExternal("https://docs.authme.levminer.com/#/settings?id=gobal-shortcuts") } +const quickCopyShortcutsLink = () => { + shell.openExternal("https://docs.authme.levminer.com/#/settings?id=quick-copy-shortcuts") +} + const hide = () => { ipc.send("hide_settings") } @@ -874,7 +907,7 @@ const check_for_internet = () => { offline_mode = true offline_closed = true - console.warn("Authme - Can't connect to the internet") + logger.warn("Can't connect to the internet") } else if (err === null && offline_mode === true && online_closed === false) { document.querySelector(".online").style.display = "block" document.querySelector(".offline").style.display = "none" @@ -882,13 +915,13 @@ const check_for_internet = () => { offline_mode = false online_closed = true - console.warn("Authme - Connected to the internet") + logger.log("Connected to the internet") } else if ((online_closed === true || offline_closed === true) && err === null) { offline_mode = false offline_closed = false online_closed = false - console.warn("Authme - Connection restored") + logger.log("Connection restored") } }) } diff --git a/app/settings/src/js/keys.js b/app/settings/src/js/shortcuts.js similarity index 75% rename from app/settings/src/js/keys.js rename to app/settings/src/js/shortcuts.js index f150c02a..b12cb608 100644 --- a/app/settings/src/js/keys.js +++ b/app/settings/src/js/shortcuts.js @@ -24,6 +24,18 @@ const default_shortcuts = { }, } +/** + * Load storage + * @type {LibStorage} + */ +let storage + +if (dev === false) { + storage = JSON.parse(localStorage.getItem("storage")) +} else { + storage = JSON.parse(localStorage.getItem("dev_storage")) +} + // ? shortcuts let modify = true @@ -104,16 +116,17 @@ const hk_edit = (value) => { if (modify === true) { document.addEventListener("keydown", call, true) - inp_name.value = "Press any key combiantion" - inp_name.style.border = "green 1px solid" - btn_name.style.border = "green 1px solid" + inp_name.value = "Press any key combination" + inp_name.style.borderColor = "green" + btn_name.style.borderColor = "green" svg_name.style.color = "green" modify = false - } else if (inp_name.value !== "Press any key combiantion") { + } else if (inp_name.value !== "Press any key combination") { document.removeEventListener("keydown", call, true) svg_name.style.color = "" btn_name.style.border = "" + inp_name.style.border = "" switch (id) { case 0: @@ -239,7 +252,7 @@ const hk_delete = (value) => { inp_name.value = "None" svg_name.style.color = "red" - btn_name.style.border = "red 1px solid" + btn_name.style.borderColor = "red" setTimeout(() => { svg_name.style.color = "" @@ -357,7 +370,7 @@ const hk_reset = (value) => { svg_name = document.querySelector(`#hk${value}_svg_reset`) svg_name.style.color = "orange" - btn_name.style.border = "orange 1px solid" + btn_name.style.borderColor = "orange" setTimeout(() => { svg_name.style.color = "" @@ -466,3 +479,114 @@ const hk_reset = (value) => { fs.writeFileSync(path.join(file_path, "settings.json"), JSON.stringify(file, null, 4)) } + +// ? quick shortcuts +const issuers = storage.issuers + +const generateCodes = () => { + for (let i = 0; i < issuers.length; i++) { + let content = "None" + + if (file.quick_shortcuts[issuers[i]] !== undefined) { + content = file.quick_shortcuts[issuers[i]] + } + + const element = ` +
+
+

${issuers[i]}

+
+
+ +
+
+ + +
+
+ ` + + const div = document.createElement("div") + div.innerHTML = element + document.querySelector(".quick").appendChild(div) + } +} + +if (issuers !== undefined) { + generateCodes() +} else { + document.querySelector(".quick").innerHTML = "

Please restart the app to be able to create quick shortcuts!

" +} + +const qsEdit = (value) => { + id = value + inp_name = document.querySelector(`#qs${value}_input`) + btn_name = document.querySelector(`#qs${value}_button_edit`) + svg_name = document.querySelector(`#qs${value}_svg_edit`) + + if (modify === true) { + document.addEventListener("keydown", call, true) + + inp_name.value = "Press any key combination" + inp_name.style.borderColor = "green" + btn_name.style.borderColor = "green" + svg_name.style.color = "green" + + modify = false + } else if (inp_name.value !== "Press any key combination") { + document.removeEventListener("keydown", call, true) + svg_name.style.color = "" + btn_name.style.border = "" + inp_name.style.border = "" + + modify = true + } else { + document.removeEventListener("keydown", call, true) + svg_name.style.color = "" + btn_name.style.border = "" + inp_name.style.border = "" + + document.querySelector(`#qs${value}_input`).value = "None" + modify = true + } + + const input = document.querySelector(`#qs${value}_input`).value + + if (input !== "Press any key combination" && input !== "None") { + file.quick_shortcuts[issuers[id]] = input + + save() + } else if (input === "None") { + delete file.quick_shortcuts[issuers[value]] + + save() + } +} + +const qsDelete = (value) => { + inp_name = document.querySelector(`#qs${value}_input`) + btn_name = document.querySelector(`#qs${value}_button_delete`) + svg_name = document.querySelector(`#qs${value}_svg_delete`) + + inp_name.value = "None" + + svg_name.style.color = "red" + btn_name.style.borderColor = "red" + + setTimeout(() => { + svg_name.style.color = "" + btn_name.style.border = "" + }, 500) + + delete file.quick_shortcuts[issuers[value]] + + save() +} diff --git a/app/splash/index.html b/app/splash/index.html index 28242752..d8b69713 100644 --- a/app/splash/index.html +++ b/app/splash/index.html @@ -7,6 +7,7 @@ + diff --git a/app/splash/src/css/index.css b/app/splash/src/css/index.css index e9c44f44..e2e8847d 100644 --- a/app/splash/src/css/index.css +++ b/app/splash/src/css/index.css @@ -13,6 +13,7 @@ h1 { font-size: 1rem; position: relative; top: 70px; + font-weight: bold !important; } .center { diff --git a/extract/README.md b/extract/README.md deleted file mode 100644 index dca5e166..00000000 --- a/extract/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Usage: - -1. Export the QR codes from "Google Authenticator" app (Top right corner). -2. Read QR codes with QR code reader (I recommend a 3rd party app). -3. Save the captured QR codes in a text file. Save each QR code on a new line (The captured QR codes look like "otpauth-migration://offline?data=..."). -4. Install dependencies (See below). -5. Open aterminal navigate to te file and call this script with the file as input (Make sure you are in the correct folder). -6. Open Authme and select the exported txt file (Just select exported.txt). - -- `python extract_2fa_secret.py example.txt` (Without QR codes) -- `python extract_2fa_secret.py -q example.txt` (With QR codes) - -# Dependencies: - -1. Install with pip: - -- `pip install protobuf` (Required) -- `pip install qrcode` (Required for QR codes) - -# License: - -Edited by: Levminer (https://www.levminer.com) - -Forked from: Scito (https://scito.ch) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . diff --git a/extract/example.txt b/extract/example.txt deleted file mode 100644 index 25311d5a..00000000 --- a/extract/example.txt +++ /dev/null @@ -1,2 +0,0 @@ -otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B -otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B \ No newline at end of file diff --git a/extract/exported.txt b/extract/exported.txt deleted file mode 100644 index 189c75c5..00000000 --- a/extract/exported.txt +++ /dev/null @@ -1,10 +0,0 @@ - -Name: pi@raspberrypi -Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY -Issuer: raspberrypi -Type: OTP_TOTP - -Name: pi@raspberrypi -Secret: 7KSQL2JTUDIS5EF65KLMRQIIGY -Issuer: raspberrypi -Type: OTP_TOTP diff --git a/extract/extract_2fa_secret.py b/extract/extract_2fa_secret.py deleted file mode 100644 index 0f8dc945..00000000 --- a/extract/extract_2fa_secret.py +++ /dev/null @@ -1,84 +0,0 @@ -import argparse -import base64 -import fileinput -import sys -from urllib.parse import parse_qs, urlencode, urlparse - -import google_2fa_export - -arg_parser = argparse.ArgumentParser() -arg_parser.add_argument("--verbose", "-v", help="verbose output", action="store_true") -arg_parser.add_argument( - "--qr", "-q", help="print QR codes (otpauth://...)", action="store_true" -) -arg_parser.add_argument( - "infile", - help='file or - for stdin (default: -) with "otpauth-migration://..." URLs separated by newlines, lines starting with # are ignored', -) -args = arg_parser.parse_args() - -verbose = args.verbose -if args.qr: - from qrcode import QRCode - - -def get_enum_name_by_number(parent, field_name): - field_value = getattr(parent, field_name) - return ( - parent.DESCRIPTOR.fields_by_name[field_name] - .enum_type.values_by_number.get(field_value) - .name - ) - - -def convert_secret_from_bytes_to_base32_str(bytes): - return str(base64.b32encode(otp.secret), "utf-8").replace("=", "") - - -def print_qr(data): - qr = QRCode() - qr.add_data(data) - qr.print_tty() - - -for line in (line.strip() for line in fileinput.input(args.infile)): - if verbose: - print(line) - if line.startswith("#"): - continue - parsed_url = urlparse(line) - params = parse_qs(parsed_url.query) - data_encoded = params["data"][0] - data = base64.b64decode(data_encoded) - payload = google_2fa_export.MigrationPayload() - payload.ParseFromString(data) - if verbose: - print(payload) - - # write to file - file = open("exported.txt", "a") - - for otp in payload.otp_parameters: - print("\nName: {}".format(otp.name)) - file.write("\nName: {} \n".format(otp.name)) - secret = convert_secret_from_bytes_to_base32_str(otp.secret) - print("Secret: {}".format(secret)) - file.write(("Secret: {} \n".format(secret))) - if otp.issuer: - print("Issuer: {}".format(otp.issuer)) - file.write("Issuer: {} \n".format(otp.issuer)) - print("Type: {}".format(get_enum_name_by_number(otp, "type"))) - file.write(("Type: {} \n".format(get_enum_name_by_number(otp, "type")))) - url_params = {"secret": secret} - if otp.type == 1: - url_params["counter"] = otp.counter - if otp.issuer: - url_params["issuer"] = otp.issuer - otp_url = "otpauth://{}/{}?".format( - "totp" if otp.type == 2 else "hotp", otp.name - ) + urlencode(url_params) - - if args.qr: - if verbose: - print(otp_url) - print_qr(otp_url) diff --git a/extract/google_2fa_export.py b/extract/google_2fa_export.py deleted file mode 100644 index 7a1d10e7..00000000 --- a/extract/google_2fa_export.py +++ /dev/null @@ -1,408 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: google_auth.proto - -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="google_auth.proto", - package="", - syntax="proto3", - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x11google_auth.proto"\xb7\x03\n\x10MigrationPayload\x12\x37\n\x0eotp_parameters\x18\x01 \x03(\x0b\x32\x1f.MigrationPayload.OtpParameters\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x12\n\nbatch_size\x18\x03 \x01(\x05\x12\x13\n\x0b\x62\x61tch_index\x18\x04 \x01(\x05\x12\x10\n\x08\x62\x61tch_id\x18\x05 \x01(\x05\x1a\xb7\x01\n\rOtpParameters\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06issuer\x18\x03 \x01(\t\x12.\n\talgorithm\x18\x04 \x01(\x0e\x32\x1b.MigrationPayload.Algorithm\x12\x0e\n\x06\x64igits\x18\x05 \x01(\x05\x12\'\n\x04type\x18\x06 \x01(\x0e\x32\x19.MigrationPayload.OtpType\x12\x0f\n\x07\x63ounter\x18\x07 \x01(\x03",\n\tAlgorithm\x12\x10\n\x0c\x41LGO_INVALID\x10\x00\x12\r\n\tALGO_SHA1\x10\x01"6\n\x07OtpType\x12\x0f\n\x0bOTP_INVALID\x10\x00\x12\x0c\n\x08OTP_HOTP\x10\x01\x12\x0c\n\x08OTP_TOTP\x10\x02\x62\x06proto3', -) - - -_MIGRATIONPAYLOAD_ALGORITHM = _descriptor.EnumDescriptor( - name="Algorithm", - full_name="MigrationPayload.Algorithm", - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name="ALGO_INVALID", - index=0, - number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.EnumValueDescriptor( - name="ALGO_SHA1", - index=1, - number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key, - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=361, - serialized_end=405, -) -_sym_db.RegisterEnumDescriptor(_MIGRATIONPAYLOAD_ALGORITHM) - -_MIGRATIONPAYLOAD_OTPTYPE = _descriptor.EnumDescriptor( - name="OtpType", - full_name="MigrationPayload.OtpType", - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name="OTP_INVALID", - index=0, - number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.EnumValueDescriptor( - name="OTP_HOTP", - index=1, - number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.EnumValueDescriptor( - name="OTP_TOTP", - index=2, - number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key, - ), - ], - containing_type=None, - serialized_options=None, - serialized_start=407, - serialized_end=461, -) -_sym_db.RegisterEnumDescriptor(_MIGRATIONPAYLOAD_OTPTYPE) - - -_MIGRATIONPAYLOAD_OTPPARAMETERS = _descriptor.Descriptor( - name="OtpParameters", - full_name="MigrationPayload.OtpParameters", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="secret", - full_name="MigrationPayload.OtpParameters.secret", - index=0, - number=1, - type=12, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"", - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="name", - full_name="MigrationPayload.OtpParameters.name", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="issuer", - full_name="MigrationPayload.OtpParameters.issuer", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="algorithm", - full_name="MigrationPayload.OtpParameters.algorithm", - index=3, - number=4, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="digits", - full_name="MigrationPayload.OtpParameters.digits", - index=4, - number=5, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="type", - full_name="MigrationPayload.OtpParameters.type", - index=5, - number=6, - type=14, - cpp_type=8, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="counter", - full_name="MigrationPayload.OtpParameters.counter", - index=6, - number=7, - type=3, - cpp_type=2, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=176, - serialized_end=359, -) - -_MIGRATIONPAYLOAD = _descriptor.Descriptor( - name="MigrationPayload", - full_name="MigrationPayload", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="otp_parameters", - full_name="MigrationPayload.otp_parameters", - index=0, - number=1, - type=11, - cpp_type=10, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="version", - full_name="MigrationPayload.version", - index=1, - number=2, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="batch_size", - full_name="MigrationPayload.batch_size", - index=2, - number=3, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="batch_index", - full_name="MigrationPayload.batch_index", - index=3, - number=4, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="batch_id", - full_name="MigrationPayload.batch_id", - index=4, - number=5, - type=5, - cpp_type=1, - label=1, - has_default_value=False, - default_value=0, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[ - _MIGRATIONPAYLOAD_OTPPARAMETERS, - ], - enum_types=[ - _MIGRATIONPAYLOAD_ALGORITHM, - _MIGRATIONPAYLOAD_OTPTYPE, - ], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=22, - serialized_end=461, -) - -_MIGRATIONPAYLOAD_OTPPARAMETERS.fields_by_name[ - "algorithm" -].enum_type = _MIGRATIONPAYLOAD_ALGORITHM -_MIGRATIONPAYLOAD_OTPPARAMETERS.fields_by_name[ - "type" -].enum_type = _MIGRATIONPAYLOAD_OTPTYPE -_MIGRATIONPAYLOAD_OTPPARAMETERS.containing_type = _MIGRATIONPAYLOAD -_MIGRATIONPAYLOAD.fields_by_name[ - "otp_parameters" -].message_type = _MIGRATIONPAYLOAD_OTPPARAMETERS -_MIGRATIONPAYLOAD_ALGORITHM.containing_type = _MIGRATIONPAYLOAD -_MIGRATIONPAYLOAD_OTPTYPE.containing_type = _MIGRATIONPAYLOAD -DESCRIPTOR.message_types_by_name["MigrationPayload"] = _MIGRATIONPAYLOAD -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -MigrationPayload = _reflection.GeneratedProtocolMessageType( - "MigrationPayload", - (_message.Message,), - { - "OtpParameters": _reflection.GeneratedProtocolMessageType( - "OtpParameters", - (_message.Message,), - { - "DESCRIPTOR": _MIGRATIONPAYLOAD_OTPPARAMETERS, - "__module__": "google_auth_pb2" - # @@protoc_insertion_point(class_scope:MigrationPayload.OtpParameters) - }, - ), - "DESCRIPTOR": _MIGRATIONPAYLOAD, - "__module__": "google_auth_pb2" - # @@protoc_insertion_point(class_scope:MigrationPayload) - }, -) -_sym_db.RegisterMessage(MigrationPayload) -_sym_db.RegisterMessage(MigrationPayload.OtpParameters) - - -# @@protoc_insertion_point(module_scope) diff --git a/img/header.png b/img/header.png new file mode 100644 index 00000000..f1a5b7b2 Binary files /dev/null and b/img/header.png differ diff --git a/lib/convert.js b/lib/convert.js new file mode 100644 index 00000000..ce71fbc6 --- /dev/null +++ b/lib/convert.js @@ -0,0 +1,116 @@ +module.exports = { + /** + * Convert codes from plain text to arrays + * @param {String} text + * @param {Number} sortNumber + * @return {LibImportFile} Import file structure + */ + fromText: (text, sortNumber) => { + const data = [] + let names = [] + let secrets = [] + const issuers = [] + const types = [] + + let c0 = 0 + let c1 = 1 + let c2 = 2 + let c3 = 3 + + // remove double quotes + const pre_data1 = text.replace(/"/g, "") + + // new line + const pre_data2 = pre_data1.replace(/,/g, "\n") + + // convert string the array + const pre_data3 = pre_data2.split(/\n/) + while (pre_data3.length) { + data.push(pre_data3.shift()) + } + + // remove first blank line + data.splice(0, 1) + + // remove blank strings + for (let i = 0; i < data.length; i++) { + if (data[i] === "" || data[i] === "\r" || data[i] === "\n" || data[i] === "\r\n") { + data.splice(i, 1) + } + } + + for (let i = 0; i < data.length; i++) { + // push names to array + if (i == c0) { + const names_before = data[i] + const names_after = names_before.slice(8) + names.push(names_after.trim()) + c0 = c0 + 4 + } + + // push secrets to array + if (i == c1) { + const secrets_before = data[i] + const secrets_after = secrets_before.slice(8) + secrets.push(secrets_after.trim()) + c1 = c1 + 4 + } + + // push issuers to array + if (i == c2) { + const issuers_before = data[i] + const issuers_after = issuers_before.slice(8) + issuers.push(issuers_after.trim()) + c2 = c2 + 4 + } + + // push types to array + if (i == c3) { + const types_before = data[i] + const types_after = types_before.slice(8) + types.push(types_after.trim()) + c3 = c3 + 4 + } + } + + const issuers_original = [...issuers] + + const sort = () => { + const names_new = [] + const secrets_new = [] + + issuers.forEach((element) => { + for (let i = 0; i < issuers_original.length; i++) { + if (element === issuers_original[i]) { + names_new.push(names[i]) + secrets_new.push(secrets[i]) + } + } + }) + + names = names_new + secrets = secrets_new + } + + if (sortNumber === 1) { + issuers.sort((a, b) => { + return a.localeCompare(b) + }) + + sort() + } else if (sortNumber === 2) { + issuers.sort((a, b) => { + return b.localeCompare(a) + }) + + sort() + } + + return { + names, + secrets, + issuers, + types, + } + }, +} diff --git a/lib/logger.js b/lib/logger/main/index.js similarity index 55% rename from lib/logger.js rename to lib/logger/main/index.js index 0cc39e56..3f45dd13 100644 --- a/lib/logger.js +++ b/lib/logger/main/index.js @@ -7,6 +7,7 @@ const colors = { yellow: "\x1b[33m", blue: "\x1b[34m", green: "\x1b[32m", + purple: "\x1b[35m", } const time = () => { @@ -17,6 +18,11 @@ let file_name = null let file_path = null module.exports = logger = { + /** + * Node.js color codes + */ + colors, + /** * Writes a log to the console * @param {String} message @@ -45,6 +51,35 @@ module.exports = logger = { } }, + /** + * Writes a log to the console + * @param {String} id + * @param {String} message + * @param {String} log + * @return {String} log + */ + rendererLog: (id, message, log) => { + if (typeof log == "object") { + log = JSON.stringify(log) + } else if (typeof log === "undefined") { + log = undefined + } + + if (typeof log === "undefined") { + console.log(`${colors.green}[AUTHME LOG] ${colors.blue}{${time()}} ${colors.white}${colors.purple}<${id}>${colors.white} ${message} ${colors.white}`) + + try { + logger.writeFile(`[AUTHME LOG] {${time()}} <${id}> ${message} \n`) + } catch (error) {} + } else { + console.log(`${colors.green}[AUTHME LOG] ${colors.blue}{${time()}}${colors.purple}<${id}>${colors.white} ${colors.white}${message} ${colors.green}(${log}) ${colors.white}`) + + try { + logger.writeFile(`[AUTHME LOG] {${time()}} <${id}> ${message} (${log}) \n`) + } catch (error) {} + } + }, + /** * Writes a warn to the console * @param {String} message @@ -73,6 +108,35 @@ module.exports = logger = { } }, + /** + * Writes a warn to the console + * * @param {String} id + * @param {String} message + * @param {String} warn + * @return {String} warn + */ + rendererWarn: (id, message, warn) => { + if (typeof warn == "object") { + warn = JSON.stringify(warn) + } else if (typeof warn == "undefined") { + warn = undefined + } + + if (typeof warn === "undefined") { + console.log(`${colors.yellow}[AUTHME WARN] ${colors.blue}{${time()}} ${colors.purple}<${id}>${colors.white} ${colors.white}${message} ${colors.white}`) + + try { + logger.writeFile(`[AUTHME WARN] {${time()}} <${id}> ${message} \n`) + } catch (error) {} + } else { + console.log(`${colors.yellow}[AUTHME WARN] ${colors.blue}{${time()}} ${colors.purple}<${id}>${colors.white} ${colors.white}${message} ${colors.yellow}(${warn}) ${colors.white}`) + + try { + logger.writeFile(`[AUTHME WARN] {${time()}} <${id}> ${message} (${warn}) \n`) + } catch (error) {} + } + }, + /** * Writes an error to the console * @param {String} message @@ -101,6 +165,35 @@ module.exports = logger = { } }, + /** + * Writes an error to the console + * @param {String} id + * @param {String} message + * @param {String} error + * @return {String} error + */ + rendererError: (id, message, error) => { + if (typeof error == "object") { + error = JSON.stringify(error) + } else if (typeof error == "undefined") { + error = undefined + } + + if (typeof error === "undefined") { + console.log(`${colors.red}[AUTHME ERROR] ${colors.blue}{${time()}} ${colors.purple}<${id}>${colors.white} ${colors.white}${message} ${colors.white}`) + + try { + logger.writeFile(`[AUTHME ERROR] {${time()}} <${id}> ${message} \n`) + } catch (error) {} + } else { + console.log(`${colors.red}[AUTHME ERROR] ${colors.blue}{${time()}} ${colors.purple}<${id}>${colors.white} ${colors.white}${message} ${colors.red}(${error}) ${colors.white}`) + + try { + logger.writeFile(`[AUTHME ERROR] {${time()}} <${id}> ${message} (${error}) \n`) + } catch (error) {} + } + }, + /** * Creates a log file * @param {String} file @@ -139,6 +232,10 @@ module.exports = logger = { }) }, + /** + * Returns current file name + * @return {String} file_name + */ fileName: () => { return file_name }, diff --git a/lib/logger/renderer/index.js b/lib/logger/renderer/index.js new file mode 100644 index 00000000..f7219179 --- /dev/null +++ b/lib/logger/renderer/index.js @@ -0,0 +1,50 @@ +const electron = require("electron") + +let window = "" + +module.exports = { + /** + * Writes a log to the console + * @param {String} message + * @param {String} log + * @return {String} log + */ + log: (message, log) => { + console.log(message) + + electron.ipcRenderer.send("loggerLog", { id: window, message: message, log: log }) + }, + + /** + * Writes a warn to the console + * @param {String} message + * @param {String} warn + * @return {String} warn + */ + warn: (message, warn) => { + console.warn(message) + + electron.ipcRenderer.send("loggerWarn", { id: window, message: message, warn: warn }) + }, + + /** + * Writes a error to the console + * @param {String} message + * @param {String} error + * @return {String} error + */ + error: (message, error) => { + console.error(message) + + electron.ipcRenderer.send("loggerError", { id: window, message: message, error: error }) + }, + + /** + * Get window name + * @param {String} name + * @return {String} window name + */ + getWindow: (name) => { + window = name + }, +} diff --git a/lib/main.js b/lib/main.js index ad4a9e23..442b00f9 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,18 +1,18 @@ const aes = require("./aes") const rsa = require("./rsa") const sha = require("./sha") -const logger = require("./logger") const markdown = require("./markdown") const qrcodeConverter = require("./qrcodeConverter") const typedef = require("./typedef") +const convert = require("./convert") // ? export modules module.exports = { aes, rsa, sha, - logger, markdown, qrcodeConverter, typedef, + convert, } diff --git a/lib/typedef.js b/lib/typedef.js index 9e83b9cf..b490ae18 100644 --- a/lib/typedef.js +++ b/lib/typedef.js @@ -19,6 +19,7 @@ * @property {Object} experimental - Experimental * @property {Null|Number} experimental.offset - Codes time offset * @property {Null|Number} experimental.sort - Sort codes + * @property {Null|Number} experimental.webcam - Webcam * * @property {Object} security - Security * @property {Null|Boolean} security.require_password - Requires password @@ -48,6 +49,8 @@ * @property {String} global_shortcuts.settings - Show settings * @property {String} global_shortcuts.exit - Exit app * + * @property {Object} quick_shortcuts - Quick shortcuts + * * @property {Object} search_history - Search history * @property {Null|String} search_history.latest - Exit app * @@ -67,4 +70,14 @@ * @property {Undefined|String} backup_key - Backup key * @property {Undefined|String} backup_string - Backup string * @property {Undefined|String} hash - Backup key hash + * @property {Array} issuers - List of issuers + */ + +/** + * Authme import file structure + * @typedef {Object} LibImportFile + * @property {Array} names - Names array + * @property {Array} secrets - Secrets array + * @property {Array} issuers - Issuers array + * @property {Array} types - Types array */ diff --git a/main.js b/main.js index 78b73b2d..24193a7f 100644 --- a/main.js +++ b/main.js @@ -1,9 +1,11 @@ const { app, BrowserWindow, Menu, Tray, shell, dialog, clipboard, globalShortcut, nativeTheme, Notification } = require("electron") -const { logger, markdown } = require("@levminer/lib") +const logger = require("@levminer/lib/logger/main") const contextmenu = require("electron-context-menu") const { version, tag } = require("./package.json") const { number, date } = require("./build.json") const remote = require("@electron/remote/main") +const { markdown } = require("@levminer/lib") +const sentry = require("@sentry/electron") const AutoLaunch = require("auto-launch") const debug = require("electron-debug") const electron = require("electron") @@ -13,19 +15,29 @@ const fs = require("fs") const os = require("os") const ipc = electron.ipcMain -// ? init +// ? crash report +process.on("uncaughtException", (error) => { + logger.error("Error on load", error.stack) + dialog.showErrorBox("Authme", `Authme crashed, crash report is sent.\n\nPlease open a GitHub Issue with a screenshot of this error.\n\n${error.stack}`) -// windows -let window_splash -let window_landing -let window_confirm -let window_application -let window_settings -let window_import -let window_export -let window_edit + shell.openExternal("https://github.com/Levminer/authme/issues") + + process.crash() +}) + +sentry.init({ dsn: "https://173234c94f8f4294a28e114c9113c1ce@o1020924.ingest.sentry.io/5986541" }) + +// ? windows +let /** @type{BrowserWindow} */ window_splash +let /** @type{BrowserWindow} */ window_landing +let /** @type{BrowserWindow} */ window_confirm +let /** @type{BrowserWindow} */ window_application +let /** @type{BrowserWindow} */ window_settings +let /** @type{BrowserWindow} */ window_import +let /** @type{BrowserWindow} */ window_export +let /** @type{BrowserWindow} */ window_edit -// window states +// ? window states let confirm_shown = false let application_shown = false let settings_shown = false @@ -33,12 +45,14 @@ let import_shown = false let export_shown = false let edit_shown = false -// other states +// ? other states let authenticated = false let offline = false let shortcuts = false +let reload = false let tray_minimized = false let update_seen = false +let animations_showed = false // ? development let dev = false @@ -47,10 +61,12 @@ if (app.isPackaged === false) { debug({ showDevTools: false, }) + dev = true } else { if (process.platform === "darwin") { debug({ + isEnabled: true, showDevTools: false, }) } @@ -148,7 +164,7 @@ const settings = `{ }, "settings": { "launch_on_startup": false, - "close_to_tray": false, + "close_to_tray": true, "show_2fa_names": false, "click_to_reveal": false, "reset_after_copy": false, @@ -157,8 +173,8 @@ const settings = `{ "disable_hardware_acceleration": true }, "experimental":{ - "offset": null, - "sort": null + "sort": null, + "webcam": "null" }, "security": { "require_password": null, @@ -188,6 +204,7 @@ const settings = `{ "settings": "CommandOrControl+Shift+s", "exit": "CommandOrControl+Shift+d" }, + "quick_shortcuts:": {}, "search_history": { "latest": null }, @@ -212,13 +229,24 @@ let file = JSON.parse(fs.readFileSync(path.join(file_path, "settings.json"), "ut // settings compatibility if (file.experimental === undefined) { file.experimental = { - offset: null, sort: null, } saveSettings() } +if (file.quick_shortcuts === undefined) { + file.quick_shortcuts = {} + + saveSettings() +} + +if (file.experimental.webcam === undefined) { + file.experimental.webcam = null + + saveSettings() +} + if (file.shortcuts.edit === undefined) { file.shortcuts.edit = "CommandOrControl+t" @@ -353,9 +381,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -369,9 +395,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -385,9 +409,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -401,9 +423,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -417,9 +437,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -433,9 +451,7 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) @@ -449,12 +465,19 @@ const createWindow = () => { webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: true, - enableRemoteModule: true, contextIsolation: false, - nativeWindowOpen: true, }, }) + // remote module + remote.enable(window_landing.webContents) + remote.enable(window_confirm.webContents) + remote.enable(window_application.webContents) + remote.enable(window_settings.webContents) + remote.enable(window_import.webContents) + remote.enable(window_export.webContents) + remote.enable(window_edit.webContents) + // load window files window_landing.loadFile("./app/landing/index.html") window_confirm.loadFile("./app/confirm/index.html") @@ -621,6 +644,13 @@ const createWindow = () => { } } + if (reload === false && file.settings.launch_on_startup === true) { + window_application.hide() + window_confirm.hide() + + reload = true + } + if (update_seen == false) { api() @@ -628,6 +658,16 @@ const createWindow = () => { } }) + window_application.on("focus", () => { + if (animations_showed === false) { + window_application.webContents.executeJavaScript("animations()") + + animations_showed = true + } + + window_application.webContents.executeJavaScript("focusSearch()") + }) + // ? global shortcuts if (file.global_shortcuts.show !== "None") { globalShortcut.register(file.global_shortcuts.show, () => { @@ -882,11 +922,6 @@ ipc.on("enable_tray", () => { logger.log("Close to tray enabled") }) -ipc.on("startup", () => { - window_application.hide() - window_confirm.hide() -}) - ipc.on("app_path", () => { shell.showItemInFolder(app.getPath("exe")) }) @@ -955,7 +990,40 @@ ipc.on("release_notes", () => { }) ipc.on("download_update", () => { - shell.openExternal("https://authme.levminer.com/#downloads") + const donwloadUpdate = async () => { + try { + await fetch("https://api.levminer.com/api/v1/authme/releases") + .then((res) => res.json()) + .then((data) => { + try { + const notes = markdown.convert(data.body).split("\n").slice(6).join("\n") + const message = `Update available: Authme ${data.tag_name}\n\nYou currently running: Authme ${tag_name}\n\nUpdates:\n\n${notes}` + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Download", "Close"], + defaultId: 0, + cancelId: 1, + noLink: true, + type: "info", + message: message, + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://authme.levminer.com/#downloads") + } + }) + } catch (error) { + return logger.error(error) + } + }) + } catch (error) { + return logger.error(error) + } + } + + donwloadUpdate() }) ipc.on("support", () => { @@ -999,11 +1067,30 @@ ipc.on("reload_application", () => { } }) +// ? reload settings window +ipc.on("reload_settings", () => { + window_settings.reload() +}) + // ? error in window ipc.on("rendererError", (event, data) => { logger.error(`Error in ${data.renderer}`, data.error) }) +// ? logger + +ipc.on("loggerLog", (event, data) => { + logger.rendererLog(data.id, data.message, data.log) +}) + +ipc.on("loggerWarn", (event, data) => { + logger.rendererWarn(data.id, data.message, data.warn) +}) + +ipc.on("loggerError", (event, data) => { + logger.rendererError(data.id, data.message, data.error) +}) + // ? logs const logs = () => { const log_path = logger.fileName() @@ -1092,508 +1179,545 @@ const support = () => { } // ? start app -app.whenReady().then(() => { - logger.log("Starting app") +app.whenReady() + .then(() => { + logger.log("Starting app") - if (dev === true) { - app.setAppUserModelId("Authme Dev") - } else { - app.setAppUserModelId("Authme") - } + if (dev === true) { + app.setAppUserModelId("Authme Dev") + } else { + app.setAppUserModelId("Authme") + } - process.on("uncaughtException", (error) => { - logger.error("Unknown error occurred", error.stack) + process.on("uncaughtException", (error) => { + logger.error("Unknown error occurred", error.stack) + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Report", "Close"], + defaultId: 0, + cancelId: 1, + noLink: true, + type: "error", + message: `Unknown error occurred! \n\n${error.stack}`, + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://github.com/Levminer/authme/issues/") + } else if (result.response === 1) { + app.exit() + } + }) + }) - dialog - .showMessageBox({ - title: "Authme", - buttons: ["Report", "Close"], - defaultId: 0, - cancelId: 1, - noLink: true, - type: "error", - message: `Unknown error occurred! \n\n${error.stack}`, - }) - .then((result) => { - if (result.response === 0) { - shell.openExternal("https://github.com/Levminer/authme/issues/") - } else if (result.response === 1) { - app.exit() - } - }) - }) + // ? quick shortcuts + const quickShortcuts = () => { + const keys = Object.keys(file.quick_shortcuts) + const values = Object.values(file.quick_shortcuts) - window_splash = new BrowserWindow({ - width: 500, - height: 550, - transparent: true, - frame: false, - alwaysOnTop: true, - resizable: false, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - nativeWindowOpen: true, - }, - }) + for (let i = 0; i < keys.length; i++) { + globalShortcut.register(values[i], () => { + window_application.webContents.executeJavaScript(`quickCopy("${keys[i]}")`) + }) + } + } - window_splash.loadFile("./app/splash/index.html") + window_splash = new BrowserWindow({ + width: 500, + height: 550, + transparent: true, + frame: false, + alwaysOnTop: true, + resizable: false, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + }, + }) - window_splash.setProgressBar(10) + window_splash.loadFile("./app/splash/index.html") - window_splash.show() + window_splash.setProgressBar(10) - window_splash.once("ready-to-show", () => { - if (dev === true) { - setTimeout(() => { - createWindow() - }, 500) + window_splash.show() - setTimeout(() => { - window_splash.destroy() - }, 1000) - } else { - setTimeout(() => { - createWindow() - }, 2000) + window_splash.once("ready-to-show", () => { + if (dev === true) { + setTimeout(() => { + createWindow() + quickShortcuts() + }, 500) - setTimeout(() => { - window_splash.destroy() - }, 2500) - } - }) + setTimeout(() => { + window_splash.destroy() + }, 1000) + } else { + setTimeout(() => { + createWindow() + quickShortcuts() + }, 2000) - // ? create tray - const icon_path = path.join(__dirname, "img/tray.png") - const tray = new Tray(icon_path) + setTimeout(() => { + window_splash.destroy() + }, 2500) + } + }) - tray.on("click", () => { - showAppFromTray() - }) + // ? create tray + const icon_path = path.join(__dirname, "img/tray.png") + const tray = new Tray(icon_path) - // generate tray - const createTray = () => { - const contextmenu = Menu.buildFromTemplate([ - { - label: `Authme ${authme_version}`, - enabled: false, - icon: path.join(__dirname, "img/traymenu.png"), - }, - { - label: pre_release ? `(${build_number})` : `(${release_date})`, - enabled: false, - }, - { type: "separator" }, - { - label: "Show app", - accelerator: shortcuts ? "" : file.global_shortcuts.show, - click: () => { - showAppFromTray() + tray.on("click", () => { + showAppFromTray() + }) + + // generate tray + const createTray = () => { + const contextmenu = Menu.buildFromTemplate([ + { + label: `Authme ${authme_version}`, + enabled: false, + icon: path.join(__dirname, "img/traymenu.png"), }, - }, - { type: "separator" }, - { - label: "Settings", - accelerator: shortcuts ? "" : file.global_shortcuts.settings, - click: () => { - settingsFromTray() + { + label: pre_release ? `(${build_number})` : `(${release_date})`, + enabled: false, }, - }, - { type: "separator" }, - { - label: "Exit app", - accelerator: shortcuts ? "" : file.global_shortcuts.exit, - click: () => { - exitFromTray() + { type: "separator" }, + { + label: "Show app", + accelerator: shortcuts ? "" : file.global_shortcuts.show, + click: () => { + showAppFromTray() + }, }, - }, - ]) - tray.setToolTip("Authme") - tray.setContextMenu(contextmenu) - } - - createTray() - - // ? create menu - const createMenu = () => { - const template = [ - { - label: "File", - submenu: [ - { - label: "Show app", - accelerator: shortcuts ? "" : file.shortcuts.show, - click: () => { - showAppFromTray() - }, + { type: "separator" }, + { + label: "Settings", + accelerator: shortcuts ? "" : file.global_shortcuts.settings, + click: () => { + settingsFromTray() }, - { - type: "separator", + }, + { type: "separator" }, + { + label: "Exit app", + accelerator: shortcuts ? "" : file.global_shortcuts.exit, + click: () => { + exitFromTray() }, - { - label: "Settings", - accelerator: shortcuts ? "" : file.shortcuts.settings, - click: () => { - const toggle = () => { - if (settings_shown === false) { - window_settings.maximize() - window_settings.show() + }, + ]) + tray.setToolTip("Authme") + tray.setContextMenu(contextmenu) + } + + createTray() + + // ? create menu + const createMenu = () => { + const template = [ + { + label: "File", + submenu: [ + { + label: "Show app", + accelerator: shortcuts ? "" : file.shortcuts.show, + click: () => { + showAppFromTray() + }, + }, + { + type: "separator", + }, + { + label: "Settings", + accelerator: shortcuts ? "" : file.shortcuts.settings, + click: () => { + const toggle = () => { + if (settings_shown === false) { + window_settings.maximize() + window_settings.show() - settings_shown = true + settings_shown = true - logger.log("Settings shown") - } else { - window_settings.hide() + logger.log("Settings shown") + } else { + window_settings.hide() - settings_shown = false + settings_shown = false - logger.log("Settings hidden") + logger.log("Settings hidden") + } } - } - if (file.security.require_password === true && authenticated === true) { - toggle() - } else if (file.security.require_password === false) { - toggle() - } + if (file.security.require_password === true && authenticated === true) { + toggle() + } else if (file.security.require_password === false) { + toggle() + } + }, }, - }, - { - type: "separator", - }, - { - label: "Exit", - accelerator: shortcuts ? "" : file.shortcuts.exit, - click: () => { - tray_minimized = false - app.exit() - - logger.log("App exited from menu") + { + type: "separator", }, - }, - ], - }, - { - label: "View", - submenu: [ - { - label: "Reset", - role: "resetZoom", - accelerator: shortcuts ? "" : file.shortcuts.zoom_reset, - }, - { - type: "separator", - }, - { - label: "Zoom in", - role: "zoomIn", - accelerator: shortcuts ? "" : file.shortcuts.zoom_in, - }, - { - type: "separator", - }, - { - label: "Zoom out", - role: "zoomOut", - accelerator: shortcuts ? "" : file.shortcuts.zoom_out, - }, - ], - }, - { - label: "Advanced", - submenu: [ - { - label: "Edit codes", - accelerator: shortcuts ? "" : file.shortcuts.edit, - click: () => { - const toggle = () => { - if (edit_shown === false) { - window_edit.maximize() - window_edit.show() - - edit_shown = true - - logger.log("Edit shown") - } else { - window_edit.hide() - - edit_shown = false - - logger.log("Edit hidden") + { + label: "Exit", + accelerator: shortcuts ? "" : file.shortcuts.exit, + click: () => { + tray_minimized = false + app.exit() + + logger.log("App exited from menu") + }, + }, + ], + }, + { + label: "View", + submenu: [ + { + label: "Reset", + role: "resetZoom", + accelerator: shortcuts ? "" : file.shortcuts.zoom_reset, + }, + { + type: "separator", + }, + { + label: "Zoom in", + role: "zoomIn", + accelerator: shortcuts ? "" : file.shortcuts.zoom_in, + }, + { + type: "separator", + }, + { + label: "Zoom out", + role: "zoomOut", + accelerator: shortcuts ? "" : file.shortcuts.zoom_out, + }, + ], + }, + { + label: "Advanced", + submenu: [ + { + label: "Edit codes", + accelerator: shortcuts ? "" : file.shortcuts.edit, + click: () => { + const toggle = () => { + if (edit_shown === false) { + window_edit.maximize() + window_edit.show() + + edit_shown = true + + logger.log("Edit shown") + } else { + window_edit.hide() + + edit_shown = false + + logger.log("Edit hidden") + } } - } - if (file.security.require_password === true && authenticated === true) { - toggle() - } else if (file.security.require_password === false) { - toggle() - } + if (file.security.require_password === true && authenticated === true) { + toggle() + } else if (file.security.require_password === false) { + toggle() + } + }, }, - }, - { - type: "separator", - }, - { - label: "Import", - accelerator: shortcuts ? "" : file.shortcuts.import, - click: () => { - const toggle = () => { - if (import_shown === false) { - window_import.maximize() - window_import.show() + { + type: "separator", + }, + { + label: "Import", + accelerator: shortcuts ? "" : file.shortcuts.import, + click: () => { + const toggle = () => { + if (import_shown === false) { + window_import.maximize() + window_import.show() - import_shown = true + import_shown = true - logger.log("Import shown") - } else { - window_import.hide() + logger.log("Import shown") + } else { + window_import.hide() - import_shown = false + import_shown = false - logger.log("Import hidden") + logger.log("Import hidden") + } } - } - if (file.security.require_password === true && authenticated === true) { - toggle() - } else if (file.security.require_password === false) { - toggle() - } + if (file.security.require_password === true && authenticated === true) { + toggle() + } else if (file.security.require_password === false) { + toggle() + } + }, }, - }, - { - type: "separator", - }, - { - label: "Export", - accelerator: shortcuts ? "" : file.shortcuts.export, - click: () => { - const toggle = () => { - if (export_shown === false) { - window_export.maximize() - window_export.show() + { + type: "separator", + }, + { + label: "Export", + accelerator: shortcuts ? "" : file.shortcuts.export, + click: () => { + const toggle = () => { + if (export_shown === false) { + window_export.maximize() + window_export.show() - export_shown = true + export_shown = true - logger.log("Export shown") - } else { - window_export.hide() + logger.log("Export shown") + } else { + window_export.hide() - export_shown = false + export_shown = false - logger.log("Export hidden") + logger.log("Export hidden") + } } - } - if (file.security.require_password === true && authenticated === true) { - toggle() - } else if (file.security.require_password === false) { - toggle() - } + if (file.security.require_password === true && authenticated === true) { + toggle() + } else if (file.security.require_password === false) { + toggle() + } + }, }, - }, - ], - }, - { - label: "Help", - submenu: [ - { - label: "Documentation", - accelerator: shortcuts ? "" : file.shortcuts.docs, - click: () => { - dialog - .showMessageBox({ - title: "Authme", - buttons: ["Open", "Close"], - defaultId: 1, - cancelId: 1, - noLink: true, - type: "info", - message: "You can view the Authme Docs in the browser. \n\nClick open to view it in your browser!", - }) - .then((result) => { - if (result.response === 0) { - shell.openExternal("https://docs.authme.levminer.com") - } - }) + ], + }, + { + label: "Help", + submenu: [ + { + label: "Documentation", + accelerator: shortcuts ? "" : file.shortcuts.docs, + click: () => { + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Open", "Close"], + defaultId: 1, + cancelId: 1, + noLink: true, + type: "info", + message: "You can view the Authme Docs in the browser. \n\nClick open to view it in your browser!", + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://docs.authme.levminer.com") + } + }) + }, }, - }, - { - type: "separator", - }, - { - label: "Release notes", - accelerator: shortcuts ? "" : file.shortcuts.release, - click: () => { - releaseNotes() + { + type: "separator", }, - }, - { - type: "separator", - }, - { - label: "Support development", - accelerator: shortcuts ? "" : file.shortcuts.support, - click: () => { - support() + { + label: "Release notes", + accelerator: shortcuts ? "" : file.shortcuts.release, + click: () => { + releaseNotes() + }, }, - }, - ], - }, - { - label: "About", - submenu: [ - { - label: "Show licenses", - accelerator: shortcuts ? "" : file.shortcuts.licenses, - click: () => { - dialog - .showMessageBox({ - title: "Authme", - buttons: ["More", "Close"], - defaultId: 1, - cancelId: 1, - noLink: true, - type: "info", - message: "This software is licensed under GPL-3.0 \n\nCopyright © 2020 Lőrik Levente", - }) - .then((result) => { - if (result.response === 0) { - shell.openExternal("https://authme.levminer.com/licenses.html") - } - }) + { + type: "separator", }, - }, - { - type: "separator", - }, - { - label: "Update", - accelerator: shortcuts ? "" : file.shortcuts.update, - click: () => { - const api = async () => { - try { - await fetch("https://api.levminer.com/api/v1/authme/releases") - .then((res) => res.json()) - .then((data) => { - try { - if (data.tag_name > tag_name && data.tag_name != undefined && data.prerelease != true) { - dialog - .showMessageBox({ - title: "Authme", - buttons: ["Yes", "No"], - defaultId: 0, - cancelId: 1, - type: "info", - message: `Update available: Authme ${data.tag_name} + { + label: "Support development", + accelerator: shortcuts ? "" : file.shortcuts.support, + click: () => { + support() + }, + }, + ], + }, + { + label: "About", + submenu: [ + { + label: "Show licenses", + accelerator: shortcuts ? "" : file.shortcuts.licenses, + click: () => { + dialog + .showMessageBox({ + title: "Authme", + buttons: ["More", "Close"], + defaultId: 1, + cancelId: 1, + noLink: true, + type: "info", + message: "This software is licensed under GPL-3.0 \n\nCopyright © 2020 Lőrik Levente", + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://authme.levminer.com/licenses.html") + } + }) + }, + }, + { + type: "separator", + }, + { + label: "Update", + accelerator: shortcuts ? "" : file.shortcuts.update, + click: () => { + const api = async () => { + try { + await fetch("https://api.levminer.com/api/v1/authme/releases") + .then((res) => res.json()) + .then((data) => { + try { + if (data.tag_name > tag_name && data.tag_name != undefined && data.prerelease != true) { + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Yes", "No"], + defaultId: 0, + cancelId: 1, + type: "info", + message: `Update available: Authme ${data.tag_name} Do you want to download it? You currently running: Authme ${tag_name}`, - }) - .then((result) => { - if (result.response === 0) { - shell.openExternal("https://authme.levminer.com#downloads") - } - }) - } else { - dialog.showMessageBox({ - title: "Authme", - buttons: ["Close"], - defaultId: 0, - cancelId: 1, - type: "info", - message: `No update available: + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://authme.levminer.com#downloads") + } + }) + } else { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + defaultId: 0, + cancelId: 1, + type: "info", + message: `No update available: You are running the latest version! You are currently running: Authme ${tag_name}`, - }) + }) + } + } catch (error) { + return logger.error("Error during manual update", error.stack) } - } catch (error) { - return logger.error("Error during manual update", error.stack) - } - }) - } catch (error) { - dialog.showMessageBox({ - title: "Authme", - buttons: ["Close"], - defaultId: 0, - cancelId: 1, - type: "info", - message: `No update available: + }) + } catch (error) { + dialog.showMessageBox({ + title: "Authme", + buttons: ["Close"], + defaultId: 0, + cancelId: 1, + type: "info", + message: `No update available: Can't connect to API, check your internet connection or the API status in the settings! You currently running: Authme ${tag_name}`, - }) + }) - return logger.error("Error during manual update", error.stack) + return logger.error("Error during manual update", error.stack) + } } - } - api() + api() + }, }, - }, - { - type: "separator", - }, - { - label: "Info", - accelerator: shortcuts ? "" : file.shortcuts.info, - click: () => { - about() + { + type: "separator", }, - }, - ], - }, - ] + { + label: "Info", + accelerator: shortcuts ? "" : file.shortcuts.info, + click: () => { + about() + }, + }, + ], + }, + ] - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) - } + const menu = Menu.buildFromTemplate(template) + Menu.setApplicationMenu(menu) + } - createMenu() + createMenu() - ipc.on("shortcuts", () => { - if (shortcuts === false) { - shortcuts = true + ipc.on("shortcuts", () => { + if (shortcuts === false) { + shortcuts = true - globalShortcut.unregisterAll() + globalShortcut.unregisterAll() - createTray() + createTray() - createMenu() + createMenu() - logger.log("Shortcuts disabled") - } else { - shortcuts = false + logger.log("Shortcuts disabled") + } else { + shortcuts = false - file = JSON.parse(fs.readFileSync(path.join(file_path, "settings.json"), "utf-8")) + file = JSON.parse(fs.readFileSync(path.join(file_path, "settings.json"), "utf-8")) - if (file.global_shortcuts.show !== "None") { - globalShortcut.register(file.global_shortcuts.show, () => { - showAppFromTray() - }) - } + if (file.global_shortcuts.show !== "None") { + globalShortcut.register(file.global_shortcuts.show, () => { + showAppFromTray() + }) + } - if (file.global_shortcuts.settings !== "None") { - globalShortcut.register(file.global_shortcuts.settings, () => { - settingsFromTray() - }) - } + if (file.global_shortcuts.settings !== "None") { + globalShortcut.register(file.global_shortcuts.settings, () => { + settingsFromTray() + }) + } - if (file.global_shortcuts.exit !== "None") { - globalShortcut.register(file.global_shortcuts.exit, () => { - exitFromTray() - }) - } + if (file.global_shortcuts.exit !== "None") { + globalShortcut.register(file.global_shortcuts.exit, () => { + exitFromTray() + }) + } - createTray() + quickShortcuts() - createMenu() + createTray() - logger.log("Shortcuts enabled") - } + createMenu() + + logger.log("Shortcuts enabled") + } + }) + }) + .catch((error) => { + logger.error("Unknown error occurred", error.stack) + + dialog + .showMessageBox({ + title: "Authme", + buttons: ["Report", "Close", "Exit"], + defaultId: 0, + cancelId: 1, + noLink: true, + type: "error", + message: `Unknown error occurred! \n\n${error.stack}`, + }) + .then((result) => { + if (result.response === 0) { + shell.openExternal("https://github.com/Levminer/authme/issues/") + } else if (result.response === 2) { + app.exit() + } + }) }) -}) diff --git a/package-lock.json b/package-lock.json index 53c8f503..e9e2f925 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,31 +1,32 @@ { "name": "authme", - "version": "2.7.0", + "version": "2.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "authme", - "version": "2.7.0", + "version": "2.7.2", "license": "GPL-3.0", "dependencies": { - "@electron/remote": "^1.2.1", + "@electron/remote": "^2.0.1", "@levminer/lib": "file:lib", "@levminer/speakeasy": "^1.3.1", + "@sentry/electron": "^2.5.4", "auto-launch": "^5.0.5", "bcryptjs": "^2.4.3", "cryptr": "^6.0.2", "electron-context-menu": "^3.1.1", "electron-debug": "^3.2.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.5", "protons": "^2.0.3", "qrcode": "^1.4.4", "qrcode-decoder": "^0.2.2" }, "devDependencies": { - "autoprefixer": "^10.3.4", - "electron": "^14.0.1", - "electron-builder": "^22.11.7", + "autoprefixer": "^10.3.7", + "electron": "^15.1.2", + "electron-builder": "^22.13.1", "eslint": "^7.32.0", "eslint-config-node": "^4.1.0", "eslint-config-prettier": "^8.3.0", @@ -35,9 +36,9 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", - "prettier": "^2.4.0", - "sass": "^1.40.1", - "tailwindcss": "^2.2.15" + "prettier": "^2.4.1", + "sass": "^1.42.1", + "tailwindcss": "^2.2.16" }, "engines": { "node": ">=15.0.0", @@ -336,9 +337,9 @@ } }, "node_modules/@electron/get": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz", - "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", + "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -357,9 +358,9 @@ } }, "node_modules/@electron/remote": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-1.2.1.tgz", - "integrity": "sha512-yKh60I8KjezQkZqeuN5Nu2O/Z72+tgNgzvAa8QQPLtQbsrCOaeIWdXZQqierz4jQ5jzTNUk6KIcK3V2kFeaxaQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.1.tgz", + "integrity": "sha512-bGX4/yB2bPZwXm1DsxgoABgH0Cz7oFtXJgkerB8VrStYdTyvhGAULzNLRn9rVmeAuC3VUDXaXpZIlZAZHpsLIA==", "peerDependencies": { "electron": ">= 10.0.0-beta.1" } @@ -604,6 +605,134 @@ "node": ">= 8" } }, + "node_modules/@sentry/browser": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.1.tgz", + "integrity": "sha512-R5PYx4TTvifcU790XkK6JVGwavKaXwycDU0MaAwfc4Vf7BLm5KCNJCsDySu1RPAap/017MVYf54p6dWvKiRviA==", + "dependencies": { + "@sentry/core": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.1.tgz", + "integrity": "sha512-VAv8OR/7INn2JfiLcuop4hfDcyC7mfL9fdPndQEhlacjmw8gRrgXjR7qyhnCTgzFLkHI7V5bcdIzA83TRPYQpA==", + "dependencies": { + "@sentry/hub": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/electron": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-2.5.4.tgz", + "integrity": "sha512-tCCK+P581TmdjsDpHBQz7qYcldzGdUk1Fd6FPxPy1JKGzeY4uf/uSLKzR80Lzs5kTpEZFOwiMHSA8yjwFp5qoA==", + "dependencies": { + "@sentry/browser": "6.7.1", + "@sentry/core": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/node": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^2.2.0" + } + }, + "node_modules/@sentry/electron/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "node_modules/@sentry/hub": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.1.tgz", + "integrity": "sha512-eVCTWvvcp6xa0A5GGNHMQEWslmKPlisE5rGmsV/kjvSUv3zSrI0eIDfb51ikdnCiBjHpK2NBWP8Vy8cZOEJegg==", + "dependencies": { + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.1.tgz", + "integrity": "sha512-HDDPEnQRD6hC0qaHdqqKDStcdE1KhkFh0RCtJNMCDn0zpav8Dj9AteF70x6kLSlliAJ/JFwi6AmQrLz+FxPexw==", + "dependencies": { + "@sentry/hub": "6.7.1", + "@sentry/types": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.7.1.tgz", + "integrity": "sha512-rtZo1O8ROv4lZwuljQz3iFZW89oXSlgXCG2VqkxQyRspPWu89abROpxLjYzsWwQ8djnur1XjFv51kOLDUTS6Qw==", + "dependencies": { + "@sentry/core": "6.7.1", + "@sentry/hub": "6.7.1", + "@sentry/tracing": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.1.tgz", + "integrity": "sha512-wyS3nWNl5mzaC1qZ2AIp1hjXnfO9EERjMIJjCihs2LWBz1r3efxrHxJHs8wXlNWvrT3KLhq/7vvF5CdU82uPeQ==", + "dependencies": { + "@sentry/hub": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.1.tgz", + "integrity": "sha512-9AO7HKoip2MBMNQJEd6+AKtjj2+q9Ze4ooWUdEvdOVSt5drg7BGpK221/p9JEOyJAZwEPEXdcMd3VAIMiOb4MA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.1.tgz", + "integrity": "sha512-Tq2otdbWlHAkctD+EWTYKkEx6BL1Qn3Z/imkO06/PvzpWvVhJWQ5qHAzz5XnwwqNHyV03KVzYB6znq1Bea9HuA==", + "dependencies": { + "@sentry/types": "6.7.1", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -624,24 +753,27 @@ } }, "node_modules/@types/debug": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.6.tgz", - "integrity": "sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA==", - "dev": true + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } }, "node_modules/@types/fs-extra": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", - "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", "dev": true, "optional": true, "dependencies": { @@ -656,12 +788,18 @@ "dev": true }, "node_modules/@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true, "optional": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, "node_modules/@types/node": { "version": "14.17.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", @@ -692,9 +830,9 @@ "optional": true }, "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.3.tgz", + "integrity": "sha512-K7rm3Ke3ag/pAniBe80A6J6fjoqRibvCrl3dRmtXV9eCEt9h/pZwmHX9MzjQVUc/elneQTL4Ky7XKorC71Lmxw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -753,6 +891,17 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -894,15 +1043,15 @@ } }, "node_modules/app-builder-bin": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.13.tgz", - "integrity": "sha512-ighVe9G+bT1ENGdp9ecO1P+94vv/f+FUwaI+XkNzeg9bYF8Oi3BQ+mJuxS00UgyHs8luuOzjzC+qnAtdb43Mpg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.7.1.tgz", + "integrity": "sha512-ql93vEUq6WsstGXD+SBLSIQw6SNnhbDEM0swzgugytMxLp3rT24Ag/jcC80ZHxiPRTdew1niuR7P3/FCrDqIjw==", "dev": true }, "node_modules/app-builder-lib": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.11.7.tgz", - "integrity": "sha512-pS9/cR4/TnNZVAHZECiSvvwTBzbwblj7KBBZkMKDG57nibq0I1XY8zAaYeHFdlYTyrRcz9JUXbAqJKezya7UFQ==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.13.1.tgz", + "integrity": "sha512-TsUe7gCdH1cnSknUcqwVRAAxsFxsxcU/BJvnKR8ASzjaZtePW7MU+AEaDVDUURycgYxQ9XeymGjmuQGS32jcbw==", "dev": true, "dependencies": { "@develar/schema-utils": "~2.6.5", @@ -911,12 +1060,13 @@ "7zip-bin": "~5.1.1", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.2", "ejs": "^3.1.6", - "electron-publish": "22.11.7", + "electron-osx-sign": "^0.5.0", + "electron-publish": "22.13.1", "fs-extra": "^10.0.0", "hosted-git-info": "^4.0.2", "is-ci": "^3.0.0", @@ -1037,9 +1187,9 @@ } }, "node_modules/asar": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.0.3.tgz", - "integrity": "sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/asar/-/asar-3.1.0.tgz", + "integrity": "sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==", "dev": true, "dependencies": { "chromium-pickle-js": "^0.2.0", @@ -1115,16 +1265,16 @@ } }, "node_modules/autoprefixer": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.4.tgz", - "integrity": "sha512-EKjKDXOq7ug+jagLzmnoTRpTT0q1KVzEJqrJd0hCBa7FiG0WbFOBCcJCy2QkW1OckpO3qgttA1aWjVbeIPAecw==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.7.tgz", + "integrity": "sha512-EmGpu0nnQVmMhX8ROoJ7Mx8mKYPlcUHuxkwrRYEYMz85lu7H09v8w6R1P0JPdn/hKU32GjpLBFEOuIlDWCRWvg==", "dev": true, "dependencies": { - "browserslist": "^4.16.8", - "caniuse-lite": "^1.0.30001252", - "colorette": "^1.3.0", + "browserslist": "^4.17.3", + "caniuse-lite": "^1.0.30001264", "fraction.js": "^4.1.1", "normalize-range": "^0.1.2", + "picocolors": "^0.2.1", "postcss-value-parser": "^4.1.0" }, "bin": { @@ -1234,9 +1384,9 @@ } }, "node_modules/boolean": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz", - "integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", + "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", "optional": true }, "node_modules/boxen": { @@ -1325,16 +1475,16 @@ } }, "node_modules/browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001251", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" }, "bin": { "browserslist": "cli.js" @@ -1412,18 +1562,19 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "node_modules/builder-util": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.11.7.tgz", - "integrity": "sha512-ihqUe5ey82LM9qqQe0/oIcaSm9w+B9UjcsWJZxJliTBsbU+sErOpDFpHW+sim0veiTF/EIcGUh9HoduWw+l9FA==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.13.1.tgz", + "integrity": "sha512-gMdoW9aQbWYxuQ4k4jT4An1BTo/hWzvsdv3pwNz18iNYnqn9j+xMllQOg9CHgfQYKSUd8VuMsZnbCvLO4NltYw==", "dev": true, "dependencies": { - "@types/debug": "^4.1.5", + "@types/debug": "^4.1.6", "@types/fs-extra": "^9.0.11", "7zip-bin": "~5.1.1", - "app-builder-bin": "3.5.13", + "app-builder-bin": "3.7.1", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "8.7.7", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", + "cross-spawn": "^7.0.3", "debug": "^4.3.2", "fs-extra": "^10.0.0", "is-ci": "^3.0.0", @@ -1434,9 +1585,9 @@ } }, "node_modules/builder-util-runtime": { - "version": "8.7.7", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.7.tgz", - "integrity": "sha512-RUfoXzVrmFFI0K/Oft0CtP1LpTIOlBeLJatt5DePTI0KlxE156am4SGUpqtbbdqZNm++LkV9mX4olBDcXyGPow==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.8.1.tgz", + "integrity": "sha512-xHxAzdsJmMV8m/N+INzYUKfyJASeKyKHnA1uGkY8Y8JKLI/c4BG+If+L0If2YETv96CiRASkvd02tIt2pvrchQ==", "dev": true, "dependencies": { "debug": "^4.3.2", @@ -1561,9 +1712,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001264", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz", + "integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==", "dev": true, "funding": { "type": "opencollective", @@ -1780,6 +1931,15 @@ "node": ">= 6" } }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1827,10 +1987,18 @@ "node": ">=8" } }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", - "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.0.tgz", + "integrity": "sha512-WJeQqq6jOYgVgg4NrXKL0KLQhi0CT4ZOCvFL+3CQ5o7I6J8HkT5wd53EadMfqTDp1so/MT1J+w2ujhWcCJtN7w==", "hasInstallScript": true, "optional": true, "funding": { @@ -2092,14 +2260,14 @@ "dev": true }, "node_modules/dmg-builder": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.11.7.tgz", - "integrity": "sha512-+I+XfP2DODHB6PwFANgpH/WMzzCA5r5XoMvbFCIYjQjJpXlO0XnqQaamzFl2vh/Wz/Qt0d0lJMgRy8gKR3MGdQ==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.13.1.tgz", + "integrity": "sha512-qgfLN2fo4q2wIWNvbcKlZ71DLRDLvWIElOB7oxlSxUrMi6xhI+9v1Mh7E0FJ+r5UXhQzaQXaGuyMsQRbGgrSwg==", "dev": true, "dependencies": { - "app-builder-lib": "22.11.7", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.6", + "app-builder-lib": "22.13.1", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "fs-extra": "^10.0.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" @@ -2108,19 +2276,6 @@ "dmg-license": "^1.0.9" } }, - "node_modules/dmg-builder/node_modules/builder-util-runtime": { - "version": "8.7.6", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.6.tgz", - "integrity": "sha512-rj9AIY7CzLSuTOXpToiaQkruYh6UEQ+kYnd5UET22ch8MGClEtIZKXHG14qEiXEr2x4EOKDMxkcTa+9TYaE+ug==", - "dev": true, - "dependencies": { - "debug": "^4.3.2", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/dmg-builder/node_modules/fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -2243,12 +2398,12 @@ } }, "node_modules/electron": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz", - "integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-15.1.2.tgz", + "integrity": "sha512-cU5x1ZvhwcTtzClJrsxqbvCocs40uhuXcdXbXFMpe6XnbnjruTbwrB2V0OxN01pStDrYihnfKo2yhfEvhO3zmw==", "hasInstallScript": true, "dependencies": { - "@electron/get": "^1.0.1", + "@electron/get": "^1.13.0", "@types/node": "^14.6.2", "extract-zip": "^1.0.3" }, @@ -2260,17 +2415,17 @@ } }, "node_modules/electron-builder": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.11.7.tgz", - "integrity": "sha512-yQExSLt7Hbz/P8lLkZDdE/OnJJ7NCX+uiQcV+XIH0TeEZcD87ZnSqBBzGUN5akySU4BXXlrVZKeUsXACWrm5Kw==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.13.1.tgz", + "integrity": "sha512-ajlI40L60qKBBxvpf770kcjxHAccMpEWpwsHAppytl3WmWgJfMut4Wz9VUFqyNtX/9a624QTatk6TqoxqewRug==", "dev": true, "dependencies": { - "@types/yargs": "^16.0.2", - "app-builder-lib": "22.11.7", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "@types/yargs": "^17.0.1", + "app-builder-lib": "22.13.1", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", - "dmg-builder": "22.11.7", + "dmg-builder": "22.13.1", "fs-extra": "^10.0.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", @@ -2442,15 +2597,63 @@ "keyboardevents-areequal": "^0.2.1" } }, + "node_modules/electron-osx-sign": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.5.0.tgz", + "integrity": "sha512-icoRLHzFz/qxzDh/N4Pi2z4yVHurlsCAYQvsCSG7fCedJ4UJXBS6PoQyGH71IfcqKupcKeK7HX/NkyfG+v6vlQ==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/electron-osx-sign/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/electron-osx-sign/node_modules/isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "dependencies": { + "buffer-alloc": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/electron-osx-sign/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "node_modules/electron-publish": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.11.7.tgz", - "integrity": "sha512-A4EhRRNBVz4SPzUlBrPO6BmuyDeI0pyprggPAV9rQ+SDVSnSB/WKPot9JwWMyArkGj3AUUTMNVT6hwZhMvhfqw==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.13.1.tgz", + "integrity": "sha512-5nCXhnsqrRxP5NsZxUKjiMkcFmQglXp7i/YY4rp3h1s1psg3utOIkM29Z93YTSXicZJU1J+8811eo5HX1vpoKg==", "dev": true, "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", "fs-extra": "^10.0.0", "lazy-val": "^1.0.5", @@ -2493,9 +2696,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.824", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.824.tgz", - "integrity": "sha512-Fk+5aD0HDi9i9ZKt9n2VPOZO1dQy7PV++hz2wJ/KIn+CvVfu4fny39squHtyVDPuHNuoJGAZIbuReEklqYIqfA==", + "version": "1.3.859", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.859.tgz", + "integrity": "sha512-gXRXKNWedfdiKIzwr0Mg/VGCvxXzy+4SuK9hp1BDvfbCwx0O5Ot+2f4CoqQkqEJ3Zj/eAV/GoAFgBVFgkBLXuQ==", "dev": true }, "node_modules/emoji-regex": { @@ -5304,6 +5507,18 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.6.tgz", @@ -6112,6 +6327,11 @@ "node": ">=0.10.0" } }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6315,17 +6535,20 @@ } }, "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, "engines": { "node": "4.x || >=6.0.0" } }, "node_modules/node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "node_modules/normalize-package-data": { @@ -6640,6 +6863,12 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -6686,15 +6915,13 @@ } }, "node_modules/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz", + "integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==", "dev": true, - "optional": true, "dependencies": { "base64-js": "^1.5.1", - "xmlbuilder": "^9.0.7", - "xmldom": "^0.5.0" + "xmlbuilder": "^9.0.7" }, "engines": { "node": ">=6" @@ -6705,7 +6932,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true, - "optional": true, "engines": { "node": ">=4.0" } @@ -6835,9 +7061,9 @@ } }, "node_modules/prettier": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", - "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -7523,9 +7749,9 @@ } }, "node_modules/sass": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.40.1.tgz", - "integrity": "sha512-M6WskYLzTfdZdb09W9SftIScjudL8jNkhdh9z96U+olQaKIcw2Knb6QLL9bUhnuSm4VD+1yJVaO2/ENDPMTtAQ==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.42.1.tgz", + "integrity": "sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0" @@ -7665,9 +7891,9 @@ } }, "node_modules/smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "optional": true, "engines": { @@ -7716,9 +7942,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -7990,9 +8216,9 @@ } }, "node_modules/tailwindcss": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", - "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.16.tgz", + "integrity": "sha512-EireCtpQyyJ4Xz8NYzHafBoy4baCOO96flM0+HgtsFcIQ9KFy/YBK3GEtlnD+rXen0e4xm8t3WiUcKBJmN6yjg==", "dev": true, "dependencies": { "arg": "^5.0.1", @@ -8194,6 +8420,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -8230,8 +8461,7 @@ "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -8479,6 +8709,20 @@ "extsprintf": "^1.2.0" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8664,16 +8908,6 @@ "node": ">=8.0" } }, - "node_modules/xmldom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz", - "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -8990,9 +9224,9 @@ } }, "@electron/get": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.12.4.tgz", - "integrity": "sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz", + "integrity": "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==", "requires": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -9006,9 +9240,9 @@ } }, "@electron/remote": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-1.2.1.tgz", - "integrity": "sha512-yKh60I8KjezQkZqeuN5Nu2O/Z72+tgNgzvAa8QQPLtQbsrCOaeIWdXZQqierz4jQ5jzTNUk6KIcK3V2kFeaxaQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.0.1.tgz", + "integrity": "sha512-bGX4/yB2bPZwXm1DsxgoABgH0Cz7oFtXJgkerB8VrStYdTyvhGAULzNLRn9rVmeAuC3VUDXaXpZIlZAZHpsLIA==", "requires": {} }, "@electron/universal": { @@ -9203,6 +9437,112 @@ "fastq": "^1.6.0" } }, + "@sentry/browser": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.7.1.tgz", + "integrity": "sha512-R5PYx4TTvifcU790XkK6JVGwavKaXwycDU0MaAwfc4Vf7BLm5KCNJCsDySu1RPAap/017MVYf54p6dWvKiRviA==", + "requires": { + "@sentry/core": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/core": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.7.1.tgz", + "integrity": "sha512-VAv8OR/7INn2JfiLcuop4hfDcyC7mfL9fdPndQEhlacjmw8gRrgXjR7qyhnCTgzFLkHI7V5bcdIzA83TRPYQpA==", + "requires": { + "@sentry/hub": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/electron": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-2.5.4.tgz", + "integrity": "sha512-tCCK+P581TmdjsDpHBQz7qYcldzGdUk1Fd6FPxPy1JKGzeY4uf/uSLKzR80Lzs5kTpEZFOwiMHSA8yjwFp5qoA==", + "requires": { + "@sentry/browser": "6.7.1", + "@sentry/core": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/node": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + } + } + }, + "@sentry/hub": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.7.1.tgz", + "integrity": "sha512-eVCTWvvcp6xa0A5GGNHMQEWslmKPlisE5rGmsV/kjvSUv3zSrI0eIDfb51ikdnCiBjHpK2NBWP8Vy8cZOEJegg==", + "requires": { + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.7.1.tgz", + "integrity": "sha512-HDDPEnQRD6hC0qaHdqqKDStcdE1KhkFh0RCtJNMCDn0zpav8Dj9AteF70x6kLSlliAJ/JFwi6AmQrLz+FxPexw==", + "requires": { + "@sentry/hub": "6.7.1", + "@sentry/types": "6.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/node": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.7.1.tgz", + "integrity": "sha512-rtZo1O8ROv4lZwuljQz3iFZW89oXSlgXCG2VqkxQyRspPWu89abROpxLjYzsWwQ8djnur1XjFv51kOLDUTS6Qw==", + "requires": { + "@sentry/core": "6.7.1", + "@sentry/hub": "6.7.1", + "@sentry/tracing": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + } + }, + "@sentry/tracing": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.7.1.tgz", + "integrity": "sha512-wyS3nWNl5mzaC1qZ2AIp1hjXnfO9EERjMIJjCihs2LWBz1r3efxrHxJHs8wXlNWvrT3KLhq/7vvF5CdU82uPeQ==", + "requires": { + "@sentry/hub": "6.7.1", + "@sentry/minimal": "6.7.1", + "@sentry/types": "6.7.1", + "@sentry/utils": "6.7.1", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.7.1.tgz", + "integrity": "sha512-9AO7HKoip2MBMNQJEd6+AKtjj2+q9Ze4ooWUdEvdOVSt5drg7BGpK221/p9JEOyJAZwEPEXdcMd3VAIMiOb4MA==" + }, + "@sentry/utils": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.7.1.tgz", + "integrity": "sha512-Tq2otdbWlHAkctD+EWTYKkEx6BL1Qn3Z/imkO06/PvzpWvVhJWQ5qHAzz5XnwwqNHyV03KVzYB6znq1Bea9HuA==", + "requires": { + "@sentry/types": "6.7.1", + "tslib": "^1.9.3" + } + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -9217,24 +9557,27 @@ } }, "@types/debug": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.6.tgz", - "integrity": "sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA==", - "dev": true + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dev": true, + "requires": { + "@types/ms": "*" + } }, "@types/fs-extra": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.11.tgz", - "integrity": "sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==", + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, "requires": { "@types/node": "*" } }, "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", "dev": true, "optional": true, "requires": { @@ -9249,12 +9592,18 @@ "dev": true }, "@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true, "optional": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, "@types/node": { "version": "14.17.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", @@ -9285,9 +9634,9 @@ "optional": true }, "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.3.tgz", + "integrity": "sha512-K7rm3Ke3ag/pAniBe80A6J6fjoqRibvCrl3dRmtXV9eCEt9h/pZwmHX9MzjQVUc/elneQTL4Ky7XKorC71Lmxw==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -9335,6 +9684,14 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -9438,15 +9795,15 @@ } }, "app-builder-bin": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.5.13.tgz", - "integrity": "sha512-ighVe9G+bT1ENGdp9ecO1P+94vv/f+FUwaI+XkNzeg9bYF8Oi3BQ+mJuxS00UgyHs8luuOzjzC+qnAtdb43Mpg==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.7.1.tgz", + "integrity": "sha512-ql93vEUq6WsstGXD+SBLSIQw6SNnhbDEM0swzgugytMxLp3rT24Ag/jcC80ZHxiPRTdew1niuR7P3/FCrDqIjw==", "dev": true }, "app-builder-lib": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.11.7.tgz", - "integrity": "sha512-pS9/cR4/TnNZVAHZECiSvvwTBzbwblj7KBBZkMKDG57nibq0I1XY8zAaYeHFdlYTyrRcz9JUXbAqJKezya7UFQ==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-22.13.1.tgz", + "integrity": "sha512-TsUe7gCdH1cnSknUcqwVRAAxsFxsxcU/BJvnKR8ASzjaZtePW7MU+AEaDVDUURycgYxQ9XeymGjmuQGS32jcbw==", "dev": true, "requires": { "@develar/schema-utils": "~2.6.5", @@ -9455,12 +9812,13 @@ "7zip-bin": "~5.1.1", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.2", "ejs": "^3.1.6", - "electron-publish": "22.11.7", + "electron-osx-sign": "^0.5.0", + "electron-publish": "22.13.1", "fs-extra": "^10.0.0", "hosted-git-info": "^4.0.2", "is-ci": "^3.0.0", @@ -9554,9 +9912,9 @@ } }, "asar": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.0.3.tgz", - "integrity": "sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/asar/-/asar-3.1.0.tgz", + "integrity": "sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==", "dev": true, "requires": { "@types/glob": "^7.1.1", @@ -9609,16 +9967,16 @@ } }, "autoprefixer": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.4.tgz", - "integrity": "sha512-EKjKDXOq7ug+jagLzmnoTRpTT0q1KVzEJqrJd0hCBa7FiG0WbFOBCcJCy2QkW1OckpO3qgttA1aWjVbeIPAecw==", + "version": "10.3.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.3.7.tgz", + "integrity": "sha512-EmGpu0nnQVmMhX8ROoJ7Mx8mKYPlcUHuxkwrRYEYMz85lu7H09v8w6R1P0JPdn/hKU32GjpLBFEOuIlDWCRWvg==", "dev": true, "requires": { - "browserslist": "^4.16.8", - "caniuse-lite": "^1.0.30001252", - "colorette": "^1.3.0", + "browserslist": "^4.17.3", + "caniuse-lite": "^1.0.30001264", "fraction.js": "^4.1.1", "normalize-range": "^0.1.2", + "picocolors": "^0.2.1", "postcss-value-parser": "^4.1.0" } }, @@ -9687,9 +10045,9 @@ } }, "boolean": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.2.tgz", - "integrity": "sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", + "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==", "optional": true }, "boxen": { @@ -9759,16 +10117,16 @@ } }, "browserslist": { - "version": "4.16.8", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.8.tgz", - "integrity": "sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001251", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.811", + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", "escalade": "^3.1.1", - "node-releases": "^1.1.75" + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" } }, "buffer": { @@ -9816,18 +10174,19 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "builder-util": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.11.7.tgz", - "integrity": "sha512-ihqUe5ey82LM9qqQe0/oIcaSm9w+B9UjcsWJZxJliTBsbU+sErOpDFpHW+sim0veiTF/EIcGUh9HoduWw+l9FA==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-22.13.1.tgz", + "integrity": "sha512-gMdoW9aQbWYxuQ4k4jT4An1BTo/hWzvsdv3pwNz18iNYnqn9j+xMllQOg9CHgfQYKSUd8VuMsZnbCvLO4NltYw==", "dev": true, "requires": { - "@types/debug": "^4.1.5", + "@types/debug": "^4.1.6", "@types/fs-extra": "^9.0.11", "7zip-bin": "~5.1.1", - "app-builder-bin": "3.5.13", + "app-builder-bin": "3.7.1", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "8.7.7", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", + "cross-spawn": "^7.0.3", "debug": "^4.3.2", "fs-extra": "^10.0.0", "is-ci": "^3.0.0", @@ -9867,9 +10226,9 @@ } }, "builder-util-runtime": { - "version": "8.7.7", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.7.tgz", - "integrity": "sha512-RUfoXzVrmFFI0K/Oft0CtP1LpTIOlBeLJatt5DePTI0KlxE156am4SGUpqtbbdqZNm++LkV9mX4olBDcXyGPow==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.8.1.tgz", + "integrity": "sha512-xHxAzdsJmMV8m/N+INzYUKfyJASeKyKHnA1uGkY8Y8JKLI/c4BG+If+L0If2YETv96CiRASkvd02tIt2pvrchQ==", "dev": true, "requires": { "debug": "^4.3.2", @@ -9934,9 +10293,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001252", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001252.tgz", - "integrity": "sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==", + "version": "1.0.30001264", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001264.tgz", + "integrity": "sha512-Ftfqqfcs/ePiUmyaySsQ4PUsdcYyXG2rfoBVsk3iY1ahHaJEw65vfb7Suzqm+cEkwwPIv/XWkg27iCpRavH4zA==", "dev": true }, "chalk": { @@ -10110,6 +10469,12 @@ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true }, + "compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -10151,10 +10516,15 @@ "xdg-basedir": "^4.0.0" } }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, "core-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", - "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.0.tgz", + "integrity": "sha512-WJeQqq6jOYgVgg4NrXKL0KLQhi0CT4ZOCvFL+3CQ5o7I6J8HkT5wd53EadMfqTDp1so/MT1J+w2ujhWcCJtN7w==", "optional": true }, "core-util-is": { @@ -10356,30 +10726,20 @@ "dev": true }, "dmg-builder": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.11.7.tgz", - "integrity": "sha512-+I+XfP2DODHB6PwFANgpH/WMzzCA5r5XoMvbFCIYjQjJpXlO0XnqQaamzFl2vh/Wz/Qt0d0lJMgRy8gKR3MGdQ==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-22.13.1.tgz", + "integrity": "sha512-qgfLN2fo4q2wIWNvbcKlZ71DLRDLvWIElOB7oxlSxUrMi6xhI+9v1Mh7E0FJ+r5UXhQzaQXaGuyMsQRbGgrSwg==", "dev": true, "requires": { - "app-builder-lib": "22.11.7", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.6", + "app-builder-lib": "22.13.1", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "dmg-license": "^1.0.9", "fs-extra": "^10.0.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "dependencies": { - "builder-util-runtime": { - "version": "8.7.6", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.6.tgz", - "integrity": "sha512-rj9AIY7CzLSuTOXpToiaQkruYh6UEQ+kYnd5UET22ch8MGClEtIZKXHG14qEiXEr2x4EOKDMxkcTa+9TYaE+ug==", - "dev": true, - "requires": { - "debug": "^4.3.2", - "sax": "^1.2.4" - } - }, "fs-extra": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", @@ -10472,27 +10832,27 @@ } }, "electron": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz", - "integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/electron/-/electron-15.1.2.tgz", + "integrity": "sha512-cU5x1ZvhwcTtzClJrsxqbvCocs40uhuXcdXbXFMpe6XnbnjruTbwrB2V0OxN01pStDrYihnfKo2yhfEvhO3zmw==", "requires": { - "@electron/get": "^1.0.1", + "@electron/get": "^1.13.0", "@types/node": "^14.6.2", "extract-zip": "^1.0.3" } }, "electron-builder": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.11.7.tgz", - "integrity": "sha512-yQExSLt7Hbz/P8lLkZDdE/OnJJ7NCX+uiQcV+XIH0TeEZcD87ZnSqBBzGUN5akySU4BXXlrVZKeUsXACWrm5Kw==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-22.13.1.tgz", + "integrity": "sha512-ajlI40L60qKBBxvpf770kcjxHAccMpEWpwsHAppytl3WmWgJfMut4Wz9VUFqyNtX/9a624QTatk6TqoxqewRug==", "dev": true, "requires": { - "@types/yargs": "^16.0.2", - "app-builder-lib": "22.11.7", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "@types/yargs": "^17.0.1", + "app-builder-lib": "22.13.1", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", - "dmg-builder": "22.11.7", + "dmg-builder": "22.13.1", "fs-extra": "^10.0.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", @@ -10628,15 +10988,55 @@ "keyboardevents-areequal": "^0.2.1" } }, + "electron-osx-sign": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.5.0.tgz", + "integrity": "sha512-icoRLHzFz/qxzDh/N4Pi2z4yVHurlsCAYQvsCSG7fCedJ4UJXBS6PoQyGH71IfcqKupcKeK7HX/NkyfG+v6vlQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.0", + "compare-version": "^0.1.2", + "debug": "^2.6.8", + "isbinaryfile": "^3.0.2", + "minimist": "^1.2.0", + "plist": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "electron-publish": { - "version": "22.11.7", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.11.7.tgz", - "integrity": "sha512-A4EhRRNBVz4SPzUlBrPO6BmuyDeI0pyprggPAV9rQ+SDVSnSB/WKPot9JwWMyArkGj3AUUTMNVT6hwZhMvhfqw==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.13.1.tgz", + "integrity": "sha512-5nCXhnsqrRxP5NsZxUKjiMkcFmQglXp7i/YY4rp3h1s1psg3utOIkM29Z93YTSXicZJU1J+8811eo5HX1vpoKg==", "dev": true, "requires": { "@types/fs-extra": "^9.0.11", - "builder-util": "22.11.7", - "builder-util-runtime": "8.7.7", + "builder-util": "22.13.1", + "builder-util-runtime": "8.8.1", "chalk": "^4.1.1", "fs-extra": "^10.0.0", "lazy-val": "^1.0.5", @@ -10673,9 +11073,9 @@ } }, "electron-to-chromium": { - "version": "1.3.824", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.824.tgz", - "integrity": "sha512-Fk+5aD0HDi9i9ZKt9n2VPOZO1dQy7PV++hz2wJ/KIn+CvVfu4fny39squHtyVDPuHNuoJGAZIbuReEklqYIqfA==", + "version": "1.3.859", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.859.tgz", + "integrity": "sha512-gXRXKNWedfdiKIzwr0Mg/VGCvxXzy+4SuK9hp1BDvfbCwx0O5Ot+2f4CoqQkqEJ3Zj/eAV/GoAFgBVFgkBLXuQ==", "dev": true }, "emoji-regex": { @@ -12835,6 +13235,15 @@ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, "iconv-corefoundation": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.6.tgz", @@ -13440,6 +13849,11 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, + "lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -13589,14 +14003,17 @@ } }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "requires": { + "whatwg-url": "^5.0.0" + } }, "node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "normalize-package-data": { @@ -13832,6 +14249,12 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -13863,23 +14286,20 @@ } }, "plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-MSrkwZBdQ6YapHy87/8hDU8MnIcyxBKjeF+McXnr5A9MtffPewTs7G3hlpodT5TacyfIyFTaJEhh3GGcmasTgQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz", + "integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==", "dev": true, - "optional": true, "requires": { "base64-js": "^1.5.1", - "xmlbuilder": "^9.0.7", - "xmldom": "^0.5.0" + "xmlbuilder": "^9.0.7" }, "dependencies": { "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true, - "optional": true + "dev": true } } }, @@ -13957,9 +14377,9 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, "prettier": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz", - "integrity": "sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", "dev": true }, "prettier-linter-helpers": { @@ -14488,9 +14908,9 @@ } }, "sass": { - "version": "1.40.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.40.1.tgz", - "integrity": "sha512-M6WskYLzTfdZdb09W9SftIScjudL8jNkhdh9z96U+olQaKIcw2Knb6QLL9bUhnuSm4VD+1yJVaO2/ENDPMTtAQ==", + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.42.1.tgz", + "integrity": "sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0" @@ -14601,9 +15021,9 @@ } }, "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "optional": true }, @@ -14636,9 +15056,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -14858,9 +15278,9 @@ } }, "tailwindcss": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.15.tgz", - "integrity": "sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.2.16.tgz", + "integrity": "sha512-EireCtpQyyJ4Xz8NYzHafBoy4baCOO96flM0+HgtsFcIQ9KFy/YBK3GEtlnD+rXen0e4xm8t3WiUcKBJmN6yjg==", "dev": true, "requires": { "arg": "^5.0.1", @@ -15024,6 +15444,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -15059,8 +15484,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tunnel": { "version": "0.0.6", @@ -15261,6 +15685,20 @@ "extsprintf": "^1.2.0" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -15408,13 +15846,6 @@ "dev": true, "optional": true }, - "xmldom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz", - "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==", - "dev": true, - "optional": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 1691d61c..8bc2ef93 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "authme", "productName": "Authme", - "version": "2.7.1", - "tag": "2.7.1", + "version": "2.7.2", + "tag": "2.7.2", "description": "Simple cross platform two-factor authentication app for desktop.", "author": "Levminer", "license": "GPL-3.0", "scripts": { "start": "node scripts/alpha-build.js && electron .", - "dev": "electron .", - "build": "electron-builder --publish=never --x64", + "dev": "node scripts/release-build.js && electron .", + "build": "npm run build:release", "build:release": "node scripts/release-build.js && electron-builder --publish=never --x64", "build:alpha": "node scripts/alpha-build.js && electron-builder --publish=never --x64", "build:test": "node scripts/alpha-build.js && electron-builder --publish=never --x64 --dir -c scripts/test-build.json", @@ -25,7 +25,6 @@ "appId": "com.levminer.authme", "files": [ "!screenshots", - "!extract", "!sample", "!scripts", "!**/*.scss", @@ -81,23 +80,24 @@ } }, "dependencies": { - "@electron/remote": "^1.2.1", + "@electron/remote": "^2.0.1", "@levminer/lib": "file:lib", "@levminer/speakeasy": "^1.3.1", + "@sentry/electron": "^2.5.4", "auto-launch": "^5.0.5", "bcryptjs": "^2.4.3", "cryptr": "^6.0.2", "electron-context-menu": "^3.1.1", "electron-debug": "^3.2.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.5", "protons": "^2.0.3", "qrcode": "^1.4.4", "qrcode-decoder": "^0.2.2" }, "devDependencies": { - "autoprefixer": "^10.3.4", - "electron": "^14.0.1", - "electron-builder": "^22.11.7", + "autoprefixer": "^10.3.7", + "electron": "^15.1.2", + "electron-builder": "^22.13.1", "eslint": "^7.32.0", "eslint-config-node": "^4.1.0", "eslint-config-prettier": "^8.3.0", @@ -107,9 +107,9 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", - "prettier": "^2.4.0", - "sass": "^1.40.1", - "tailwindcss": "^2.2.15" + "prettier": "^2.4.1", + "sass": "^1.42.1", + "tailwindcss": "^2.2.16" }, "engines": { "node": ">=15.0.0", diff --git a/preload.js b/preload.js index 335af30c..32f020d2 100644 --- a/preload.js +++ b/preload.js @@ -1,9 +1,13 @@ -const fs = require("fs") -const electron = require("electron") const { app } = require("@electron/remote") +const sentry = require("@sentry/electron") +const electron = require("electron") const path = require("path") +const fs = require("fs") const ipc = electron.ipcRenderer +// ? crash report +sentry.init({ dsn: "https://173234c94f8f4294a28e114c9113c1ce@o1020924.ingest.sentry.io/5986541" }) + // ? if development let dev = false let integrity = false @@ -40,11 +44,6 @@ const file = JSON.parse( }) ) -// settings launch_on_startup -if (file.settings.launch_on_startup === true) { - ipc.send("startup") -} - // ? local storage let storage @@ -93,3 +92,7 @@ document.addEventListener("keydown", (event) => { event.preventDefault() } }) + +// prevent drag and drop +document.addEventListener("dragover", (event) => event.preventDefault()) +document.addEventListener("drop", (event) => event.preventDefault()) diff --git a/screenshots/old/screenshot-2.7.1.png b/screenshots/old/screenshot-2.7.1.png new file mode 100644 index 00000000..e388073b Binary files /dev/null and b/screenshots/old/screenshot-2.7.1.png differ diff --git a/screenshots/screenshot.png b/screenshots/screenshot.png index e388073b..1ab6239e 100644 Binary files a/screenshots/screenshot.png and b/screenshots/screenshot.png differ diff --git a/scripts/test-build.json b/scripts/test-build.json index 89149c00..b5f3f793 100644 --- a/scripts/test-build.json +++ b/scripts/test-build.json @@ -1,6 +1,7 @@ { "productName": "Authme Test", - "appId": "com.levminer.authmetest", + "appId": "com.levminer.authme-test", "asar": false, - "files": ["!screenshots", "!extract", "!sample", "!scripts", "!**/*.scss", "!**/*.ts", "!.*"] + "files": ["!screenshots", "!sample", "!scripts", "!**/*.scss", "!**/*.ts", "!.*"], + "icon": "../img/icon.ico" } diff --git a/src/dragonfly.css b/src/dragonfly.css index 8e881ba9..9f4a9d46 100644 --- a/src/dragonfly.css +++ b/src/dragonfly.css @@ -1 +1 @@ -:root{--df_primary_color: rgb(255, 255, 255);--df_secondary_color: rgb(255, 255, 255);--df_tertiary_color: rgb(0, 0, 0);--df_primary_background: rgb(10, 10, 10);--df_secondary_background: rgb(10, 10, 10);--df_gradiant_start: rgb(20, 20, 20);--df_gradiant_end: rgb(30, 30, 30)}body{color:#fff;box-sizing:border-box;display:flex;flex-direction:column;min-height:100vh;background:#0a0a0a;user-select:none}::selection{color:#fff;background:gray}*{font-family:Arial,Helvetica,sans-serif;margin:0;padding:0;outline:none}a:link{color:#fff;font-size:24px;font-weight:bold;transition:.2s ease-in;text-decoration:underline !important}a:hover{color:gray;text-decoration:underline !important}a:visited{color:#fff;text-decoration:none !important}.button1{color:#000;background:#fff;border:2px solid #fff;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in}.button1:hover{border-color:#fff;background:transparent;color:#fff}.buttoni{color:#000;background:#fff;border:2px solid #fff;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;text-align:center;width:18rem}.buttoni:hover{border-color:#fff;background:transparent;color:#fff}.buttoni>svg{position:relative;top:4px}.buttonr{margin:3px;color:#000;background:#fff;border:2px solid var(--df_primary_color);font-size:1.25rem;padding:13px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;height:61px}.buttonr:hover{border:2px solid var(--df_primary_color);background:transparent;color:var(--df_primary_color)}h1,h2,h3,h4{margin:2rem;font-weight:600}h1{font-size:64px}h2{font-size:48px}h3{font-size:32px}h4{font-size:24px}.text1,.text2,.text3,.text4{margin:1rem}.text1{font-size:2rem}.text2{font-size:1.75rem}.text3{font-size:1.5rem}.text4{font-size:1rem}.input1:focus::placeholder,.input2:focus::placeholder{transition:.2s ease-in;color:transparent}.input1:hover::placeholder,.input2:hover::placeholder{transition:.2s ease-in;color:transparent}.input1{color:#000;background:#fff;border:2px solid #fff;font-size:1rem;padding:15px 30px;border-radius:1.8rem;cursor:text;text-align:center;transition:.2s ease-in}.input1:hover{color:#fff;background-color:transparent;border:2px solid #fff}.input1::placeholder{color:var(--df_secondary_color)}.input2{color:var(--df_primary_color);background:var(--df_primary_background);border:2px solid var(--df_primary_color);font-size:1rem;padding:15px 30px;border-radius:1.8rem;cursor:text;text-align:center;transition:.2s ease-in}.input2:hover{color:var(--df_secondary_color);background-color:var(--df_primary_background);border:2px solid var(--df_secondary_color)}.input2::placeholder{color:var(--df_primary_color)}.center{background-color:#141414;position:relative;top:200px;text-align:center;padding-top:30px;margin:130px auto;height:300px;width:600px;border-radius:30px}::-webkit-scrollbar-track{background-color:#292a2d}::-webkit-scrollbar{width:12px;background-color:#f5f5f5}::-webkit-scrollbar-thumb{background-color:#141414}::-webkit-scrollbar-thumb:hover{background-color:#1e1e1e}::-webkit-scrollbar-corner{background:#292a2d}.build{display:none;top:0;position:sticky;z-index:999999}details{font-size:24px}summary{cursor:pointer}details[open] summary~*{animation:fadeEffect .5s ease-in-out}@keyframes fadeEffect{from{opacity:0}to{opacity:1}}.online{display:none}.offline{display:none}.update{display:none}.info{display:none}.warning{color:#a30015;font-weight:bold;margin-top:30px;font-size:24px}.unstable{color:#f5ab00;font-weight:bold;margin-top:30px;font-size:24px}.dropdown-button{color:#000;background:#fff;border:1px solid #000;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;text-align:center;width:18rem;height:3.8rem}.dropdown-button:hover{color:#fff;background-color:transparent;border-color:#fff}.dropdown{position:relative;display:inline-block}.dropdown-content{top:65px;display:none;position:absolute;width:18rem;z-index:1;border-radius:30px}.dropdown-content a{color:#000;padding:10px 10px;text-decoration:none;display:block;background-color:#fff}.dropdown-content a:hover{background-color:#141414;color:#fff}.dropdown-content a:first-child{border-top-left-radius:30px;border-top-right-radius:30px}.dropdown-content :last-child{border-bottom-left-radius:30px;border-bottom-right-radius:30px}.dropdown-link>svg{height:1em;width:1em;margin:0 .05em 0 .1em;vertical-align:-0.1em}.dropdown-link:link{text-decoration:none !important;font-weight:normal;font-size:20px}.dropdown-button>svg{position:relative;top:4px} +:root{--df_primary_color: rgb(255, 255, 255);--df_secondary_color: rgb(255, 255, 255);--df_tertiary_color: rgb(0, 0, 0);--df_primary_background: rgb(10, 10, 10);--df_secondary_background: rgb(10, 10, 10);--df_gradiant_start: rgb(20, 20, 20);--df_gradiant_end: rgb(30, 30, 30)}body{color:#fff;box-sizing:border-box;display:flex;flex-direction:column;min-height:100vh;background:#0a0a0a}::selection{color:#fff;background:gray}*{font-family:Arial,Helvetica,sans-serif;margin:0;padding:0;outline:none}a:link{color:#fff;font-size:24px;font-weight:bold;transition:.2s ease-in;text-decoration:underline !important}a:hover{color:gray;text-decoration:underline !important}a:visited{color:#fff;text-decoration:none !important}.button1{color:#000;background:#fff;border:2px solid #fff;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in}.button1:hover{border-color:#fff;background:transparent;color:#fff}.buttoni{color:#000;background:#fff;border:2px solid #fff;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;text-align:center;width:18rem}.buttoni:hover{border-color:#fff;background:transparent;color:#fff}.buttoni>svg{position:relative;top:4px}.buttonr{margin:3px;color:#000;background:#fff;border:2px solid var(--df_primary_color);font-size:1.25rem;padding:13px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;height:61px}.buttonr:hover{border:2px solid var(--df_primary_color);background:transparent;color:var(--df_primary_color)}.text1,.text2,.text3,.text4{margin:1rem}.text1{font-size:2rem}.text2{font-size:1.75rem}.text3{font-size:1.5rem}.text4{font-size:1rem}.input1:focus::placeholder,.input2:focus::placeholder{transition:.2s ease-in;color:transparent}.input1:hover::placeholder,.input2:hover::placeholder{transition:.2s ease-in;color:transparent}.input1{color:#000;background:#fff;border:2px solid #fff;font-size:1rem;padding:15px 30px;border-radius:1.8rem;cursor:text;text-align:center;transition:.2s ease-in}.input1:hover{color:#fff;background-color:transparent;border:2px solid #fff}.input1::placeholder{color:var(--df_secondary_color)}.input2{color:var(--df_primary_color);background:var(--df_primary_background);border:2px solid var(--df_primary_color);font-size:1rem;padding:15px 30px;border-radius:1.8rem;cursor:text;text-align:center;transition:.2s ease-in}.input2:hover{color:var(--df_secondary_color);background-color:var(--df_primary_background);border:2px solid var(--df_secondary_color)}.input2::placeholder{color:var(--df_primary_color)}.center{background-color:#141414;position:relative;top:200px;text-align:center;padding-top:30px;margin:130px auto;height:300px;width:60%;border-radius:30px}::-webkit-scrollbar-track{background-color:#292a2d}::-webkit-scrollbar{width:12px;background-color:#f5f5f5}::-webkit-scrollbar-thumb{background-color:#141414}::-webkit-scrollbar-thumb:hover{background-color:#1e1e1e}::-webkit-scrollbar-corner{background:#292a2d}.build{display:none;top:0;position:sticky;z-index:999999}details{font-size:24px}summary{cursor:pointer;font-weight:bold}details[open] summary~*{animation:fadeEffect .5s ease-in-out}@keyframes fadeEffect{from{opacity:0}to{opacity:1}}.online{display:none}.offline{display:none}.update{display:none}.info{display:none}.warning{color:#a30015;font-weight:bold;margin-top:30px;font-size:24px}.unstable{color:#f5ab00;font-weight:bold;margin-top:30px;font-size:24px}.dropdown-button{color:#000;background:#fff;border:2px solid #000;font-size:1.25rem;padding:15px 30px;border-radius:30px;cursor:pointer;text-decoration:none;transition:.2s ease-in;text-align:center;width:18rem;height:3.8rem}.dropdown-button:hover{color:#fff;background-color:transparent;border-color:#fff}.dropdown{position:relative;display:inline-block}.dropdown-content{top:65px;display:none;position:absolute;width:18rem;z-index:1;border-radius:30px}.dropdown-content a{color:#000;padding:10px 10px;text-decoration:none;display:block;background-color:#fff}.dropdown-content a:hover{background-color:#1e1e1e;color:#fff}.dropdown-content a:first-child{border-top-left-radius:30px;border-top-right-radius:30px}.dropdown-content :last-child{border-bottom-left-radius:30px;border-bottom-right-radius:30px}.dropdown-link>svg{height:1em;width:1em;margin:0 .05em 0 .1em;vertical-align:-0.1em}.dropdown-link:link{text-decoration:none !important;font-weight:normal;font-size:20px}.dropdown-button>svg{position:relative;top:4px} diff --git a/src/dragonfly.scss b/src/dragonfly.scss index f1b9129a..d5fe80f6 100644 --- a/src/dragonfly.scss +++ b/src/dragonfly.scss @@ -23,7 +23,6 @@ body { flex-direction: column; min-height: 100vh; background: rgb(10, 10, 10); - user-select: none; } /* selection */ @@ -123,31 +122,6 @@ a:visited { color: var(--df_primary_color); } -/* headings */ -h1, -h2, -h3, -h4 { - margin: 2rem; - font-weight: 600; -} - -h1 { - font-size: 64px; -} - -h2 { - font-size: 48px; -} - -h3 { - font-size: 32px; -} - -h4 { - font-size: 24px; -} - /* text */ .text1, .text2, @@ -238,7 +212,7 @@ h4 { padding-top: 30px; margin: 130px auto; height: 300px; - width: 600px; + width: 60%; border-radius: 30px; } @@ -279,6 +253,7 @@ details { summary { cursor: pointer; + font-weight: bold; } details[open] summary ~ * { @@ -330,7 +305,7 @@ details[open] summary ~ * { .dropdown-button { color: black; background: white; - border: 1px solid black; + border: 2px solid black; font-size: 1.25rem; padding: 15px 30px; border-radius: 30px; @@ -371,7 +346,7 @@ details[open] summary ~ * { } .dropdown-content a:hover { - background-color: rgb(20, 20, 20); + background-color: rgb(30, 30, 30); color: white; } diff --git a/src/tailwind.css b/src/tailwind.css index 8f25b368..733aecbb 100644 --- a/src/tailwind.css +++ b/src/tailwind.css @@ -1 +1 @@ -*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity));--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-blur:var(--tw-empty,/*!*/ /*!*/);--tw-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-invert:var(--tw-empty,/*!*/ /*!*/);--tw-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-drop-shadow:var(--tw-empty,/*!*/ /*!*/);--tw-filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}button:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}input[type=checkbox]:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}summary:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}a:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}p:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}svg:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.top-1{top:.25rem}.top-2{top:.5rem}.top-3{top:.75rem}.-top-2\.5{top:-.625rem}.-top-2{top:-.5rem}.-top-9{top:-2.25rem}.left-32{left:8rem}.right-3{right:.75rem}.left-56{left:14rem}.right-5{right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.mx-3{margin-left:.75rem;margin-right:.75rem}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mb-5{margin-bottom:1.25rem}.mb-8{margin-bottom:2rem}.mb-12{margin-bottom:3rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.mb-20{margin-bottom:5rem}.mb-16{margin-bottom:4rem}.mt-5{margin-top:1.25rem}.mr-4{margin-right:1rem}.ml-4{margin-left:1rem}.mt-1{margin-top:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-6{height:1.5rem}.h-16{height:4rem}.h-4{height:1rem}.h-screen{height:100vh}.w-full{width:100%}.w-6{width:1.5rem}.w-2\/3{width:66.666667%}.w-72{width:18rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-4\/5{width:80%}.flex-1{flex:1 1 0%}.transform{transform:var(--tw-transform)}.cursor-pointer{cursor:pointer}.select-none{user-select:none}.appearance-none{appearance:none}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-3{gap:.75rem}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-scroll{overflow-y:scroll}.rounded-md{border-radius:.375rem}.rounded-full{border-radius:9999px}.rounded-2xl{border-radius:30px}.border-2{border-width:2px}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgba(0,0,0,var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}.bg-popup-red{--tw-bg-opacity:1;background-color:rgba(163,0,21,var(--tw-bg-opacity))}.bg-popup-green{--tw-bg-opacity:1;background-color:rgba(40,164,67,var(--tw-bg-opacity))}.bg-popup-blue{--tw-bg-opacity:1;background-color:rgba(22,163,223,var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-popup-yellow{--tw-bg-opacity:1;background-color:rgba(245,171,0,var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgba(10,10,10,var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}.p-1{padding:.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.pt-3{padding-top:.75rem}.pb-3{padding-bottom:.75rem}.pb-5{padding-bottom:1.25rem}.text-center{text-align:center}.align-middle{vertical-align:middle}.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur:blur(8px);filter:var(--tw-filter)}.transition-colors{transition-property:background-color,border-color,color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}*,:after,:before{border:0 solid}.checked\:bg-white:checked{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.hover\:bg-transparent:hover{background-color:initial}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgba(10,10,10,var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px} \ No newline at end of file +*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity));--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-blur:var(--tw-empty,/*!*/ /*!*/);--tw-brightness:var(--tw-empty,/*!*/ /*!*/);--tw-contrast:var(--tw-empty,/*!*/ /*!*/);--tw-grayscale:var(--tw-empty,/*!*/ /*!*/);--tw-hue-rotate:var(--tw-empty,/*!*/ /*!*/);--tw-invert:var(--tw-empty,/*!*/ /*!*/);--tw-saturate:var(--tw-empty,/*!*/ /*!*/);--tw-sepia:var(--tw-empty,/*!*/ /*!*/);--tw-drop-shadow:var(--tw-empty,/*!*/ /*!*/);--tw-filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}button:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}input[type=checkbox]:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}summary:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}a:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}p:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}svg:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}h3:focus-visible{border-radius:30px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(155,80,148,var(--tw-ring-opacity))}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1400px){.container{max-width:1400px}}@media (min-width:1536px){.container{max-width:1536px}}.visible{visibility:visible}.invisible{visibility:hidden}.absolute{position:absolute}.relative{position:relative}.top-1{top:.25rem}.top-2{top:.5rem}.top-3{top:.75rem}.-top-2\.5{top:-.625rem}.-top-2{top:-.5rem}.-top-9{top:-2.25rem}.left-32{left:8rem}.top-5{top:1.25rem}.right-3{right:.75rem}.left-56{left:14rem}.right-5{right:1.25rem}.m-auto{margin:auto}.m-3{margin:.75rem}.mx-auto{margin-left:auto;margin-right:auto}.mx-3{margin-left:.75rem;margin-right:.75rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mr-2{margin-right:.5rem}.ml-3{margin-left:.75rem}.mt-20{margin-top:5rem}.mb-60{margin-bottom:15rem}.mb-5{margin-bottom:1.25rem}.mb-8{margin-bottom:2rem}.mt-60{margin-top:15rem}.mb-32{margin-bottom:8rem}.mb-12{margin-bottom:3rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-20{margin-bottom:5rem}.mt-40{margin-top:10rem}.mb-6{margin-bottom:1.5rem}.mb-16{margin-bottom:4rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mb-7{margin-bottom:1.75rem}.mr-4{margin-right:1rem}.ml-4{margin-left:1rem}.mt-1{margin-top:.25rem}.mb-10{margin-bottom:2.5rem}.mt-11{margin-top:2.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-6{height:1.5rem}.h-16{height:4rem}.h-4{height:1rem}.h-screen{height:100vh}.w-full{width:100%}.w-6{width:1.5rem}.w-3\/5{width:60%}.w-2\/3{width:66.666667%}.w-16{width:4rem}.w-72{width:18rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-4\/5{width:80%}.flex-1{flex:1 1 0%}.transform{transform:var(--tw-transform)}.cursor-pointer{cursor:pointer}.select-none{user-select:none}.appearance-none{appearance:none}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-3{gap:.75rem}.gap-1{gap:.25rem}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.-space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(-.75rem*var(--tw-space-x-reverse));margin-left:calc(-.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-scroll{overflow-y:scroll}.rounded-md{border-radius:.375rem}.rounded-full{border-radius:9999px}.rounded-2xl{border-radius:30px}.border-2{border-width:2px}.border{border-width:1px}.border-white{--tw-border-opacity:1;border-color:rgba(255,255,255,var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgba(0,0,0,var(--tw-border-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}.bg-popup-red{--tw-bg-opacity:1;background-color:rgba(163,0,21,var(--tw-bg-opacity))}.bg-popup-green{--tw-bg-opacity:1;background-color:rgba(40,164,67,var(--tw-bg-opacity))}.bg-popup-blue{--tw-bg-opacity:1;background-color:rgba(22,163,223,var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-popup-yellow{--tw-bg-opacity:1;background-color:rgba(245,171,0,var(--tw-bg-opacity))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgba(20,20,20,var(--tw-bg-opacity))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgba(10,10,10,var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(0,0,0,var(--tw-bg-opacity))}.p-1{padding:.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.pt-3{padding-top:.75rem}.pb-3{padding-bottom:.75rem}.pb-10{padding-bottom:2.5rem}.pt-1{padding-top:.25rem}.pt-5{padding-top:1.25rem}.pb-5{padding-bottom:1.25rem}.text-center{text-align:center}.align-middle{vertical-align:middle}.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgba(0,0,0,var(--tw-text-opacity))}.outline-none{outline:2px solid transparent;outline-offset:2px}.blur{--tw-blur:blur(8px);filter:var(--tw-filter)}.transition-colors{transition-property:background-color,border-color,color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition{transition-property:background-color,border-color,color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}*,:after,:before{border:0 solid;user-select:none;-webkit-user-drag:none;-webkit-app-region:no-drag}h1{font-size:64px}h1,h2{font-weight:700;margin:2rem}h2{font-size:48px}h3{font-size:32px;font-weight:700;margin:2rem}h4{font-size:24px}h4,h5{margin:2rem;font-weight:400}h5{font-size:18px}.checked\:bg-white:checked{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.hover\:bg-transparent:hover{background-color:initial}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgba(10,10,10,var(--tw-text-opacity))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}@media (min-width:1400px){.lg\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}} \ No newline at end of file diff --git a/src/tailwind.scss b/src/tailwind.scss index a5cb6ab1..8dfefb70 100644 --- a/src/tailwind.scss +++ b/src/tailwind.scss @@ -8,6 +8,12 @@ border-width: 0; border-style: solid; border-color: theme("borderColor.default", currentColor); + user-select: none; + -webkit-user-drag: none; + -webkit-app-region: no-drag; +} + +@layer components { } @layer base { @@ -34,4 +40,39 @@ svg { @apply focus-visible:ring-popup-magenta focus-visible:ring-4 focus-visible:rounded-2xl; } + + h3 { + @apply focus-visible:ring-popup-magenta focus-visible:ring-4 focus-visible:rounded-2xl; + } +} + +/* headings */ +h1 { + font-size: 64px; + font-weight: bold; + margin: 2rem; +} + +h2 { + font-size: 48px; + font-weight: bold; + margin: 2rem; +} + +h3 { + font-size: 32px; + font-weight: bold; + margin: 2rem; +} + +h4 { + font-size: 24px; + margin: 2rem; + font-weight: normal; +} + +h5 { + font-size: 18px; + margin: 2rem; + font-weight: normal; }