Updates and more
Dieser Commit ist enthalten in:
Ursprung
e97e86f9ac
Commit
7450ecdabb
@ -19,6 +19,7 @@
|
|||||||
"@astrojs/tailwind": "^5.0.2",
|
"@astrojs/tailwind": "^5.0.2",
|
||||||
"@astropub/icons": "^0.2.0",
|
"@astropub/icons": "^0.2.0",
|
||||||
"@types/color": "^3.0.5",
|
"@types/color": "^3.0.5",
|
||||||
|
"@types/crypto-js": "^4.1.3",
|
||||||
"@types/node": "^20.8.10",
|
"@types/node": "^20.8.10",
|
||||||
"cssnano": "^6.0.1",
|
"cssnano": "^6.0.1",
|
||||||
"esbuild": "^0.19.5",
|
"esbuild": "^0.19.5",
|
||||||
@ -34,6 +35,7 @@
|
|||||||
"astro": "^3.4.3",
|
"astro": "^3.4.3",
|
||||||
"astro-i18n": "^2.1.18",
|
"astro-i18n": "^2.1.18",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
"flowbite": "^1.8.1",
|
"flowbite": "^1.8.1",
|
||||||
"flowbite-svelte": "^0.44.19",
|
"flowbite-svelte": "^0.44.19",
|
||||||
"flowbite-svelte-icons": "^0.4.5",
|
"flowbite-svelte-icons": "^0.4.5",
|
||||||
|
227
src/components/Dashboard.svelte
Normale Datei
227
src/components/Dashboard.svelte
Normale Datei
@ -0,0 +1,227 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {astroI18n, t} from "astro-i18n"
|
||||||
|
import {l} from "../util/util.ts"
|
||||||
|
import {fetchWithToken, tokenStore} from "./repo/repo.ts";
|
||||||
|
import {get} from "svelte/store";
|
||||||
|
import {SchematicListSchema} from "./types/schem.ts";
|
||||||
|
import {PlayerSchema} from "./types/data.ts";
|
||||||
|
import moment from "moment";
|
||||||
|
import {
|
||||||
|
CheckSolid,
|
||||||
|
ChevronDoubleRightOutline,
|
||||||
|
FileOutline,
|
||||||
|
FolderOutline, HomeOutline,
|
||||||
|
InfoCircleOutline,
|
||||||
|
XCircleOutline
|
||||||
|
} from "flowbite-svelte-icons";
|
||||||
|
import {Breadcrumb, BreadcrumbItem, Modal, Tooltip} from "flowbite-svelte";
|
||||||
|
import SchematicInfo from "./SchematicInfo.svelte";
|
||||||
|
|
||||||
|
let userFetch = getUser();
|
||||||
|
let schematicFetch = getSchematics();
|
||||||
|
|
||||||
|
let uploadOpen = false;
|
||||||
|
let infoModalId: number | null = null;
|
||||||
|
|
||||||
|
function getUser() {
|
||||||
|
return fetchWithToken(get(tokenStore), "/data/me").then(value => value.json()).then(value => PlayerSchema.parse(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
tokenStore.set("")
|
||||||
|
window.location.href = l("/login")
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSchematics() {
|
||||||
|
return fetchWithToken(get(tokenStore), "/schem/list").then(value => value.json()).then(value => SchematicListSchema.parse(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSchematic(id: number) {
|
||||||
|
return fetchWithToken(get(tokenStore), "/schem/list/" + id).then(value => value.json()).then(value => SchematicListSchema.parse(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function schemListClick(isDir: boolean, id: number) {
|
||||||
|
if (isDir) {
|
||||||
|
return () => schematicFetch = getSchematic(id)
|
||||||
|
} else {
|
||||||
|
return () => infoModalId = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await userFetch}
|
||||||
|
<p>loading...</p>
|
||||||
|
{:then user}
|
||||||
|
<div class="flex mb-4 flex-col md:flex-row">
|
||||||
|
<div>
|
||||||
|
<div class="bg-zinc-50 border-gray-100 py-24 px-12 border-2 m-2 transition duration-300 ease-in-out rounded-xl shadow-lg hidden md:block
|
||||||
|
hover:scale-105 hover:shadow-2xl
|
||||||
|
dark:bg-neutral-900 dark:border-gray-800 dark:text-white">
|
||||||
|
<figure>
|
||||||
|
<figcaption class="text-center mb-4 text-2xl">{user.name}</figcaption>
|
||||||
|
<img src={`https://visage.surgeplay.com/bust/150/${user.uuid}`} class="transition duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl" alt={user.name + "s bust"} width="150" height="150" />
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
<button class="btn mt-2" on:click={logout}>Logout</button>
|
||||||
|
{#if user.perms.includes("MODERATION")}
|
||||||
|
<a class="btn w-fit mt-2" href="/admin">Admin Panel</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-4xl font-bold">Hello, {user.name}</h1>
|
||||||
|
<p>Rang: {t("home.prefix." + user.prefix)}</p>
|
||||||
|
<p>Permissions:</p>
|
||||||
|
<ul>
|
||||||
|
{#each user.perms as permission}
|
||||||
|
<li class="list-disc ml-6">{permission}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
{#await schematicFetch}
|
||||||
|
<p>Loading...</p>
|
||||||
|
{:then schematics}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<Breadcrumb navClass="py-4">
|
||||||
|
<BreadcrumbItem home>
|
||||||
|
<svelte:fragment slot="icon">
|
||||||
|
<HomeOutline class="w-6 h-6 mx-2 dark:text-white" />
|
||||||
|
</svelte:fragment>
|
||||||
|
<span on:click={() => schematicFetch = getSchematics()} class="hover:underline hover:cursor-pointer text-2xl">Schematics</span>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
{#each schematics.breadcrumbs as bread}
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<svelte:fragment slot="icon">
|
||||||
|
<ChevronDoubleRightOutline class="w-4 h-4 mx-2 dark:text-white" />
|
||||||
|
</svelte:fragment>
|
||||||
|
<span on:click={() => schematicFetch = getSchematic(bread.id)} class="hover:underline hover:cursor-pointer text-2xl">{bread.name}</span>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
{/each}
|
||||||
|
</Breadcrumb>
|
||||||
|
<div class="flex flex-col justify-center">
|
||||||
|
<button class="btn" on:click={() => uploadOpen = true}>
|
||||||
|
Upload
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr class="!cursor-auto">
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th class="hidden sm:table-cell">Owner</th>
|
||||||
|
<th class="hidden sm:table-cell"></th>
|
||||||
|
<th class="hidden md:table-cell">Updated</th>
|
||||||
|
<th>
|
||||||
|
<InfoCircleOutline />
|
||||||
|
<Tooltip>
|
||||||
|
<span>Replace Color</span>
|
||||||
|
</Tooltip>
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<InfoCircleOutline />
|
||||||
|
<Tooltip>
|
||||||
|
<span>Allow Replay</span>
|
||||||
|
</Tooltip>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
{#if schematics.breadcrumbs.length !== 0}
|
||||||
|
<tr on:click|preventDefault={() => {
|
||||||
|
if (schematics.breadcrumbs.length === 1) {
|
||||||
|
schematicFetch = getSchematics()
|
||||||
|
} else {
|
||||||
|
schematicFetch = getSchematic(schematics.breadcrumbs[schematics.breadcrumbs.length - 2].id)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<th>
|
||||||
|
<FolderOutline />
|
||||||
|
</th>
|
||||||
|
<th>../</th>
|
||||||
|
<th class="hidden sm:table-cell"></th>
|
||||||
|
<th class="hidden sm:table-cell">Directory</th>
|
||||||
|
<th class="hidden md:table-cell"></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{#each schematics.schematics as schem}
|
||||||
|
<tr on:click|preventDefault={schemListClick(schem.type == null, schem.id)}>
|
||||||
|
<th>
|
||||||
|
{#if schem.type == null}
|
||||||
|
<FolderOutline />
|
||||||
|
{:else}
|
||||||
|
<FileOutline />
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{schem.name}{#if schem.type == null}/{/if}
|
||||||
|
</th>
|
||||||
|
<th class="hidden sm:table-cell">{schematics.players[schem.owner].name}</th>
|
||||||
|
<th class="hidden sm:table-cell">{schem.type ?? "Directory"}</th>
|
||||||
|
<th class="hidden md:table-cell">{new Intl.DateTimeFormat(astroI18n.locale, {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric"
|
||||||
|
}).format(moment(schem.lastUpdate).utc(false).toDate())}</th>
|
||||||
|
<th>
|
||||||
|
{#if schem.replaceColor}
|
||||||
|
<CheckSolid class="text-green-500" />
|
||||||
|
{:else}
|
||||||
|
<XCircleOutline class="text-red-500" />
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{#if schem.allowReplay}
|
||||||
|
<CheckSolid class="text-green-500" />
|
||||||
|
{:else}
|
||||||
|
<XCircleOutline class="text-red-500" />
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:catch error}
|
||||||
|
<p>error: {error.message}</p>
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
{:catch error}
|
||||||
|
<p>error: {error.message}</p>
|
||||||
|
{/await}
|
||||||
|
|
||||||
|
<Modal title="Upload Schematic" bind:open={uploadOpen} autoclose outsideclose>
|
||||||
|
<form>
|
||||||
|
<input type="file" multiple />
|
||||||
|
</form>
|
||||||
|
<svelte:fragment slot="footer">
|
||||||
|
<button class="btn !ml-auto" on:click={() => uploadOpen = false}>Upload</button>
|
||||||
|
<button class="btn btn-gray" on:click={() => uploadOpen = false}>Close</button>
|
||||||
|
</svelte:fragment>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{#if infoModalId !== null}
|
||||||
|
<SchematicInfo schematicId={infoModalId} on:reset={() => infoModalId = null} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
table {
|
||||||
|
@apply w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
@apply transition-colors cursor-pointer border-b
|
||||||
|
dark:hover:bg-gray-800 hover:bg-gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
@apply text-left py-4 md:px-2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
67
src/components/Login.svelte
Normale Datei
67
src/components/Login.svelte
Normale Datei
@ -0,0 +1,67 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {l} from "../util/util.ts";
|
||||||
|
import {t} from "astro-i18n";
|
||||||
|
|
||||||
|
let username = "";
|
||||||
|
let token = "";
|
||||||
|
|
||||||
|
let error = "";
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
let {tokenStore} = await import("./repo/repo.ts");
|
||||||
|
if (username === "" || token === "") {
|
||||||
|
token = "";
|
||||||
|
alert(t("login.error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let res = await fetch(import.meta.env.PUBLIC_API_SERVER + "/data/me", {
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Bearer " + token
|
||||||
|
}
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
|
if (res.name !== username) {
|
||||||
|
alert(t("login.error"));
|
||||||
|
token = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenStore.set(token);
|
||||||
|
window.location.href = l("/dashboard");
|
||||||
|
} catch (e) {
|
||||||
|
alert(t("login.error"));
|
||||||
|
token = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form class="bg-gray-100 dark:bg-neutral-900 p-12 rounded-2xl shadow-2xl border-2 border-gray-600 flex flex-col" on:submit|preventDefault={login}>
|
||||||
|
<h1 class="text-4xl text-white text-center">{t("login.title")}</h1>
|
||||||
|
<div class="ml-2 flex flex-col">
|
||||||
|
<label for="username">{t("login.label.username")}</label>
|
||||||
|
<input type="text" id="username" name="username" placeholder={t("login.placeholder.username")} bind:value={username} />
|
||||||
|
<label for="token">{t("login.label.token")}</label>
|
||||||
|
<input type="password" id="token" name="token" placeholder={t("login.placeholder.token")} bind:value={token} />
|
||||||
|
</div>
|
||||||
|
<p class="mt-2">
|
||||||
|
<a class="text-neutral-500 hover:underline" href={l("/help/token")}>{t("login.generateToken")}</a></p>
|
||||||
|
|
||||||
|
{#if error}
|
||||||
|
<p class="mt-2 text-red-500">{error}</p>
|
||||||
|
{/if}
|
||||||
|
<button class="btn mt-4 !mx-0 justify-center" type="submit" on:click|preventDefault={login}>{t("login.submit")}</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
input {
|
||||||
|
@apply border-2 rounded-md p-2 shadow-2xl w-80
|
||||||
|
dark:bg-neutral-800
|
||||||
|
focus:outline-none focus:ring-2 focus:ring-neutral-500 focus:border-transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
@apply text-neutral-300;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,11 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import {server} from "./stores/server.ts";
|
import {server} from "./stores/stores.ts";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await $server}
|
{#await $server}
|
||||||
{:then data}
|
{:then data}
|
||||||
{data.players.online}
|
{data.players.online}
|
||||||
{:catch error}
|
{:catch error}
|
||||||
Error
|
|
||||||
{/await}
|
{/await}
|
65
src/components/SchematicInfo.svelte
Normale Datei
65
src/components/SchematicInfo.svelte
Normale Datei
@ -0,0 +1,65 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
import {createEventDispatcher} from "svelte";
|
||||||
|
import {fetchWithToken, tokenStore} from "./repo/repo.ts";
|
||||||
|
import {get} from "svelte/store";
|
||||||
|
import {Modal, Spinner} from "flowbite-svelte";
|
||||||
|
import {SchematicInfoSchema} from "./types/schem.ts";
|
||||||
|
import {astroI18n} from "astro-i18n";
|
||||||
|
import moment from "moment/moment";
|
||||||
|
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
export let schematicId: number;
|
||||||
|
|
||||||
|
let schemInfo = getSchematicInfo(schematicId);
|
||||||
|
|
||||||
|
function getSchematicInfo(id: number) {
|
||||||
|
return fetchWithToken(get(tokenStore), "/schem/info/" + id).then(r => r.json()).then(r => SchematicInfoSchema.parse(r))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await schemInfo}
|
||||||
|
<Modal title="Loading" open>
|
||||||
|
<Spinner />
|
||||||
|
</Modal>
|
||||||
|
{:then info}
|
||||||
|
<Modal title={info.schem.name} autoclose open>
|
||||||
|
<p>Path: {info.path}</p>
|
||||||
|
<p class="flex !mt-0">
|
||||||
|
Replace Color:
|
||||||
|
{#if info.schem.replaceColor}
|
||||||
|
<CheckSolid class="text-green-500 ml-2" />
|
||||||
|
{:else}
|
||||||
|
<XCircleOutline class="text-red-500 ml-2" />
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
<p class="flex !mt-0">
|
||||||
|
Allow Replay: {#if info.schem.allowReplay}
|
||||||
|
<CheckSolid class="text-green-500 ml-2" />
|
||||||
|
{:else}
|
||||||
|
<XCircleOutline class="text-red-500 ml-2" />
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
<p class="!mt-0">Type: {info.schem.type ?? "Directory"}</p>
|
||||||
|
<p class="!mt-0">Updated: {new Intl.DateTimeFormat(astroI18n.locale, {
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
month: "2-digit",
|
||||||
|
year: "numeric"
|
||||||
|
}).format(moment(info.schem.lastUpdate).utc(false).toDate())}</p>
|
||||||
|
<p class="!mt-0">Item: {info.schem.item ?? (info.schem.type == null ? "CHEST" : "CAULDRON_ITEM")}</p>
|
||||||
|
{#if info.members.length !== 0}
|
||||||
|
<p class="!mt-0">Member: {info.members.join(", ")}</p>
|
||||||
|
{/if}
|
||||||
|
<button class="btn !ml-auto" slot="footer" on:click={() => dispatch("reset")}>Close</button>
|
||||||
|
</Modal>
|
||||||
|
{:catch e}
|
||||||
|
<Modal title="Error" open>
|
||||||
|
<p>{e.message}</p>
|
||||||
|
<button class="btn !ml-auto" slot="footer" on:click={() => dispatch("reset")}>Close</button>
|
||||||
|
</Modal>
|
||||||
|
{/await}
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { t } from "astro-i18n"
|
import { t } from "astro-i18n"
|
||||||
import {server} from "./stores/server.ts";
|
import {server} from "./stores/stores.ts";
|
||||||
|
|
||||||
function generateVersionString(version: string): string {
|
function generateVersionString(version: string): string {
|
||||||
let versions = version.split(" ").slice(1)
|
let versions = version.split(" ").slice(1)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import wrap from "svelte-spa-router/wrap";
|
import wrap from "svelte-spa-router/wrap";
|
||||||
import Router, {replace} from "svelte-spa-router";
|
import Router, {replace} from "svelte-spa-router";
|
||||||
import {get} from "svelte/store";
|
import {get} from "svelte/store";
|
||||||
import {tokenStore} from "./repo/repo.js";
|
import {tokenStore} from "../repo/repo.js";
|
||||||
|
|
||||||
const routes: RouteDefinition = {
|
const routes: RouteDefinition = {
|
||||||
'/': wrap({asyncComponent: () => import('./pages/Home.svelte'), conditions: detail => get(tokenStore) != ""}),
|
'/': wrap({asyncComponent: () => import('./pages/Home.svelte'), conditions: detail => get(tokenStore) != ""}),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Input, Label, Select} from "flowbite-svelte";
|
import {Input, Label, Select} from "flowbite-svelte";
|
||||||
import TypeAheadSearch from "./TypeAheadSearch.svelte";
|
import TypeAheadSearch from "./TypeAheadSearch.svelte";
|
||||||
import {gamemodes, groups, maps, players} from "../stores/stores.js";
|
import {gamemodes, groups, maps, players} from "../../stores/stores.js";
|
||||||
import type {Team} from '../types/team.js';
|
import type {Team} from '../../types/team.js';
|
||||||
|
|
||||||
export let teams: Team[] = [];
|
export let teams: Team[] = [];
|
||||||
export let blueTeam: string;
|
export let blueTeam: string;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||||
import {Button, Card, Input, Label, Navbar, NavBrand, NavHamburger, NavUl, Spinner} from "flowbite-svelte";
|
import {Button, Card, Input, Label, Navbar, NavBrand, NavHamburger, NavUl, Spinner} from "flowbite-svelte";
|
||||||
import {pageRepo} from "../repo/repo.js";
|
import {pageRepo} from "../../repo/repo.js";
|
||||||
import {mapToMap, nameRegex} from "../util.ts";
|
import {mapToMap, nameRegex} from "../util.ts";
|
||||||
import TypeAheadSearch from "../components/TypeAheadSearch.svelte";
|
import TypeAheadSearch from "../components/TypeAheadSearch.svelte";
|
||||||
import {branches} from "../stores/stores.ts";
|
import {branches} from "../../stores/stores.ts";
|
||||||
import Editor from "./edit/Editor.svelte";
|
import Editor from "./edit/Editor.svelte";
|
||||||
|
|
||||||
$: pagesFuture = $pageRepo.listPages(selectedBranch);
|
$: pagesFuture = $pageRepo.listPages(selectedBranch);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import EventEdit from "./event/EventEdit.svelte";
|
import EventEdit from "./event/EventEdit.svelte";
|
||||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||||
import FightList from "./event/FightList.svelte";
|
import FightList from "./event/FightList.svelte";
|
||||||
import {eventRepo} from "../repo/repo.js";
|
import {eventRepo} from "../../repo/repo.js";
|
||||||
import TeamList from "./event/TeamList.svelte";
|
import TeamList from "./event/TeamList.svelte";
|
||||||
|
|
||||||
export let params: { id: number } = {};
|
export let params: { id: number } = {};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import EventEdit from "./event/EventEdit.svelte";
|
import EventEdit from "./event/EventEdit.svelte";
|
||||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||||
import FightList from "./event/FightList.svelte";
|
import FightList from "./event/FightList.svelte";
|
||||||
import {eventRepo} from "../repo/repo.js";
|
import {eventRepo} from "../../repo/repo.js";
|
||||||
import TeamList from "./event/TeamList.svelte";
|
import TeamList from "./event/TeamList.svelte";
|
||||||
import GroupGenerator from "./generate/GroupGenerator.svelte";
|
import GroupGenerator from "./generate/GroupGenerator.svelte";
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import {PlusSolid} from "flowbite-svelte-icons";
|
import {PlusSolid} from "flowbite-svelte-icons";
|
||||||
import EventCard from "./home/EventCard.svelte";
|
import EventCard from "./home/EventCard.svelte";
|
||||||
import CreateEventModal from "./home/CreateEventModal.svelte";
|
import CreateEventModal from "./home/CreateEventModal.svelte";
|
||||||
import {eventRepo, tokenStore} from "../repo/repo.js";
|
import {eventRepo, tokenStore} from "../../repo/repo.js";
|
||||||
|
|
||||||
let events = $eventRepo.listEvents()
|
let events = $eventRepo.listEvents()
|
||||||
let showAdd = false
|
let showAdd = false
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import {fly} from "svelte/transition";
|
import {fly} from "svelte/transition";
|
||||||
import {replace} from "svelte-spa-router";
|
import {replace} from "svelte-spa-router";
|
||||||
import {EyeOutline, EyeSlashOutline, EyeSolid} from "flowbite-svelte-icons";
|
import {EyeOutline, EyeSlashOutline, EyeSolid} from "flowbite-svelte-icons";
|
||||||
import {fetchWithToken, tokenStore} from "../repo/repo.js";
|
import {fetchWithToken, tokenStore} from "../../repo/repo.js";
|
||||||
|
|
||||||
let show = false;
|
let show = false;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Button, Card, Checkbox, Input, Label, Navbar, NavBrand, Radio, Spinner} from "flowbite-svelte";
|
import {Button, Card, Checkbox, Input, Label, Navbar, NavBrand, Radio, Spinner} from "flowbite-svelte";
|
||||||
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
import {ArrowLeftSolid} from "flowbite-svelte-icons";
|
||||||
import {players} from "../stores/stores.ts";
|
import {players} from "../../stores/stores.ts";
|
||||||
import {permsRepo} from "../repo/repo.ts";
|
import {permsRepo} from "../../repo/repo.ts";
|
||||||
import {capitalize} from "../util.ts";
|
import {capitalize} from "../util.ts";
|
||||||
|
|
||||||
let search = "";
|
let search = "";
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
import {Spinner, Toolbar, ToolbarButton, ToolbarGroup, Tooltip} from "flowbite-svelte";
|
import {Spinner, Toolbar, ToolbarButton, ToolbarGroup, Tooltip} from "flowbite-svelte";
|
||||||
import {markdown} from "@codemirror/lang-markdown";
|
import {markdown} from "@codemirror/lang-markdown";
|
||||||
import CodeMirror from "svelte-codemirror-editor";
|
import CodeMirror from "svelte-codemirror-editor";
|
||||||
import {pageRepo} from "../../repo/repo.ts";
|
import {pageRepo} from "../../../repo/repo.ts";
|
||||||
import {base64ToBytes} from "../../util.ts";
|
import {base64ToBytes} from "../../util.ts";
|
||||||
import type {Page} from "../../types/page.ts";
|
import type {Page} from "../../../types/page.ts";
|
||||||
import {materialDark} from '@ddietr/codemirror-themes/material-dark.js'
|
import {materialDark} from '@ddietr/codemirror-themes/material-dark.js'
|
||||||
import {EditOutline} from "flowbite-svelte-icons";
|
import {EditOutline} from "flowbite-svelte-icons";
|
||||||
import {createEventDispatcher} from "svelte";
|
import {createEventDispatcher} from "svelte";
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {ExtendedEvent} from "../../types/event.js";
|
import type {ExtendedEvent} from "../../../types/event.js";
|
||||||
import {Button, Heading, Input, Label, Modal, Range, Select, Toast, Toggle} from "flowbite-svelte";
|
import {Button, Heading, Input, Label, Modal, Range, Select, Toast, Toggle} from "flowbite-svelte";
|
||||||
import {schemTypes} from "../../stores/stores.js";
|
import {schemTypes} from "../../../stores/stores.js";
|
||||||
import moment from "moment/moment.js";
|
import moment from "moment/moment.js";
|
||||||
import type {UpdateEvent} from "../../repo/event.js";
|
import type {UpdateEvent} from "../../../repo/event.js";
|
||||||
import {eventRepo} from "../../repo/repo.js";
|
import {eventRepo} from "../../../repo/repo.js";
|
||||||
import ErrorModal from "../../components/ErrorModal.svelte";
|
import ErrorModal from "../../components/ErrorModal.svelte";
|
||||||
import {replace} from "svelte-spa-router";
|
import {replace} from "svelte-spa-router";
|
||||||
import {CheckCircleOutline} from "flowbite-svelte-icons";
|
import {CheckCircleOutline} from "flowbite-svelte-icons";
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {EditOutline, InboxSolid, TrashBinOutline} from "flowbite-svelte-icons";
|
import {EditOutline, InboxSolid, TrashBinOutline} from "flowbite-svelte-icons";
|
||||||
import {Button, Checkbox, Modal, Toolbar, ToolbarButton} from "flowbite-svelte";
|
import {Button, Checkbox, Modal, Toolbar, ToolbarButton} from "flowbite-svelte";
|
||||||
import type {EventFight, ExtendedEvent} from "../../types/event.js";
|
import type {EventFight, ExtendedEvent} from "../../../types/event.js";
|
||||||
import FightEditModal from "./modals/FightEditModal.svelte";
|
import FightEditModal from "./modals/FightEditModal.svelte";
|
||||||
import {createEventDispatcher, onMount} from "svelte";
|
import {createEventDispatcher, onMount} from "svelte";
|
||||||
import {fightRepo} from "../../repo/repo.js";
|
import {fightRepo} from "../../../repo/repo.js";
|
||||||
import {isWide} from "../../stores/stores.js";
|
import {isWide} from "../../../stores/stores.js";
|
||||||
|
|
||||||
export let fight: EventFight;
|
export let fight: EventFight;
|
||||||
export let data: ExtendedEvent;
|
export let data: ExtendedEvent;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {EventFight, ExtendedEvent} from "../../types/event.js";
|
import type {EventFight, ExtendedEvent} from "../../../types/event.js";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Checkbox, Input, Label,
|
Checkbox, Input, Label,
|
||||||
@ -17,10 +17,10 @@
|
|||||||
} from "flowbite-svelte-icons";
|
} from "flowbite-svelte-icons";
|
||||||
import FightCard from "./FightCard.svelte";
|
import FightCard from "./FightCard.svelte";
|
||||||
import CreateFightModal from "./modals/CreateFightModal.svelte";
|
import CreateFightModal from "./modals/CreateFightModal.svelte";
|
||||||
import {fightRepo} from "../../repo/repo.js";
|
import {fightRepo} from "../../../repo/repo.js";
|
||||||
import {groups, players} from "../../stores/stores.js";
|
import {groups, players} from "../../../stores/stores.js";
|
||||||
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
|
import TypeAheadSearch from "../../components/TypeAheadSearch.svelte";
|
||||||
import type {UpdateFight} from "../../repo/fight.js";
|
import type {UpdateFight} from "../../../repo/fight.js";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
export let data: ExtendedEvent;
|
export let data: ExtendedEvent;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Avatar, Button, Modal} from "flowbite-svelte";
|
import {Avatar, Button, Modal} from "flowbite-svelte";
|
||||||
import type {ExtendedEvent} from "../../types/event.js";
|
import type {ExtendedEvent} from "../../../types/event.js";
|
||||||
|
|
||||||
export let data: ExtendedEvent;
|
export let data: ExtendedEvent;
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Button, Modal} from "flowbite-svelte";
|
import {Button, Modal} from "flowbite-svelte";
|
||||||
import type {ExtendedEvent} from "../../../types/event.js";
|
import type {ExtendedEvent} from "../../../../types/event.js";
|
||||||
import FightEditPart from "../../../components/FightEditPart.svelte";
|
import FightEditPart from "../../../components/FightEditPart.svelte";
|
||||||
import {fightRepo} from "../../../repo/repo.js";
|
import {fightRepo} from "../../../../repo/repo.js";
|
||||||
import type {CreateFight} from "../../../repo/fight.ts";
|
import type {CreateFight} from "../../../../repo/fight.ts";
|
||||||
import ErrorModal from "../../../components/ErrorModal.svelte";
|
import ErrorModal from "../../../components/ErrorModal.svelte";
|
||||||
import {createEventDispatcher} from "svelte";
|
import {createEventDispatcher} from "svelte";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Button, Input, Label, Modal, Select} from "flowbite-svelte";
|
import {Button, Input, Label, Modal, Select} from "flowbite-svelte";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {gamemodes, groups, maps, players} from "../../../stores/stores.js";
|
import {gamemodes, groups, maps, players} from "../../../../stores/stores.js";
|
||||||
import type {EventFight, ExtendedEvent} from "../../../types/event.js";
|
import type {EventFight, ExtendedEvent} from "../../../../types/event.js";
|
||||||
import TypeAheadSearch from "../../../components/TypeAheadSearch.svelte";
|
import TypeAheadSearch from "../../../components/TypeAheadSearch.svelte";
|
||||||
import FightEditPart from "../../../components/FightEditPart.svelte";
|
import FightEditPart from "../../../components/FightEditPart.svelte";
|
||||||
import type {UpdateFight} from "../../../repo/fight.js";
|
import type {UpdateFight} from "../../../../repo/fight.js";
|
||||||
import {fightRepo} from "../../../repo/repo.js";
|
import {fightRepo} from "../../../../repo/repo.js";
|
||||||
import ErrorModal from "../../../components/ErrorModal.svelte";
|
import ErrorModal from "../../../components/ErrorModal.svelte";
|
||||||
import {createEventDispatcher} from "svelte";
|
import {createEventDispatcher} from "svelte";
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {ExtendedEvent} from "../../types/event.js";
|
import type {ExtendedEvent} from "../../../types/event.js";
|
||||||
import TeamChip from "./TeamChip.svelte";
|
import TeamChip from "./TeamChip.svelte";
|
||||||
import type {Team} from "../../types/team.js";
|
import type {Team} from "../../../types/team.js";
|
||||||
import DragAcceptor from "./DragAcceptor.svelte";
|
import DragAcceptor from "./DragAcceptor.svelte";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {Button, Input, Label, Modal, Range, Select} from "flowbite-svelte";
|
import {Button, Input, Label, Modal, Range, Select} from "flowbite-svelte";
|
||||||
import {gamemodes, maps} from "../../stores/stores.js";
|
import {gamemodes, maps} from "../../../stores/stores.js";
|
||||||
import {PlusSolid} from "flowbite-svelte-icons";
|
import {PlusSolid} from "flowbite-svelte-icons";
|
||||||
import {fightRepo} from "../../repo/repo.js";
|
import {fightRepo} from "../../../repo/repo.js";
|
||||||
import {replace} from "svelte-spa-router";
|
import {replace} from "svelte-spa-router";
|
||||||
|
|
||||||
export let data: ExtendedEvent;
|
export let data: ExtendedEvent;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {Team} from "../../types/team.js";
|
import type {Team} from "../../../types/team.js";
|
||||||
import {brightness, colorFromTeam, lighten} from "../../util.js";
|
import {brightness, colorFromTeam, lighten} from "../../util.js";
|
||||||
|
|
||||||
export let team: Team;
|
export let team: Team;
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import {createEventDispatcher} from "svelte";
|
import {createEventDispatcher} from "svelte";
|
||||||
import ErrorModal from "../../components/ErrorModal.svelte";
|
import ErrorModal from "../../components/ErrorModal.svelte";
|
||||||
import {eventRepo} from "../../repo/repo.js";
|
import {eventRepo} from "../../../repo/repo.js";
|
||||||
import type {SWEvent} from "../../types/event.js";
|
import type {SWEvent} from "../../../types/event.js";
|
||||||
|
|
||||||
export let open = false;
|
export let open = false;
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {Card} from "flowbite-svelte";
|
import {Card} from "flowbite-svelte";
|
||||||
import {link} from 'svelte-spa-router'
|
import {link} from 'svelte-spa-router'
|
||||||
import type {ShortEvent} from "../../types/event.js";
|
import type {ShortEvent} from "../../../types/event.js";
|
||||||
|
|
||||||
export let event: ShortEvent;
|
export let event: ShortEvent;
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import {z} from "zod";
|
|
||||||
|
|
||||||
export const SchematicTypeSchema = z.object({
|
|
||||||
name: z.string(),
|
|
||||||
db: z.string(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export type SchematicType = z.infer<typeof SchematicTypeSchema>;
|
|
||||||
|
|
||||||
export const PlayerSchema = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
name: z.string(),
|
|
||||||
uuid: z.string(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export type Player = z.infer<typeof PlayerSchema>;
|
|
@ -1,6 +1,6 @@
|
|||||||
import Color from "color";
|
import Color from "color";
|
||||||
import type {Team} from "./types/team.js";
|
import type {Team} from "../types/team.js";
|
||||||
import type {ListPage, PageList} from "./types/page.ts";
|
import type {ListPage, PageList} from "../types/page.ts";
|
||||||
|
|
||||||
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export const nameRegex = new RegExp("(?!.*\/).+(?=\\.md)");
|
|||||||
export function mapToMap(pages: PageList): Map<string, ListPage[]> {
|
export function mapToMap(pages: PageList): Map<string, ListPage[]> {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
let folder = page.path.substring(0, page.path.indexOf(nameRegex.exec(page.path)[0]));
|
let folder = page.path.substring(0, page.path.indexOf(nameRegex.exec(page.path)!![0]));
|
||||||
if (!map.has(folder)) {
|
if (!map.has(folder)) {
|
||||||
map.set(folder, []);
|
map.set(folder, []);
|
||||||
}
|
}
|
||||||
|
11
src/components/repo/data.ts
Normale Datei
11
src/components/repo/data.ts
Normale Datei
@ -0,0 +1,11 @@
|
|||||||
|
import type {Server} from "../types/data.ts";
|
||||||
|
import {ServerSchema} from "../types/data.ts";
|
||||||
|
import {fetchWithToken} from "./repo.ts";
|
||||||
|
|
||||||
|
export class DataRepo {
|
||||||
|
constructor(private token: string) {}
|
||||||
|
|
||||||
|
public async getServer(): Promise<Server> {
|
||||||
|
return await fetchWithToken(this.token, "/data/server").then(value => value.json()).then(value => ServerSchema.parse(value));
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import type {Page, PageList} from "../types/page.ts";
|
import type {Page, PageList} from "../types/page.ts";
|
||||||
import {fetchWithToken} from "./repo.ts";
|
import {fetchWithToken} from "./repo.ts";
|
||||||
import {PageListSchema, PageSchema} from "../types/page.ts";
|
import {PageListSchema, PageSchema} from "../types/page.ts";
|
||||||
import {bytesToBase64} from "../util.ts";
|
import {bytesToBase64} from "../admin/util.ts";
|
||||||
import {branches} from "../stores/stores.ts";
|
import {branches} from "../stores/stores.ts";
|
||||||
|
|
||||||
export class PageRepo {
|
export class PageRepo {
|
@ -3,16 +3,29 @@ import {EventRepo} from "./event.js";
|
|||||||
import {FightRepo} from "./fight.js";
|
import {FightRepo} from "./fight.js";
|
||||||
import {PermsRepo} from "./perms.js";
|
import {PermsRepo} from "./perms.js";
|
||||||
import {PageRepo} from "./page.ts";
|
import {PageRepo} from "./page.ts";
|
||||||
|
import {DataRepo} from "./data.ts";
|
||||||
|
|
||||||
|
import { AES, enc, format } from "crypto-js";
|
||||||
|
|
||||||
export { EventRepo } from "./event.js"
|
export { EventRepo } from "./event.js"
|
||||||
|
|
||||||
export const apiUrl = import.meta.env.PUBLIC_API_SERVER;
|
export const apiUrl = import.meta.env.PUBLIC_API_SERVER;
|
||||||
|
const secret = import.meta.env.PUBLIC_SECRET;
|
||||||
|
|
||||||
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) => fetch(`${apiUrl}${url}`, {...params, headers: {"Authorization": "Bearer " + (token), "Content-Type": "application/json", ...params.headers}});
|
function encryptToken(token: string): string {
|
||||||
export const tokenStore = writable(localStorage.getItem("sw-api-token") ?? "")
|
return AES.encrypt(token, secret).toString();
|
||||||
tokenStore.subscribe((value) => localStorage.setItem("sw-api-token", value))
|
}
|
||||||
|
|
||||||
|
function decryptToken(token: string): string {
|
||||||
|
return AES.decrypt(token, secret).toString(enc.Utf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) => fetch(`${apiUrl}${url}`, {...params, headers: {...(token !== "" ? {"Authorization": "Bearer " + (token)}:{}), "Content-Type": "application/json", ...params.headers}});
|
||||||
|
export const tokenStore = writable(decryptToken(localStorage.getItem("sw-session") ?? ""))
|
||||||
|
tokenStore.subscribe((value) => localStorage.setItem("sw-session", encryptToken(value)))
|
||||||
|
|
||||||
export const eventRepo = derived(tokenStore, ($token) => new EventRepo($token))
|
export const eventRepo = derived(tokenStore, ($token) => new EventRepo($token))
|
||||||
export const fightRepo = derived(tokenStore, ($token) => new FightRepo($token))
|
export const fightRepo = derived(tokenStore, ($token) => new FightRepo($token))
|
||||||
export const permsRepo = derived(tokenStore, ($token) => new PermsRepo($token))
|
export const permsRepo = derived(tokenStore, ($token) => new PermsRepo($token))
|
||||||
export const pageRepo = derived(tokenStore, ($token) => new PageRepo($token))
|
export const pageRepo = derived(tokenStore, ($token) => new PageRepo($token))
|
||||||
|
export const dataRepo = derived(tokenStore, ($token) => new DataRepo($token))
|
@ -1,4 +0,0 @@
|
|||||||
import {readable} from "svelte/store";
|
|
||||||
import type {Readable} from "svelte/store";
|
|
||||||
|
|
||||||
export const server = readable(fetch(import.meta.env.PUBLIC_API_SERVER + "/data/server").then(res => res.json()))
|
|
@ -1,10 +1,10 @@
|
|||||||
import type {Player, SchematicType} from "../types/data.js";
|
import type {Player, SchematicType, Server} from "../types/data.js";
|
||||||
import {PlayerSchema} from "../types/data.js";
|
import {PlayerSchema, ServerSchema} from "../types/data.js";
|
||||||
import {cached, cachedFamily} from "./cached.js";
|
import {cached, cachedFamily} from "./cached.js";
|
||||||
import type {Team} from "../types/team.js";
|
import type {Team} from "../types/team.js";
|
||||||
import {TeamSchema} from "../types/team.js";
|
import {TeamSchema} from "../types/team.js";
|
||||||
import {get, writable} from "svelte/store";
|
import {derived, get, readable, writable} from "svelte/store";
|
||||||
import {apiUrl, fetchWithToken, pageRepo, permsRepo, tokenStore} from "../repo/repo.js";
|
import {dataRepo, fetchWithToken, pageRepo, tokenStore} from "../repo/repo.js";
|
||||||
import {z} from "zod";
|
import {z} from "zod";
|
||||||
|
|
||||||
export const schemTypes = cached<SchematicType[]>([], () =>
|
export const schemTypes = cached<SchematicType[]>([], () =>
|
||||||
@ -47,5 +47,7 @@ export const branches = cached<string[]>([], async () => {
|
|||||||
return z.array(z.string()).parse(res);
|
return z.array(z.string()).parse(res);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const server = readable(new Promise((resolve) => {}), (set) => set(get(dataRepo).getServer()));
|
||||||
|
|
||||||
export const isWide = writable(window.innerWidth >= 640);
|
export const isWide = writable(window.innerWidth >= 640);
|
||||||
window.addEventListener("resize", () => isWide.set(window.innerWidth >= 640));
|
window.addEventListener("resize", () => isWide.set(window.innerWidth >= 640));
|
34
src/components/types/data.ts
Normale Datei
34
src/components/types/data.ts
Normale Datei
@ -0,0 +1,34 @@
|
|||||||
|
import {z} from "zod";
|
||||||
|
|
||||||
|
export const SchematicTypeSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
db: z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SchematicType = z.infer<typeof SchematicTypeSchema>;
|
||||||
|
|
||||||
|
export const PlayerSchema = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
uuid: z.string(),
|
||||||
|
prefix: z.string(),
|
||||||
|
perms: z.array(z.string()),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Player = z.infer<typeof PlayerSchema>;
|
||||||
|
|
||||||
|
export const ServerSchema = z.object({
|
||||||
|
description: z.any(),
|
||||||
|
players: z.object({
|
||||||
|
online: z.number(),
|
||||||
|
max: z.number(),
|
||||||
|
sample: z.array(z.any()).optional()
|
||||||
|
}),
|
||||||
|
version: z.object({
|
||||||
|
name: z.string(),
|
||||||
|
protocol: z.number()
|
||||||
|
}),
|
||||||
|
favicon: z.string().optional()
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Server = z.infer<typeof ServerSchema>;
|
35
src/components/types/schem.ts
Normale Datei
35
src/components/types/schem.ts
Normale Datei
@ -0,0 +1,35 @@
|
|||||||
|
import {z} from "zod";
|
||||||
|
import {PlayerSchema} from "./data.ts";
|
||||||
|
|
||||||
|
export const SchematicSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.number(),
|
||||||
|
type: z.string().nullable(),
|
||||||
|
owner: z.number(),
|
||||||
|
item: z.string(),
|
||||||
|
lastUpdate: z.number().positive(),
|
||||||
|
rank: z.number(),
|
||||||
|
replaceColor: z.boolean(),
|
||||||
|
allowReplay: z.boolean()
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Schematic = z.infer<typeof SchematicSchema>
|
||||||
|
|
||||||
|
export const SchematicListSchema = z.object({
|
||||||
|
breadcrumbs: z.array(z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.number()
|
||||||
|
})),
|
||||||
|
schematics: z.array(SchematicSchema),
|
||||||
|
players: z.record(z.string(), PlayerSchema)
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SchematicList = z.infer<typeof SchematicListSchema>
|
||||||
|
|
||||||
|
export const SchematicInfoSchema = z.object({
|
||||||
|
members: z.array(PlayerSchema),
|
||||||
|
path: z.string(),
|
||||||
|
schem: SchematicSchema
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
|
6
src/env.d.ts
vendored
6
src/env.d.ts
vendored
@ -10,9 +10,9 @@
|
|||||||
type PrimaryLocale = "en"
|
type PrimaryLocale = "en"
|
||||||
type SecondaryLocale = "de"
|
type SecondaryLocale = "de"
|
||||||
type Locale = PrimaryLocale | SecondaryLocale
|
type Locale = PrimaryLocale | SecondaryLocale
|
||||||
type RouteParameters = {"/blog":undefined;"/":undefined;"/[...slug]":{"slug":unknown;};"/admin":undefined;}
|
type RouteParameters = {"/dashboard":undefined;"/":undefined;"/login":undefined;"/[...slug]":{"slug":unknown;};"/admin":undefined;"/help":undefined;"/help/[...slug]":{"slug":unknown;};}
|
||||||
type Route = keyof RouteParameters
|
type Route = keyof RouteParameters
|
||||||
type TranslationVariables = {"home.page":object|undefined;"home.title.first":object|undefined;"home.title.second":object|undefined;"home.subtitle.1":object|undefined;"home.subtitle.2":object|undefined;"home.subtitle.3":object|undefined;"home.join":object|undefined;"home.benefits.historic.title":object|undefined;"home.benefits.historic.description.1":object|undefined;"home.benefits.historic.description.2":object|undefined;"home.benefits.server.title":object|undefined;"home.benefits.server.description":object|undefined;"home.benefits.events.title":object|undefined;"home.benefits.events.description.1":object|undefined;"home.benefits.events.description.2":object|undefined;"home.prefix.Dev":object|undefined;"home.prefix.Mod":object|undefined;}
|
type TranslationVariables = {"login.title":object|undefined;"login.placeholder.username":object|undefined;"login.placeholder.token":object|undefined;"login.label.username":object|undefined;"login.label.token":object|undefined;"login.generateToken":object|undefined;"login.submit":object|undefined;"navbar.title":object|undefined;"navbar.logo.alt":object|undefined;"navbar.links.home.title":object|undefined;"navbar.links.home.about":object|undefined;"navbar.links.home.downloads":object|undefined;"navbar.links.home.faq":object|undefined;"navbar.links.announcements":object|undefined;"navbar.links.rules.title":object|undefined;"navbar.links.rules.gamemode":object|undefined;"navbar.links.rules.wg":object|undefined;"navbar.links.rules.mwg":object|undefined;"navbar.links.rules.ws":object|undefined;"navbar.links.rules.as":object|undefined;"navbar.links.rules.rotating":object|undefined;"navbar.links.rules.megawg":object|undefined;"navbar.links.rules.micro":object|undefined;"navbar.links.rules.sf":object|undefined;"navbar.links.rules.general":object|undefined;"navbar.links.rules.coc":object|undefined;"navbar.links.help.title":object|undefined;"navbar.links.help.center":object|undefined;"navbar.links.help.docs":object|undefined;"navbar.links.account":object|undefined;"status.loading":object|undefined;"status.status":object|undefined;"status.online":object|undefined;"status.offline":object|undefined;"status.players":{"count"?:unknown;}|undefined;"status.version":{"version"?:unknown;}|undefined;"home.page":object|undefined;"home.title.first":object|undefined;"home.title.second":object|undefined;"home.subtitle.1":object|undefined;"home.subtitle.2":object|undefined;"home.subtitle.3":object|undefined;"home.join":object|undefined;"home.benefits.historic.title":object|undefined;"home.benefits.historic.description.1":object|undefined;"home.benefits.historic.description.2":object|undefined;"home.benefits.server.title":object|undefined;"home.benefits.server.description":object|undefined;"home.benefits.events.title":object|undefined;"home.benefits.events.description.1":object|undefined;"home.benefits.events.description.2":object|undefined;"home.prefix.Admin":object|undefined;"home.prefix.Dev":object|undefined;"home.prefix.Mod":object|undefined;"home.prefix.Sup":object|undefined;"home.prefix.Arch":object|undefined;}
|
||||||
type Translation = keyof TranslationVariables
|
type Translation = keyof TranslationVariables
|
||||||
type Environment = "none"|"node"|"browser"
|
type Environment = "none"|"node"|"browser"
|
||||||
declare module "astro-i18n" {
|
declare module "astro-i18n" {
|
||||||
@ -203,6 +203,8 @@ declare module "astro-i18n" {
|
|||||||
extractRouteLocale(route: string): string|null
|
extractRouteLocale(route: string): string|null
|
||||||
/** Initializes astro-i18n on the server-side. */
|
/** Initializes astro-i18n on the server-side. */
|
||||||
initialize(config?: Partial<AstroI18nConfig> | string, formatters?: TranslationFormatters = {}): Promise<void>
|
initialize(config?: Partial<AstroI18nConfig> | string, formatters?: TranslationFormatters = {}): Promise<void>
|
||||||
|
/** Redirects the user to the given destination. */
|
||||||
|
redirect(destination: string | URL, status = 301)
|
||||||
}
|
}
|
||||||
export const astroI18n: AstroI18n
|
export const astroI18n: AstroI18n
|
||||||
}
|
}
|
||||||
|
15
src/i18n/pages/login/en.json
Normale Datei
15
src/i18n/pages/login/en.json
Normale Datei
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"login": {
|
||||||
|
"title": "Login",
|
||||||
|
"placeholder": {
|
||||||
|
"username": "Username...",
|
||||||
|
"token": "***************"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"username": "Username",
|
||||||
|
"token": "Token"
|
||||||
|
},
|
||||||
|
"generateToken": "How to generate Token",
|
||||||
|
"submit": "Login"
|
||||||
|
}
|
||||||
|
}
|
24
src/pages/dashboard.astro
Normale Datei
24
src/pages/dashboard.astro
Normale Datei
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
import NavbarLayout from "../layouts/NavbarLayout.astro";
|
||||||
|
import localBau from "../images/2023-10-08_20.43.43.png";
|
||||||
|
import { Image } from "astro:assets";
|
||||||
|
import Dashboard from "../components/Dashboard.svelte";
|
||||||
|
---
|
||||||
|
|
||||||
|
<NavbarLayout title="Dashboard">
|
||||||
|
<div>
|
||||||
|
<Image src={localBau} alt="Bau" width="1920" height="1080" class="w-screen h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" />
|
||||||
|
<div class="mx-auto bg-gray-100 p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14
|
||||||
|
dark:text-white dark:bg-neutral-900" style="width: min(100vw, 75em)">
|
||||||
|
<script>
|
||||||
|
import {AES} from "crypto-js";
|
||||||
|
import {l} from "../util/util";
|
||||||
|
if (AES.decrypt(localStorage.getItem("sw-session") ?? "", import.meta.env.PUBLIC_SECRET)?.toString() === "") {
|
||||||
|
window.location.href = l("/login");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<Dashboard client:only="svelte" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NavbarLayout>
|
@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
|
|
||||||
---
|
|
@ -4,7 +4,7 @@ import NavbarLayout from "../layouts/NavbarLayout.astro";
|
|||||||
import { Image } from "astro:assets";
|
import { Image } from "astro:assets";
|
||||||
import localBau from "../images/2023-10-08_20.43.43.png";
|
import localBau from "../images/2023-10-08_20.43.43.png";
|
||||||
import {CaretRight, Archive, Rocket, Bell} from "@astropub/icons"
|
import {CaretRight, Archive, Rocket, Bell} from "@astropub/icons"
|
||||||
import {astroI18n, t} from "astro-i18n";
|
import {t} from "astro-i18n";
|
||||||
import {l} from "../util/util"
|
import {l} from "../util/util"
|
||||||
import PlayerCount from "../components/PlayerCount.svelte";
|
import PlayerCount from "../components/PlayerCount.svelte";
|
||||||
|
|
||||||
|
@ -2,41 +2,22 @@
|
|||||||
import NavbarLayout from "../layouts/NavbarLayout.astro";
|
import NavbarLayout from "../layouts/NavbarLayout.astro";
|
||||||
import localBau from "../images/2023-10-08_20.43.43.png";
|
import localBau from "../images/2023-10-08_20.43.43.png";
|
||||||
import {Image} from "astro:assets";
|
import {Image} from "astro:assets";
|
||||||
|
import {l} from "../util/util";
|
||||||
|
import Login from "../components/Login.svelte";
|
||||||
---
|
---
|
||||||
|
|
||||||
<NavbarLayout title="Login">
|
<NavbarLayout title="Login">
|
||||||
|
<script>
|
||||||
|
import {l} from "../util/util";
|
||||||
|
import {AES} from "crypto-js";
|
||||||
|
|
||||||
|
if (AES.decrypt(localStorage.getItem("sw-session") ?? "", import.meta.env.PUBLIC_SECRET)?.toString() !== "") {
|
||||||
|
window.location.href = l("/dashboard");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<Image src={localBau} alt="Bau" width="1920" height="1080" class="w-screen h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" />
|
<Image src={localBau} alt="Bau" width="1920" height="1080" class="w-screen h-screen dark:brightness-75 fixed -z-10 object-cover" draggable="false" />
|
||||||
<div class="h-screen mx-auto p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14 flex flex-col justify-center items-center
|
<div class="h-screen mx-auto p-8 rounded-b-md shadow-md pt-40 sm:pt-28 md:pt-14 flex flex-col justify-center items-center
|
||||||
dark:text-white " style="width: min(100vw, 75em);">
|
dark:text-white " style="width: min(100vw, 75em);">
|
||||||
<div class="bg-gray-100 dark:bg-neutral-900 p-12 rounded-2xl shadow-2xl border-2 border-gray-600 flex flex-col">
|
<Login client:load />
|
||||||
<h1 class="text-4xl text-white text-center">Login</h1>
|
|
||||||
<div class="ml-2 flex flex-col">
|
|
||||||
<label for="username">Username</label>
|
|
||||||
<input type="text" id="username" name="username" placeholder="Username..." />
|
|
||||||
<label for="password">Password</label>
|
|
||||||
<input type="password" id="password" name="password" placeholder="****************" />
|
|
||||||
</div>
|
|
||||||
<button class="btn mt-4 !mx-0 justify-center">Login</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
import {l} from "../util/util";
|
|
||||||
|
|
||||||
if (localStorage.getItem("sw-api-token") !== null) {
|
|
||||||
window.location.href = l("/account")
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</NavbarLayout>
|
</NavbarLayout>
|
||||||
|
|
||||||
<style>
|
|
||||||
input {
|
|
||||||
@apply border-2 rounded-md p-2 shadow-2xl w-80
|
|
||||||
dark:bg-neutral-800
|
|
||||||
focus:outline-none focus:ring-2 focus:ring-neutral-500 focus:border-transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
@apply text-neutral-300;
|
|
||||||
}
|
|
||||||
</style>
|
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren