diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fc9c5a4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,36 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Defaults +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +# CSS +[*.css] +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + +# HTML +[*.{htm,html}] +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + +# GNU make +[Makefile] +indent_style = tab + +# Svelte +[*.svelte] +indent_size = 2 +indent_style = space + +# YAML +[*.{yaml,yml}] +indent_size = 2 +indent_style = space diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..4e5b51c --- /dev/null +++ b/.env.production @@ -0,0 +1,4 @@ +VITE_SSO_ORIGIN=https://sso.1f349.net + +VITE_API_VIOLET=https://api.1f349.net/v1/violet +VITE_API_ORCHID=https://api.1f349.net/v1/orchid diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..09fb809 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Build/release + +on: [push] + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Check out Git repository + uses: actions/checkout@v3 + + - name: Install Node.js, NPM and Yarn + uses: actions/setup-node@v3 + with: + node-version: 18 + + - run: yarn + - run: yarn build + + - name: Archive + run: tar -czvf upload.tar.gz -C ./dist . + + - name: Release + run: 'curl -X POST -H "Authorization: Bearer ${{ secrets.DEPLOY }}" -F "upload=@upload.tar.gz" "https://sites.1f349.net/u?site=admin.1f349.net&branch=${{ github.ref_name }}"' diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e630e5d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "printWidth": 150, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": false, + "bracketSameLine": false, + "arrowParens": "avoid", + "requirePragma": false, + "insertPragma": false +} diff --git a/index.html b/index.html index b6c5f0a..f3a0dac 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,16 @@ - - + + - - Vite + Svelte + TS + 1f349 Admin Dashboard + + + + + + +
diff --git a/package.json b/package.json index 493849c..7e4c2df 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "version": "0.0.0", "type": "module", "scripts": { + "prettier:check:ci": "./node_modules/.bin/prettier --check .", + "format": "./node_modules/.bin/prettier --write .", "dev": "vite", "build": "vite build", "preview": "vite preview", @@ -12,6 +14,7 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^2.4.2", "@tsconfig/svelte": "^5.0.0", + "sass": "^1.69.4", "svelte": "^4.0.5", "svelte-check": "^3.4.6", "tslib": "^2.6.0", diff --git a/public/1f349.svg b/public/1f349.svg new file mode 100644 index 0000000..76f1b4a --- /dev/null +++ b/public/1f349.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/App.svelte b/src/App.svelte index e8b590f..2fd8697 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,47 +1,161 @@ -
+
- - - - - - +

🍉 - 1f349 Admin Dashboard

-

Vite + Svelte

- -
- +
+ + {#if $loginStore == null} + + {:else} +
+ {$loginStore.userinfo.name}'s profile picture +
{$loginStore.userinfo.name}
+ +
+ {/if} +
+
+ +
+

{sidebarSelection.name}

+ {#if $loginStore == null} +
Please login to continue
+ {:else} + + {/if}
- -

- Check out SvelteKit, the official Svelte app framework powered by Vite! -

- -

- Click on the Vite and Svelte logos to learn more -

- diff --git a/src/app.css b/src/app.css deleted file mode 100644 index b87aec7..0000000 --- a/src/app.css +++ /dev/null @@ -1,80 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -.card { - padding: 2em; -} - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/app.scss b/src/app.scss new file mode 100644 index 0000000..6ba2807 --- /dev/null +++ b/src/app.scss @@ -0,0 +1,46 @@ +$theme-text: rgba(255, 255, 255, 0.87); +$theme-bg: #242424; + +@media (prefers-color-scheme: light) { + $theme-text: #213547; + $theme-bg: #ffffff; +} + +:root { + font-family: Ubuntu, Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: normal; + font-weight: 400; + + color-scheme: light dark; + color: $theme-text; + background-color: $theme-bg; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +#app { + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; + align-content: stretch; +} + +a { + font-weight: 500; + color: tomato; + text-decoration: inherit; +} + +a:hover { + color: tomato; +} + +body { + margin: 0; +} diff --git a/src/assets/svelte.svg b/src/assets/svelte.svg deleted file mode 100644 index c5e0848..0000000 --- a/src/assets/svelte.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/lib/Counter.svelte b/src/lib/Counter.svelte deleted file mode 100644 index 979b4df..0000000 --- a/src/lib/Counter.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/src/main.ts b/src/main.ts index 8a909a1..50dff1a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import './app.css' +import './app.scss' import App from './App.svelte' const app = new App({ diff --git a/src/stores/login.ts b/src/stores/login.ts new file mode 100644 index 0000000..b7a4544 --- /dev/null +++ b/src/stores/login.ts @@ -0,0 +1,28 @@ +import {writable} from "svelte/store"; + +export interface LoginStore { + userinfo: { + email: string; + name: string; + sub: string; + picture: string; + }; + tokens: { + access: string; + refresh: string; + }; +} + +export const loginStore = writable( + (() => { + try { + return JSON.parse(localStorage.getItem("login-session") || ""); + } catch (_) { + return null; + } + })(), +); + +loginStore.subscribe(x => { + localStorage.setItem("login-session", JSON.stringify(x)); +}); diff --git a/src/utils/login-popup.ts b/src/utils/login-popup.ts new file mode 100644 index 0000000..79064f0 --- /dev/null +++ b/src/utils/login-popup.ts @@ -0,0 +1,53 @@ +import {loginStore} from "../stores/login"; + +let currentLoginPopup: {close: () => void} | null = null; + +const ssoOrigin = import.meta.env.VITE_SSO_ORIGIN; + +window.addEventListener("message", function (event) { + if (event.origin !== ssoOrigin) return; + if (isObject(event.data)) { + loginStore.set(event.data); + if (currentLoginPopup) currentLoginPopup.close(); + return; + } + alert("Failed to log user in: the login data was probably corrupted"); +}); + +function isObject(obj: Object) { + return obj != null && obj.constructor.name === "Object"; +} + +function popupCenterScreen(url: string, title: string, w: number, h: number, focus: boolean) { + const top = (screen.availHeight - h) / 4, + left = (screen.availWidth - w) / 2; + const popup = openWindow(url, title, `scrollbars=yes,width=${w},height=${h},top=${top},left=${left}`); + if (focus === true && window.focus !== undefined && popup !== null) popup.focus(); + return popup; +} + +function openWindow(url: string | URL, winnm: string, options: string): Window | null { + var wTop = firstAvailableValue([window.screen.availTop, window.screenY, window.screenTop, 0]); + var wLeft = firstAvailableValue([window.screen.availLeft, window.screenX, window.screenLeft, 0]); + let w: Window | null; + var top = 0, + left = 0; + var result; + if ((result = /top=(\d+)/g.exec(options))) top = parseInt(result[1]); + if ((result = /left=(\d+)/g.exec(options))) left = parseInt(result[1]); + if (options) { + options = options.replace("top=" + top, "top=" + (top + wTop)); + options = options.replace("left=" + left, "left=" + (left + wLeft)); + w = window.open(url, winnm, options); + } else w = window.open(url, winnm); + return w; +} + +function firstAvailableValue(arr: any) { + for (var i = 0; i < arr.length; i++) if (typeof arr[i] != "undefined") return arr[i]; +} + +export function openLoginPopup() { + if (currentLoginPopup) currentLoginPopup.close(); + currentLoginPopup = popupCenterScreen(ssoOrigin + "/popup?origin=" + encodeURIComponent(location.origin), "Login with 1f349 SSO", 500, 500, false); +} diff --git a/src/views/GeneralView.svelte b/src/views/GeneralView.svelte new file mode 100644 index 0000000..7360a9e --- /dev/null +++ b/src/views/GeneralView.svelte @@ -0,0 +1 @@ +
No options available here
diff --git a/src/views/OrchidView.svelte b/src/views/OrchidView.svelte new file mode 100644 index 0000000..ad7456e --- /dev/null +++ b/src/views/OrchidView.svelte @@ -0,0 +1 @@ +
No options available here: {import.meta.env.VITE_API_ORCHID}
diff --git a/src/views/VioletView.svelte b/src/views/VioletView.svelte new file mode 100644 index 0000000..a42c7e6 --- /dev/null +++ b/src/views/VioletView.svelte @@ -0,0 +1,36 @@ + + +{#await promiseForRoutes} +
Loading...
+{:then allRoutes} + + + + + + + + + +
SourceDestinationFlagsActive
+ + + +
+{:catch err} +
{err}
+{/await} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 4078e74..cfb23bc 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1,2 +1,12 @@ /// /// + +interface ImportMetaEnv { + VITE_SSO_ORIGIN: string; + VITE_API_VIOLET: string; + VITE_API_ORCHID: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/yarn.lock b/yarn.lock index 6dfac6a..a5574fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -270,7 +270,7 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -chokidar@^3.4.1: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -430,6 +430,11 @@ graceful-fs@^4.1.3: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +immutable@^4.0.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== + import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -675,6 +680,15 @@ sander@^0.5.0: mkdirp "^0.5.1" rimraf "^2.5.2" +sass@^1.69.4: + version "1.69.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.4.tgz#10c735f55e3ea0b7742c6efa940bce30e07fbca2" + integrity sha512-+qEreVhqAy8o++aQfCJwp0sklr2xyEzkm9Pp/Igu9wNPoe7EZEQ8X/MBvvXggI2ql607cxKg/RKOwDj6pp2XDA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sorcery@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.0.tgz#310c80ee993433854bb55bb9aa4003acd147fca8" @@ -685,7 +699,7 @@ sorcery@^0.11.0: minimist "^1.2.0" sander "^0.5.0" -source-map-js@^1.0.1, source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==