Author SHA1 Message Date
Chaoscaot da6f741806 Trigger Rebuild
SteamWarCI Build successful
2025-04-14 17:55:39 +02:00
Chaoscaot 6b54791331 Merge pull request 'Merge branch sw-arcade' (#9) from sw-arcade into master
SteamWarCI Build failed
Reviewed-on: #9
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2025-04-14 17:54:22 +02:00
Chaoscaot 36685bffd1 Fix wording in SteamWar Arcade event announcement for clarity
SteamWarCI Build successful
2025-04-14 17:53:46 +02:00
Chaoscaot caf9ea6cf1 Add SteamWar Arcade event image and update markdown file
SteamWarCI Build successful
2025-04-14 17:48:14 +02:00
Chaoscaot d505265910 Update sw-arcade.md with event details and correct creation date
SteamWarCI Build successful
2025-04-14 17:36:20 +02:00
Lixfel 78e1a7b726 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-04-12 21:29:10 +02:00
Chaoscaot cd485e8dda Update sw-arcade.md
SteamWarCI Build successful
2025-04-06 23:07:10 +02:00
Chaoscaot 182c402c7e Update sw-arcade.md
SteamWarCI Build failed
2025-04-06 23:05:35 +02:00
Chaoscaot 098f5b9270 Create page announcements/de/sw-arcade.md
SteamWarCI Build failed
2025-04-06 23:04:24 +02:00
Lixfel cf0c66c910 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-04-06 14:12:00 +02:00
Chaoscaot c8156ea47e Set fixed height for chart and disable aspect ratio maintenance
SteamWarCI Build successful
2025-04-05 15:21:36 +02:00
Chaoscaot 20a47ca6b6 Add favicon.svg to the public directory
SteamWarCI Build successful
2025-04-05 15:06:10 +02:00
Chaoscaot 2d601b9c4d Update schematic stats label and remove permission check
SteamWarCI Build successful
2025-04-02 09:58:11 +02:00
Chaoscaot 48586f1a50 Add error handling and improve file upload UX
SteamWarCI Build successful
2025-04-02 09:52:37 +02:00
Chaoscaot 7153cacbab Merge remote-tracking branch 'origin/master'
SteamWarCI Build successful
# Conflicts:
#	src/content/modes/missilewars.json
2025-04-02 09:40:05 +02:00
Chaoscaot 73cee211f2 Refactor referee management into standalone component 2025-04-02 09:39:58 +02:00
YoyoNow f8a16acfeb Remove shop
SteamWarCI Build successful
2025-04-02 09:00:37 +02:00
YoyoNow 9ca63cd286 Add shop.md
SteamWarCI Build successful
2025-03-31 22:30:26 +02:00
YoyoNow a2456c8b46 Merge remote-tracking branch 'origin/master'
SteamWarCI Build successful
2025-03-31 22:28:54 +02:00
YoyoNow 0952035091 Add shop.md 2025-03-31 22:28:51 +02:00
Lixfel 9c8c02f679 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-31 19:43:24 +02:00
Lixfel 3b5fdc57c0 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-25 06:40:14 +01:00
Lixfel 733c63946f Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-25 06:32:35 +01:00
Chaoscaot fd846250ab Merge pull request 'Addapted script side for new example hotkey script' (#8) from add-example-script-to-downloads into master
SteamWarCI Build successful
Reviewed-on: #8
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2025-03-22 10:36:18 +01:00
D4rkr34lm 17460772e9 Addapted script side for new example hotkey script
SteamWarCI Build successful
2025-03-21 18:54:26 +01:00
Lixfel 9a20860072 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-18 06:48:53 +01:00
YoyoNow 8f51723a3b Fix Graf Spee name
SteamWarCI Build successful
2025-03-14 13:49:36 +01:00
YoyoNow 8ad2f283aa Fixup things
SteamWarCI Build successful
2025-03-13 16:30:12 +01:00
YoyoNow 39f1af8b73 Add MissileWars.md
SteamWarCI Build successful
2025-03-13 16:27:48 +01:00
YoyoNow 266c4cb4ea Add MissileWars ranking to Navbar.svelte
SteamWarCI Build successful
2025-03-13 16:24:45 +01:00
YoyoNow f3df3c0000 Add ranked to Micro WarGear
SteamWarCI Build successful
2025-03-13 16:05:26 +01:00
Lixfel cb78fc598b Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-09 16:52:42 +01:00
Chaoscaot ba7ecc1a8e Merge remote-tracking branch 'origin/master'
SteamWarCI Build successful
2025-03-04 23:33:17 +01:00
Chaoscaot 6ea92f9383 Tracking and adding LFS artifacts 2025-03-04 23:33:00 +01:00
Lixfel 998770bf59 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-04 07:24:58 +01:00
Chaoscaot a231032555 Add and standardize MissileWars translations and links
SteamWarCI Build successful
2025-03-02 16:21:43 +01:00
Chaoscaot 3aa3731bcb Add MissileWars mode config and leaderboard link
SteamWarCI Build successful
2025-03-02 16:18:37 +01:00
Chaoscaot 5e80c95bfd Add download link and update source URL in teamserver.json
SteamWarCI Build successful
2025-03-02 16:12:47 +01:00
Lixfel 09dc28b6da Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-02 15:07:14 +01:00
Lixfel fd7cf716ca Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-01 22:08:23 +01:00
Chaoscaot 73bd6a5e96 Fix Website
SteamWarCI Build successful
2025-03-01 20:07:09 +01:00
Chaoscaot 9c02cc1f4d Fix Website
SteamWarCI Build successful
2025-03-01 20:04:41 +01:00
Chaoscaot de8457fe45 Fix Website
SteamWarCI Build successful
2025-03-01 20:03:04 +01:00
Chaoscaot bccd5eb5a0 Enhance request handling with token refresh and retries
SteamWarCI Build successful
2025-03-01 11:55:01 +01:00
Chaoscaot 53afe70b27 Refactor token refresh logic to streamline error handling.
SteamWarCI Build successful
2025-03-01 11:34:09 +01:00
Chaoscaot 4bbdaa06a9 Refactor auth handling to improve token refresh logic
SteamWarCI Build successful
2025-03-01 11:30:30 +01:00
Chaoscaot f03867b9a7 Add retry mechanism and limit for token requests
SteamWarCI Build successful
2025-03-01 11:27:06 +01:00
Chaoscaot 23e10eef0f Fix group filtering logic in FightTable.svelte
SteamWarCI Build successful
2025-03-01 11:15:39 +01:00
Lixfel 4c72f4f26b FIx MW3 creation date
SteamWarCI Build successful
2025-03-01 10:52:44 +01:00
Lixfel 624ba7f296 Merge pull request 'Merge branch wgs25-kampfplan' (#7) from wgs25-kampfplan into master
SteamWarCI Build successful
Reviewed-on: #7
2025-03-01 10:51:19 +01:00
Lixfel d7d20e4347 Update wgs25-kampfplan.md
SteamWarCI Build successful
2025-03-01 10:50:17 +01:00
Lixfel 43bd8f4a7c Update wgs25-kampfplan.md
SteamWarCI Build failed
2025-03-01 10:49:03 +01:00
Lixfel 18e8627b54 Update wgs25-kampfplan.md
SteamWarCI Build failed
2025-03-01 10:42:19 +01:00
Lixfel 0efc46c7e2 Create page announcements/de/wgs25-kampfplan.md
SteamWarCI Build failed
2025-03-01 09:53:50 +01:00
Chaoscaot 62fff0c0b2 Merge pull request 'Refactor authentication and implement password reset.' (#3) from develop/authv2 into master
SteamWarCI Build successful
Reviewed-on: #3
Reviewed-by: Lixfel <lixfel@noreply.localhost>
2025-02-25 22:39:40 +01:00
TheBreadBeard 86b479fb28 Update missilewars-iii-eventplan.md
SteamWarCI Build successful
2025-02-23 18:34:53 +01:00
Chaoscaot 489402292d Update adventskalender-schems.md
SteamWarCI Build successful
2025-02-23 18:31:10 +01:00
Chaoscaot b53ce04a75 Remove reset password functionality
SteamWarCI Build successful
2025-02-23 17:23:45 +01:00
Chaoscaot 069a9973a4 Add Gitea link and icon to navbar layout
SteamWarCI Build successful
2025-02-23 15:20:50 +01:00
Chaoscaot c3410de1d7 Refactor event handling to use Promises for better efficiency.
SteamWarCI Build successful
2025-02-23 12:25:56 +01:00
Chaoscaot a23c514102 Revert "Refactor event mounts and update script management."
This reverts commit bf8110af6c.
2025-02-23 12:20:34 +01:00
Chaoscaot bf8110af6c Refactor event mounts and update script management.
SteamWarCI Build successful
2025-02-23 12:18:58 +01:00
Chaoscaot 349f71af1c Add event listener for "astro:before-swap" in slug page
SteamWarCI Build successful
2025-02-23 12:14:11 +01:00
Chaoscaot dda37127ca Use type import and update page load event handling.
SteamWarCI Build successful
2025-02-23 09:59:37 +01:00
Chaoscaot 6d210eb0ff Merge pull request 'Merge branch missilewars-iii-eventplan' (#4) from missilewars-iii-eventplan into master
SteamWarCI Build successful
Reviewed-on: #4
2025-02-23 09:49:04 +01:00
Chaoscaot cfede8f299 Update missilewars-iii-eventplan.md
SteamWarCI Build successful
2025-02-23 09:47:30 +01:00
TheBreadBeard 597153ed39 Update missilewars-iii-eventplan.md
SteamWarCI Build successful
2025-02-23 07:43:22 +01:00
TheBreadBeard 697e903a26 Create page announcements/de/missilewars-iii-eventplan.md
SteamWarCI Build failed
2025-02-23 07:21:02 +01:00
Chaoscaot 1433784369 Update auth API endpoints to remove "/v2" prefix
SteamWarCI Build successful
2025-02-20 22:15:02 +01:00
Chaoscaot 2c63a33bda Refine token validation and update user stats endpoint.
SteamWarCI Build successful
Extend access token validation to include a 10-second buffer to prevent potential expiry issues. Modify the user stats API call to use the base `/stats/user` endpoint for improved consistency.
2025-02-18 00:09:06 +01:00
Chaoscaot 87265e5ccc Add "Repeat Password" label to i18n and form components
SteamWarCI Build successful
2025-02-17 18:32:54 +01:00
Chaoscaot 75f1a6528b Refactor authentication and implement password reset.
SteamWarCI Build successful
2025-02-17 18:29:17 +01:00
47 changed files with 599 additions and 186 deletions
+3
View File
@@ -0,0 +1,3 @@
.jpg filter=lfs diff=lfs merge=lfs -text
.png filter=lfs diff=lfs merge=lfs -text
.glb filter=lfs diff=lfs merge=lfs -text
-2
View File
@@ -7,7 +7,6 @@ import sitemap from "@astrojs/sitemap";
import robotsTxt from "astro-robots-txt";
import path from "node:path";
import mdx from "@astrojs/mdx";
import pagefind from "astro-pagefind";
// https://astro.build/config
export default defineConfig({
@@ -22,7 +21,6 @@ export default defineConfig({
tailwind({
configFile: "./tailwind.config.cjs",
}),
pagefind(),
configureI18n(),
sitemap({
i18n: {
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 406 KiB

+21
View File
@@ -0,0 +1,21 @@
{
"name": "MyWebSite",
"short_name": "MySite",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+1 -4
View File
@@ -20,6 +20,7 @@
<script lang="ts">
import {t} from "astro-i18n";
import {statsRepo} from "@repo/stats.ts";
import "@styles/table.css"
interface Props {
@@ -64,7 +65,3 @@
<p>{error.message}</p>
{/await}
<style>
@import "../styles/table.css";
</style>
+3 -1
View File
@@ -79,6 +79,8 @@
})
},
options: {
maintainAspectRatio: false,
scales: {
x: {
type: "time",
@@ -105,5 +107,5 @@
</script>
<div>
<canvas bind:this={canvas}></canvas>
<canvas height="500" bind:this={canvas}></canvas>
</div>
+1 -1
View File
@@ -55,7 +55,7 @@
</tr>
</thead>
<tbody>
{#each window(event.fights.filter(f => f.group === group), rows) as fights}
{#each window(event.fights.filter(f => group === undefined ? true : f.group === group), rows) as fights}
<tr>
{#each fights as fight (fight.id)}
<td>{Intl.DateTimeFormat(astroI18n.locale, {
+4 -6
View File
@@ -31,8 +31,7 @@
let error: string = $state("");
async function login() {
let {tokenStore} = await import("./repo/repo.ts");
let {authRepo} = await import("./repo/auth.ts");
let {authV2Repo} = await import("./repo/authv2.ts");
if (username === "" || pw === "") {
pw = "";
error = t("login.error");
@@ -40,15 +39,14 @@
}
try {
let auth = await get(authRepo).login(username, pw);
if (auth == undefined) {
let auth = await get(authV2Repo).login(username, pw);
if (!auth) {
pw = "";
error = t("login.error");
return;
}
tokenStore.set(auth);
navigate(l("/dashboard"));
await navigate(l("/dashboard"));
} catch (e: any) {
pw = "";
error = t("login.error");
+17 -4
View File
@@ -23,24 +23,35 @@
import {t} from "astro-i18n";
import {l} from "../util/util";
import {onMount} from "svelte";
import {loggedIn} from "@repo/authv2.ts";
interface Props {
logo?: import('svelte').Snippet;
}
let { logo }: Props = $props();
let navbar: HTMLDivElement = $state();
let navbar = $state<HTMLDivElement>();
let searchOpen = $state(false);
let accountBtn = $state<HTMLAnchorElement>();
$effect(() => {
if ($loggedIn) {
accountBtn!.href = l("/dashboard");
} else {
accountBtn!.href = l("/login");
}
})
onMount(() => {
handleScroll();
})
function handleScroll() {
if (window.scrollY > 0) {
navbar.classList.add("before:scale-y-100");
navbar!.classList.add("before:scale-y-100");
} else {
navbar.classList.remove("before:scale-y-100");
navbar!.classList.remove("before:scale-y-100");
}
}
</script>
@@ -90,6 +101,8 @@
<a href={l("/rules/microwargear")}
class="btn btn-gray">{t("navbar.links.rules.micro")}</a>
<a href={l("/rules/streetfight")} class="btn btn-gray">{t("navbar.links.rules.sf")}</a>
<h2 class="px-2 text-gray-300">{t("navbar.links.rules.ranked")}</h2>
<a href={l("/rangliste/missilewars")} class="btn btn-gray">{t("navbar.links.ranked.mw")}</a>
</div>
</div>
<!-- TODO: Add help center
@@ -106,7 +119,7 @@
</div>
</div>
-->
<a class="btn" href={l("/login")}>
<a class="btn" href={l("/login")} bind:this={accountBtn}>
<span class="btn__text">{t("navbar.links.account")}</span>
</a>
<!--
+1 -1
View File
@@ -1,5 +1,5 @@
---
import {CollectionEntry} from "astro:content";
import type {CollectionEntry} from "astro:content";
import {l} from "../util/util";
import {astroI18n} from "astro-i18n";
import {Image} from "astro:assets";
+10 -8
View File
@@ -22,38 +22,40 @@
import wrap from "svelte-spa-router/wrap";
import Router, {replace} from "svelte-spa-router";
import {get} from "svelte/store";
import {tokenStore} from "@repo/repo";
import {loggedIn} from "@repo/authv2.ts";
const routes: RouteDefinition = {
"/": wrap({asyncComponent: () => import("./pages/Home.svelte"), conditions: detail => get(tokenStore) != ""}),
"/": wrap({asyncComponent: () => import("./pages/Home.svelte"), conditions: detail => get(loggedIn)}),
"/perms": wrap({
asyncComponent: () => import("./pages/Perms.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/login": wrap({
asyncComponent: () => import("./pages/Login.svelte"),
conditions: detail => get(tokenStore) == ""
conditions: detail => !get(loggedIn)
}),
"/event/:id": wrap({
asyncComponent: () => import("./pages/Event.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/event/:id/generate": wrap({
asyncComponent: () => import("./pages/Generate.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/edit": wrap({
asyncComponent: () => import("./pages/Edit.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"/display/:event": wrap({
asyncComponent: () => import("./pages/Display.svelte"),
conditions: detail => get(tokenStore) != ""
conditions: detail => get(loggedIn)
}),
"*": wrap({asyncComponent: () => import("./pages/NotFound.svelte")})
};
function conditionsFailed(event: ConditionsFailedEvent) {
console.log(event)
if (event.detail.location === "/login") {
replace("/");
} else {
+1 -3
View File
@@ -43,7 +43,5 @@
maximumFractionDigits: 2
}).format(data.playtime)})}h</p>
<p>{t("dashboard.stats.fights", {fights: data.fights})}</p>
{#if user.perms.includes("CHECK")}
<p>{t("dashboard.stats.checked", {checked: data.acceptedSchematics})}</p>
{/if}
<p>{t("dashboard.stats.checked", {checked: data.acceptedSchematics})}</p>
{/await}
+33 -18
View File
@@ -21,19 +21,21 @@
import {createEventDispatcher} from "svelte";
import {schemRepo} from "@repo/schem.ts";
import SWModal from "@components/styled/SWModal.svelte";
import {t} from "astro-i18n"
import {t} from "astro-i18n";
const dispatch = createEventDispatcher();
interface Props {
open?: boolean;
}
interface Props {
open?: boolean;
}
let { open = $bindable(false) }: Props = $props();
let {open = $bindable(false)}: Props = $props();
async function upload() {
async function upload(e: Event) {
e.stopPropagation();
if (uploadFile == null) {
return
error = "dashboard.schematic.errors.noFile";
return;
}
let file = uploadFile[0];
@@ -42,33 +44,46 @@
let type = name.split(".").pop();
if (type !== "schem" && type !== "schematic") {
return
error = "dashboard.schematic.errors.invalidEnding";
return;
}
let content = await file.arrayBuffer();
// @ts-ignore
let b64 = btoa(String.fromCharCode.apply(null, new Uint8Array(content)));
let response = await $schemRepo.uploadSchematic(name, b64);
try {
await $schemRepo.uploadSchematic(name, b64);
open = false;
open = false;
value = "";
dispatch("reset");
} catch (e) {
error = "dashboard.schematic.errors.upload";
}
}
function reset(e: Event) {
e.stopPropagation();
open = false
value = "";
dispatch("reset")
}
let uploadFile: FileList | null = $state(null);
let value = $state("");
let error = $state(null)
</script>
<SWModal title={t("dashboard.schematic.title")} bind:open>
<form>
<input type="file" bind:files={uploadFile} bind:value />
<label for="schem-upload">{t("dashboard.schematic.title")}</label>
<input type="file" id="schem-upload" bind:files={uploadFile} class="overflow-ellipsis" bind:value accept=".schem, .schematic"/>
{#if error !== null}
<p class="text-red-400">{t(error)}</p>
{/if}
</form>
{#snippet footer()}
<button class="btn !ml-auto" onclick={upload}>{t("dashboard.schematic.upload")}</button>
<button class="btn btn-gray" onclick={() => open = false}>{t("dashboard.schematic.cancel")}</button>
{/snippet}
<button class="btn" onclick={upload}>{t("dashboard.schematic.upload")}</button>
<button class="btn btn-gray" onclick={reset}>{t("dashboard.schematic.cancel")}</button>
{/snippet}
</SWModal>
+4 -5
View File
@@ -22,9 +22,9 @@
import type {Player} from "@type/data.ts";
import {l} from "@utils/util.ts";
import Statistics from "./Statistics.svelte";
import {authRepo} from "@repo/auth.ts";
import {tokenStore} from "@repo/repo.ts";
import {authV2Repo} from "@repo/authv2.ts";
import Card from "@components/Card.svelte";
import {navigate} from "astro:transitions/client";
interface Props {
user: Player;
@@ -33,9 +33,8 @@
let { user }: Props = $props();
async function logout() {
await $authRepo.logout()
tokenStore.set("")
window.location.href = l("/login")
await $authV2Repo.logout();
await navigate(l("/login"));
}
</script>
-44
View File
@@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {fetchWithToken, tokenStore} from "./repo.ts";
import {derived} from "svelte/store";
export class AuthRepo {
constructor(private token: string) {
}
public async login(username: string, password: string): Promise<string> {
return await fetchWithToken(this.token, "/auth/login", {
body: JSON.stringify({
username,
password,
}),
method: "POST",
}).then(value => value.json()).then(value => value.token);
}
public async logout(): Promise<void> {
await fetchWithToken(this.token, "/auth/tokens/logout", {
method: "POST",
});
}
}
export const authRepo = derived(tokenStore, ($token) => new AuthRepo($token));
+184
View File
@@ -0,0 +1,184 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {readable, writable} from "svelte/store";
import dayjs, {type Dayjs} from "dayjs";
import {type AuthToken, AuthTokenSchema} from "@type/auth.ts";
export class AuthV2Repo {
private accessToken: string | undefined;
private accessTokenExpires: Dayjs | undefined;
private refreshToken: string | undefined;
private refreshTokenExpires: Dayjs | undefined;
constructor() {
if (typeof localStorage === "undefined") {
return;
}
this.accessToken = localStorage.getItem("sw-access-token") ?? undefined;
if (this.accessToken) {
this.accessTokenExpires = dayjs(localStorage.getItem("sw-access-token-expires") ?? "");
}
this.refreshToken = localStorage.getItem("sw-refresh-token") ?? undefined;
if (this.refreshToken) {
loggedIn.set(true);
this.refreshTokenExpires = dayjs(localStorage.getItem("sw-refresh-token-expires") ?? "");
}
}
async login(name: string, password: string) {
if (this.accessToken !== undefined || this.refreshToken !== undefined) {
throw new Error("Already logged in");
}
try {
const login = await this.request("/auth", {
method: "POST",
body: JSON.stringify({
name,
password,
keepLoggedIn: true,
}),
}).then(value => value.json()).then(value => AuthTokenSchema.parse(value));
this.setLoginState(login);
return true;
} catch (e) {
return false;
}
}
async logout() {
if (this.accessToken === undefined) {
return;
}
await this.request("/auth", {
method: "DELETE",
});
this.resetAccessToken();
this.resetRefreshToken();
}
private setLoginState(tokens: AuthToken) {
this.setAccessToken(tokens.accessToken.token, dayjs(tokens.accessToken.expires));
this.setRefreshToken(tokens.refreshToken.token, dayjs(tokens.refreshToken.expires));
loggedIn.set(true);
}
private setAccessToken(token: string, expires: Dayjs) {
this.accessToken = token;
this.accessTokenExpires = expires;
localStorage.setItem("sw-access-token", token);
localStorage.setItem("sw-access-token-expires", expires.toString());
}
private resetAccessToken() {
if (this.accessToken === undefined) {
return;
}
this.accessToken = undefined;
this.accessTokenExpires = undefined;
localStorage.removeItem("sw-access-token");
localStorage.removeItem("sw-access-token-expires");
}
private setRefreshToken(token: string, expires: Dayjs) {
this.refreshToken = token;
this.refreshTokenExpires = expires;
localStorage.setItem("sw-refresh-token", token);
localStorage.setItem("sw-refresh-token-expires", expires.toString());
}
private resetRefreshToken() {
if (this.refreshToken === undefined) {
return;
}
this.refreshToken = undefined;
this.refreshTokenExpires = undefined;
localStorage.removeItem("sw-refresh-token");
localStorage.removeItem("sw-refresh-token-expires");
loggedIn.set(false);
}
private async refresh() {
if (this.refreshToken === undefined || this.refreshTokenExpires === undefined || this.refreshTokenExpires.isBefore(dayjs().add(10, "seconds"))) {
this.resetRefreshToken();
this.resetAccessToken();
return;
}
const response = await this.requestWithToken(this.refreshToken!, "/auth", {
method: "PUT",
}).then(value => {
if (value.status === 401) {
this.resetRefreshToken();
this.resetAccessToken();
return undefined;
}
return value.json();
}).then(value => AuthTokenSchema.parse(value));
this.setLoginState(response);
}
async request(url: string, params: RequestInit = {}, retryCount: number = 0) {
if (this.accessToken !== undefined && this.accessTokenExpires !== undefined && this.accessTokenExpires.isBefore(dayjs().add(10, "seconds"))) {
await this.refresh();
}
return this.requestWithToken(this.accessToken ?? "", url, params, retryCount);
}
private async requestWithToken(token: string, url: string, params: RequestInit = {}, retryCount: number = 0): Promise<Response> {
if (retryCount >= 3) {
throw new Error("Too many retries");
}
return fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params,
headers: {
...(token !== "" ? {"Authorization": "Bearer " + (token)} : {}),
"Content-Type": "application/json", ...params.headers,
},
})
.then(async value => {
if (value.status === 401 && url !== "/auth") {
try {
await this.refresh();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (_e) { /* empty */ }
return this.request(url, params, retryCount + 1);
}
return value;
});
}
}
export const loggedIn = writable(false);
export const authV2Repo = readable(new AuthV2Repo());
+4 -26
View File
@@ -17,31 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {writable} from "svelte/store";
import {get, writable} from "svelte/store";
import {authV2Repo} from "@repo/authv2.ts";
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) =>
fetch(`${import.meta.env.PUBLIC_API_SERVER}${url}`, {...params,
headers: {
...(token !== "" ? {"Authorization": "Bearer " + (token)} : {}),
"Content-Type": "application/json", ...params.headers,
},
})
.then(value => {
if (value.status === 401) {
tokenStore.set("");
}
return value;
});
export const fetchWithToken = (token: string, url: string, params: RequestInit = {}) => get(authV2Repo).request(url, params);
export function getLocalStorage() {
if (typeof localStorage === "undefined") {
return {
getItem: () => "",
setItem: () => {},
};
}
return localStorage;
}
export const tokenStore = writable((getLocalStorage().getItem("sw-session") ?? ""));
tokenStore.subscribe((value) => getLocalStorage().setItem("sw-session", value));
export const tokenStore = writable("");
+1 -1
View File
@@ -36,7 +36,7 @@ export class StatsRepo {
}
public async getUserStats(id: string): Promise<UserStats> {
return await fetchWithToken(this.token, `/stats/user/${id}`).then(value => value.json()).then(UserStatsSchema.parse);
return await fetchWithToken(this.token, `/stats/user`).then(value => value.json()).then(UserStatsSchema.parse);
}
}
+6 -4
View File
@@ -68,16 +68,18 @@
})
</script>
<dialog bind:this={dialog} onclose={close} onclick={(e) => dialog.close()} aria-hidden="true" class="max-h-full max-w-md w-full rounded-lg shadow-lg dark:bg-neutral-800 dark:text-neutral-100">
<div onclick={stopPropagation(onclick)} aria-hidden="true">
<dialog bind:this={dialog} onclose={close} onclick={(e) => dialog.close()} aria-hidden="true" class="max-h-full min-w-md w-fit rounded-lg shadow-lg dark:bg-neutral-800 dark:text-neutral-100">
<div onclick={stopPropagation(onclick)} aria-hidden="true" class="w-fit">
<div class="p-6 border-b border-neutral-200 dark:border-neutral-700">
<h1 class="text-4xl font-bold">{title}</h1>
</div>
<div class="p-6 main border-b border-neutral-200 dark:border-neutral-700">
{@render children?.()}
</div>
<div class="flex mx-4 my-2 p-6" onclick={() => dialog.close()} aria-hidden="true">
{@render footer?.()}
<div class="mx-4 my-2 p-6">
<div class="ml-auto flex justify-end" onclick={() => dialog.close()} aria-hidden="true">
{@render footer?.()}
</div>
</div>
</div>
</dialog>
+34
View File
@@ -0,0 +1,34 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {z} from "zod";
export const TokenSchema = z.object({
token: z.string(),
expires: z.string(),
});
export type Token = z.infer<typeof TokenSchema>;
export const AuthTokenSchema = z.object({
accessToken: TokenSchema,
refreshToken: TokenSchema,
});
export type AuthToken = z.infer<typeof AuthTokenSchema>;
@@ -0,0 +1,46 @@
---
title: MissileWars III Eventplan
key: missilewars3-eventplan
description: Der Eventplan für MissileWars 3
created: 2025-02-23
tags:
- event
- missilewars
---
### Infos:
Eventleitung: TheBreadBeard
Fights werden nach Möglichkeit mit einer Pause von 10 Minuten vorverschoben.
# Gruppenphase
## Punkte aus der Gruppenphase
<group-table data-event="67"> </group-table>
### Fights
| Start | Teams |
|-------|------------|
| 16:00:00 | KT vs Borg |
| 16:00:30 | VI vs FK |
| 16:30:00 | FK vs KT |
| 16:30:30 | Hlcy vs VI |
| 17:00:00 | VI vs KT |
| 17:00:30 | Borg vs Hlcy |
| 17:30:00 | KT vs Hlcy|
| 17:30:30 | FK vs Borg |
| 18:00:00 | VI vs Borg |
| 18:00:30 | FK vs Hlcy |
## KO-Phase
| Start | Teams |
|-------|------------|
| 17:50:00 | VI vs Borg |
| 18:20:00 | Hlcy vs FK |
| 18:22:00 | VI vs FK |
| 18:44:00 | Hlcy vs Borg |
## Ergebnisse
<fight-table data-event="67" data-group="Gruppe 1"> </fight-table>
@@ -4,8 +4,8 @@ key: schneeballschlacht
description: Die Schneeballschlacht auf SteamWar.
created: 2024-11-17
tags:
- event
- miniwargear
- event
- miniwargear
image: ../../../images/SchneeballschlachtMWG.png
---
@@ -13,26 +13,27 @@ image: ../../../images/SchneeballschlachtMWG.png
Die Tage werden kürzer und kälter, es geht auf die weiße Jahreszeit zu. Wenn der erste Schnee liegt, wird es Zeit, sich mit Mütze und Handschuhen zu bewaffnen und nach draußen zu eilen. Auch auf SteamWar wird dieses Jahr eine Schneeballschlacht stattfinden, im Modus MiniWarGear. Das Event wird, wie auch schon das Halloween-Event, von einer Community-Abstimmung begleitet, Das MWG-Regelwerk wurde mit einigen zusätzlichen Regeln erweitert.
- Das Event wird am **22.12.2024** stattfinden
- Einsendeschluss **18.12.2024**, Hotfixes dürfen bis zum **20.12.2024** Eingesendet werden
- Design-Regel: **Weihnachtliches/Winterliches Design**
- Das Event wird am **22.12.2024** stattfinden
- Einsendeschluss **18.12.2024**, Hotfixes dürfen bis zum **20.12.2024** Eingesendet werden
- Design-Regel: **Weihnachtliches/Winterliches Design**
## Regeln:
- Regelwerk: **MiniWarGear**, ohne MGs, 120 TNT trotzdem erlaubt
- **Weißer Trockenbeton**/**White Concrete Powder** darf zum Gegner geschossen und/oder geboosted werden, beginnend ab der Ausfahrzeit
- Solche „Schneeballkanonen“ zählen nicht zum Kanonenlimit und dürfen vorbeladen sein, solange sie keine Projektile schießen können
- Schneeballkanonen dürfen Wasser für ihre Treibladungen benutzen
- Der Turnieraufbau ist wie folgt:
- Alle 20 min Start, jeweils zwei Fights je 10 min versetzt
- Struktur: Ranglistenturnier (Gruppenphase, in KO-Phase wird jeder Platz (1, 2, 3, 4, 5, 6, …) umkämpft
- Regelwerk: **MiniWarGear**, ohne MGs, 120 TNT trotzdem erlaubt
- **Weißer Trockenbeton**/**White Concrete Powder** darf zum Gegner geschossen und/oder geboosted werden, beginnend ab der Ausfahrzeit
- Solche „Schneeballkanonen“ zählen nicht zum Kanonenlimit und dürfen vorbeladen sein, solange sie keine Projektile schießen können
- Schneeballkanonen dürfen Wasser für ihre Treibladungen benutzen
- Der Turnieraufbau ist wie folgt:
- Alle 20 min Start, jeweils zwei Fights je 10 min versetzt
- Struktur: Ranglistenturnier (Gruppenphase, in KO-Phase wird jeder Platz (1, 2, 3, 4, 5, 6, …) umkämpft
## Die beste „Schneeballkanone“ wird per Uservoting wie folgt besimmt
- Jedes Team muss am Tag des Turnieres (z.B. nach Ende der Fights) Bild/Videomaterial von ihrer Schneeballkanone per Discord an Zent4rus oder zSalos schicken, diese werden danach unter einem Thread zur Abstimmung veröffentlicht
- Die Darstellung der Schneeballkanone (mit/ohne Techhider, mit/ohne MWG außen rum) obliegt den Teams selbst
- Sieger von bester Schneeballkanone wird durch Uservote bestimmt (ähnlich wie bei WG Designcontest)
- Die Abstimmung endet drei Tage nach Veröffentlichung der Abstimmung
- Jedes Team muss am Tag des Turnieres (z.B. nach Ende der Fights) Bild/Videomaterial von ihrer Schneeballkanone per Discord an Zent4rus oder zSalos schicken, diese werden danach unter einem Thread zur Abstimmung veröffentlicht
- Die Darstellung der Schneeballkanone (mit/ohne Techhider, mit/ohne MWG außen rum) obliegt den Teams selbst
- Sieger von bester Schneeballkanone wird durch Uservote bestimmt (ähnlich wie bei WG Designcontest)
- Die Abstimmung endet drei Tage nach Veröffentlichung der Abstimmung
Der Gesamtsieger wird mit folgender Formel bestimmt: 0,66 * Fightplatzierung + 0,33 * Platzierung Uservoting, bei Gleichstand bestimmt die Fightplatzierung
Der Gesamtsieger wird mit folgender Formel bestimmt: 0,66 _ Fightplatzierung + 0,33 _ Platzierung Uservoting, bei Gleichstand bestimmt die Fightplatzierung
**Wir wünschen allen teilnehmenden Teams viel Erfolg!**
+28
View File
@@ -0,0 +1,28 @@
---
title: SteamWar Arcade
description: "Das erste SteamWar Arcade Event: MissleWars, TowerRun und TNTLeague"
key: steamwar-arcade
created: 2025-04-14
tags:
- event
- towerrun
- misslewars
- tntleague
- arcade
image: ../../../images/sw-arcade-image.png
---
**Ahoi Liebe Community,**
Am **18. Mai** feiert SteamWar seinen 6. Geburtstag. Zur Feier dieses Meilensteins werden wir ein kleines Event veranstalten: **SteamWar Arcade**
- Anmeldeschluss ist der **11.5.2025**
- In jedem Modus können bis zu **drei Personen** an einem Kampf teilnehmen
## Ablauf:
- Es gibt drei Modi zur Auswahl: **TowerRun**, **TNTLeague** und **MissleWars**
- Die Teams stimmen 5 Minuten vor Beginn den Modus ab. Sind die Teams sich einig, wird dieser Modus gespielt. Sind die Teams sich nicht einig, wird der dritte Modus gespielt.
- Es wird eine einfache Gruppenphase geben: alle gegen alle
**Wir wünschen allen teilnehmenden Teams viel Erfolg!**
@@ -0,0 +1,105 @@
---
title: WGS 2025 Kampfplan
description: WGS 2025 Kampfplan
key: wgs-2025-kampfplan
created: 2025-03-01
tags:
- event
- wargear
---
# WGS 2025 Kampfplan
### Platzierung
| Platzierung | Team | Punkte |
|-------------|-------------|--------|
| 1. | Halcyon | 10 |
| 2. | Happy-End | 8 |
| 3. | ABYSS | 5 |
| 4. | Borg | 4 |
| 5. | BlackFire | 2 |
| 6. | TheNoobTeam | 1 |
### Aktueller Spieltag
<fight-table data-event="68"></fight-table>
### Qualifikation
| 01.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | HPY vs ABYS | HPY qualifiziert |
| 19:30 | Borg vs Hlcy | Hlcy qualifiziert |
| 20:00 | TNT vs BF | TNT qualifiziert |
| 20:30 | ABYS vs Borg | Sieg ABYS |
| 21:00 | Borg vs BF | Sieg Borg |
| 21:30 | BF vs ABYS | ABYS, Borg qualifiziert |
PL hat sich nach der Qualifikation zurückgezogen, womit BF nachgerückt ist.
### 1. Spieltag
| 08.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | ABYS vs Borg | Sieg ABYS |
| 19:30 | Hlcy vs HPY | Sieg Hlcy |
| 20:00 | BF vs TNT | Sieg TNT |
| 20:30 | HPY vs ABYS | Sieg HPY |
| 21:00 | Borg vs BF | Sieg Borg |
| 21:30 | TNT vs Hlcy | Sieg Hlcy |
### 2. Spieltag
| 15.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | HPY vs TNT | Sieg HPY |
| 19:30 | ABYS vs BF | Sieg ABYS |
| 20:00 | Borg vs Hlcy | Sieg Hlcy |
| 20:30 | BF vs HPY | Sieg HPY |
| 21:00 | TNT vs Borg | Sieg Borg |
| 21:30 | Hlcy vs ABYS | Sieg Hlcy |
### 3. Spieltag
| 22.03.2025 | Begegnung | Ergebnis |
|------------|-------------|----------|
| 19:00 | ABYS vs TNT | Sieg ABYS |
| 19:30 | Borg vs HPY | Sieg HPY |
| 20:00 | Hlcy vs BF | Sieg Hlcy |
| 20:30 | - | / |
| 21:00 | - | / |
| 21:30 | - | / |
### 4. Spieltag
| 29.03.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | TNT vs ABYS | Sieg ABYS |
| 19:30 | HPY vs Borg | Sieg HPY |
| 20:00 | BF vs Hlcy | Sieg Hlcy |
| 20:30 | Borg vs TNT | Sieg Borg |
| 21:00 | HPY vs BF | Sieg HPY |
| 21:30 | ABYS vs Hlcy | Sieg Hlcy |
### 5. Spieltag
| 05.04.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | TNT vs HPY | Sieg HPY |
| 19:30 | BF vs ABYS | Sieg BF |
| 20:00 | Hlcy vs Borg | Sieg Hlcy |
| 20:30 | ABYS vs HPY | Sieg HPY |
| 21:00 | BF vs Borg | Sieg Borg |
| 21:30 | Hlcy vs TNT | Sieg Hlcy |
### 6. Spieltag
| 12.04.2025 | Begegnung | Ergebnis |
|------------|--------------|----------|
| 19:00 | Borg vs ABYS | Sieg ABYS |
| 19:30 | HPY vs Hlcy | Sieg Hlcy |
| 20:00 | TNT vs BF | Sieg BF |
| 20:30 | - | / |
| 21:00 | - | / |
| 21:30 | - | / |
+2 -1
View File
@@ -4,7 +4,8 @@
"url": {
"1.21.4": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.2.0/AdvancedScripts-2.2.0.jar",
"1.20.6": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.1.0/AdvancedScripts-2.1.0.jar",
"1.19.3": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.0.0/AdvancedScripts-2.0.0.jar"
"1.19.3": "https://git.steamwar.de/SteamWar/AdvancedScripts/releases/download/2.0.0/AdvancedScripts-2.0.0.jar",
"Hotkey script": "https://git.steamwar.de/SteamWar/SteamWar/src/branch/main/BauSystem/hotkeys.lua"
},
"sourceUrl": "https://git.steamwar.de/SteamWar/AdvancedScripts"
}
+3 -2
View File
@@ -2,7 +2,8 @@
"name": "SteamWarTeamserver",
"description": "Dieses Plugin ermöglicht die einfache Einbindung deines Servers in SteamWar. Wie du deinen (Team-)Server über SteamWar erreichbar machen kannst findest du hier.",
"url": {
"Info": "/teamserverintegration"
"Info": "/teamserverintegration",
"Download": "https://git.steamwar.de/SteamWar/SteamWarTeamserverIntegration/releases/download/latest/SteamWarTeamserverIntegration.jar"
},
"sourceUrl": "https://git.steamwar.de/SteamWar/SteamWarBungeeTeamserver"
"sourceUrl": "https://git.steamwar.de/SteamWar/SteamWarTeamserverIntegration"
}
+2 -1
View File
@@ -1,4 +1,5 @@
{
"translationKey": "microwg",
"main": false
"main": false,
"ranked": true
}
+5
View File
@@ -0,0 +1,5 @@
{
"translationKey": "mw",
"main": false,
"ranked": true
}
@@ -1,9 +1,9 @@
{
"name": "Graf Spree",
"name": "Graf Spee",
"description": "A simple, lightweight description of a frostbite.",
"id": 123,
"creator": ["Test", "Test2"],
"gamemode": "warship",
"image": "../../../images/publics/grafspree/area_render.png",
"image": "../../../images/publics/grafspee/area_render.png",
"3d": false
}
+3
View File
@@ -0,0 +1,3 @@
---
translationKey: mw
---
+16 -4
View File
@@ -87,7 +87,10 @@
"title": "Hilfe",
"docs": "Dokumentation"
},
"account": "Konto"
"account": "Konto",
"ranked": {
"mw": "MissileWars"
}
}
},
"wg": {
@@ -123,6 +126,9 @@
"microwg": {
"title": "MicroWarGear"
},
"mw": {
"title": "MissileWars"
},
"footer": {
"imprint": "Impressum",
"privacy": "Datenschutzerklärung",
@@ -167,7 +173,7 @@
"stats": {
"playtime": "Spielzeit: {# playtime #}",
"fights": "Kämpfe: {# fights #}",
"checked": "Schematics Geprüft: {# checked #}"
"checked": "Freigegebene Schematics: {# checked #}"
},
"schematic": {
"upload": "Hochladen",
@@ -194,7 +200,12 @@
}
},
"cancel": "Abbrechen",
"title": "Schematic hochladen"
"title": "Schematic hochladen",
"errors": {
"invalidEnding": "Diese Dateiendung kann nicht Hochgeladen werden.",
"noFile": "Keine Datei.",
"upload": "Fehler beim Hochladen, Überprüfe deine Schematic!"
}
}
},
"login": {
@@ -205,7 +216,8 @@
},
"label": {
"username": "Nutzername",
"password": "Passwort"
"password": "Passwort",
"repeat": "Passwort Wiederholen"
},
"setPassword": "Wie setzte ich mein Passwort?",
"submit": "Login",
+8 -2
View File
@@ -111,7 +111,7 @@
"stats": {
"playtime": "Playtime: {# playtime #}",
"fights": "Fights: {# fights #}",
"checked": "Schematics Checked: {# checked #}"
"checked": "Accepted Schematics: {# checked #}"
},
"schematic": {
"upload": "Upload",
@@ -137,6 +137,11 @@
"download": "Download",
"close": "Close"
}
},
"errors": {
"invalidEnding": "This file extension cannot be uploaded.",
"noFile": "No file.",
"upload": "Error uploading, check your schematic!"
}
}
},
@@ -149,7 +154,8 @@
},
"label": {
"username": "Username",
"password": "Password"
"password": "Password",
"repeat": "Repeat Password"
},
"setPassword": "How to set a Password",
"submit": "Login",

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

+6 -5
View File
@@ -1,13 +1,9 @@
---
import icon from "../images/logo.png";
import {getImage} from "astro:assets";
import {astroI18n} from "astro-i18n";
import {SEO} from "astro-seo";
import {ClientRouter} from "astro:transitions";
const {title, description, clientSideRouter = true} = Astro.props.frontmatter || Astro.props;
import "../../public/fonts/roboto/roboto.css";
const iconImage = await getImage({src: icon, height: 32, width: 32, format: "png", quality: 100});
---
<html lang={astroI18n.locale} class="dark">
@@ -17,7 +13,12 @@ const iconImage = await getImage({src: icon, height: 32, width: 32, format: "png
content="width=device-width, user-scalable=5, initial-scale=1.0, maximum-scale=5.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="icbm" content="52.370216;4.895168"/>
<link rel="icon" type="imgage/png" href={iconImage.src}/>
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="MyWebSite" />
<link rel="manifest" href="/site.webmanifest" />
<SEO
title={title}
+4 -1
View File
@@ -3,7 +3,7 @@ import {Image} from "astro:assets";
import Basic from "./Basic.astro";
import "../styles/button.css";
import localLogo from "../images/logo.png";
import {YoutubeSolid, DiscordSolid} from "flowbite-svelte-icons";
import {YoutubeSolid, DiscordSolid, FileCodeSolid} from "flowbite-svelte-icons";
import {t} from "astro-i18n";
import {l} from "../util/util";
@@ -50,6 +50,9 @@ const {title, description} = Astro.props;
<a class="flex" href="/discord">
<DiscordSolid class="mr-2"/>
Discord</a>
<a class="flex" href="https://git.steamwar.de">
<FileCodeSolid class="mr-2"/>
Gitea</a>
</div>
</div>
<span class="text-sm text-white text-center mt-1">© SteamWar.de - Made with ❤️ by Chaoscaot</span>
+10 -18
View File
@@ -77,7 +77,7 @@ const ogImage = await getImage({
)}
<div class={post.data.image ? "absolute bottom-8 left-2" : "mb-4"}>
<h1 class="text-4xl mb-0" transition:name={post.data.title + "-title"}>{post.data.title}</h1>
<div class="flex items-center mt-2 text-neutral-300">
<div class="flex items-center mt-2 text-neutral-800 dark:text-neutral-300">
<TagSolid class="w-4 h-4 mr-2"/>
<div transition:name={post.data.title + "-tags"}>
{post.data.tags.map(tag => (
@@ -111,15 +111,13 @@ const ogImage = await getImage({
import type {ExtendedEvent} from "@type/event";
import {mount} from "svelte";
const eventMounts: Map<string, ((ev: ExtendedEvent) => void)[]> = new Map();
const eventMounts: Map<string, Promise<ExtendedEvent>> = new Map();
class FightTableElement extends HTMLElement {
connectedCallback(): void {
if (!eventMounts.has(this.dataset["event"]!)) {
eventMounts.set(this.dataset["event"]!, []);
}
loadEvent(this.dataset["event"]!);
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
eventMounts.get(this.dataset["event"]!)!.then(ev => {
mount(FightTable, {
target: this,
props: {
@@ -134,11 +132,9 @@ const ogImage = await getImage({
class GroupTableElement extends HTMLElement {
connectedCallback(): void {
if (!eventMounts.has(this.dataset["event"]!)) {
eventMounts.set(this.dataset["event"]!, []);
}
loadEvent(this.dataset["event"]!);
const rows = Number.parseInt(this.dataset["rows"]!);
eventMounts.get(this.dataset["event"]!)!.push(ev => {
eventMounts.get(this.dataset["event"]!)!.then(ev => {
mount(GroupTable, {
target: this,
props: {
@@ -154,17 +150,13 @@ const ogImage = await getImage({
customElements.define("fight-table", FightTableElement);
customElements.define("group-table", GroupTableElement);
function mountEvent() {
for (const key of eventMounts.keys()) {
get(eventRepo).getEvent(key).then(ev => {
for (const mount of eventMounts.get(key)!) {
mount(ev);
}
});
function loadEvent(id: string) {
if (!eventMounts.has(id)) {
eventMounts.set(id, get(eventRepo).getEvent(id));
}
}
mountEvent();
document.addEventListener("astro:before-swap", eventMounts.clear);
</script>
</article>
</PageLayout>
+3 -1
View File
@@ -8,9 +8,11 @@ import {t} from "astro-i18n";
<script>
import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
import {get} from "svelte/store";
import {loggedIn} from "../components/repo/authv2";
document.addEventListener("astro:page-load", () => {
if (window.location.href.endsWith("/dashboard") || window.location.href.endsWith("/dashboard/")) {
if ((localStorage.getItem("sw-session") ?? "") === "") {
if (!get(loggedIn)) {
navigate(l("/login"), {});
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ import {type Player} from "../components/types/data";
import PostComponent from "../components/PostComponent.astro";
import BackgroundImage from "../components/BackgroundImage.astro";
const teamMember: { [key: string]: Player[]} = await fetch("http://127.0.0.1:1337/data/team")
const teamMember: { [key: string]: Player[]} = await fetch(import.meta.env.PUBLIC_API_SERVER + "/data/team")
.then(value => value.json());
const posts = await getCollection("announcements", entry => entry.id.split("/")[0] === astroI18n.locale);
+3 -1
View File
@@ -9,10 +9,12 @@ import BackgroundImage from "../components/BackgroundImage.astro";
<script>
import {l} from "../util/util";
import {navigate} from "astro:transitions/client";
import {loggedIn} from "../components/repo/authv2";
import {get} from "svelte/store";
document.addEventListener("astro:page-load", () => {
if (window.location.href.endsWith("/login") || window.location.href.endsWith("/login/")) {
if ((localStorage.getItem("sw-session") ?? "") !== "") {
if (get(loggedIn)) {
navigate(l("/dashboard"), {history: "replace"});
}
}
+1
View File
@@ -44,4 +44,5 @@ const modes = await getCollection("modes", entry => entry.data.main);
</div>
</div>
</div>))}
<a href={l("/rangliste/MissileWars")}>MissileWars Rangliste</a>
</PageLayout>
+1 -1
View File
@@ -31,7 +31,7 @@ table {
text-align: center;
tr:nth-child(odd) {
@apply bg-neutral-800;
@apply bg-neutral-200 dark:bg-neutral-800;
}
}
}