├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── funding.yml └── workflows │ └── production.yml ├── .gitignore ├── .vscode └── swissknifeDecorators.json ├── .yarnrc.yml ├── LICENSE.md ├── README.md ├── domain-specific-pages ├── bigjuicyf.art.html ├── cloudflare-status.com.html ├── cub.cafe.mp4 ├── digiorno.delivery.html ├── discord-status.com.html ├── fortnitebattlepass.shop.mp4 ├── furbyafterlife.com.html ├── gay.obama.solutions.mp4 ├── its-morbin-ti.me.gif ├── lavy.cloud.html ├── legacy.obama.solutions.mp4 ├── linus-sex.tips.html ├── linus-tech.tips.html ├── marcusgray.is-cute-and.gay.html ├── milkers.mom.mp4 ├── monox-will.hack-the.network.html ├── nagifur.is-cute-and.gay.html ├── obama.solutions.mp4 ├── peter.became.gay.html ├── piss.industries.html ├── shit.computer.html ├── the-fool.net.html ├── toilets.pics.html └── you.are-so.gay.html ├── index.html ├── nginx ├── errors │ ├── error500.html │ └── error503.html ├── nginx-errors.conf └── nginx.conf ├── package.json ├── public ├── Cumulonimbus.webp ├── icons │ ├── 128x128.png │ ├── 144x144.png │ ├── 152x152.png │ ├── 192x192.png │ ├── 384x384.png │ ├── 512x512.png │ ├── 72x72.png │ ├── 96x96.png │ ├── apple-touch-icon-152x152.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── maskable.png │ └── msapplication-icon-144x144.png ├── robots.txt └── sitemap.xml ├── src ├── App.vue ├── assets │ ├── fonts │ │ ├── Montserrat │ │ │ ├── Montserrat-Black.ttf │ │ │ ├── Montserrat-BlackItalic.ttf │ │ │ ├── Montserrat-Bold.ttf │ │ │ ├── Montserrat-BoldItalic.ttf │ │ │ ├── Montserrat-ExtraBold.ttf │ │ │ ├── Montserrat-ExtraBoldItalic.ttf │ │ │ ├── Montserrat-ExtraLight.ttf │ │ │ ├── Montserrat-ExtraLightItalic.ttf │ │ │ ├── Montserrat-Italic.ttf │ │ │ ├── Montserrat-Light.ttf │ │ │ ├── Montserrat-LightItalic.ttf │ │ │ ├── Montserrat-Medium.ttf │ │ │ ├── Montserrat-MediumItalic.ttf │ │ │ ├── Montserrat-Regular.ttf │ │ │ ├── Montserrat-SemiBold.ttf │ │ │ ├── Montserrat-SemiBoldItalic.ttf │ │ │ ├── Montserrat-Thin.ttf │ │ │ └── Montserrat-ThinItalic.ttf │ │ ├── SourceCodePro │ │ │ ├── SourceCodePro-Italic-VariableFont_wght.ttf │ │ │ ├── SourceCodePro-VariableFont_wght.ttf │ │ │ └── static │ │ │ │ ├── SourceCodePro-Black.ttf │ │ │ │ ├── SourceCodePro-BlackItalic.ttf │ │ │ │ ├── SourceCodePro-Bold.ttf │ │ │ │ ├── SourceCodePro-BoldItalic.ttf │ │ │ │ ├── SourceCodePro-ExtraBold.ttf │ │ │ │ ├── SourceCodePro-ExtraBoldItalic.ttf │ │ │ │ ├── SourceCodePro-ExtraLight.ttf │ │ │ │ ├── SourceCodePro-ExtraLightItalic.ttf │ │ │ │ ├── SourceCodePro-Italic.ttf │ │ │ │ ├── SourceCodePro-Light.ttf │ │ │ │ ├── SourceCodePro-LightItalic.ttf │ │ │ │ ├── SourceCodePro-Medium.ttf │ │ │ │ ├── SourceCodePro-MediumItalic.ttf │ │ │ │ ├── SourceCodePro-Regular.ttf │ │ │ │ ├── SourceCodePro-SemiBold.ttf │ │ │ │ └── SourceCodePro-SemiBoldItalic.ttf │ │ ├── Ubuntu │ │ │ ├── Ubuntu-Bold.ttf │ │ │ ├── Ubuntu-BoldItalic.ttf │ │ │ ├── Ubuntu-Italic.ttf │ │ │ ├── Ubuntu-Light.ttf │ │ │ ├── Ubuntu-LightItalic.ttf │ │ │ ├── Ubuntu-Medium.ttf │ │ │ ├── Ubuntu-MediumItalic.ttf │ │ │ └── Ubuntu-Regular.ttf │ │ └── fonts.css │ └── images │ │ ├── Cumulonimbus.png │ │ ├── Cumulonimbus.svg │ │ ├── Cumulonimbus.webp │ │ ├── checkmark.svg │ │ ├── close.svg │ │ ├── empty.gif │ │ ├── exclamation-mark.svg │ │ ├── file.svg │ │ ├── gear.svg │ │ ├── info.svg │ │ ├── newtab.svg │ │ ├── no-preview.svg │ │ ├── plus.svg │ │ ├── profile.svg │ │ ├── upload.svg │ │ └── wrench.svg ├── components │ ├── AnimatedEllipsis.vue │ ├── BackButton.vue │ ├── ConfirmModal.vue │ ├── ContentBox.vue │ ├── DomainModal.vue │ ├── EmphasizedBox.vue │ ├── FileContentBox.vue │ ├── Form.vue │ ├── FormModal.vue │ ├── FullscreenLoadingMessage.vue │ ├── LoadingMessage.vue │ ├── LoadingSpinner.vue │ ├── Marquee.vue │ ├── Modal.vue │ ├── Online.vue │ ├── Paginator.vue │ ├── PreviewContentBox.vue │ ├── ProgressBar.vue │ ├── RouterButton.vue │ ├── SecondFactorModal.vue │ ├── SelectableContentBox.vue │ ├── Separator.vue │ ├── SkeletonContentBoxes.vue │ ├── Switch.vue │ └── ThemeManager.vue ├── env.d.ts ├── main.ts ├── router.ts ├── stores │ ├── displayPref.ts │ ├── domainPicker.ts │ ├── ptb.ts │ ├── secondFactorChallenger.ts │ ├── staff-space │ │ ├── domains.ts │ │ ├── file.ts │ │ ├── files.ts │ │ ├── instruction.ts │ │ ├── instructions.ts │ │ ├── killswitches.ts │ │ ├── secondFactors.ts │ │ ├── sessions.ts │ │ ├── user.ts │ │ ├── users.ts │ │ └── utilities.ts │ ├── toast.ts │ ├── user-space │ │ ├── file.ts │ │ ├── files.ts │ │ ├── instruction.ts │ │ ├── instructions.ts │ │ ├── secondFactors.ts │ │ └── sessions.ts │ └── user.ts ├── sw.ts ├── utils │ ├── ConnectivityCheck.ts │ ├── defaultErrorHandler.ts │ ├── loadWhenOnline.ts │ ├── persistPiniaRef.ts │ ├── routerBackWithFallback.ts │ ├── size.ts │ ├── swRouter.ts │ ├── time.ts │ ├── toDateString.ts │ ├── toLogin.ts │ └── wait.ts └── views │ ├── 404.vue │ ├── About.vue │ ├── AccountSwitcher.vue │ ├── Auth.vue │ ├── Home.vue │ ├── Privacy.vue │ ├── TOS.vue │ ├── staff-space │ ├── Dashboard.vue │ ├── Domains.vue │ ├── File.vue │ ├── Files.vue │ ├── KillSwitches.vue │ ├── SetupGuide.vue │ ├── SetupGuides.vue │ ├── User.vue │ ├── UserSecondFactors.vue │ ├── UserSessions.vue │ ├── Users.vue │ └── Utilities.vue │ ├── test │ ├── BasicEmphasizedBox.vue │ ├── ContentBoxes.vue │ ├── FileContentBoxes.vue │ ├── FormEmphasizedBox.vue │ ├── FullscreenLoadingMessage.vue │ ├── FullscreenProgressBar.vue │ ├── LoadingMessage.vue │ ├── MarqueeText.vue │ ├── Modals.vue │ ├── Paginator.vue │ ├── ProgressBar.vue │ ├── SkeletonContentBoxes.vue │ ├── Switches.vue │ ├── Testing.vue │ └── TestingLinkSink.vue │ └── user-space │ ├── Account.vue │ ├── AccountSecondFactors.vue │ ├── AccountSessions.vue │ ├── Dashboard.vue │ ├── File.vue │ ├── Files.vue │ ├── SetupGuide.vue │ ├── SetupGuides.vue │ ├── Upload.vue │ └── Verify.vue ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.development.ts ├── vite.config.ptb.ts └── vite.config.ts /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | patreon: alekeagle 2 | github: AlekEagle 3 | -------------------------------------------------------------------------------- /.github/workflows/production.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy Cumulonimbus Production 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: self-hosted 12 | 13 | env: 14 | YARN_ENABLE_IMMUTABLE_INSTALLS: false 15 | 16 | steps: 17 | - name: Remove Stale Build Artifacts 18 | run: | 19 | rm -rf /var/www-data/dist 20 | rm -rf /var/www-data/domain-specific-pages 21 | - name: Checkout Repository 22 | uses: actions/checkout@v4 23 | - name: Copy Static Build Artifacts 24 | run: | 25 | cp -R nginx/ /var/www-data 26 | cp -R domain-specific-pages/ /var/www-data 27 | - name: Install Dependencies 28 | run: yarn 29 | - name: Build Production 30 | run: yarn build-prod 31 | - name: Copy Build Artifacts 32 | run: cp -R dist/ /var/www-data 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Certificates 2 | certs/ 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | pnpm-debug.log* 11 | lerna-debug.log* 12 | 13 | node_modules 14 | dist 15 | dev-dist 16 | dist-ssr 17 | *.local 18 | .yarn 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | !.vscode/swissknifeDecorators.json 24 | .idea 25 | .DS_Store 26 | *.suo 27 | *.ntvs* 28 | *.njsproj 29 | *.sln 30 | *.sw? 31 | 32 | # Lockfiles 33 | package-lock.json 34 | yarn.lock -------------------------------------------------------------------------------- /.vscode/swissknifeDecorators.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cumulonimbus Frontend 2 | 3 | The frontend of the Cumulonimbus ecosystem. 4 | 5 | ## The Cumulonimbus Ecosystem 6 | 7 | - [Cumulonimbus API](https://github.com/AlekEagle/cumulonimbus-api) 8 | - [Cumulonimbus Thumbnailer](https://github.com/AlekEagle/cumulonimbus-thumbnailer) 9 | - [Cumulonimbus Wrapper](https://github.com/AlekEagle/cumulonimbus-wrapper) 10 | - [Cumulonimbus Documentation](https://github.com/AlekEagle/cumulonimbus-docs) 11 | - Cumulonimbus Frontend (You are here.) 12 | 13 | ## What is in this repository? 14 | 15 | This repository contains the UI that an end-user will interact with when visiting the [website](https://alekeagle.me), all special landing pages for domains connected to Cumulonimbus, and the NGINX configuration for the NGINX webserver. 16 | -------------------------------------------------------------------------------- /domain-specific-pages/bigjuicyf.art.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | He Clearly Likes It! 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 45 | 46 | 47 |
48 | He Clearly Likes It! 53 |
54 |

Hosted with ❤️ on 67 | Cumulonimbus 70 | by 71 | Alek Evans

75 | 76 | 77 | -------------------------------------------------------------------------------- /domain-specific-pages/cloudflare-status.com.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not Cloudflare Status 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 46 | 47 | 48 |
49 | Cloudflare logo 54 |

55 | I think you meant 56 | cloudflarestatus.com 57 |

58 |
59 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

60 | 61 | 62 | -------------------------------------------------------------------------------- /domain-specific-pages/cub.cafe.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/cub.cafe.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/digiorno.delivery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Its not Digiorno its Disaster 5 | 6 | 7 | 11 | 12 | 13 | 14 | 18 | 22 | 51 | 52 | 53 |
54 | Disaster 59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /domain-specific-pages/discord-status.com.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not Discord Status 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 46 | 47 | 48 |
49 | Discord logo 54 |

55 | I think you meant 56 | discordstatus.com 57 |

58 |
59 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

60 | 61 | 62 | -------------------------------------------------------------------------------- /domain-specific-pages/fortnitebattlepass.shop.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/fortnitebattlepass.shop.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/furbyafterlife.com.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /domain-specific-pages/gay.obama.solutions.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/gay.obama.solutions.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/its-morbin-ti.me.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/its-morbin-ti.me.gif -------------------------------------------------------------------------------- /domain-specific-pages/lavy.cloud.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 14 | 15 | 16 |
17 |

Why are you here? 🤨

18 |
19 |

31 | Hosted with ❤️ on 32 | Cumulonimbus 39 | by 40 | Alek Evans 47 |

48 | 49 | 50 | 69 | -------------------------------------------------------------------------------- /domain-specific-pages/legacy.obama.solutions.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/legacy.obama.solutions.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/linus-sex.tips.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Linus Sex Tips 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 45 | 46 | 47 |
48 | Linus's Tip 53 |
54 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

55 | 56 | 57 | -------------------------------------------------------------------------------- /domain-specific-pages/linus-tech.tips.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Linus Likes the Headphones! 5 | 6 | 10 | 14 | 15 | 16 | 17 | 18 | 22 | 26 | 104 | 105 | 106 |
109 | 110 | 111 | -------------------------------------------------------------------------------- /domain-specific-pages/marcusgray.is-cute-and.gay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /domain-specific-pages/milkers.mom.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/milkers.mom.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/monox-will.hack-the.network.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Linus Sex Tips 5 | 6 | 7 | 11 | 12 | 13 | 17 | 18 | 22 | 49 | 50 | 51 |
52 | Monox from Monox Network 57 |

58 | I am monox!!! Do not talk to me or I will hak you!!!! (NOT CLICKBAIT) 59 |

60 |
61 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

62 | 63 | 64 | -------------------------------------------------------------------------------- /domain-specific-pages/nagifur.is-cute-and.gay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Nagifur is cute and gay 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /domain-specific-pages/obama.solutions.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/domain-specific-pages/obama.solutions.mp4 -------------------------------------------------------------------------------- /domain-specific-pages/peter.became.gay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Redirecting...

12 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

13 | 14 | 15 | -------------------------------------------------------------------------------- /domain-specific-pages/piss.industries.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |

12 |

WELCOME TO THE PISS ZONE

13 |

FOR INQUEERIES, CONTACT:

14 | telegram: @kadixfox 15 | discord: @78d26e80 16 |
17 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

18 | 19 | 20 | -------------------------------------------------------------------------------- /domain-specific-pages/shit.computer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shit Computers 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 97 | 98 | 99 |
102 | 103 | 121 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

122 | 123 | 124 | -------------------------------------------------------------------------------- /domain-specific-pages/toilets.pics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Toilets! 6 | 7 | 8 | 9 | 10 | 94 | 95 | 96 |

Hosted with ❤️ on 108 | Cumulonimbus 115 | by 116 | Alek Evans

124 | 125 | 126 | -------------------------------------------------------------------------------- /domain-specific-pages/you.are-so.gay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | You're Gay 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 43 | 44 | 45 |
46 | Your gayness in image form 51 |

You're gay.

52 |
53 |

Hosted with ❤️ on Cumulonimbus by Alek Evans

54 | 55 | 56 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cumulonimbus 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 29 | 34 | 35 | 36 | 37 | 42 | 43 | 44 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /nginx/errors/error500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Uh-oh! 5 | 6 | 20 | 21 | 22 |

Error of the bad variety!! The 500th error!

23 |

The Cumulonimbus dashboard is currently unavailable.

24 |

25 | Nginx didn't quite know how to handle this request, so it gave up and gave 26 | you this page. The API and any uploaded content should still be 27 | accessible. If this doesn't resolve itself after a few minutes or the API 28 | and/or your uploaded content is unavailable, let us know in the 29 | Discord Server. Here's a picture of a cat. 32 |

33 | A picture of a cat in a computer. 34 | 35 | 36 | -------------------------------------------------------------------------------- /nginx/errors/error503.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Uh-oh! 5 | 6 | 20 | 21 | 22 |

Error of the bad variety!! The 503rd error!

23 |

The Cumulonimbus dashboard is currently unavailable.

24 |

25 | This usually only happens when you catch Cumulonimbus just as we're 26 | updating the dashboard. The API and any uploaded content should still be 27 | accessible. If this doesn't resolve itself after a few minutes or the API 28 | and/or your uploaded content is unavailable, let us know in the 29 | Discord Server. Here's a picture of a cat. 32 |

33 | Picture of a cat sleeping in the shell of an old iMac 37 | 38 | 39 | -------------------------------------------------------------------------------- /nginx/nginx-errors.conf: -------------------------------------------------------------------------------- 1 | # More errors to be included. 2 | location /error500.html { 3 | root /var/www-data/nginx/errors; 4 | internal; 5 | } 6 | error_page 500 =500 /error500.html; 7 | location /error503.html { 8 | root /var/www-data/nginx/errors; 9 | internal; 10 | } 11 | error_page 503 =503 /error503.html; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cumulonimbus-frontend", 3 | "private": true, 4 | "version": "4.7.0", 5 | "type": "module", 6 | "license": "GNU-GPL-3.0", 7 | "scripts": { 8 | "live-prod": "vite --host --port 6173 -c vite.config.ts", 9 | "live-ptb": "vite --host --port 4173 -c vite.config.ptb.ts", 10 | "live-dev": "vite --host --port 5173 -c vite.config.development.ts", 11 | "build-prod": "vue-tsc --noEmit && vite build", 12 | "build-ptb": "vue-tsc --noEmit && vite build -c vite.config.ptb.ts", 13 | "build-dev": "vue-tsc --noEmit && vite build -c vite.config.development.ts", 14 | "preview": "vite preview --host" 15 | }, 16 | "dependencies": { 17 | "@simplewebauthn/browser": "^13.0.0", 18 | "@vueuse/components": "^13.9.0", 19 | "@vueuse/core": "^13.9.0", 20 | "cumulonimbus-wrapper": "5.1.0", 21 | "ms": "^2.1.3", 22 | "pinia": "^3.0.3", 23 | "qrcode": "^1.5.4", 24 | "vue": "^3.5.21", 25 | "vue-router": "^4.5.1" 26 | }, 27 | "devDependencies": { 28 | "@simplewebauthn/types": "^12.0.0", 29 | "@types/ms": "^2.1.0", 30 | "@types/node": "^24.3.2", 31 | "@types/qrcode": "^1.5.5", 32 | "@vitejs/plugin-vue": "^6.0.1", 33 | "terser": "^5.44.0", 34 | "typescript": "^5.9.2", 35 | "vite": "^7.1.5", 36 | "vite-plugin-pwa": "^1.0.3", 37 | "vue-tsc": "^3.0.7" 38 | }, 39 | "packageManager": "yarn@3.8.7+sha512.bbe7e310ff7fd20dc63b111110f96fe18192234bb0d4f10441fa6b85d2b644c8923db8fbe6d7886257ace948440ab1f83325ad02af457a1806cdc97f03d2508e" 40 | } 41 | -------------------------------------------------------------------------------- /public/Cumulonimbus.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/Cumulonimbus.webp -------------------------------------------------------------------------------- /public/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/128x128.png -------------------------------------------------------------------------------- /public/icons/144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/144x144.png -------------------------------------------------------------------------------- /public/icons/152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/152x152.png -------------------------------------------------------------------------------- /public/icons/192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/192x192.png -------------------------------------------------------------------------------- /public/icons/384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/384x384.png -------------------------------------------------------------------------------- /public/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/512x512.png -------------------------------------------------------------------------------- /public/icons/72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/72x72.png -------------------------------------------------------------------------------- /public/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/96x96.png -------------------------------------------------------------------------------- /public/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/maskable.png -------------------------------------------------------------------------------- /public/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/public/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | 4 | Sitemap: https://alekeagle.me/sitemap.xml -------------------------------------------------------------------------------- /public/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://alekeagle.me/ 5 | 2023-10-11 6 | 7 | 8 | https://alekeagle.me/about 9 | 2023-10-11 10 | 11 | 12 | https://alekeagle.me/tos 13 | 2023-10-28 14 | 15 | 16 | https://alekeagle.me/privacy 17 | 2023-10-28 18 | 19 | 20 | https://docs.alekeagle.me/ 21 | 2023-10-28 22 | 23 | -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Black.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-BlackItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-ExtraLight.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-Thin.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Montserrat/Montserrat-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Montserrat/Montserrat-ThinItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/SourceCodePro-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/SourceCodePro-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/SourceCodePro-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/SourceCodePro-VariableFont_wght.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Black.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-BlackItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraLight.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/SourceCodePro/static/SourceCodePro-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/SourceCodePro/static/SourceCodePro-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-BoldItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-Light.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-LightItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-MediumItalic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Ubuntu/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/fonts/Ubuntu/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /src/assets/images/Cumulonimbus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/images/Cumulonimbus.png -------------------------------------------------------------------------------- /src/assets/images/Cumulonimbus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | bg-bright 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/assets/images/Cumulonimbus.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/images/Cumulonimbus.webp -------------------------------------------------------------------------------- /src/assets/images/checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | image/svg+xml 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/assets/images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /src/assets/images/empty.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlekEagle/cumulonimbus-frontend/adf18930248dcc37fac602fa726b1bdd56692d25/src/assets/images/empty.gif -------------------------------------------------------------------------------- /src/assets/images/exclamation-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/images/gear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/assets/images/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 14 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/assets/images/newtab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/no-preview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /src/assets/images/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/assets/images/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | image/svg+xml 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/assets/images/wrench.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | image/svg+xml 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/components/AnimatedEllipsis.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 50 | -------------------------------------------------------------------------------- /src/components/BackButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | -------------------------------------------------------------------------------- /src/components/ConfirmModal.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 92 | -------------------------------------------------------------------------------- /src/components/EmphasizedBox.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | 25 | 44 | -------------------------------------------------------------------------------- /src/components/Form.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 102 | -------------------------------------------------------------------------------- /src/components/FormModal.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 92 | -------------------------------------------------------------------------------- /src/components/FullscreenLoadingMessage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 41 | 42 | 80 | -------------------------------------------------------------------------------- /src/components/LoadingMessage.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 102 | 103 | 117 | -------------------------------------------------------------------------------- /src/components/LoadingSpinner.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 108 | -------------------------------------------------------------------------------- /src/components/Online.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | -------------------------------------------------------------------------------- /src/components/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 28 | 29 | 85 | -------------------------------------------------------------------------------- /src/components/RouterButton.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 50 | 51 | 65 | -------------------------------------------------------------------------------- /src/components/SecondFactorModal.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 118 | 119 | 130 | -------------------------------------------------------------------------------- /src/components/SelectableContentBox.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 118 | -------------------------------------------------------------------------------- /src/components/Separator.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | -------------------------------------------------------------------------------- /src/components/SkeletonContentBoxes.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 67 | -------------------------------------------------------------------------------- /src/components/ThemeManager.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 36 | 37 | 114 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export {}; 4 | 5 | declare module '*.vue' { 6 | import type { DefineComponent } from 'vue'; 7 | const component: DefineComponent<{}, {}, any>; 8 | export default component; 9 | } 10 | 11 | declare module 'vue' { 12 | interface ComponentCustomProperties { 13 | $version: string; 14 | $dependencies: { [key: string]: string }; 15 | $devDependencies: { [key: string]: string }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import App from './App.vue'; 3 | import { ConnectivityCheckPlugin } from './utils/ConnectivityCheck.js'; 4 | import { router } from './router.js'; 5 | import packageJson from '../package.json'; 6 | 7 | // External Modules 8 | import { createApp } from 'vue'; 9 | import { createPinia } from 'pinia'; 10 | 11 | import '@/assets/fonts/fonts.css'; 12 | 13 | const app = createApp(App); 14 | 15 | app.config.globalProperties.$version = packageJson.version; 16 | app.config.globalProperties.$dependencies = packageJson.dependencies; 17 | app.config.globalProperties.$devDependencies = packageJson.devDependencies; 18 | 19 | app.use(router); 20 | app.use(ConnectivityCheckPlugin); 21 | app.use(createPinia()); 22 | app.mount('html body'); 23 | -------------------------------------------------------------------------------- /src/stores/displayPref.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import persistPiniaRef from '@/utils/persistPiniaRef.js'; 3 | 4 | // Other Store Modules 5 | // No Other Store Modules to import here. 6 | 7 | // External Modules 8 | import { defineStore } from 'pinia'; 9 | import { ref, watch } from 'vue'; 10 | import { usePreferredDark } from '@vueuse/core'; 11 | 12 | // Store Definition 13 | export const displayPrefStore = defineStore('displayPref', () => { 14 | // --- Persistent Refs --- 15 | // -- Dark Theme -- 16 | const dark = ref(window.matchMedia('(prefers-color-scheme: dark)').matches); 17 | // If 'dark' is not in localStorage, set it to the preferred dark theme, otherwise use the stored value 18 | persistPiniaRef(dark, 'dark', { immediate: true }); 19 | // -- Time Format -- 20 | const hour12 = ref(true); 21 | persistPiniaRef(hour12, 'hour12', { immediate: true }); 22 | // -- Items Per Page -- 23 | const itemsPerPage = ref(50); 24 | persistPiniaRef(itemsPerPage, 'itemsPerPage', { immediate: true }); 25 | // More persistent refs can be added here 26 | // --- End Persistent Refs --- 27 | // --- Internal Values --- 28 | const preferredDark = usePreferredDark(), 29 | html = document.documentElement; 30 | 31 | // --- Functions --- 32 | 33 | function setDarkTheme(newThemeVal: boolean = !dark.value) { 34 | const metaThemeColor = document.querySelector( 35 | 'meta[name=theme-color]', 36 | ) as HTMLMetaElement; 37 | if (newThemeVal) { 38 | html.classList.add('dark-theme'); 39 | metaThemeColor.setAttribute('content', '#212121'); 40 | } else { 41 | html.classList.remove('dark-theme'); 42 | metaThemeColor.setAttribute('content', '#ffffff'); 43 | } 44 | } 45 | 46 | // Watch for changes to the dark theme preference 47 | watch(dark, (newVal: boolean) => { 48 | setDarkTheme(newVal); 49 | }); 50 | 51 | // Wait 500ms before removing the 'no-theme' class to allow the theme to be set 52 | setTimeout(() => { 53 | html.classList.remove('no-theme'); 54 | // Watch for changes to the preferred dark theme 55 | watch(preferredDark, (newVal: boolean) => { 56 | dark.value = newVal; 57 | }); 58 | }, 500); 59 | 60 | // Set the theme 61 | setDarkTheme(dark.value); 62 | 63 | return { 64 | dark, 65 | hour12, 66 | itemsPerPage, 67 | }; 68 | }); 69 | -------------------------------------------------------------------------------- /src/stores/domainPicker.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from './user.js'; 7 | 8 | // External Modules 9 | import { defineStore } from 'pinia'; 10 | import { ref } from 'vue'; 11 | import { useRouter } from 'vue-router'; 12 | 13 | // Store Definition 14 | export const domainPickerStore = defineStore('domainPicker', () => { 15 | const user = userStore(); 16 | const router = useRouter(); 17 | const loading = ref(false); 18 | const domains = ref | null>( 19 | null, 20 | ); 21 | 22 | async function sync(): Promise { 23 | if (user.client === null) return false; 24 | loading.value = true; 25 | try { 26 | const result = await (user.client as Cumulonimbus).getDomains({ 27 | limit: 'all', 28 | }); 29 | domains.value = result.result; 30 | } catch (error) { 31 | // Pass our error to the default error handler and check if it was handled. 32 | switch (await defaultErrorHandler(error, router)) { 33 | case 'OK': 34 | // If the error was handled, return true to signify success. 35 | return false; 36 | case 'NOT_HANDLED': 37 | // No special cases to handle here. 38 | case 'NOT_RESPONSE_ERROR': 39 | default: 40 | // If the error wasn't handled, throw it. 41 | throw error; 42 | } 43 | } finally { 44 | loading.value = false; 45 | } 46 | return true; 47 | } 48 | 49 | return { 50 | domains, 51 | loading, 52 | sync, 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /src/stores/ptb.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import persistPiniaRef from '@/utils/persistPiniaRef.js'; 3 | 4 | // Other Store Modules 5 | // No Other Store Modules to import here. 6 | 7 | // External Modules 8 | import { defineStore } from 'pinia'; 9 | import { ref, computed } from 'vue'; 10 | 11 | // Store Definition 12 | export const ptbStore = defineStore('ptb', () => { 13 | const shownWarning = ref(false); 14 | const isPtb = computed(() => import.meta.env.MODE === 'ptb'); 15 | 16 | persistPiniaRef(shownWarning, 'shownWarning', { immediate: true }); 17 | 18 | return { 19 | shownWarning, 20 | isPtb, 21 | }; 22 | }); 23 | -------------------------------------------------------------------------------- /src/stores/secondFactorChallenger.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import type Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import { waitUntil } from '@/utils/wait.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from './user.js'; 7 | import { toastStore } from './toast.js'; 8 | 9 | // External Modules 10 | import { defineStore } from 'pinia'; 11 | import { ref, computed } from 'vue'; 12 | import type { 13 | PublicKeyCredentialRequestOptionsJSON, 14 | AuthenticationResponseJSON, 15 | } from '@simplewebauthn/types'; 16 | 17 | export const secondFactorChallengerStore = defineStore( 18 | 'second-factor-challenger', 19 | () => { 20 | const isChallenging = ref(false), 21 | user = userStore(), 22 | toast = toastStore(), 23 | challenge = ref( 24 | null, 25 | ), 26 | selectedMethod = ref<'totp' | 'webauthn' | 'backup' | null>(null), 27 | challengeResponse = ref(); 28 | 29 | const availableMethods = computed<('totp' | 'webauthn' | 'backup')[]>( 30 | () => challenge.value?.types ?? [], 31 | ), 32 | webauthnChallenge = 33 | computed( 34 | () => challenge.value?.challenge ?? null, 35 | ); 36 | 37 | function startChallenge( 38 | challengeError: Cumulonimbus.SecondFactorChallengeRequiredError, 39 | ): Promise { 40 | if (isChallenging.value) { 41 | throw new Error("We're already challenging a second factor!"); 42 | } 43 | return new Promise((res) => { 44 | // Setup the challenge 45 | challenge.value = challengeError; 46 | selectedMethod.value = availableMethods.value[0]; 47 | challengeResponse.value = undefined; 48 | isChallenging.value = true; 49 | 50 | // Wait for the user to respond 51 | waitUntil(challengeResponse, (response) => response !== undefined).then( 52 | () => { 53 | res(challengeResponse.value as Cumulonimbus.SecondFactorResponse); 54 | isChallenging.value = false; 55 | selectedMethod.value = null; 56 | challenge.value = null; 57 | }, 58 | ); 59 | }); 60 | } 61 | 62 | function completeChallenge( 63 | response: string | AuthenticationResponseJSON, 64 | ): void { 65 | challengeResponse.value = { 66 | type: selectedMethod.value!, 67 | ...{ 68 | code: typeof response === 'string' ? response : undefined, 69 | response: typeof response === 'object' ? response : undefined, 70 | }, 71 | token: challenge.value!.token, 72 | } as Cumulonimbus.SecondFactorResponse; 73 | isChallenging.value = false; 74 | } 75 | 76 | function cancelChallenge(): void { 77 | challengeResponse.value = null; 78 | isChallenging.value = false; 79 | } 80 | 81 | return { 82 | isChallenging, 83 | selectedMethod, 84 | availableMethods, 85 | webauthnChallenge, 86 | startChallenge, 87 | completeChallenge, 88 | cancelChallenge, 89 | }; 90 | }, 91 | ); 92 | -------------------------------------------------------------------------------- /src/stores/staff-space/files.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from '../user.js'; 7 | import { displayPrefStore } from '../displayPref.js'; 8 | 9 | // External Modules 10 | import { defineStore } from 'pinia'; 11 | import { ref } from 'vue'; 12 | import { useRouter } from 'vue-router'; 13 | 14 | export const filesStore = defineStore('staff-space-files', () => { 15 | const user = userStore(); 16 | const router = useRouter(); 17 | const displayPref = displayPrefStore(); 18 | const loading = ref(false); 19 | const data = ref | null>(null); 20 | const errored = ref(false); 21 | const page = ref(0); 22 | const selectedUser = ref(null); 23 | 24 | async function getFiles(p: number): Promise { 25 | if (user.client === null) return false; 26 | errored.value = false; 27 | loading.value = true; 28 | try { 29 | let result; 30 | if (selectedUser.value !== null) 31 | result = await (user.client as Cumulonimbus).getUserFiles( 32 | selectedUser.value.id, 33 | { 34 | limit: displayPref.itemsPerPage, 35 | offset: displayPref.itemsPerPage * p, 36 | }, 37 | ); 38 | else 39 | result = await (user.client as Cumulonimbus).getAllFiles({ 40 | limit: displayPref.itemsPerPage, 41 | offset: displayPref.itemsPerPage * p, 42 | }); 43 | 44 | page.value = p; 45 | data.value = result.result; 46 | } catch (error) { 47 | errored.value = true; 48 | // Pass our error to the default error handler and check if it was handled. 49 | switch (await defaultErrorHandler(error, router)) { 50 | case 'OK': 51 | // If the error was handled, return true to signify success. 52 | return false; 53 | case 'NOT_HANDLED': 54 | // No special cases to handle here. 55 | case 'NOT_RESPONSE_ERROR': 56 | default: 57 | // If the error wasn't handled, throw it. 58 | throw error; 59 | } 60 | } finally { 61 | loading.value = false; 62 | } 63 | return true; 64 | } 65 | 66 | async function deleteFiles(files: string[]): Promise { 67 | if (user.client === null) return -1; 68 | errored.value = false; 69 | loading.value = true; 70 | try { 71 | let result; 72 | 73 | // Use the appropriate method based on whether a user is selected. 74 | if (selectedUser.value == null) 75 | result = await (user.client as Cumulonimbus).deleteArbitraryFiles( 76 | files, 77 | ); 78 | else 79 | result = await (user.client as Cumulonimbus).deleteUserFiles( 80 | selectedUser.value.id, 81 | files, 82 | ); 83 | 84 | return result.result.count!; 85 | } catch (error) { 86 | // Pass our error to the default error handler and check if it was handled. 87 | switch (await defaultErrorHandler(error, router)) { 88 | case 'OK': 89 | // If the error was handled, return true to signify success. 90 | return -1; 91 | case 'NOT_HANDLED': 92 | // No special cases to handle here. 93 | case 'NOT_RESPONSE_ERROR': 94 | default: 95 | // If the error wasn't handled, throw it. 96 | throw error; 97 | } 98 | } finally { 99 | loading.value = false; 100 | } 101 | } 102 | 103 | return { 104 | data, 105 | loading, 106 | errored, 107 | page, 108 | selectedUser, 109 | getFiles, 110 | deleteFiles, 111 | }; 112 | }); 113 | -------------------------------------------------------------------------------- /src/stores/staff-space/instructions.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from '../user.js'; 7 | import { displayPrefStore } from '../displayPref.js'; 8 | import { toastStore } from '../toast.js'; 9 | 10 | // External Modules 11 | import { defineStore } from 'pinia'; 12 | import { ref } from 'vue'; 13 | import { useRouter } from 'vue-router'; 14 | 15 | export const instructionsStore = defineStore('staff-space-instructions', () => { 16 | const user = userStore(), 17 | displayPref = displayPrefStore(), 18 | router = useRouter(), 19 | toast = toastStore(), 20 | data = ref | null>( 21 | null, 22 | ), 23 | loading = ref(false), 24 | errored = ref(false), 25 | page = ref(0); 26 | 27 | async function getInstructions(p: number): Promise { 28 | if (user.client === null) return false; 29 | errored.value = false; 30 | loading.value = true; 31 | try { 32 | const result = await user.client!.getInstructions({ 33 | limit: displayPref.itemsPerPage, 34 | offset: p * displayPref.itemsPerPage, 35 | }); 36 | page.value = p; 37 | data.value = result.result; 38 | } catch (error) { 39 | errored.value = true; 40 | // Pass our error to the default error handler and check if it was handled. 41 | switch (await defaultErrorHandler(error, router)) { 42 | case 'OK': 43 | // If the error was handled, return true to signify success. 44 | return false; 45 | case 'NOT_HANDLED': 46 | // No special cases to handle here. 47 | case 'NOT_RESPONSE_ERROR': 48 | default: 49 | // If the error wasn't handled, throw it. 50 | throw error; 51 | } 52 | } finally { 53 | loading.value = false; 54 | } 55 | return true; 56 | } 57 | 58 | async function deleteInstructions(ids: string[]): Promise { 59 | if (user.client === null) return -1; 60 | errored.value = false; 61 | loading.value = true; 62 | try { 63 | const result = await user.client!.deleteInstructions(ids); 64 | return result.result.count!; 65 | } catch (error) { 66 | errored.value = true; 67 | // Pass our error to the default error handler and check if it was handled. 68 | switch (await defaultErrorHandler(error, router)) { 69 | case 'OK': 70 | // If the error was handled, return true to signify success. 71 | return -1; 72 | case 'NOT_HANDLED': 73 | // No special cases to handle here. 74 | case 'NOT_RESPONSE_ERROR': 75 | default: 76 | // If the error wasn't handled, throw it. 77 | throw error; 78 | } 79 | } finally { 80 | loading.value = false; 81 | } 82 | } 83 | 84 | async function createInstruction( 85 | name: string, 86 | description: string, 87 | ): Promise { 88 | if (user.client === null) return false; 89 | errored.value = false; 90 | loading.value = true; 91 | try { 92 | await user.client!.createInstruction({ 93 | id: name.toLowerCase().replace(/\s/g, '-'), 94 | name, 95 | description, 96 | steps: [], 97 | content: '{{token}}', 98 | }); 99 | toast.show('Instruction created.'); 100 | return true; 101 | } catch (error) { 102 | errored.value = true; 103 | // Pass our error to the default error handler and check if it was handled. 104 | switch (await defaultErrorHandler(error, router)) { 105 | case 'OK': 106 | // If the error was handled, return true to signify success. 107 | return false; 108 | case 'NOT_HANDLED': 109 | // No special cases to handle here. 110 | case 'NOT_RESPONSE_ERROR': 111 | default: 112 | // If the error wasn't handled, throw it. 113 | throw error; 114 | } 115 | } finally { 116 | loading.value = false; 117 | } 118 | } 119 | 120 | return { 121 | data, 122 | loading, 123 | errored, 124 | page, 125 | getInstructions, 126 | deleteInstructions, 127 | createInstruction, 128 | }; 129 | }); 130 | -------------------------------------------------------------------------------- /src/stores/staff-space/users.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from '../user.js'; 7 | import { displayPrefStore } from '../displayPref.js'; 8 | import { secondFactorChallengerStore } from '../secondFactorChallenger.js'; 9 | 10 | // External Modules 11 | import { defineStore } from 'pinia'; 12 | import { ref } from 'vue'; 13 | import { useRouter } from 'vue-router'; 14 | 15 | export const usersStore = defineStore('staff-space-users', () => { 16 | const user = userStore(); 17 | const displayPref = displayPrefStore(); 18 | const secondFactorChallenger = secondFactorChallengerStore(); 19 | const router = useRouter(); 20 | const loading = ref(false); 21 | const data = ref | null>(null); 22 | const errored = ref(false); 23 | const page = ref(0); 24 | 25 | async function getUsers(p: number): Promise { 26 | if (user.client === null) return false; 27 | errored.value = false; 28 | loading.value = true; 29 | try { 30 | const result = await (user.client as Cumulonimbus).getUsers({ 31 | limit: displayPref.itemsPerPage, 32 | offset: displayPref.itemsPerPage * p, 33 | }); 34 | page.value = p; 35 | data.value = result.result; 36 | } catch (error) { 37 | errored.value = true; 38 | // Pass our error to the default error handler and check if it was handled. 39 | switch (await defaultErrorHandler(error, router)) { 40 | case 'OK': 41 | // If the error was handled, return true to signify success. 42 | return false; 43 | case 'NOT_HANDLED': 44 | // No special cases to handle here. 45 | case 'NOT_RESPONSE_ERROR': 46 | default: 47 | // If the error wasn't handled, throw it. 48 | throw error; 49 | } 50 | } finally { 51 | loading.value = false; 52 | } 53 | return true; 54 | } 55 | 56 | async function deleteUsers( 57 | users: string[], 58 | password: string, 59 | ): Promise { 60 | if (user.client === null) return -1; 61 | errored.value = false; 62 | loading.value = true; 63 | try { 64 | const result = await user.client!.deleteUsers(users, password); 65 | return result.result.count; 66 | } catch (error) { 67 | // Pass our error to the default error handler and check if it was handled. 68 | const reason = await defaultErrorHandler(error, router); 69 | switch (reason) { 70 | case 'OK': 71 | errored.value = true; 72 | // If the error was handled, return true to signify success. 73 | return -1; 74 | case 'SECOND_FACTOR_CHALLENGE_REQUIRED': 75 | const SFR = await secondFactorChallenger.startChallenge( 76 | error as Cumulonimbus.SecondFactorChallengeRequiredError, 77 | ); 78 | 79 | if (SFR === null) { 80 | return -1; 81 | } 82 | 83 | try { 84 | const result = await user.client!.deleteUsers(users, SFR); 85 | return result.result.count; 86 | } catch (error) { 87 | // Pass our error to the default error handler and check if it was handled. 88 | const reason = await defaultErrorHandler(error, router); 89 | switch (reason) { 90 | case 'OK': 91 | errored.value = true; 92 | // If the error was handled, return true to signify success. 93 | return -1; 94 | case 'NOT_HANDLED': 95 | // No special cases to handle here. 96 | case 'NOT_RESPONSE_ERROR': 97 | default: 98 | // If the error wasn't handled, throw it. 99 | throw error; 100 | } 101 | } 102 | case 'NOT_HANDLED': 103 | // No special cases to handle here. 104 | case 'NOT_RESPONSE_ERROR': 105 | default: 106 | errored.value = true; 107 | // If the error wasn't handled, throw it. 108 | throw error; 109 | } 110 | } finally { 111 | loading.value = false; 112 | } 113 | } 114 | 115 | return { 116 | loading, 117 | data, 118 | errored, 119 | page, 120 | getUsers, 121 | deleteUsers, 122 | }; 123 | }); 124 | -------------------------------------------------------------------------------- /src/stores/staff-space/utilities.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { userStore } from '../user.js'; 7 | import { toastStore } from '../toast.js'; 8 | import { secondFactorChallengerStore } from '../secondFactorChallenger.js'; 9 | 10 | // External Modules 11 | import { defineStore } from 'pinia'; 12 | import { ref } from 'vue'; 13 | import { useRouter } from 'vue-router'; 14 | 15 | export const utilitiesStore = defineStore('staff-space-utilities', () => { 16 | const user = userStore(), 17 | router = useRouter(), 18 | toast = toastStore(), 19 | secondFactorChallenger = secondFactorChallengerStore(), 20 | logLevelData = ref(null), // Explicitly named to avoid confusion with any future data properties that may be added. 21 | loading = ref(false), 22 | errored = ref(false); 23 | 24 | async function getLogLevel(): Promise { 25 | if (user.client === null) return false; 26 | errored.value = false; 27 | loading.value = true; 28 | try { 29 | const result = await user.client!.getLogLevel(); 30 | logLevelData.value = result.result; 31 | } catch (error) { 32 | errored.value = true; 33 | // Pass our error to the default error handler and check if it was handled. 34 | switch (await defaultErrorHandler(error, router)) { 35 | case 'OK': 36 | // If the error was handled, return true to signify success. 37 | return false; 38 | case 'NOT_HANDLED': 39 | // No special cases to handle here. 40 | case 'NOT_RESPONSE_ERROR': 41 | default: 42 | // If the error wasn't handled, throw it. 43 | throw error; 44 | } 45 | } finally { 46 | loading.value = false; 47 | } 48 | return true; 49 | } 50 | 51 | async function setLogLevel( 52 | level: Cumulonimbus.LogLevel, 53 | password: string, 54 | ): Promise { 55 | if (user.client === null) return false; 56 | errored.value = false; 57 | loading.value = true; 58 | try { 59 | const result = await user.client!.setLogLevel(level, password); 60 | logLevelData.value = result.result; 61 | return true; 62 | } catch (error) { 63 | // Pass our error to the default error handler and check if it was handled. 64 | switch (await defaultErrorHandler(error, router)) { 65 | case 'OK': 66 | errored.value = true; 67 | // If the error was handled, return true to signify success. 68 | return false; 69 | case 'NOT_HANDLED': 70 | errored.value = true; 71 | // Handle special cases. 72 | switch ((error as Cumulonimbus.ResponseError).code) { 73 | case 'INVALID_LOGLEVEL_ERROR': 74 | toast.show('Invalid log level.'); 75 | return false; 76 | default: 77 | // If it still wasn't handled, throw the error. 78 | throw error; 79 | } 80 | case 'SECOND_FACTOR_CHALLENGE_REQUIRED': 81 | const SFR = await secondFactorChallenger.startChallenge( 82 | error as Cumulonimbus.SecondFactorChallengeRequiredError, 83 | ); 84 | 85 | if (SFR === null) { 86 | return false; 87 | } 88 | 89 | try { 90 | const result = await user.client!.setLogLevel(level, SFR); 91 | logLevelData.value = result.result; 92 | return true; 93 | } catch (error) { 94 | errored.value = true; 95 | switch (await defaultErrorHandler(error, router)) { 96 | case 'OK': 97 | return false; 98 | case 'NOT_HANDLED': 99 | case 'NOT_RESPONSE_ERROR': 100 | default: 101 | throw error; 102 | } 103 | } 104 | case 'NOT_RESPONSE_ERROR': 105 | default: 106 | // If the error wasn't handled, throw it. 107 | throw error; 108 | } 109 | } finally { 110 | loading.value = false; 111 | } 112 | } 113 | 114 | return { 115 | logLevelData, 116 | loading, 117 | errored, 118 | getLogLevel, 119 | setLogLevel, 120 | }; 121 | }); 122 | -------------------------------------------------------------------------------- /src/stores/user-space/files.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { displayPrefStore } from '../displayPref.js'; 7 | import { userStore } from '../user.js'; 8 | 9 | // External Modules 10 | import { defineStore } from 'pinia'; 11 | import { ref } from 'vue'; 12 | import { useRouter } from 'vue-router'; 13 | 14 | // Store Definition 15 | export const filesStore = defineStore('user-space-files', () => { 16 | const user = userStore(); 17 | const displayPref = displayPrefStore(); 18 | const router = useRouter(); 19 | const loading = ref(false); 20 | const data = ref | null>(null); 21 | const errored = ref(false); 22 | const page = ref(0); 23 | const selected = ref([]); 24 | 25 | async function getFiles(p: number): Promise { 26 | if (user.client === null) return false; 27 | errored.value = false; 28 | loading.value = true; 29 | try { 30 | const result = await (user.client as Cumulonimbus).getSelfFiles({ 31 | limit: displayPref.itemsPerPage, 32 | offset: displayPref.itemsPerPage * p, 33 | }); 34 | page.value = p; 35 | data.value = result.result; 36 | } catch (error) { 37 | errored.value = true; 38 | // Pass our error to the default error handler and check if it was handled. 39 | switch (await defaultErrorHandler(error, router)) { 40 | case 'OK': 41 | // If the error was handled, return true to signify success. 42 | return false; 43 | case 'NOT_HANDLED': 44 | // No special cases to handle here. 45 | case 'NOT_RESPONSE_ERROR': 46 | default: 47 | // If the error wasn't handled, throw it. 48 | throw error; 49 | } 50 | } finally { 51 | loading.value = false; 52 | } 53 | return true; 54 | } 55 | 56 | async function deleteFiles(files: string[]): Promise { 57 | if (user.client === null) return -1; 58 | errored.value = false; 59 | loading.value = true; 60 | try { 61 | const result = await (user.client as Cumulonimbus).deleteSelfFiles(files); 62 | return result.result.count!; 63 | } catch (error) { 64 | // Pass our error to the default error handler and check if it was handled. 65 | switch (await defaultErrorHandler(error, router)) { 66 | case 'OK': 67 | // If the error was handled, return true to signify success. 68 | return -1; 69 | case 'NOT_HANDLED': 70 | // No special cases to handle here. 71 | case 'NOT_RESPONSE_ERROR': 72 | default: 73 | // If the error wasn't handled, throw it. 74 | throw error; 75 | } 76 | } finally { 77 | loading.value = false; 78 | } 79 | } 80 | 81 | async function clear(): Promise { 82 | data.value = null; 83 | page.value = 0; 84 | loading.value = false; 85 | errored.value = false; 86 | } 87 | 88 | return { 89 | data, 90 | loading, 91 | errored, 92 | page, 93 | getFiles, 94 | deleteFiles, 95 | clear, 96 | }; 97 | }); 98 | -------------------------------------------------------------------------------- /src/stores/user-space/instruction.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { toastStore } from '../toast.js'; 7 | import { userStore } from '../user.js'; 8 | 9 | // External Modules 10 | import { defineStore } from 'pinia'; 11 | import { ref } from 'vue'; 12 | import { useRouter } from 'vue-router'; 13 | 14 | // Store Definition 15 | export const instructionStore = defineStore('user-space-instruction', () => { 16 | const user = userStore(); 17 | const loading = ref(false); 18 | const router = useRouter(); 19 | const toast = toastStore(); 20 | const data = ref(null); 21 | const errored = ref(false); 22 | 23 | async function getInstruction(id: string): Promise { 24 | if (user.client === null) return false; 25 | errored.value = false; 26 | loading.value = true; 27 | try { 28 | const result = await (user.client as Cumulonimbus).getInstruction(id); 29 | data.value = result.result; 30 | } catch (error) { 31 | errored.value = true; 32 | // Pass our error to the default error handler and check if it was handled. 33 | switch (await defaultErrorHandler(error, router)) { 34 | case 'OK': 35 | // If the error was handled, return true to signify success. 36 | return false; 37 | case 'NOT_HANDLED': 38 | // Handle special cases. 39 | switch ((error as Cumulonimbus.ResponseError).code) { 40 | case 'INVALID_INSTRUCTION_ERROR': 41 | toast.show('This setup guide does not exist.'); 42 | return false; 43 | default: 44 | // If it still wasn't handled, throw the error. 45 | throw error; 46 | } 47 | case 'NOT_RESPONSE_ERROR': 48 | default: 49 | // If the error wasn't handled, throw it. 50 | throw error; 51 | } 52 | } finally { 53 | loading.value = false; 54 | } 55 | return true; 56 | } 57 | 58 | return { 59 | loading, 60 | data, 61 | errored, 62 | getInstruction, 63 | }; 64 | }); 65 | -------------------------------------------------------------------------------- /src/stores/user-space/instructions.ts: -------------------------------------------------------------------------------- 1 | // In-House Modules 2 | import Cumulonimbus from 'cumulonimbus-wrapper'; 3 | import defaultErrorHandler from '@/utils/defaultErrorHandler.js'; 4 | 5 | // Other Store Modules 6 | import { displayPrefStore } from '../displayPref.js'; 7 | import { toastStore } from '../toast.js'; 8 | import { userStore } from '../user.js'; 9 | 10 | // External Modules 11 | import { defineStore } from 'pinia'; 12 | import { ref } from 'vue'; 13 | import { useRouter } from 'vue-router'; 14 | 15 | // Store Definition 16 | export const instructionsStore = defineStore('user-space-instructions', () => { 17 | const user = userStore(); 18 | const displayPref = displayPrefStore(); 19 | const toast = toastStore(); 20 | const router = useRouter(); 21 | const loading = ref(false); 22 | const data = 23 | ref | null>(null); 24 | const errored = ref(false); 25 | const page = ref(0); 26 | 27 | async function getInstructions(p: number): Promise { 28 | if (user.client === null) return false; 29 | errored.value = false; 30 | loading.value = true; 31 | try { 32 | const result = await (user.client as Cumulonimbus).getInstructions({ 33 | limit: displayPref.itemsPerPage, 34 | offset: displayPref.itemsPerPage * p, 35 | }); 36 | page.value = p; 37 | data.value = result.result; 38 | } catch (error) { 39 | errored.value = true; 40 | // Pass our error to the default error handler and check if it was handled. 41 | const reason = await defaultErrorHandler(error, router); 42 | switch (reason) { 43 | case 'OK': 44 | // If the error was handled, return true to signify success. 45 | return false; 46 | case 'NOT_HANDLED': 47 | // No special cases to handle here. 48 | case 'NOT_RESPONSE_ERROR': 49 | default: 50 | // If the error wasn't handled, throw it. 51 | throw error; 52 | } 53 | } finally { 54 | loading.value = false; 55 | } 56 | return true; 57 | } 58 | 59 | return { 60 | loading, 61 | data, 62 | errored, 63 | page, 64 | getInstructions, 65 | }; 66 | }); 67 | -------------------------------------------------------------------------------- /src/utils/ConnectivityCheck.ts: -------------------------------------------------------------------------------- 1 | import { inject, Plugin, ref } from 'vue'; 2 | 3 | export const ConnectivityCheckPlugin: Plugin = { 4 | install(app) { 5 | const isOnline = ref(true); 6 | app.provide('isOnline', isOnline); 7 | window.navigator.serviceWorker?.controller?.postMessage({ 8 | type: 'checkOnline', 9 | }); 10 | 11 | function informAboutOnlineStatus(status: boolean) { 12 | window.navigator.serviceWorker?.controller?.postMessage({ 13 | type: 'onlineChange', 14 | payload: status, 15 | }); 16 | } 17 | 18 | navigator.serviceWorker?.addEventListener('message', (event) => { 19 | if (event.data.type === 'isOnline') { 20 | isOnline.value = event.data.payload; 21 | } 22 | }); 23 | 24 | // Since the service worker's 'online' event is not reliable, we use the navigator's 'online' event and forward it to the service worker. 25 | window.addEventListener('online', () => { 26 | informAboutOnlineStatus(true); 27 | }); 28 | window.addEventListener('offline', () => { 29 | informAboutOnlineStatus(false); 30 | }); 31 | }, 32 | }; 33 | 34 | export const useOnline = () => { 35 | return inject('isOnline') as ReturnType; 36 | }; 37 | -------------------------------------------------------------------------------- /src/utils/loadWhenOnline.ts: -------------------------------------------------------------------------------- 1 | import { useOnline } from '@/utils/ConnectivityCheck.js'; 2 | import { watch } from 'vue'; 3 | 4 | // This function takes a callback and conditions for when to call the callback. 5 | // It will call the callback when the conditions are met and the online state changes to true. 6 | // It will also call the callback immediately if the conditions are met and the online state is already true. 7 | // It returns a function that can be called to cancel the callback. 8 | export default function loadWhenOnline( 9 | callback: () => void, 10 | ...conditions: boolean[] 11 | ): (() => void) | void { 12 | const online = useOnline(); 13 | if (online.value && conditions.every((condition) => condition)) { 14 | callback(); 15 | return; 16 | } 17 | const cancel = watch( 18 | online, 19 | (online) => { 20 | if (online && conditions.every((condition) => condition)) { 21 | callback(); 22 | cancel(); 23 | } 24 | }, 25 | { immediate: true }, 26 | ); 27 | return cancel; 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/persistPiniaRef.ts: -------------------------------------------------------------------------------- 1 | import { Ref, watch, WatchOptions } from 'vue'; 2 | 3 | export default function persistPiniaRef( 4 | ref: Ref, 5 | key: string, 6 | watchOptions?: WatchOptions, 7 | ) { 8 | if (localStorage.getItem(key)) { 9 | ref.value = JSON.parse(localStorage.getItem(key) as string); 10 | } 11 | 12 | watch( 13 | ref, 14 | (newVal) => { 15 | localStorage.setItem(key, JSON.stringify(newVal)); 16 | }, 17 | watchOptions, 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/routerBackWithFallback.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'vue-router'; 2 | 3 | export default async function backWithFallback( 4 | router: Router, 5 | fallback: string, 6 | fallbackWithoutRouter: boolean = false, 7 | ) { 8 | let current = router.currentRoute.value; 9 | let fallbackResolved = await router.resolve(fallback); 10 | if (window.history.state.back != null) { 11 | router.back(); 12 | } else { 13 | if (!fallbackWithoutRouter) { 14 | await router.replace(fallback); 15 | await router.push(current); 16 | router.back(); 17 | } else { 18 | window.history.replaceState( 19 | { 20 | back: null, 21 | current: fallbackResolved.fullPath, 22 | forward: null, 23 | position: 0, 24 | replaced: true, 25 | scroll: { top: 0, left: 0 }, 26 | }, 27 | '', 28 | fallbackResolved.fullPath, 29 | ); 30 | window.history.pushState( 31 | { 32 | back: fallbackResolved.fullPath, 33 | current: current.fullPath, 34 | forward: null, 35 | position: 1, 36 | replaced: false, 37 | scroll: null, 38 | }, 39 | '', 40 | current.fullPath, 41 | ); 42 | router.back(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/size.ts: -------------------------------------------------------------------------------- 1 | export default function size(bytes: number): string { 2 | if (bytes < 1024) return `${bytes} B`; 3 | if (bytes < 1024 * 1024) return `${Math.round((bytes / 1024) * 10) / 10} KB`; 4 | if (bytes < 1024 * 1024 * 1024) 5 | return `${Math.round((bytes / 1024 / 1024) * 10) / 10} MB`; 6 | return `${Math.round((bytes / 1024 / 1024 / 1024) * 10) / 10} GB`; 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/swRouter.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export interface RouteParams { 4 | request: Request; 5 | url: URL; 6 | event: FetchEvent; 7 | } 8 | 9 | export type RouteHandler = ( 10 | options: RouteParams, 11 | ) => Promise; 12 | 13 | export class Route { 14 | public matcher: 15 | | string 16 | | RegExp 17 | | ((options: RouteParams) => boolean | Promise); 18 | public handler: ( 19 | options: RouteParams, 20 | ) => Promise | Response | false; 21 | public method?: string | string[]; 22 | 23 | constructor( 24 | matcher: 25 | | string 26 | | RegExp 27 | | ((options: RouteParams) => boolean | Promise), 28 | handler: ( 29 | options: RouteParams, 30 | ) => Promise | Response | false, 31 | method?: string | string[], 32 | ) { 33 | this.matcher = matcher; 34 | this.handler = handler; 35 | this.method = method; 36 | } 37 | } 38 | 39 | export class Router { 40 | public routes: Route[] = []; 41 | 42 | public addRoute( 43 | matcher: 44 | | string 45 | | RegExp 46 | | ((options: RouteParams) => boolean | Promise) 47 | | Route, 48 | handler: ( 49 | options: RouteParams, 50 | ) => Promise | Response | false, 51 | method?: string | string[], 52 | ) { 53 | if (matcher instanceof Route) { 54 | this.routes.push(matcher); 55 | } else this.routes.push(new Route(matcher, handler, method)); 56 | } 57 | 58 | public async handleRequest(event: FetchEvent): Promise { 59 | const reqURL = new URL(event.request.url); 60 | for (const route of this.routes) { 61 | switch (typeof route.matcher) { 62 | // If the matcher is a string, treat it as a URL pathname. Must be an absolute path. 63 | case 'string': 64 | if (reqURL.pathname !== route.matcher) continue; 65 | break; 66 | // If the matcher is a function, call it with the event and request. 67 | case 'function': 68 | if ( 69 | !(await route.matcher({ 70 | url: reqURL, 71 | request: event.request, 72 | event, 73 | })) 74 | ) { 75 | continue; 76 | } 77 | break; 78 | // If the matcher is a RegExp, test the request URL against it. 79 | case 'object': 80 | if (!(route.matcher instanceof RegExp)) { 81 | throw new Error( 82 | 'Route matcher must be a string, RegExp, or function.', 83 | ); 84 | } 85 | if (!route.matcher.test(event.request.url)) { 86 | continue; 87 | } 88 | break; 89 | default: 90 | throw new Error( 91 | 'Route matcher must be a string, RegExp, or function.', 92 | ); 93 | } 94 | // If the route has a method, check if the request method matches. 95 | if (route.method) { 96 | if (typeof route.method === 'string') { 97 | if (route.method !== event.request.method) continue; 98 | } else if (!route.method.includes(event.request.method)) continue; 99 | } 100 | // If the route matches, call the handler and return the response. 101 | const response = await route.handler({ 102 | url: reqURL, 103 | request: event.request, 104 | event, 105 | }); 106 | // If the handler returns false, continue to the next route. 107 | if (response === false) continue; 108 | // Otherwise, return the response. 109 | return response; 110 | } 111 | return false; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/utils/toDateString.ts: -------------------------------------------------------------------------------- 1 | import { displayPrefStore } from '@/stores/displayPref.js'; 2 | 3 | export default function toDateString(date: Date): string { 4 | const displayPref = displayPrefStore(); 5 | return `${date.toLocaleDateString()} ${date.toLocaleString('en-US', { 6 | timeStyle: 'long', 7 | hour12: displayPref.hour12, 8 | })}`; 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/toLogin.ts: -------------------------------------------------------------------------------- 1 | import { Router } from 'vue-router'; 2 | import { userStore } from '@/stores/user.js'; 3 | 4 | export default async function toLogin(router: Router) { 5 | const user = userStore(); 6 | if (user.loggedIn) { 7 | await user.logout(); 8 | } 9 | return await router.replace({ 10 | name: 'account-switcher', 11 | query: { 12 | redirect: router.currentRoute.value.fullPath, 13 | }, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/wait.ts: -------------------------------------------------------------------------------- 1 | import { Ref, watch } from 'vue'; 2 | 3 | export function wait(ms: number): Promise { 4 | return new Promise((resolve) => { 5 | setTimeout(resolve, ms); 6 | }); 7 | } 8 | 9 | export function waitFor(observee: Ref, matchValue: T): Promise { 10 | return new Promise((resolve) => { 11 | const unwatch = watch(observee, (value) => { 12 | if (value === matchValue) { 13 | unwatch(); 14 | resolve(); 15 | } 16 | }); 17 | }); 18 | } 19 | 20 | export function waitUntil( 21 | observee: Ref, 22 | predicate: (value: T) => boolean, 23 | ): Promise { 24 | return new Promise((resolve) => { 25 | const unwatch = watch(observee, (value) => { 26 | if (predicate(value)) { 27 | unwatch(); 28 | resolve(); 29 | } 30 | }); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 66 | -------------------------------------------------------------------------------- /src/views/TOS.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /src/views/staff-space/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 65 | -------------------------------------------------------------------------------- /src/views/staff-space/Utilities.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 110 | -------------------------------------------------------------------------------- /src/views/test/BasicEmphasizedBox.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/views/test/ContentBoxes.vue: -------------------------------------------------------------------------------- 1 | 113 | 114 | 132 | -------------------------------------------------------------------------------- /src/views/test/FileContentBoxes.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | -------------------------------------------------------------------------------- /src/views/test/FormEmphasizedBox.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 76 | -------------------------------------------------------------------------------- /src/views/test/FullscreenLoadingMessage.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | -------------------------------------------------------------------------------- /src/views/test/FullscreenProgressBar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 45 | -------------------------------------------------------------------------------- /src/views/test/LoadingMessage.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /src/views/test/MarqueeText.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 103 | 104 | 114 | -------------------------------------------------------------------------------- /src/views/test/Paginator.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | -------------------------------------------------------------------------------- /src/views/test/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 38 | -------------------------------------------------------------------------------- /src/views/test/SkeletonContentBoxes.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /src/views/test/Switches.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 49 | -------------------------------------------------------------------------------- /src/views/test/Testing.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 122 | 123 | 128 | -------------------------------------------------------------------------------- /src/views/test/TestingLinkSink.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 27 | -------------------------------------------------------------------------------- /src/views/user-space/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 90 | -------------------------------------------------------------------------------- /src/views/user-space/Files.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 145 | -------------------------------------------------------------------------------- /src/views/user-space/SetupGuides.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 94 | -------------------------------------------------------------------------------- /src/views/user-space/Verify.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 80 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2023", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "bundler", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["ES2023", "DOM"], 14 | "skipLibCheck": true, 15 | "paths": { 16 | "@/*": ["./src/*"] 17 | }, 18 | "types": ["vite-plugin-pwa/vue"] 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | --------------------------------------------------------------------------------