Updates and more

This commit is contained in:
2023-11-12 22:43:42 +01:00
parent 7450ecdabb
commit 3889f28eb8
43 changed files with 5188 additions and 322 deletions

View File

@@ -1,10 +1,7 @@
<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 {dataRepo, schemRepo, tokenStore} from "./repo/repo.ts";
import moment from "moment";
import {
CheckSolid,
@@ -15,179 +12,38 @@
XCircleOutline
} from "flowbite-svelte-icons";
import {Breadcrumb, BreadcrumbItem, Modal, Tooltip} from "flowbite-svelte";
import SchematicInfo from "./SchematicInfo.svelte";
import SchematicInfo from "./dashboard/SchematicInfo.svelte";
import SchematicListTile from "./dashboard/SchematicListTile.svelte";
import UploadModal from "./dashboard/UploadModal.svelte";
import SchematicList from "./dashboard/SchematicList.svelte";
import UserInfo from "./dashboard/UserInfo.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")
return $dataRepo.getMe()
}
function getSchematics() {
return fetchWithToken(get(tokenStore), "/schem/list").then(value => value.json()).then(value => SchematicListSchema.parse(value))
return $schemRepo.getRootSchematicList()
}
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
}
return $schemRepo.getSchematicList(id)
}
</script>
{#await userFetch}
<p>loading...</p>
<p>{t("status.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>
<UserInfo {user} />
<hr>
<div>
{#await schematicFetch}
<p>Loading...</p>
<p>{t("status.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>
<SchematicList {schematics} on:reset={() => schematicFetch = getSchematics()} on:to={(e) => schematicFetch = getSchematic(e.detail.id) } />
{:catch error}
<p>error: {error.message}</p>
{/await}
@@ -196,32 +52,3 @@
<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>

View File

@@ -11,7 +11,7 @@
let {tokenStore} = await import("./repo/repo.ts");
if (username === "" || token === "") {
token = "";
alert(t("login.error"));
error = t("login.error");
return;
}
try {
@@ -22,7 +22,7 @@
}).then(res => res.json());
if (res.name !== username) {
alert(t("login.error"));
error = t("login.error");
token = "";
return;
}
@@ -30,7 +30,7 @@
tokenStore.set(token);
window.location.href = l("/dashboard");
} catch (e) {
alert(t("login.error"));
error = t("login.error");
token = "";
return;
}

View File

@@ -1,65 +0,0 @@
<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}

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import {t} from "astro-i18n"
import {createEventDispatcher} from "svelte";
import {schemRepo} from "../repo/repo.ts";
import {Modal, Spinner} from "flowbite-svelte";
import {astroI18n} from "astro-i18n";
import moment from "moment/moment";
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
import SchematicInfoModal from "./SchematicInfoModal.svelte";
const dispatch = createEventDispatcher();
export let schematicId: number;
let schemInfo = getSchematicInfo(schematicId);
function getSchematicInfo(id: number) {
return $schemRepo.getSchematicInfo(id);
}
</script>
{#await schemInfo}
<Modal title="Loading" open on:close={() => dispatch("reset")}>
<Spinner />
</Modal>
{:then info}
<SchematicInfoModal {info} on:reset />
{:catch e}
<Modal title="Error" open on:close={() => dispatch("reset")}>
<p>{e.message}</p>
<button class="btn !ml-auto" slot="footer" on:click={() => dispatch("reset")}>Close</button>
</Modal>
{/await}

View File

@@ -0,0 +1,56 @@
<script lang="ts">
import {astroI18n, t} from "astro-i18n";
import moment from "moment";
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
import {Modal} from "flowbite-svelte";
import type {SchematicInfo} from "../types/schem.ts";
import {createEventDispatcher} from "svelte";
import {schemRepo} from "../repo/repo.ts";
const dispatch = createEventDispatcher();
export let info: SchematicInfo;
async function download() {
const code = await $schemRepo.createDownload(info.schem.id);
window.open(import.meta.env.PUBLIC_API_SERVER + "/download/" + code.code, "_blank")
dispatch("reset")
}
</script>
<Modal title={info.schem.name} autoclose open on:close={() => dispatch("reset")}>
<p>{t("dashboard.schematic.info.path", {path: info.path})}</p>
<p class="flex !mt-0">
{t("dashboard.schematic.info.replaceColor")}
{#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">
{t("dashboard.schematic.info.allowReplay")}
{#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">{t("dashboard.schematic.info.type", {type: info.schem.type ?? t("dashboard.schematic.dir")})}</p>
<p class="!mt-0">{t("dashboard.schematic.info.updated", {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">{t("dashboard.schematic.info.item", {item: info.schem.item ?? (info.schem.type == null ? "CHEST" : "CAULDRON_ITEM")})}</p>
{#if info.members.length !== 0}
<p class="!mt-0">{t("dashboard.schematic.info.members", {members: info.members.join(", ")})}</p>
{/if}
<svelte:fragment slot="footer">
<button class="btn !ml-auto" on:click={download}>{t("dashboard.schematic.info.btn.download")}</button>
<button class="btn" on:click={() => dispatch("reset")}>{t("dashboard.schematic.info.btn.close")}</button>
</svelte:fragment>
</Modal>

View File

@@ -0,0 +1,115 @@
<script lang="ts">
import {t} from "astro-i18n";
import {ChevronDoubleRightOutline, FolderOutline, HomeOutline, InfoCircleOutline} from "flowbite-svelte-icons";
import SchematicListTile from "./SchematicListTile.svelte";
import {Breadcrumb, BreadcrumbItem, Tooltip} from "flowbite-svelte";
import {createEventDispatcher} from "svelte";
import type {SchematicList} from "../types/schem.ts";
import SchematicInfo from "./SchematicInfo.svelte";
import UploadModal from "./UploadModal.svelte";
const dispatch = createEventDispatcher();
export let schematics: SchematicList;
let uploadOpen = false;
let infoModalId: number | null = null;
function schemListClick(isDir: boolean, id: number) {
if (isDir) {
return () => dispatch("to", {id})
} else {
return () => infoModalId = id
}
}
</script>
<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={() => dispatch("reset")} class="hover:underline hover:cursor-pointer text-2xl">{t("dashboard.schematic.home")}</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={() => dispatch("to", {id: 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}>
{t("dashboard.schematic.upload")}
</button>
</div>
</div>
<table>
<tbody>
<tr class="!cursor-auto">
<th>{t("dashboard.schematic.head.type")}</th>
<th>{t("dashboard.schematic.head.name")}</th>
<th class="hidden sm:table-cell">{t("dashboard.schematic.head.owner")}</th>
<th class="hidden sm:table-cell"></th>
<th class="hidden md:table-cell">{t("dashboard.schematic.head.updated")}</th>
<th>
<InfoCircleOutline />
<Tooltip>
<span>{t("dashboard.schematic.head.replaceColor")}</span>
</Tooltip>
</th>
<th>
<InfoCircleOutline />
<Tooltip>
<span>{t("dashboard.schematic.head.allowReplay")}</span>
</Tooltip>
</th>
</tr>
{#if schematics.breadcrumbs.length !== 0}
<tr on:click|preventDefault={() => {
if (schematics.breadcrumbs.length === 1) {
dispatch("reset")
} else {
dispatch("to", {id: 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">{t("dashboard.schematic.dir")}</th>
<th class="hidden md:table-cell"></th>
<th></th>
<th></th>
</tr>
{/if}
{#each schematics.schematics as schem}
<SchematicListTile schem={schem} players={schematics.players} on:click={schemListClick(schem.type == null, schem.id)} />
{/each}
</tbody>
</table>
<UploadModal bind:open={uploadOpen} on:refresh />
{#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>

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import {astroI18n, t} from "astro-i18n";
import moment from "moment/moment.js";
import {CheckSolid, FileOutline, FolderOutline, XCircleOutline} from "flowbite-svelte-icons";
import type {Schematic} from "../types/schem.ts";
import type {Player} from "../types/data.ts";
export let schem: Schematic;
export let players: Record<number, Player>;
</script>
<tr on:click|preventDefault>
<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">{players[schem.owner].name}</th>
<th class="hidden sm:table-cell">{schem.type ?? t("dashboard.schematic.dir")}</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>
<style lang="scss">
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>

View File

@@ -0,0 +1,47 @@
<script lang="ts">
import {Modal} from "flowbite-svelte";
import {schemRepo} from "../repo/repo.js";
import {createEventDispatcher} from "svelte";
const dispatch = createEventDispatcher();
export let open = false;
async function upload() {
if (uploadFile == null) {
return
}
let file = uploadFile[0];
let name = file.name;
let type = name.split(".").pop();
if (type !== "schem" && type !== "schematic") {
return
}
let content = await file.arrayBuffer();
// @ts-ignore
let b64 = btoa(String.fromCharCode.apply(null, new Uint8Array(content)));
await $schemRepo.uploadSchematic(name, b64);
open = false;
uploadFile = null;
dispatch("reset")
}
let uploadFile: File[] | null = null;
</script>
<Modal title="Upload Schematic" bind:open autoclose outsideclose>
<form>
<input type="file" bind:files={uploadFile} />
</form>
<svelte:fragment slot="footer">
<button class="btn !ml-auto" on:click={upload}>Upload</button>
<button class="btn btn-gray" on:click={() => open = false}>Close</button>
</svelte:fragment>
</Modal>

View File

@@ -0,0 +1,41 @@
<script lang="ts">
import {t} from "astro-i18n";
import type {Player} from "../types/data.ts";
import {tokenStore} from "../repo/repo.ts";
import {l} from "../../util/util.ts";
export let user: Player;
function logout() {
tokenStore.set("")
window.location.href = l("/login")
}
</script>
<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}>{t("dashboard.buttons.logout")}</button>
{#if user.perms.includes("MODERATION")}
<a class="btn w-fit mt-2" href="/admin">{t("dashboard.buttons.admin")}</a>
{/if}
</div>
</div>
<div>
<h1 class="text-4xl font-bold">{t("dashboard.title", {name: user.name})}</h1>
<p>{t("dashboard.rank", {rank: t("home.prefix." + user.prefix)})}</p>
<p>{t("dashboard.permissions")}</p>
<ul>
{#each user.perms as permission}
<li class="list-disc ml-6">{permission}</li>
{/each}
</ul>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import type {Server} from "../types/data.ts";
import {ServerSchema} from "../types/data.ts";
import type {Player, Server} from "../types/data.ts";
import {PlayerSchema, ServerSchema} from "../types/data.ts";
import {fetchWithToken} from "./repo.ts";
export class DataRepo {
@@ -8,4 +8,8 @@ export class DataRepo {
public async getServer(): Promise<Server> {
return await fetchWithToken(this.token, "/data/server").then(value => value.json()).then(value => ServerSchema.parse(value));
}
public async getMe(): Promise<Player> {
return await fetchWithToken(this.token, "/data/me").then(value => value.json()).then(value => PlayerSchema.parse(value));
}
}

View File

@@ -2,7 +2,6 @@ import type {Page, PageList} from "../types/page.ts";
import {fetchWithToken} from "./repo.ts";
import {PageListSchema, PageSchema} from "../types/page.ts";
import {bytesToBase64} from "../admin/util.ts";
import {branches} from "../stores/stores.ts";
export class PageRepo {
constructor(private token: string) {}

View File

@@ -6,6 +6,7 @@ import {PageRepo} from "./page.ts";
import {DataRepo} from "./data.ts";
import { AES, enc, format } from "crypto-js";
import {SchematicRepo} from "./schem.ts";
export { EventRepo } from "./event.js"
@@ -29,3 +30,4 @@ export const fightRepo = derived(tokenStore, ($token) => new FightRepo($token))
export const permsRepo = derived(tokenStore, ($token) => new PermsRepo($token))
export const pageRepo = derived(tokenStore, ($token) => new PageRepo($token))
export const dataRepo = derived(tokenStore, ($token) => new DataRepo($token))
export const schemRepo = derived(tokenStore, ($token) => new SchematicRepo($token))

View File

@@ -0,0 +1,33 @@
import {fetchWithToken} from "./repo.ts";
import type {SchematicCode, SchematicInfo, SchematicList} from "../types/schem.ts";
import {SchematicCodeSchema, SchematicInfoSchema, SchematicListSchema} from "../types/schem.ts";
export class SchematicRepo {
constructor(private token: string) {}
public async getRootSchematicList(): Promise<SchematicList> {
return await fetchWithToken(this.token, "/schem").then(value => value.json()).then(value => SchematicListSchema.parse(value));
}
public async getSchematicList(id: number): Promise<SchematicList> {
return await fetchWithToken(this.token, `/schem/${id}/list`).then(value => value.json()).then(value => SchematicListSchema.parse(value));
}
public async getSchematicInfo(id: number): Promise<SchematicInfo> {
return await fetchWithToken(this.token, `/schem/${id}`).then(value => value.json()).then(value => SchematicInfoSchema.parse(value));
}
public async createDownload(id: number): Promise<SchematicCode> {
return await fetchWithToken(this.token, `/schem/${id}/download`).then(value => value.json()).then(value => SchematicCodeSchema.parse(value));
}
public async uploadSchematic(name: string, content: string) {
return await fetchWithToken(this.token, `/schem`, {
method: "POST",
body: JSON.stringify({
name,
content
})
})
}
}

View File

@@ -1,9 +1,9 @@
import type {Player, SchematicType, Server} from "../types/data.js";
import {PlayerSchema, ServerSchema} from "../types/data.js";
import type {Player, SchematicType} from "../types/data.js";
import {PlayerSchema} from "../types/data.js";
import {cached, cachedFamily} from "./cached.js";
import type {Team} from "../types/team.js";
import {TeamSchema} from "../types/team.js";
import {derived, get, readable, writable} from "svelte/store";
import {derived, get, writable} from "svelte/store";
import {dataRepo, fetchWithToken, pageRepo, tokenStore} from "../repo/repo.js";
import {z} from "zod";
@@ -47,7 +47,7 @@ export const branches = cached<string[]>([], async () => {
return z.array(z.string()).parse(res);
})
export const server = readable(new Promise((resolve) => {}), (set) => set(get(dataRepo).getServer()));
export const server = derived(dataRepo, $dataRepo => $dataRepo.getServer());
export const isWide = writable(window.innerWidth >= 640);
window.addEventListener("resize", () => isWide.set(window.innerWidth >= 640));

View File

@@ -1,5 +1,3 @@
import type {Team} from "./team.js";
import type {Player} from "./data.js";
import {z} from "zod";
import {TeamSchema} from "./team.js";
import {PlayerSchema} from "./data.js";

View File

@@ -32,4 +32,12 @@ export const SchematicInfoSchema = z.object({
schem: SchematicSchema
})
export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
export type SchematicInfo = z.infer<typeof SchematicInfoSchema>
export const SchematicCodeSchema = z.object({
id: z.number().gte(0),
code: z.string(),
expires: z.number().positive()
})
export type SchematicCode = z.infer<typeof SchematicCodeSchema>