From 678b54ac805d6757f1b3190b943311b2a2cf6afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannick=20F=C3=BCreder?= <64203572+YannickFuereder@users.noreply.github.com> Date: Sun, 11 Apr 2021 13:57:32 +0200 Subject: [PATCH] Reset --- .github/dependabot.yml | 6 + .github/workflows/main.yml | 87 +++ .gitignore | 2 + Assets/css/dialog.css | 143 +++++ Assets/css/profile.css | 446 ++++++++++++++ Assets/css/style.css | 258 ++++++++ Assets/js/app.js | 115 ++++ Assets/js/dialog.js | 118 ++++ Assets/js/profile.js | 1145 ++++++++++++++++++++++++++++++++++++ README.md | 76 +++ index.html | 312 ++++++++++ package-lock.json | 720 +++++++++++++++++++++++ package.json | 17 + profile/index.html | 359 +++++++++++ vite.config.js | 13 + 15 files changed, 3817 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 Assets/css/dialog.css create mode 100644 Assets/css/profile.css create mode 100644 Assets/css/style.css create mode 100644 Assets/js/app.js create mode 100644 Assets/js/dialog.js create mode 100644 Assets/js/profile.js create mode 100644 README.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 profile/index.html create mode 100644 vite.config.js diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..66fbc2e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/.github/workflows" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..8a90de2 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,87 @@ +name: Build & Deploy + +on: + push: + branches: [ main ] + +jobs: + build: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Npm Packages + uses: actions/setup-node@v4 + with: + node-version: 21 + + - run: npm i + + - run: npm run build + + - name: Zip + run: | + touch build.tar.gz + tar -zcf build.tar.gz -C ./dist . + + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: build + path: ./build.tar.gz + + deploy: + needs: build + runs-on: ubuntu-latest + env: + SSH_USER: github + SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + SSH_HOST: 194.36.146.51 + REPO_NAME: ${{ github.event.repository.name }} + steps: + - name: Download build + uses: actions/download-artifact@v4 + with: + name: build + + - name: Install ssh key + run: | + mkdir -p ~/.ssh/ + echo "$SSH_KEY" > ~/.ssh/staging.key + chmod 600 ~/.ssh/staging.key + ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts + + - name: Configure SSH + run: | + cat >>~/.ssh/config < .item { + background-color: #1e1e1e; + border-radius: .5em; + width: calc(100% - 2em); + padding: 1em; + margin: 1em 0; + box-shadow: 0 0 1em rgba(0, 0, 0, .2); +} + +.settings-container > .item > h1 { + margin: 0; + margin-bottom: 1em; + font-size: 1.5em; +} + +input { + width: calc(100% - 2em); + padding: 1em; + margin: .5em 0; + background-color: #393939; + border-radius: .2em; + color: #fff; + outline: none; + border: none; + outline: 2px solid transparent; + transition: all .2s ease-in-out; +} + +input:focus { + outline: 2px solid white; +} + +input:hover { + background-color: #4d4d4d; +} + +input[disabled] { + background-color: #222; + color: #4d4d4d; + cursor: pointer; +} + +input.invalid { + outline: 2px solid red; +} + +button { + border: 2px solid #4d4d4d; + background-color: transparent; + outline: none; + padding: .5em 8em; + border-radius: .2em; + color: #fff; + cursor: pointer; + transition: all .2s ease-in-out; +} + +button:hover { + background-color: #4d4d4d; +} + +button:active { + background-color: #393939; +} + +button[disabled] { + background-color: #222; + color: #4d4d4d; + cursor: pointer; +} + +select { + padding: 1em; + margin: .5em 0; + background-color: #393939; + border-radius: .2em; + color: #fff; + outline: none; + border: none; + outline: 2px solid transparent; + transition: all .2s ease-in-out; +} + +select:focus { + outline: 2px solid white; +} + +select:hover { + background-color: #4d4d4d; +} + +select[disabled] { + background-color: #222; + color: #4d4d4d; + cursor: pointer; +} + +.connected-accounts { + display: flex; + gap: 1em; + width: 100%; + flex-wrap: wrap; +} + +.connected-accounts .item { + display: flex; + align-items: center; + flex: calc(50% - 1em); + overflow: hidden; + border-radius: .5em; + border-collapse: collapse; + justify-content: space-between; + height: 2.5em; + box-shadow: 0 0 1em rgba(0, 0, 0, .2); + margin: 0; + font-size: 1.5em; +} + +.connected-accounts .item img { + height: 1em; + object-fit: contain; + padding: .5em; +} + +.connected-accounts h1 span:nth-child(1) { + height: 100%; + align-items: center; + display: flex; + opacity: .9; +} + +.connected-accounts h1 span:nth-child(2), .connected-accounts h1 span:nth-child(3) { + background-color: #222; + height: 100%; + align-items: center; + display: flex; + opacity: .9; + cursor: pointer; + transition: all .2s ease-in-out; +} + +.connected-accounts h1 span:nth-child(2):hover, .connected-accounts h1 span:nth-child(3):hover { + background-color: #333; +} + +.connected-accounts h1 span:nth-child(2) h1, .connected-accounts h1 span:nth-child(3) h1 { + background-color: transparent; + font-size: .7em; + padding: 0 .5em; + margin: 0; +} + +.connected-accounts h1:nth-child(1) { + background-color: #1dd05e; +} + +.connected-accounts h1:nth-child(2) { + background-color: #613f9f; +} + +.connected-accounts h1:nth-child(3) { + background-color: #7289da; +} + +.connected-accounts h1:nth-child(4) { + background-color: white; +} + +.connected-accounts h1:nth-child(5) { + background-color: black; +} + +.connected-accounts h1:nth-child(6) { + background-color: black; +} + +.connected-accounts .item > span:nth-child(3) { + display: none; +} + +.connected-accounts .item.connected > span:nth-child(3) { + display: flex; +} + +.connected-accounts .item.connected > span:nth-child(2) { + display: none; +} + +.flex { + display: flex; + align-items: center; + margin-top: .5em; + gap: .5em; +} + +.flex.end { + justify-content: flex-end; +} + +.input-group { + display: flex; + align-items: center; + gap: .5em; +} + +.searchbar { + width: 100%; + position: relative; +} + +.searchbar input { + width: 100%; +} + +.searchbar .autocom-box { + display: none; + position: absolute; + background-color: #393939; + margin-top: -.5em; + width: 100%; + max-height: 12em; + overflow: hidden; + overflow-y: auto; +} + +.searchbar .autocom-box.active { + display: block; +} + +.searchbar .autocom-box h1 { + cursor: pointer; + font-size: 1em; + padding: 1em; +} + +.searchbar .autocom-box h1:hover { + background-color: #4d4d4d; +} + +.dangerZone { + outline: 2px solid #510c0c; + background-color: #261b1b !important; +} + +td, th { + border: none; + padding: 8px; +} + +tr:nth-child(even){background-color: #222;} + +tr:hover {background-color: 222;} + +th { + padding-top: 12px; + padding-bottom: 12px; + text-align: left; + background-color: #111; + color: white; +} + +.apiKeysTable { + width: 100%; + table-layout: fixed; + border-collapse: collapse; + border-radius: .5em; + overflow: hidden; +} + +.apiKeysTable td:nth-child(2) { + display: flex; + gap: 1em; +} + +.apiKeysTable td:nth-child(2) button { + padding: .8em; +} + +.apiKeysTable td { + overflow: hidden; + text-overflow: ellipsis; +} + +.collapsible-content { + padding: 0 18px; + max-height: 0; + overflow: hidden; + transition: max-height 0.2s ease-out; + overflow-y: auto; +} + +.collapsible-content > div:nth-child(1) { + padding-top: 18px; +} + +.ssoClients { + display: flex; + width: 100%; + gap: 1em; + flex-wrap: wrap; +} + +.ssoClients > div { + background-color: #111; + padding: 1em; + border-radius: .5em; + width: 100%; + cursor: pointer; +} + +.ssoClients > div > .collapsible { + display: flex; + align-items: center; + gap: 1em; +} + +.ssoClients > div > .collapsible h1 { + font-size: 1.5em; +} + +.ssoClients > div > .collapsible img { + height: 4em; + object-fit: contain; +} + +.ssoClients > div > .collapsible a { + margin-right: 0; + margin-left: auto; +} + +.ssoClients a { + color: #fff; + text-decoration: none; +} + +.ssoRedirects > div{ + display: flex; + align-items: center; + gap: 1em; +} + +.thirdPartyApps-container { + display: flex; + width: 100%; + gap: 1em; + flex-wrap: wrap; +} + +.thirdPartyApps-container > div { + background-color: #111; + padding: 1em; + border-radius: .5em; + width: 100%; + display: flex; + align-items: center; + gap: 1em; +} + +.thirdPartyApps-container > div > img { + border-radius: 1em; + height: 4em; + object-fit: contain; +} + +.thirdPartyApps-container > div > button { + margin-left: auto; +} + +.thirdPartyApps-container > div > h1 { + font-size: 1.5em; + margin: 0; +} + +button.success { + background-color: #1dd05e; + border: 2px solid #1dd05e; +} + +button.error { + background-color: #d01d1d; + border: 2px solid #d01d1d; +} + +a { + color: #fff; + text-decoration: none; +} diff --git a/Assets/css/style.css b/Assets/css/style.css new file mode 100644 index 0000000..b37dee6 --- /dev/null +++ b/Assets/css/style.css @@ -0,0 +1,258 @@ +body { + background-color: black; + font-family: 'Roboto Mono', monospace; + color: white; + margin: 0; +} + +.stroke { + fill: none; + stroke-width: 3px; + stroke: white; +} + +h1 { + margin: 0; +} + +.st0 { + fill: none; +} + +.st1 { + fill: url(#SVGID_1_); +} + +nav { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1em; + z-index: 10; +} + +a { + color: white; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.logo { + width: 60vw; + margin-left: auto; + margin-right: auto; + position: relative; + margin-top: 25vh; +} + +footer { + padding: 1em 12vw; + padding-top: 3em; + border-top: #666 solid 1px; +} + +.logo-background { + position: absolute; + filter: blur(15vw); + z-index: -1; +} + +.logo-container { + background-repeat: no-repeat; + background-color: #ff000000; + background-image: radial-gradient(at 2% 0%, hsla(1, 0%, 34%, 1) 0px, black 50%); + height: 90vh; + width: 100%; + position: absolute; + top: 0; + z-index: -1; +} + +.container { + position: relative; + padding: 0 15vw; +} + +.feature-container { + display: flex; + justify-content: space-between; + gap: 1em; + flex-wrap: wrap; + width: 100%; +} + +.feature-container .item { + background-color: #222222b0; + border-radius: 15px; + border: 1px solid #666; + padding: 1em; + width: calc(33% - 2em - 1em); + box-shadow: 0 0 10px #060606; + backdrop-filter: blur(16px); +} + +.service-container { + display: flex; + padding: 1em; + gap: 1em; + flex-wrap: wrap; + justify-content: center; +} + +.service-container .item { + width: calc(33% - 2em - 1em); + border: 1px solid #444; + border-radius: 1em; + padding: 1em; + box-shadow: 0 0 10px #060606; + position: relative; + min-height: 10em; + background: linear-gradient(180deg, #242424, #121212 65.62%); + overflow: hidden; +} + +/* .service-container .item::before { + content: ""; + position: absolute; + inset: 0; + padding: 1px; + mask: linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + width: 100%; + height: 100%; + --accents-2: #44444400; + border-radius: inherit; + background: conic-gradient(from 100deg at 50% 50%,var(--accents-2) 0deg,var(--accents-2) 176deg,#fff 193deg,var(--accents-2) 217deg,var(--accents-2) 1turn); + } */ + +.service-container .item .footer { + font-size: .8em; + display: flex; + gap: .5em; + align-items: center; + position: absolute; + bottom: 1em; +} + +.service-container .item .footer hr { + border: 0; + background: #ffffff; + height: 19px; + width: 1px; + margin: 0; +} + +.service-container .item:nth-child(1) { + background: linear-gradient(180deg, #ffad1e69, #ffa2008a 65.62%); +} + +.service-container .item:nth-child(2) { + background: linear-gradient(180deg, #63a0d969, #63a0d98a 65.62%); +} + +.service-container .item:nth-child(5) { + background: linear-gradient(180deg, #c5120b69, #c5120b8a 65.62%); +} + +.service-container .item:nth-child(6) { + background: linear-gradient(180deg, #6600ff69, #6600ff8a 65.62%); +} + +footer hr { + border: 0; + background: gray; + height: 19px; + width: 1px; + margin: 0; +} + +footer>div { + display: flex; + width: 100%; + gap: 1em; +} + +footer>div:nth-child(1) { + flex-wrap: wrap; +} + +@media (max-width: 600px) { + footer>div:nth-child(2) { + justify-content: center; + } + + footer>div:nth-child(1)>div { + width: 100%; + } +} + +@media (max-width: 1100px) { + + .feature-container .item, + .service-container .item { + width: 100%; + } +} + +.service-outer { + padding-top: 6em; + padding-bottom: 5em; +} + +.divider { + margin-top: 65vh; + margin-bottom: -36em; +} + +.divider svg { + margin-bottom: -.5em; +} + +.divider>div { + background: #222; + background: linear-gradient(180deg, #222 34%, rgba(66, 53, 53, 0) 100%); + height: 20em; +} + +.pfb { + width: 2.5em; + height: 2.5em; + border-radius: 50%; + background-color: #333; + animation: skeleton 2s infinite; + overflow: hidden; + border: 1px solid #666; +} + +.name.loading { + width: 10em; + height: 1em; + border-radius: 0.5em; + background-color: #333; + animation: skeleton 2s infinite; +} + +.profile { + display: flex; + align-items: center; + justify-content: end; + gap: 1em; +} + +.profile img { + object-fit: cover; +} + +@keyframes skeleton { + 50% { + background-color: #555; + } +} + +.d-none { + display: none; +} \ No newline at end of file diff --git a/Assets/js/app.js b/Assets/js/app.js new file mode 100644 index 0000000..bd9abe2 --- /dev/null +++ b/Assets/js/app.js @@ -0,0 +1,115 @@ +import anime from 'animejs'; + +LoginManager.isLoggedIn().then(async (e) => { + if (e) { + document.getElementById('loginLoading').style.display = 'none'; + document.getElementById('profile').classList.remove('d-none'); + + document.getElementById('username').innerText = LoginManager.getUsername(); + document.getElementById('avatar').src = LoginManager.getAvatar(); + } else { + document.getElementById('loginLoading').style.display = 'none'; + document.getElementById('loginLink').classList.remove('d-none'); + } +}); + +var lineDrawing = anime({ + targets: '.logo path', + strokeDashoffset: [anime.setDashoffset, 0], + easing: 'easeInOutCubic', + duration: 1500, + begin: function (anim) { + document.querySelector('path').setAttribute('stroke', 'black'); + document.querySelector('path').setAttribute('fill', 'none'); + }, + complete: function (anim) { + document.querySelector('path').setAttribute('fill', 'yellow'); + }, + autoplay: true, +}); + +var border = anime({ + targets: '.stroke', + duration: 1500, + delay: 1000, + strokeWidth: ['3px', '0'], + direction: 'forward', + easing: 'easeInOutQuad', +}); + +var white = anime({ + targets: '.st0', + duration: 1500, + delay: 800, + fill: ['#F2F2F2'], + direction: 'forward', + easing: 'easeInOutQuad', +}); + +var startColor = anime({ + targets: '#startColor', + duration: 1500, + delay: 800, + stopColor: ['none', '#fd1414'], + direction: 'forward', + easing: 'easeInOutQuad', +}); + +var endColor = anime({ + targets: '#endColor', + duration: 1500, + delay: 800, + stopColor: ['none', '#7c0d00'], + direction: 'forward', + easing: 'easeInOutQuad', +}); + +setColors(); +setInterval(setColors, 5000 * (4 - 1)); + +function setColors() { + setTimeout(function () { + changeColor('#e29c25', '#db7220'); + + setTimeout(function () { + changeColor('#63a0d9', '#394f6b'); + + setTimeout(function () { + changeColor('#444', '#111'); + + setTimeout(function () { + changeColor('#fd1414', '#7c0d00'); + }, 5000); + }, 5000); + }, 5000); + }, 5000); +} + +function changeColor(startColor, endColor) { + var white = anime({ + targets: '.st0', + duration: 1500, + delay: 1000, + fill: ['#F2F2F2'], + direction: 'forward', + easing: 'easeInOutQuad', + }); + + var startColor = anime({ + targets: '#startColor', + duration: 1500, + delay: 1000, + stopColor: [startColor], + direction: 'forward', + easing: 'easeInOutQuad', + }); + + var endColor = anime({ + targets: '#endColor', + duration: 1500, + delay: 1000, + stopColor: [endColor], + direction: 'forward', + easing: 'easeInOutQuad', + }); +} diff --git a/Assets/js/dialog.js b/Assets/js/dialog.js new file mode 100644 index 0000000..224d9f5 --- /dev/null +++ b/Assets/js/dialog.js @@ -0,0 +1,118 @@ +export function createDialog(title = 'Information', text = 'No information available', type = 'info') { + const container = document.getElementById('dialog-container'); + + if (container) { + const dialog = document.createElement('div'); + dialog.classList.add('dialog'); + dialog.classList.add(type); + + const dialogTitle = document.createElement('h2'); + dialogTitle.classList.add('dialog-title'); + dialogTitle.textContent = title; + + const dialogText = document.createElement('p'); + dialogText.classList.add('dialog-content'); + dialogText.textContent = text; + + const dialogButton = document.createElement('button'); + dialogButton.classList.add('dialog-button'); + dialogButton.textContent = 'OK'; + + dialog.appendChild(dialogTitle); + dialog.appendChild(dialogText); + dialog.appendChild(dialogButton); + + dialogButton.addEventListener('click', () => { + if (container.children.length === 1) container.classList.remove('visible'); + + dialog.remove(); + }); + + container.appendChild(dialog); + container.classList.add('visible'); + } else console.error('Dialog container not found'); +} + +export function initDialog(element = undefined) { + element = element || window.document; + + Array.from(element.querySelectorAll('[target-dialog]')).forEach((button) => { + const target = button.getAttribute('target'); + const dialog = document.getElementById(target); + const submitButton = dialog.querySelector('[type="submit"]'); + + dialog.hide = () => { + dialog.classList.remove('visible'); + document.getElementById('dialog-container').classList.remove('visible'); + dialog.dispatchEvent(new CustomEvent('onHide', { + detail: { + reason: 'submit' + } + })); + }; + + dialog.show = () => { + dialog.classList.add('visible'); + document.getElementById('dialog-container').classList.add('visible'); + dialog.dispatchEvent(new CustomEvent('onShow')); + }; + + if (dialog === undefined) { + console.error('Dialog not found'); + return; + } + + const onShowEvent = dialog.getAttribute('onShow'); + if (onShowEvent !== undefined) dialog.addEventListener('onShow', () => eval(onShowEvent)); + + submitButton.addEventListener('click', (event) => { + dialog.hide(); + }); + + button.addEventListener('click', () => { + dialog.classList.add('visible'); + document.getElementById('dialog-container').classList.add('visible'); + + dialog.dispatchEvent(new CustomEvent('onShow')); + + setTimeout(() => { + window.addEventListener( + 'click', + function _listener(e) { + if (e.target.closest('#' + dialog.id) === null) { + dialog.classList.remove('visible'); + dialog.dispatchEvent(new CustomEvent('onHide')); + window.removeEventListener('click', _listener, true); + } + }, + true + ); + }, 100); + }); + }); + + if (document.getElementById('dialog-container') !== null) return; + + const container = document.createElement('div'); + container.id = 'dialog-container'; + container.classList.add('dialog-container'); + + //add event listener to window + container.addEventListener('click', (event) => { + if (event.target === container) { + container.classList.remove('visible'); + + Array.from(container.querySelectorAll('.dialog')).forEach((dialog) => { + dialog.classList.remove('visible'); + dialog.dispatchEvent(new CustomEvent('onHide', { detail: { reason: 'canceled' }})); + }); + + Array.from(document.querySelectorAll('.custom-dialog.visible')).forEach((dialog) => { + dialog.classList.remove('visible'); + dialog.dispatchEvent(new CustomEvent('onHide', { detail: { reason: 'canceled' }})); + }); + } + }); + + document.body.appendChild(container); +} diff --git a/Assets/js/profile.js b/Assets/js/profile.js new file mode 100644 index 0000000..045a0fe --- /dev/null +++ b/Assets/js/profile.js @@ -0,0 +1,1145 @@ +import { createDialog, initDialog } from './dialog'; + +const languages = [ + { key: 'en-us', name: 'English' }, + { key: 'de-at', name: 'German' }, + { key: 'fr-fr', name: 'French' }, + { key: 'it-it', name: 'Italian' }, + { key: 'es-es', name: 'Spanish' }, + { key: 'pl-pl', name: 'Polish' }, + { key: 'nl-nl', name: 'Dutch' }, + { key: 'pt-pt', name: 'Portuguese' }, + { key: 'ru-ru', name: 'Russian' }, + { key: 'tr-tr', name: 'Turkish' }, + { key: 'zh-cn', name: 'Chinese' }, + { key: 'ja-jp', name: 'Japanese' }, + { key: 'ko-kr', name: 'Korean' }, + { key: 'ar-sa', name: 'Arabic' }, + { key: 'hi-in', name: 'Hindi' }, + { key: 'no-no', name: 'Norwegian' }, + { key: 'sv-se', name: 'Swedish' }, + { key: 'fi-fi', name: 'Finnish' }, + { key: 'da-dk', name: 'Danish' }, + { key: 'cs-cz', name: 'Czech' }, + { key: 'hu-hu', name: 'Hungarian' }, + { key: 'el-gr', name: 'Greek' }, + { key: 'th-th', name: 'Thai' }, + { key: 'id-id', name: 'Indonesian' }, + { key: 'ro-ro', name: 'Romanian' }, + { key: 'sk-sk', name: 'Slovak' }, + { key: 'uk-ua', name: 'Ukrainian' }, + { key: 'bg-bg', name: 'Bulgarian' }, + { key: 'hr-hr', name: 'Croatian' }, + { key: 'ca-es', name: 'Catalan' }, + { key: 'et-ee', name: 'Estonian' }, + { key: 'fa-ir', name: 'Persian' }, + { key: 'he-il', name: 'Hebrew' }, + { key: 'is-is', name: 'Icelandic' }, + { key: 'lt-lt', name: 'Lithuanian' }, + { key: 'lv-lv', name: 'Latvian' }, + { key: 'sr-rs', name: 'Serbian' }, + { key: 'sl-si', name: 'Slovenian' }, + { key: 'vi-vn', name: 'Vietnamese' }, +]; + +const countries = [ + { key: 'at', name: 'Austria' }, + { key: 'de', name: 'Germany' }, + { key: 'ch', name: 'Switzerland' }, + { key: 'us', name: 'United States' }, + { key: 'gb', name: 'United Kingdom' }, + { key: 'fr', name: 'France' }, + { key: 'it', name: 'Italy' }, + { key: 'es', name: 'Spain' }, + { key: 'pl', name: 'Poland' }, + { key: 'nl', name: 'Netherlands' }, + { key: 'pt', name: 'Portugal' }, + { key: 'ru', name: 'Russia' }, + { key: 'tr', name: 'Turkey' }, + { key: 'cn', name: 'China' }, + { key: 'jp', name: 'Japan' }, + { key: 'kr', name: 'Korea' }, + { key: 'ar', name: 'Argentina' }, + { key: 'au', name: 'Australia' }, + { key: 'be', name: 'Belgium' }, +]; +var currentUser; + +initDialog(); +document.getElementById('pi_save').addEventListener('click', savePersonalInformation); +document.getElementById('cp_save').addEventListener('click', changePassword); +document.getElementById('createApiKey').addEventListener('click', createApiKey); +document.getElementById('ca_spotify_link').addEventListener('click', () => LinkAccounts('spotify')); +document.getElementById('ca_twitch_link').addEventListener('click', () => LinkAccounts('twitch')); +document.getElementById('ca_discord_link').addEventListener('click', () => LinkAccounts('discord')); +document.getElementById('ca_google_link').addEventListener('click', () => LinkAccounts('google')); +document.getElementById('ca_github_link').addEventListener('click', () => LinkAccounts('github')); +document.getElementById('logout').addEventListener('click', () => LoginManager.logout()); +document.getElementById('deleteAccount').addEventListener('click', async (e) => await doubleClickButton(e, deleteAccount)); +document.getElementById('createSsoCredentials').addEventListener('click', async () => await createSsoCredentials()); + +LoginManager.isLoggedIn().then(async (e) => { + if (!e && import.meta.env.MODE !== 'development') { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const token = LoginManager.getCookie('token'); + + const urlParams = new URLSearchParams(window.location.search); + + if (urlParams.has('code')) { + const code = urlParams.get('code'); + + LinkAccount(code); + } + + if (import.meta.env.MODE === 'development') { + const user = { + username: 'TestUser', + firstname: 'Test', + lastname: 'User', + email: 'amogus@example.com', + country: 'at', + preferredLang: 'de-at', + discordId: null, + spotifyId: null, + twitchId: null, + githubId: null, + googleId: null, + '2fa': false, + '2faType': 'App', + api_keys: [ + "1234567890" + ], + trusted_sso_clients: [ + { + id: '1234567890', + name: 'TestClient', + logo: 'https://via.placeholder.com/150', + }, + ], + sso_clients: [ + { + id: '1234567890', + name: 'TestClient', + logo: 'https://via.placeholder.com/150', + url: 'https://example.com', + secret: '1234567890', + redirects: [ + { + id: '1234567890', + url: 'https://example.com', + }, + ], + }, + ], + }; + + currentUser = user; + } else { + const req = await fetch('https://api.login.netdb.at/user', { + method: 'GET', + headers: { + Authorization: 'Bearer ' + token, + }, + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + return; + } + + const user = res.data; + currentUser = user; + } + + document.getElementById('username').value = currentUser.username; + document.getElementById('firstname').value = currentUser.firstname; + document.getElementById('lastname').value = currentUser.lastname; + document.getElementById('pi_email').value = currentUser.email; + document.getElementById('cp_email').value = currentUser.email; + document.getElementById('country').dataset.key = currentUser.country; + document.getElementById('preferredlang').dataset.key = currentUser.preferredLang; + + if (currentUser.discordId !== null) document.getElementById('ca_discord').classList.add('connected'); + + if (currentUser.spotifyId !== null) document.getElementById('ca_spotify').classList.add('connected'); + + if (currentUser.twitchId !== null) document.getElementById('ca_twitch').classList.add('connected'); + + if (currentUser.githubId !== null) document.getElementById('ca_github').classList.add('connected'); + + if (currentUser.googleId !== null) document.getElementById('ca_google').classList.add('connected'); + + Array.from(document.getElementsByClassName('connected')).forEach((element) => { + element.addEventListener('click', disconnectAccount); + }); + + if (currentUser['2fa'] && currentUser['2faType'] == 'App') document.getElementById('cp_2fa').classList.remove('d-none'); + + if (currentUser['2fa']) { + document.getElementById('2fa_status').innerText = 'Enabled'; + document.getElementById('2fa_type').value = currentUser['2faType'] == 'App' ? 0 : currentUser['2faType'] == 'Mail' ? 1 : 2; + document.getElementById('2fa_type').disabled = true; + document.getElementById('2fa_enable').innerText = 'Disable'; + document.getElementById('2fa_enable').addEventListener('click', disable2fa); + } else { + document.getElementById('2fa_enable').addEventListener('click', enable2fa); + } + + initSearchbar(countries, 'country_search'); + initSearchbar(languages, 'language_search'); + + currentUser.api_keys.forEach((key) => { + document.getElementById('apiKeysTable').appendChild(createApiKeyRow(key, '*************')); + }); + + if (currentUser.trusted_sso_clients.length > 0) document.getElementById('thirdPartyAppsContainer').innerHTML = ''; + + currentUser.trusted_sso_clients.forEach((client) => { + const row = document.createElement('div'); + row.id = 'trusted_' + client.id; + const img = document.createElement('img'); + img.src = client.logo; + img.alt = client.name; + img.title = client.name; + row.appendChild(img); + const name = document.createElement('h1'); + name.innerText = client.name; + row.appendChild(name); + const deleteBtn = document.createElement('button'); + deleteBtn.innerText = 'Delete'; + deleteBtn.addEventListener('click', async () => await deleteTrustedSsoClient(client.id)); + row.appendChild(deleteBtn); + document.getElementById('thirdPartyAppsContainer').appendChild(row); + }); + + if (currentUser.sso_clients.length > 0) document.getElementById('ssoClientsContainer').innerHTML = ''; + + currentUser.sso_clients.forEach((client) => { + document.getElementById('ssoClientsContainer').appendChild(createSSOClient(client.logo, client.name, client.url, client.id, client.secret, client.redirects)); + }); + + Array.from(document.getElementsByClassName('collapsible')).forEach((element) => { + element.addEventListener('click', function () { + this.classList.toggle('active'); + + if (this.nextElementSibling.style.maxHeight) this.nextElementSibling.style.maxHeight = null; + else this.nextElementSibling.style.maxHeight = this.nextElementSibling.scrollHeight + 'px'; + }); + }); + + Array.from(document.getElementsByTagName('input')).forEach((element) => { + element.addEventListener('keyup', (e) => e.target.classList.remove('invalid')); + }); +}); + +async function LinkAccount(code) { + const provider = localStorage.getItem('linkType'); + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/link/' + provider + '?code=' + code, { + method: 'GET', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + createDialog('Error', 'An error occured while linking your account!', 'error'); + return; + } + + localStorage.removeItem('linkType'); + const urlParams = new URLSearchParams(window.location.search); + urlParams.delete('code'); + + window.history.replaceState({}, document.title, window.location.pathname + window.location.search); + + createDialog('Success', 'Account successfully linked!', 'info'); +} + +async function deleteTrustedSsoClient(clientId) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/oauth/untrust', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: '"' + clientId + '"', + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + return; + } + + document.getElementById('trusted_' + clientId).remove(); + + const container = document.getElementById('thirdPartyAppsContainer'); + container.innerHTML = '

No third party apps connected!

'; +} + +async function createSsoCredentials() { + document + .getElementById('createSSODialog') + .querySelectorAll('input') + .forEach((element) => { + element.value = ''; + }); + + document.getElementById('createSSODialog').show(); + + const canceled = await new Promise((resolve) => { + document.getElementById('createSSODialog').addEventListener( + 'onHide', + (e) => { + if (e.detail.reason == 'canceled') { + resolve(false); + return; + } + + resolve(true); + }, + { once: true } + ); + }); + + if (!canceled) return; + + const name = document.getElementById('c_sso_name').value; + const websiteUrl = document.getElementById('c_sso_url').value; + const logoUrl = document.getElementById('c_sso_logoUrl').value; + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/sso', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: name, + url: websiteUrl, + logoUrl: logoUrl, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (req.status != 200 || res.statusCode != 203) { + createDialog('Error', 'An error occured while creating the SSO credentials!', 'error'); + return; + } + + if (document.getElementById('ssoClientsContainer').children[0].tagName == 'P') document.getElementById('ssoClientsContainer').innerHTML = ''; + + const item = createSSOClient(logoUrl, name, websiteUrl, res.data.clientId, res.data.clientSecret, []); + document.getElementById('ssoClientsContainer').appendChild(item); + + item.children[0].addEventListener('click', function () { + item.classList.toggle('active'); + + if (item.children[0].nextElementSibling.style.maxHeight) item.children[0].nextElementSibling.style.maxHeight = null; + else item.children[0].nextElementSibling.style.maxHeight = item.children[0].nextElementSibling.scrollHeight + 'px'; + }); +} + +function createSSOClient(logoUrl, clientName, websiteUrl, clientId, clientSecret, redirects) { + const template = document.importNode(document.getElementById('ssoCredentials').getElementsByTagName('template')[0].content, true); + const item = template.querySelector('div'); + + item.id = 'sso_' + clientId; + item.querySelector('img').src = logoUrl; + item.querySelector('img').alt = clientName; + item.querySelector('img').title = clientName; + item.querySelector('h1').innerText = clientName; + item.querySelector('a').href = websiteUrl; + item.querySelector('a').innerText = websiteUrl; + + const content = item.querySelector('.collapsible-content'); + content.querySelector('#sso_clientId').value = clientId; + content.querySelector('#sso_clientSecret').value = clientSecret; + content.querySelector('#sso_logoUrl').value = logoUrl; + content.querySelector('#sso_websiteUrl').value = websiteUrl; + content.querySelector('#sso_name').value = clientName; + content.querySelector('#sso_save').addEventListener('click', async () => await saveSSOClient(clientId)); + item.querySelector('#sso_delete').addEventListener('click', async () => await deleteSSOClient(clientId)); + content.querySelector('#sso_addRedirect').addEventListener('click', async () => await addSSORedirect(clientId)); + + const redirectsContainer = content.querySelector('#sso_redirects'); + + redirects.forEach((redirect) => { + const redirectItem = document.createElement('div'); + redirectItem.id = 'sso_redirect_' + redirect.id; + + const url = document.createElement('input'); + url.type = 'text'; + url.value = redirect.url; + url.disabled = true; + redirectItem.appendChild(url); + + const deleteBtn = document.createElement('button'); + deleteBtn.innerText = 'Delete'; + deleteBtn.addEventListener('click', async () => await deleteSSORedirect(clientId, redirect.id)); + redirectItem.appendChild(deleteBtn); + + redirectsContainer.appendChild(redirectItem); + }); + + return item; +} + +async function deleteSSOClient(clientId) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/sso', { + method: 'DELETE', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: '"' + clientId + '"', + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (req.status != 200 || res.statusCode != 200) { + createDialog('Error', 'An error occured while deleting the SSO credentials!', 'error'); + return; + } + + document.getElementById('sso_' + clientId).remove(); +} + +async function deleteSSORedirect(clientId, redirectId) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/sso/redirects', { + method: 'DELETE', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + clientId: clientId, + redirectId: redirectId, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + return; + } + + document.getElementById('sso_redirect_' + redirectId).remove(); +} + +async function addSSORedirect(clientId) { + const item = document.getElementById('sso_' + clientId); + const redirects = item.querySelector('#sso_redirects'); + + //TODO: add button feedback + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/sso/redirects', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + clientId: clientId, + url: item.querySelector('#sso_addRedirect').previousElementSibling.value, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 203) { + console.log(res); + return; + } + + item.querySelector('#sso_addRedirect').previousElementSibling.value = ''; + + const redirect = document.createElement('div'); + redirect.id = 'sso_redirect_' + res.data.id; + const url = document.createElement('input'); + url.type = 'text'; + url.value = res.data.url; + url.disabled = true; + redirect.appendChild(url); + const deleteBtn = document.createElement('button'); + deleteBtn.innerText = 'Delete'; + deleteBtn.addEventListener('click', async () => await deleteSSORedirect(clientId, res.data.id)); + redirect.appendChild(deleteBtn); + + if (redirects.children.length > 1) { + redirects.insertBefore(redirect, redirects.children[1]); + return; + } + + redirects.append(redirect); +} + +async function saveSSOClient(clientId) { + const item = document.getElementById('sso_' + clientId); + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/sso', { + method: 'PUT', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + clientId: clientId, + logoUrl: item.querySelector('#sso_logoUrl').value, + url: item.querySelector('#sso_websiteUrl').value, + name: item.querySelector('#sso_name').value, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + createDialog('Error', 'An error occured while saving the SSO credentials!', 'error'); + return; + } + + displayButtonFeedback(item.querySelector('#sso_save'), 'success'); +} + +async function doubleClickButton(e, func) { + if (e.target.dataset.clicked == 'true') { + await func(); + return; + } + + e.target.dataset.clicked = 'true'; + e.target.innerText = 'Confirm'; + + setTimeout(() => { + e.target.dataset.clicked = 'false'; + e.target.innerText = 'Delete Account'; + }, 3000); +} + +function createApiKeyRow(client_id) { + const row = document.createElement('tr'); + row.id = client_id; + row.innerHTML = '' + client_id + ''; + const td = document.createElement('td'); + const deleteBtn = document.createElement('button'); + const regenBtn = document.createElement('button'); + deleteBtn.innerText = 'Delete'; + regenBtn.innerText = 'Regenerate'; + + deleteBtn.addEventListener('click', () => deleteApiKey(client_id)); + regenBtn.addEventListener('click', () => regenerateApiKey(client_id)); + + td.appendChild(regenBtn); + td.appendChild(deleteBtn); + row.appendChild(td); + + return row; +} + +async function createApiKey() { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/apikey', { + method: 'GET', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 203) { + console.log(res); + createDialog('Error', 'An error occured while creating the API key!', 'error'); + return; + } + + document.getElementById('apiKeysTable').appendChild(createApiKeyRow(res.data.clientId)); + + createDialog('Success', 'Successfully created API key! Please save it now, as it will not be shown again! ' + res.data.clientSecret, 'info'); +} + +async function regenerateApiKey(clientId) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/apikey', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: '"' + clientId + '"', + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + createDialog('Error', 'An error occured while regenerating the API key!', 'error'); + return; + } + + createDialog('Success', 'Successfully regenerated API key! Please save it now, as it will not be shown again! ' + res.data.clientSecret, 'info'); +} + +async function deleteApiKey(clientId) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user/apikey', { + method: 'DELETE', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: '"' + clientId + '"', + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const data = await req.json(); + + if (data.statusCode != 200) { + console.log(data); + createDialog('Error', 'An error occured while deleting the API key!', 'error'); + return; + } + + document.getElementById(clientId).remove(); +} + +function LinkAccounts(type) { + localStorage.setItem('linkType', type); + + switch (type) { + case 'spotify': { + window.location.href = 'https://accounts.spotify.com/de/authorize?client_id=a7c2014c0531405983d7050277dee3cb&response_type=code&redirect_uri=https://netdb.at/profile&scope=user-read-private%20user-read-email'; + break; + } + case 'discord': { + window.location.href = 'https://discord.com/api/oauth2/authorize?client_id=802237562625196084&redirect_uri=https://netdb.at/profile&response_type=code&scope=identify%20email'; + break; + } + case 'twitch': { + window.location.href = 'https://id.twitch.tv/oauth2/authorize?client_id=okxhfdyyoyx724c5zf0h869x9ry1sx&redirect_uri=https://netdb.at/profile&response_type=code&scope=user_read'; + break; + } + case 'github': { + window.location.href = 'https://github.com/login/oauth/authorize?scope=user:email&client_id=de5e22518d66ab50a805'; + break; + } + case 'google': { + window.location.href = + 'https://accounts.google.com/o/oauth2/v2/auth?scope=https%3A//www.googleapis.com/auth/userinfo.email&access_type=offline&include_granted_scopes=true&response_type=code&state=state_parameter_passthrough_value&redirect_uri=https://netdb.at/profile&client_id=736018590984-nh2ifch6ps8art9v35avipv16se1b720.apps.googleusercontent.com'; + break; + } + } +} + +function initSearchbar(data, id) { + let searchbar = document.getElementById(id); + let inputBox = searchbar.querySelector('input'); + + try { + if (inputBox.dataset.key && inputBox.dataset.key != 'null') inputBox.value = data.find((e) => e.key == inputBox.dataset.key).name; + } catch (e) { + console.log(e); + } + + showSuggestions(data, searchbar); + + inputBox.addEventListener('keyup', (e) => filterSearch(e.target.value, data, searchbar)); + inputBox.addEventListener('focus', () => searchbar.querySelector('.autocom-box').classList.add('active')); + searchbar.addEventListener('focusout', (e) => { + setTimeout(() => { + searchbar.querySelector('.autocom-box').classList.remove('active'); + }, 300); + }); + + inputBox.onkeydown = (e) => { + if (e.key == 'Enter') { + searchbar.querySelector('input').value = searchbar.querySelector('.autocom-box h1').innerText; + searchbar.querySelector('input').dataset.key = searchbar.querySelector('.autocom-box h1').dataset.key; + } + }; +} + +function filterSearch(userData, data, searchbar) { + const suggestions = []; + + for (var i = 0; i < data.length; i++) suggestions.push(data[i]); + + let emptyArray = suggestions.filter((data) => data.name.toLocaleLowerCase().startsWith(userData.toLocaleLowerCase())); + + emptyArray.sort((a, b) => { + if (a.name < b.name) return -1; + + if (a.name > b.name) return 1; + + return 0; + }); + + showSuggestions(emptyArray, searchbar); +} + +function showSuggestions(list, searchbar) { + list = list.map((data) => { + return (data = '

' + data.name + '

'); + }); + + let listData = ''; + if (!list.length) { + // const userValue = searchbar.querySelector('input').value; + // listData = '

' + userValue + '

'; + } else { + listData = list.join(''); + } + + searchbar.querySelector('.autocom-box').innerHTML = listData; + + const allList = searchbar.querySelector('.autocom-box').querySelectorAll('h1'); + for (let i = 0; i < allList.length; i++) + allList[i].addEventListener('click', (e) => { + searchbar.querySelector('input').value = e.target.innerText; + searchbar.querySelector('input').dataset.key = e.target.dataset.key; + }); +} + +async function savePersonalInformation() { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user', { + method: 'PUT', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + firstname: document.getElementById('firstname').value, + lastname: document.getElementById('lastname').value, + country: document.getElementById('country').dataset.key, + preferredLang: document.getElementById('preferredlang').dataset.key, + username: document.getElementById('username').value, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + displayButtonFeedback(document.getElementById('pi_save'), 'error'); + return; + } + + displayButtonFeedback(document.getElementById('pi_save'), 'success'); +} + +function displayButtonFeedback(btn, type) { + btn.classList.add(type); + + setTimeout(() => { + btn.classList.remove(type); + }, 1500); +} + +async function changePassword() { + const pw = document.getElementById('newPassword1').value; + const rpw = document.getElementById('newPassword2').value; + const oldPw = document.getElementById('oldPassword').value; + + const error = validatePw(oldPw, pw, rpw); + + if (error) { + document.getElementById('newPassword1').value = ''; + document.getElementById('newPassword2').value = ''; + + document.getElementById('newPassword1').classList.add('invalid'); + document.getElementById('newPassword2').classList.add('invalid'); + + createDialog('Error', error, 'error'); + return; + } + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/resetpassword', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + OldPassword: oldPw, + Email: document.getElementById('cp_email').value, + Password: pw, + TwoFaToken: currentUser['2fa'] ? document.getElementById('cp_2fa').value : null, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + console.log(res); + return; + } + + LoginManager.deleteCookie('token'); + LoginManager.deleteCookie('refreshToken'); + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); +} + +function validatePw(oldPw, pw, rpw) { + if (pw == null || rpw == null) return 'Please fill out all fields'; + + if (pw.length < 8) return 'The password has to be at least 8 characters long'; + + if (!isUpperCase(pw)) return 'The password has to contain at least one uppercase letter'; + + if (!isLowerCase(pw)) return 'The password has to contain at least one lowercase letter'; + + if (!isNumber(pw)) return 'The password has to contain at least one number'; + + if (pw != rpw) return 'Passwords do not match'; + + if (pw == oldPw) return 'The new password cannot be the same as the old one'; +} + +function isUpperCase(str) { + return /[A-Z]/.test(str); +} + +function isLowerCase(str) { + return /[a-z]/.test(str); +} + +function isNumber(str) { + return /[0-9]/.test(str); +} + +async function disconnectAccount(e) { + const element = e.target.closest('[data-type]'); + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/unlink/' + element.dataset.type, { + method: 'GET', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + }, + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + element.classList.remove('connected'); +} + +async function deleteAccount() { + const creds = await getCreds(true); + + if (!creds) return; + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/user', { + method: 'DELETE', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + Password: creds.password, + TwoFaToken: creds.mfaToken, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const data = await req.json(); + + if (data.statusCode != 200) { + createDialog('Error', 'An error occured while deleting your account!', 'error'); + return; + } + + LoginManager.deleteCookie('token', '/', '.netdb.at'); + LoginManager.deleteCookie('refreshToken', '/', '.netdb.at'); + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); +} + +async function enable2fa() { + const mfaType = document.getElementById('2fa_type').value; + + if (mfaType == 2) { + alert('Select a valid 2FA type!'); + return; + } + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/2fa/activate', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: mfaType == 0 ? '"app"' : mfaType == 1 ? '"mail"' : null, + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + if (req.status != 200) { + createDialog('Error', 'An error occured while enabling 2FA!', 'error'); + return; + } + + const res = await req.json(); + + if (mfaType == 0) { + //App + const qr = res.data.qrCodeSetupImageUrl; + const secret = res.data.manualEntryKey; + + document.getElementById('authenticatorDialog').querySelector('img').src = qr; + document.getElementById('authenticatorDialog').querySelector('h1').innerText = secret; + document.getElementById('authenticatorDialog').show(); + + const canceled = await new Promise((resolve) => { + document.getElementById('authenticatorDialog').addEventListener( + 'onHide', + (e) => { + if (e.detail.reason == 'canceled') { + resolve(false); + return; + } + + resolve(true); + }, + { once: true } + ); + }); + + if (!canceled) return false; + } + + await verify2fa(); +} + +async function verify2fa() { + const creds = await getCreds(true, false, true); + + if (!creds) return; + + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/2fa/verify', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + MFAToken: creds.mfaToken, + Password: creds.password, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return false; + } + + if (req.status != 200) { + createDialog('Error', 'An error occured while verifying 2FA!', 'error'); + return false; + } + + const res = await req.json(); + + if (res.statusCode != 200) { + createDialog('Error', 'An error occured while verifying 2FA!', 'error'); + return false; + } + + createDialog('Success', '2FA successfully enabled!', 'info'); +} + +async function disable2fa() { + const creds = await getCreds(true); + + if (!creds) return; + + const success = await disableMfaRequest(creds.password, creds.mfaToken); + + if (!success) { + const mfaToken = await getCreds(true, true); + + if (!mfaToken) return; + + await disableMfaRequest(creds.password, mfaToken.mfaToken); + } +} + +async function disableMfaRequest(password, mfaToken) { + await LoginManager.validateToken(); + const req = await fetch('https://api.login.netdb.at/2fa/deactivate', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + LoginManager.getCookie('token'), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + Password: password, + MFAToken: mfaToken, + }), + }); + + if (req.status == 401) { + window.location.href = 'https://login.netdb.at?redirect=' + encodeURIComponent(window.location.href); + return; + } + + const res = await req.json(); + + if (res.statusCode == 409) return false; + + if (req.status != 200 || res.statusCode != 200) createDialog('Error', 'An error occured while disabling 2FA!', 'error'); + + return true; +} + +async function getCreds(mfa = false, mfaOnly = false, forceMfa = false) { + document.getElementById('passwordInput').value = ''; + document.getElementById('2faInput').value = ''; + + if ((mfa && (currentUser['2faType'] == 'App' || mfaOnly)) || forceMfa) document.getElementById('2faInput').classList.remove('d-none'); + else document.getElementById('2faInput').classList.add('d-none'); + + if (mfaOnly) document.getElementById('passwordInput').classList.add('d-none'); + else document.getElementById('passwordInput').classList.remove('d-none'); + + document.getElementById('passwordDialog').show(); + + const res = await new Promise((resolve) => { + document.getElementById('passwordDialog').addEventListener( + 'onHide', + (e) => { + if (e.detail.reason == 'canceled') { + resolve(false); + return; + } + + resolve(true); + }, + { once: true } + ); + }); + + if (!res) return false; + + const pw = document.getElementById('passwordInput').classList.contains('d-none') ? null : document.getElementById('passwordInput').value; + const mfaToken = document.getElementById('2faInput').classList.contains('d-none') ? null : document.getElementById('2faInput').value; + + if (pw != null && validatePw(null, pw, pw)) { + createDialog('Invalid password', 'The password you entered is invalid!', 'error'); + return false; + } + if (mfaToken != null && !validateMfaToken(mfaToken)) { + createDialog('Invalid 2FA token', 'The 2FA token you entered is invalid!', 'error'); + return false; + } + + return { + password: pw, + mfaToken: mfaToken, + }; +} + +function validateMfaToken(token) { + if (token.length != 6) return false; + if (!isNumber(token)) return false; + + return true; +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..205ed1a --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +
+

+ + Logo + + +

Netdb

+ +

+ An unofficial Netflix API +
+ Explore the docs » +
+
+ View Page + · + Report Bug + · + Request Feature +

+

+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. Roadmap
  4. +
  5. Contributing
  6. +
  7. License
  8. +
  9. Contact
  10. +
  11. Acknowledgements
  12. +
+
+ + + + +## About The Project + +Netdb is an unofficial Netflix API. + +### Built With +* [Javascript](https://www.javascript.com) +* Html,Css + + +## Roadmap + +See the [open issues](https://github.com/Netflix-Database/Netdb/issues) for a list of proposed features (and known issues). + + +## License + +Distributed under the MIT License. See `LICENSE` for more information. + + + + +## Contact + +support@netdb.ga + +Project Link: [https://github.com/Netflix-Database/Netdb](https://github.com/Netflix-Database/Netdb) + + + + +## Acknowledgements diff --git a/index.html b/index.html new file mode 100644 index 0000000..e5cef67 --- /dev/null +++ b/index.html @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + Netdb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + + +
+
+ +
+
+
+

What is this?

+

+ Netdb is a collection of services that are hosted by us. We want to make the internet a better place by providing free services that are helpful and easy to use. +

+
+
+

Our Mission

+

+ We are constantly trying to improve our coding abilities. By creating these services we want to learn new things and improve our skills. +

+
+
+

+ Interested? +

+

+ We are always looking for new people to join our team. If you are interested in coding, design or just want to help us, please contact us. +

+
+
+
+

Services

+
+
+

Hub

+

+ A video streaming plattform that is similar to Netflix. +

+

+ + + + + +
+ hub.netdb.at +

+
+
+

Music

+

+ (Coming Soon) +

+

+ + + + + +
+ music.netdb.at +

+
+
+

Fileshare

+

+ (Coming Soon) +

+

+ + + + + +
+ fileshare.netdb.at +

+
+
+

Stocks

+

+ (Coming Soon) +

+

+ + + + + +
+ stocks.netdb.at +

+
+
+

Auth

+

+ A login system that is used by all of our services. +

+

+ + + + + +
+ login.netdb.at +

+
+
+

Url Shortener

+

+ Create short links for your website. +

+

+ + + + + +
+ us.netdb.at +

+
+
+

Flows

+

+ Automate anything you want. +

+

+ + + + + +
+ flows.netdb.at +

+
+
+ +

Contact Us

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7a84f77 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,720 @@ +{ + "name": "netdb", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "netdb", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "animejs": "^3.2.2", + "vite": "^5.1.5" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.1.tgz", + "integrity": "sha512-iU2Sya8hNn1LhsYyf0N+L4Gf9Qc+9eBTJJJsaOGUp+7x4n2M9dxTt8UvhJl3oeftSjblSlpCfvjA/IfP3g5VjQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.1.tgz", + "integrity": "sha512-wlzcWiH2Ir7rdMELxFE5vuM7D6TsOcJ2Yw0c3vaBR3VOsJFVTx9xvwnAvhgU5Ii8Gd6+I11qNHwndDscIm0HXg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.1.tgz", + "integrity": "sha512-YRXa1+aZIFN5BaImK+84B3uNK8C6+ynKLPgvn29X9s0LTVCByp54TB7tdSMHDR7GTV39bz1lOmlLDuedgTwwHg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.1.tgz", + "integrity": "sha512-opjWJ4MevxeA8FhlngQWPBOvVWYNPFkq6/25rGgG+KOy0r8clYwL1CFd+PGwRqqMFVQ4/Qd3sQu5t7ucP7C/Uw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.1.tgz", + "integrity": "sha512-uBkwaI+gBUlIe+EfbNnY5xNyXuhZbDSx2nzzW8tRMjUmpScd6lCQYKY2V9BATHtv5Ef2OBq6SChEP8h+/cxifQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.1.tgz", + "integrity": "sha512-0bK9aG1kIg0Su7OcFTlexkVeNZ5IzEsnz1ept87a0TUgZ6HplSgkJAnFpEVRW7GRcikT4GlPV0pbtVedOaXHQQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.1.tgz", + "integrity": "sha512-qB6AFRXuP8bdkBI4D7UPUbE7OQf7u5OL+R94JE42Z2Qjmyj74FtDdLGeriRyBDhm4rQSvqAGCGC01b8Fu2LthQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.1.tgz", + "integrity": "sha512-sHig3LaGlpNgDj5o8uPEoGs98RII8HpNIqFtAI8/pYABO8i0nb1QzT0JDoXF/pxzqO+FkxvwkHZo9k0NJYDedg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.1.tgz", + "integrity": "sha512-nD3YcUv6jBJbBNFvSbp0IV66+ba/1teuBcu+fBBPZ33sidxitc6ErhON3JNavaH8HlswhWMC3s5rgZpM4MtPqQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.1.tgz", + "integrity": "sha512-7/XVZqgBby2qp/cO0TQ8uJK+9xnSdJ9ct6gSDdEr4MfABrjTyrW6Bau7HQ73a2a5tPB7hno49A0y1jhWGDN9OQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.1.tgz", + "integrity": "sha512-CYc64bnICG42UPL7TrhIwsJW4QcKkIt9gGlj21gq3VV0LL6XNb1yAdHVp1pIi9gkts9gGcT3OfUYHjGP7ETAiw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.1.tgz", + "integrity": "sha512-LN+vnlZ9g0qlHGlS920GR4zFCqAwbv2lULrR29yGaWP9u7wF5L7GqWu9Ah6/kFZPXPUkpdZwd//TNR+9XC9hvA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.1.tgz", + "integrity": "sha512-n+vkrSyphvmU0qkQ6QBNXCGr2mKjhP08mPRM/Xp5Ck2FV4NrHU+y6axzDeixUrCBHVUS51TZhjqrKBBsHLKb2Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/animejs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.2.tgz", + "integrity": "sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==" + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.1.tgz", + "integrity": "sha512-ggqQKvx/PsB0FaWXhIvVkSWh7a/PCLQAsMjBc+nA2M8Rv2/HG0X6zvixAB7KyZBRtifBUhy5k8voQX/mRnABPg==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.12.1", + "@rollup/rollup-android-arm64": "4.12.1", + "@rollup/rollup-darwin-arm64": "4.12.1", + "@rollup/rollup-darwin-x64": "4.12.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.1", + "@rollup/rollup-linux-arm64-gnu": "4.12.1", + "@rollup/rollup-linux-arm64-musl": "4.12.1", + "@rollup/rollup-linux-riscv64-gnu": "4.12.1", + "@rollup/rollup-linux-x64-gnu": "4.12.1", + "@rollup/rollup-linux-x64-musl": "4.12.1", + "@rollup/rollup-win32-arm64-msvc": "4.12.1", + "@rollup/rollup-win32-ia32-msvc": "4.12.1", + "@rollup/rollup-win32-x64-msvc": "4.12.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", + "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1946cde --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "netdb", + "version": "1.0.0", + "description": "
\r

\r \r \"Logo\"\r ", + "main": "index.js", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "author": "", + "license": "ISC", + "dependencies": { + "animejs": "^3.2.2", + "vite": "^5.1.5" + } +} diff --git a/profile/index.html b/profile/index.html new file mode 100644 index 0000000..c5c167b --- /dev/null +++ b/profile/index.html @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + Netdb | Profile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
+
+

Personal Information

+ + +
+ + +
+
+ + +
+ +
+ +
+
+
+

Password

+ + + + + + +
+ +
+
+
+

Connected Accounts

+ +
+

+ + + + + + + + + + + +

Disconnect

+
+

+

+ + + + + + + + + + + + + + + + +

Disconnect

+
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

Disconnect

+
+

+

+ + + + + + + + + +

Disconnect

+
+

+

+ + + + + + + + + + + +

Disconnect

+
+

+
+ +
+
+

Two Factor Authentication

+
+

Status:

+

Disabled

+
+
+

Type:

+ +
+ +
+ +
+
+
+

My Devices

+ +

Coming Soon!

+ +
+ +
+
+
+

Third Party Apps

+
+

No third party apps connected!

+
+
+
+

Api Keys

+ + + + + + +
KeyActions
+
+ +
+
+
+

Single Sign On Credentials

+
+

No sso clients found!

+
+ +
+ +
+ + +
+ +
+

Danger Zone

+ +

+ Warning: Deleting your account is irreversible. All your data will be deleted and cannot be restored. +

+ + +
+
+
+ + + +
+ + + +
+ +
+
+
+ Qr Code +

+ +
+ +
+
+
+

Create new SSO client

+ + + + +
+ +
+
+ + + \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..d385f56 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,13 @@ +import { resolve } from 'path' +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + rollupOptions: { + input: { + main: resolve(__dirname, 'index.html'), + profile: resolve(__dirname, 'profile/index.html'), + }, + }, + }, +}) \ No newline at end of file