svelte-migrate: updated files

This commit is contained in:
nemunaire 2024-04-19 11:46:06 +02:00
parent ca12b3dde5
commit 3cf92b4798
37 changed files with 600 additions and 536 deletions

View file

@ -16,6 +16,7 @@
import { blake2b } from 'hash-wasm';
import { my } from '../stores/my.js';
import { teams } from '../stores/teams.js';
import { settings } from '../stores/settings.js';
import DateFormat from './DateFormat.svelte';
@ -25,14 +26,11 @@
export let exercice = { };
export let flags = [];
export let refresh_my;
export let refresh_teams;
function waitDiff(i) {
refresh_my((my) => {
my.refresh((my) => {
if (my && (my.exercices[exercice.id].tries != exercice.tries || my.exercices[exercice.id].solved_rank != exercice.solved_rank || my.exercices[exercice.id].solved_time != exercice.solved_time)) {
submitInProgress = false;
refresh_teams();
teams.refresh();
} else if (i > 0) {
setTimeout(waitDiff, (12-i)*50+440, i-1);
} else {
@ -208,7 +206,6 @@
<FlagMCQ
exercice_id={exercice.id}
{flag}
{refresh_my}
bind:values={responses.mcqs}
bind:justifications={responses.justifications}
/>
@ -216,7 +213,6 @@
<FlagKey
exercice_id={exercice.id}
{flag}
{refresh_my}
bind:value={responses.flags[flag.id]}
/>
{/if}

View file

@ -14,12 +14,11 @@
export let hints = [];
export let exercice = {};
export let refresh_my = null;
let hints_submitted = {};
function waitDiff(i, hint) {
refresh_my((my) => {
my.refresh((my) => {
let openedHint = false;
if (my && my.exercices[exercice.id].hints) {

View file

@ -6,16 +6,16 @@
} from 'sveltestrap';
import { tick } from 'svelte';
import { my } from '../stores/my.js';
import { settings } from '../stores/settings.js';
export let refresh_my = null;
export let exercice_id = 0;
export let flag = { };
export let value = "";
let values = [""];
function waitChoices(i) {
refresh_my((my) => {
my.refresh((my) => {
let haveChoices = false;
if (my && my.exercices[exercice_id].flags) {

View file

@ -6,7 +6,6 @@
import FlagKey from './FlagKey.svelte';
export let refresh_my = null;
export let exercice_id = 0;
export let flag = { };
export let values = { };
@ -36,7 +35,6 @@
<FlagKey
{exercice_id}
flag={flag.choices[cid].justification}
{refresh_my}
bind:values={justifications[flag.choices[cid].justification.id]}
/>
{/if}

View file

@ -10,12 +10,10 @@
import { my } from '../stores/my.js';
import { settings } from '../stores/settings.js';
export let refresh_my;
let newTeamName = "";
function gotoHomeOnDiff(i) {
refresh_my((my) => {
my.refresh((my) => {
if (my && my.name == newTeamName) {
newTeamName = "";
messageClass = "info";

View file

@ -1,6 +0,0 @@
export async function handle({ event, resolve }) {
const response = await resolve(event, {
ssr: false,
});
return response;
}

View file

@ -1,15 +1,5 @@
<script context="module">
export function load({ error, status }) {
return {
props: {
title: `${status}: ${error.message}`
}
};
}
</script>
<script>
export let title;
import { page } from '$app/stores';
</script>
<h1>{title}</h1>
<h1>{$page.status} : {$page.error.message}</h1>

View file

@ -0,0 +1,21 @@
import { challengeInfo } from '../stores/challengeinfo.js';
import { issuesStore } from '../stores/issues.js';
import { my } from '../stores/my.js';
import { teamsStore } from '../stores/teams.js';
import { themesStore } from '../stores/themes.js';
import { settings, time } from '../stores/settings.js';
export const ssr = false;
export async function load() {
await challengeInfo.refresh();
await settings.refresh();
await themes.refresh();
teams.refresh();
my.refresh((my) => {
if (my && my.team_id === 0) {
stop_refresh = true;
}
});
issues.refresh();
}

View file

@ -1,137 +1,3 @@
<script context="module">
import { challengeInfo } from '../stores/challengeinfo.js';
import { issuesStore } from '../stores/issues.js';
import { my } from '../stores/my.js';
import { teamsStore } from '../stores/teams.js';
import { themesStore } from '../stores/themes.js';
import { settings, time } from '../stores/settings.js';
let stop_refresh = false;
let refresh_interval_settings = null;
async function refresh_settings(cb=null, interval=null) {
if (refresh_interval_settings)
clearInterval(refresh_interval_settings);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_settings = setInterval(refresh_settings, interval);
if (!cb) {
// Before we start, update settings more frequently.
cb = function(stgs) {
const srv_cur = new Date(Date.now() + (stgs.currentTime - stgs.recvTime));
if (settings.start > srv_cur) {
const startIn = settings.start - srv_cur;
if (startIn > 15000) {
setTimeout(refresh_settings, Math.floor(Math.random() * 10000) + 2400)
} else if (startIn > 1500) {
setTimeout(refresh_settings, startIn - 1000 - Math.floor(Math.random() * 500))
} else {
// On scheduled start time, refresh my.json file
setTimeout(refresh_my, startIn + Math.floor(Math.random() * 200))
}
}
};
}
settings.update(await fetch('settings.json', {headers: {'Accept': 'application/json'}}), cb);
}
async function refresh_challengeInfo(cb=null) {
challengeInfo.update(await fetch('challenge.json', {headers: {'Accept': 'application/json'}}), cb);
}
let refresh_interval_teams = null;
async function refresh_teams(cb=null, interval=null) {
if (refresh_interval_teams)
clearInterval(refresh_interval_teams);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_teams = setInterval(refresh_teams, interval);
teamsStore.update(await fetch('teams.json', {headers: {'Accept': 'application/json'}}), cb);
}
let refresh_interval_themes = null;
async function refresh_themes(cb=null, interval=null) {
if (refresh_interval_themes)
clearInterval(refresh_interval_themes);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_themes = setInterval(refresh_themes, interval);
await themesStore.update(await fetch('themes.json', {headers: {'Accept': 'application/json'}}), cb);
}
let refresh_interval_my = null;
async function refresh_my(cb=null, interval=null) {
if (refresh_interval_my)
clearInterval(refresh_interval_my);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 24000;
}
if (stop_refresh) {
return;
}
refresh_interval_my = setInterval(refresh_my, interval);
my.update(await fetch('my.json', {headers: {'Accept': 'application/json'}}), cb);
}
let refresh_interval_issues = null;
async function refresh_issues(cb=null, interval=null) {
if (refresh_interval_issues)
clearInterval(refresh_interval_issues);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_issues = setInterval(refresh_issues, interval);
issuesStore.update(await fetch('issues.json', {headers: {'Accept': 'application/json'}}), cb);
}
export async function load({ stuff }) {
await refresh_challengeInfo();
await refresh_settings();
await refresh_themes();
refresh_teams();
refresh_my((my) => {
if (my && my.team_id === 0) {
stop_refresh = true;
}
});
refresh_issues();
return {
stuff: {
...stuff,
refresh_challengeInfo,
refresh_settings,
refresh_teams,
refresh_themes,
refresh_my,
refresh_issues,
}
};
}
</script>
<script>
import '../fic.scss'
import "bootstrap-icons/font/bootstrap-icons.css";

View file

@ -0,0 +1,19 @@
import { get_store_value } from 'svelte/internal';
import { themes } from '../../stores/themes.js';
export async function load({ params }) {
const thms = get_store_value(themes);
let theme = null;
for (let th in thms) {
if (thms[th] && thms[th].urlid === params.theme) {
theme = thms[th];
break;
}
}
return {
theme,
};
}

View file

@ -1,30 +1,3 @@
<script context="module">
import { get_store_value } from 'svelte/internal';
import { themes } from '../../stores/themes.js';
export async function load({ params, stuff }) {
const thms = get_store_value(themes);
let theme = null;
for (let th in thms) {
if (thms[th] && thms[th].urlid === params.theme) {
theme = thms[th];
break;
}
}
return {
stuff: {
...stuff,
theme: theme,
}, props: {
theme: theme,
}
};
}
</script>
<script>
import {
Container,

View file

@ -0,0 +1,7 @@
export async function load({ parent }) {
const data = await parent();
return {
theme: data.theme,
};
}

View file

@ -1,13 +1,3 @@
<script context="module">
export async function load({ stuff }) {
return {
props: {
theme: stuff.theme,
},
};
}
</script>
<script>
import {
Alert,
@ -25,31 +15,31 @@
import { my } from '../../stores/my.js';
export let theme = null;
export let data;
</script>
{#if theme && theme.exercices}
{#if data.theme && data.theme.exercices}
<Card class="niceborder text-indent mt-2 mb-4">
<CardBody class="bg-dark text-light">
<Row>
<Col>
<p class="mt-4 mx-3 card-text lead text-justify">{@html theme.headline}</p>
<p class="mb-4 mx-3 card-text text-justify">{@html theme.intro}</p>
<p class="mt-4 mx-3 card-text lead text-justify">{@html data.theme.headline}</p>
<p class="mb-4 mx-3 card-text text-justify">{@html data.theme.intro}</p>
</Col>
{#if theme.partner_txt || theme.partner_img || theme.partner_href}
{#if data.theme.partner_txt || data.theme.partner_img || data.theme.partner_href}
<Col md="2" lg="3" class="d-none d-md-block">
<Card class="pt-3 px-3">
{#if theme.partner_img}
<img src="{theme.partner_img}" class="card-img-top">
{#if data.theme.partner_img}
<img src="{data.theme.partner_img}" class="card-img-top">
{/if}
{#if theme.partner_txt || theme.partner_href}
{#if data.theme.partner_txt || data.theme.partner_href}
<CardBody class="p-0 mt-3">
{#if theme.partner_txt}
{@html theme.partner_txt}
{#if data.theme.partner_txt}
{@html data.theme.partner_txt}
{/if}
{#if theme.partner_href}
<Button tag="a" color="primary" href="{theme.partner_href}">
{#if data.theme.partner_href}
<Button tag="a" color="primary" href="{data.theme.partner_href}">
Visiter le site
</Button>
{/if}
@ -62,11 +52,11 @@
</CardBody>
<ul class="list-group">
{#each Object.keys(theme.exercices) as k, index}
{#each Object.keys(data.theme.exercices) as k, index}
<li
class="list-group-item"
class:list-group-item-action={$my && $my.exercices[k]}
on:click={goto(`${theme.urlid}/${theme.exercices[k].urlid}`)}
on:click={goto(`${data.theme.urlid}/${data.theme.exercices[k].urlid}`)}
>
<div class="row">
<div class="col-1" style="margin-top: -0.5rem; margin-bottom: -0.5rem; text-align: right; border-right: 5px solid #{$my && $my.exercices[k] && $my.exercices[k].solved_rank ? '62c462' : 'bbb'}">
@ -75,37 +65,37 @@
<div style="position: absolute; margin-left: calc(var(--bs-gutter-x) * -.5 - 15px); margin-top: -0.5rem;">
<svg style="height: 50px; width: 23px;">
<rect
style="fill:#{$my && $my.exercices[k] && (index < 1 || ($my.exercices[Object.keys(theme.exercices)[index-1]] && $my.exercices[Object.keys(theme.exercices)[index-1]].solved_rank)) ? '62c462' : 'bbb'}"
style="fill:#{$my && $my.exercices[k] && (index < 1 || ($my.exercices[Object.keys(data.data.theme.exercices)[index-1]] && $my.exercices[Object.keys(data.theme.exercices)[index-1]].solved_rank)) ? '62c462' : 'bbb'}"
width="5"
height="30"
x="10"
y="0" />
<path
style="fill:#{$my && $my.exercices[k] ? ($my.exercices[k].solved_rank ? '62c462' : (theme.exercices[k].curcoeff > 1.0 ? 'f89406' : '5bc0de')) : '555'}"
style="fill:#{$my && $my.exercices[k] ? ($my.exercices[k].solved_rank ? '62c462' : (data.theme.exercices[k].curcoeff > 1.0 ? 'f89406' : '5bc0de')) : '555'}"
d="m 22,20 a 9.5700617,9.5700617 0 0 1 -9.5690181,9.57006 9.5700617,9.5700617 0 0 1 -9.57110534,-9.56797 9.5700617,9.5700617 0 0 1 9.56692984,-9.57215 9.5700617,9.5700617 0 0 1 9.5731926,9.56588" />
</svg>
</div>
{#each theme.exercices[k].tags as tag, idx}
{#each data.theme.exercices[k].tags as tag, idx}
<Badge href="tags/{tag}" pill color="secondary" class="mx-1 float-end">#{tag}</Badge>
{/each}
<h5 class="fw-bold">
{#if $my && $my.exercices[k]}
{theme.exercices[k].title}
{data.theme.exercices[k].title}
{:else}
<span style="white-space: nowrap">
<Icon name="lock-fill" aria-hidden="true" title="Vous n'avez pas encore accès à ce défi" />
{theme.exercices[k].title}
{data.theme.exercices[k].title}
</span>
{/if}
{#if theme.exercices[k].curcoeff > 1.0}
{#if data.theme.exercices[k].curcoeff > 1.0}
<Icon name="gift" aria-hidden="true" title="Un bonus est actuellement appliqué lors de la résolution de ce défi" />
{/if}
</h5>
<p>{@html theme.exercices[k].headline}</p>
<p>{@html data.theme.exercices[k].headline}</p>
</div>
<div class="d-none d-md-block col-1">
{#if $my && $my.exercices[k]}
<a class="float-right" href="{theme.urlid}/{theme.exercices[k].urlid}" style="font-size: 3rem">
<a class="float-right" href="{data.theme.urlid}/{data.theme.exercices[k].urlid}" style="font-size: 3rem">
<Icon name="chevron-right" aria-hidden="true" />
</a>
{:else}

View file

@ -0,0 +1,22 @@
import { get_store_value } from 'svelte/internal';
import { themes } from '../../../stores/themes.js';
export async function load({ parent }) {
const stuff = await parent();
let exercice = null;
for (let ex in stuff.theme.exercices) {
if (stuff.theme.exercices[ex].urlid === params.exercice) {
exercice = stuff.theme.exercices[ex];
exercice.id = ex;
break;
}
}
return {
theme: stuff.theme,
exercice: exercice,
};
}

View file

@ -1,30 +1,3 @@
<script context="module">
import { get_store_value } from 'svelte/internal';
import { themes } from '../../stores/themes.js';
export async function load({ params, stuff }) {
let exercice = null;
for (let ex in stuff.theme.exercices) {
if (stuff.theme.exercices[ex].urlid === params.exercice) {
exercice = stuff.theme.exercices[ex];
exercice.id = ex;
break;
}
}
return {
props: {
theme: stuff.theme,
exercice: exercice,
refresh_my: stuff.refresh_my,
refresh_teams: stuff.refresh_teams,
}
};
}
</script>
<script>
import {
Alert,
@ -38,55 +11,51 @@
Row,
} from 'sveltestrap';
import ExerciceDownloads from '../../components/ExerciceDownloads.svelte';
import ExerciceFlags from '../../components/ExerciceFlags.svelte';
import ExerciceHints from '../../components/ExerciceHints.svelte';
import ExerciceSolved from '../../components/ExerciceSolved.svelte';
import ExerciceVideo from '../../components/ExerciceVideo.svelte';
import ThemeNav from '../../components/ThemeNav.svelte';
import ExerciceDownloads from '../../../components/ExerciceDownloads.svelte';
import ExerciceFlags from '../../../components/ExerciceFlags.svelte';
import ExerciceHints from '../../../components/ExerciceHints.svelte';
import ExerciceSolved from '../../../components/ExerciceSolved.svelte';
import ExerciceVideo from '../../../components/ExerciceVideo.svelte';
import ThemeNav from '../../../components/ThemeNav.svelte';
import { challengeInfo } from '../../stores/challengeinfo.js';
import { my } from '../../stores/my.js';
import { settings } from '../../stores/settings.js';
import { challengeInfo } from '../../../stores/challengeinfo.js';
import { my } from '../../../stores/my.js';
import { settings } from '../../../stores/settings.js';
export let theme;
export let exercice;
export let data;
let solved = {};
export let refresh_my;
export let refresh_teams;
</script>
<svelte:head>
<title>{exercice?exercice.title+" - ":""}{$challengeInfo.title}</title>
<title>{data.exercice?data.exercice.title+" - ":""}{$challengeInfo.title}</title>
</svelte:head>
{#if exercice}
<ThemeNav {theme} {exercice} />
{#if data.exercice}
<ThemeNav theme={data.theme} exercice={data.exercice} />
{/if}
{#if !$my || !exercice || !$my.exercices[exercice.id]}
{#if !$my || !data.exercice || !$my.exercices[data.exercice.id]}
<Alert color="warning" class="mt-3" fade={false}>
<Icon name="dash-circle-fill" />
Vous n'avez pas encore accès à ce défi.
</Alert>
{/if}
{#if exercice}
{#if data.exercice}
<Card body class="niceborder text-indent my-3">
<h3 class="display-4">{exercice.title}</h3>
<h3 class="display-4">{data.exercice.title}</h3>
<div>
{#each exercice.tags as tag, index}
{#each data.exercice.tags as tag, index}
<Badge href="tags/{tag}" pill color="secondary" class="mx-1 mb-2" >#{tag}</Badge>
{/each}
</div>
{#if !$my || !$my.exercices[exercice.id]}
<p class="lead text-justify">{@html exercice.headline}</p>
{#if !$my || !$my.exercices[data.exercice.id]}
<p class="lead text-justify">{@html data.exercice.headline}</p>
{:else}
<p class="lead text-justify">{@html $my.exercices[exercice.id].statement}</p>
{#if $my.exercices[exercice.id].issue}
<Alert color="{$my.exercices[exercice.id].issuekind}">
{@html $my.exercices[exercice.id].issue}
<p class="lead text-justify">{@html $my.exercices[data.exercice.id].statement}</p>
{#if $my.exercices[data.exercice.id].issue}
<Alert color="{$my.exercices[data.exercice.id].issuekind}">
{@html $my.exercices[data.exercice.id].issue}
</Alert>
{/if}
{/if}
@ -96,31 +65,31 @@
<ul>
<li>
<strong>Gain&nbsp;:</strong>
{exercice.gain} {exercice.gain==1?"point":"points"}
{#if $settings.firstBlood && exercice.solved < 1}
{data.exercice.gain} {data.exercice.gain==1?"point":"points"}
{#if $settings.firstBlood && data.exercice.solved < 1}
<em>+{$settings.firstBlood * 100}% (prem's)</em>
{/if}
{#if exercice.curcoeff != 1.0 || $settings.exerciceCurrentCoefficient != 1.0}
<em>{#if exercice.curcoeff * $settings.exerciceCurrentCoefficient > 1}+{Math.round((exercice.curcoeff * $settings.exerciceCurrentCoefficient - 1) * 100)}{:else}-{Math.round((1-(exercice.curcoeff * $settings.exerciceCurrentCoefficient)) * 100)}{/if}% (bonus)</em>
{#if data.exercice.curcoeff != 1.0 || $settings.exerciceCurrentCoefficient != 1.0}
<em>{#if data.exercice.curcoeff * $settings.exerciceCurrentCoefficient > 1}+{Math.round((data.exercice.curcoeff * $settings.exerciceCurrentCoefficient - 1) * 100)}{:else}-{Math.round((1-(data.exercice.curcoeff * $settings.exerciceCurrentCoefficient)) * 100)}{/if}% (bonus)</em>
{/if}
</li>
<li>
<strong>Tenté par&nbsp;:</strong>
{#if !exercice.tried}
{#if !data.exercice.tried}
aucune équipe
{:else}
{exercice.tried} {exercice.tried == 1?"équipe":"équipes"}
{#if $my && $my.exercices[exercice.id] && $my.exercices[exercice.id].total_tries}
(cumulant {$my.exercices[exercice.id].total_tries} {$my.exercices[exercice.id].total_tries == 1?"tentative":"tentatives"})
{data.exercice.tried} {data.exercice.tried == 1?"équipe":"équipes"}
{#if $my && $my.exercices[data.exercice.id] && $my.exercices[data.exercice.id].total_tries}
(cumulant {$my.exercices[data.exercice.id].total_tries} {$my.exercices[data.exercice.id].total_tries == 1?"tentative":"tentatives"})
{/if}
{/if}
</li>
<li>
<strong>Résolu par&nbsp;:</strong>
{#if !exercice.solved}
{#if !data.exercice.solved}
aucune équipe
{:else}
{exercice.solved} {exercice.solved == 1?"équipe":"équipes"}
{data.exercice.solved} {data.exercice.solved == 1?"équipe":"équipes"}
{/if}
</li>
</ul>
@ -128,13 +97,13 @@
{#if $my && $my.team_id}
<Col>
{#if $settings.acceptNewIssue}
<a href="issues/?eid={exercice.id}" class="float-end btn btn-sm btn-warning">
<a href="issues/?eid={data.exercice.id}" class="float-end btn btn-sm btn-warning">
<Icon name="bug" />
Rapporter une anomalie sur ce défi
</a>
{/if}
{#if $settings.QAenabled}
<a href="qa/exercices/{exercice.id}" class="float-end btn btn-sm btn-info" target="_self">
<a href="qa/exercices/{data.exercice.id}" class="float-end btn btn-sm btn-info" target="_self">
<Icon name="bug" />
Voir les éléments QA sur ce défi
</a>
@ -144,51 +113,48 @@
</Row>
</Card>
{#if $my && $my.exercices[exercice.id]}
{#if $my && $my.exercices[data.exercice.id]}
<Row class="mt-4">
<Col lg="6" class="mb-5">
{#if $my.exercices[exercice.id].files}
{#if $my.exercices[data.exercice.id].files}
<ExerciceDownloads
files={$my.exercices[exercice.id].files}
files={$my.exercices[data.exercice.id].files}
/>
{/if}
{#if $my.exercices[exercice.id].hints}
{#if $my.exercices[data.exercice.id].hints}
<ExerciceHints
{refresh_my}
exercice={$my.exercices[exercice.id]}
hints={$my.exercices[exercice.id].hints}
exercice={$my.exercices[data.exercice.id]}
hints={$my.exercices[data.exercice.id].hints}
/>
{/if}
</Col>
<Col lg="6" class="mb-5">
{#if $my.exercices[exercice.id].flags && $my.exercices[exercice.id].non_found_flags > 0 && !solved[exercice.id]}
{#if $my.exercices[data.exercice.id].flags && $my.exercices[data.exercice.id].non_found_flags > 0 && !solved[data.exercice.id]}
<ExerciceFlags
{refresh_my}
{refresh_teams}
exercice={$my.exercices[exercice.id]}
bind:forcesolved={solved[exercice.id]}
flags={$my.exercices[exercice.id].flags}
exercice={$my.exercices[data.exercice.id]}
bind:forcesolved={solved[data.exercice.id]}
flags={$my.exercices[data.exercice.id].flags}
/>
{/if}
{#if $my.exercices[exercice.id].solved_rank || solved[exercice.id]}
{#if $my.exercices[data.exercice.id].solved_rank || solved[data.exercice.id]}
<ExerciceSolved
{theme}
exercice={$my.exercices[exercice.id]}
theme={data.theme}
exercice={$my.exercices[data.exercice.id]}
/>
{/if}
{#if $my.exercices[exercice.id].resolution || $my.exercices[exercice.id].video_uri}
{#if $my.exercices[data.exercice.id].resolution || $my.exercices[data.exercice.id].video_uri}
<Card class="border-success mb-2">
<CardHeader class="bg-success text-light">
<Icon name="laptop-fill" />
Solution du défi
</CardHeader>
{#if $my.exercices[exercice.id].resolution}
{#if $my.exercices[data.exercice.id].resolution}
<CardBody>
{@html $my.exercices[exercice.id].resolution}
{@html $my.exercices[data.exercice.id].resolution}
</CardBody>
{/if}
{#if $my.exercices[exercice.id].video_uri}
<ExerciceVideo uri={$my.exercices[exercice.id].video_uri} />
{#if $my.exercices[data.exercice.id].video_uri}
<ExerciceVideo uri={$my.exercices[data.exercice.id].video_uri} />
{/if}
</Card>
{/if}

View file

@ -1,13 +1,3 @@
<script context="module">
export async function load({ stuff }) {
return {
props: {
refresh_my: stuff.refresh_my,
}
}
}
</script>
<script>
import {
Alert,
@ -20,14 +10,12 @@
Row,
} from 'sveltestrap';
import ScoreGrid from '../components/ScoreGrid.svelte';
import TeamChangeName from '../components/TeamChangeName.svelte';
import TeamMembers from '../components/TeamMembers.svelte';
import ScoreGrid from '../../components/ScoreGrid.svelte';
import TeamChangeName from '../../components/TeamChangeName.svelte';
import TeamMembers from '../../components/TeamMembers.svelte';
import { my } from '../stores/my.js';
import { settings } from '../stores/settings.js';
export let refresh_my;
import { my } from '../../stores/my.js';
import { settings } from '../../stores/settings.js';
</script>
<Container class="my-3">
@ -43,7 +31,7 @@
<Col md>
<TeamMembers members={$my.members} />
{#if !$settings.denyNameChange}
<TeamChangeName {refresh_my} />
<TeamChangeName />
{/if}
</Col>
<Col md>

View file

@ -0,0 +1,14 @@
import { get_store_value } from 'svelte/internal';
import { exercices_idx } from '../../stores/themes.js';
export async function load({ url }) {
const eidx = get_store_value(exercices_idx);
const exercice = eidx[url.searchParams.get("eid")]?eidx[url.searchParams.get("eid")]:null;
return {
exercice: exercice,
fillIssue: exercice !== null || url.searchParams.get("fill-issue") !== null,
};
}

View file

@ -1,23 +1,3 @@
<script context="module">
import { get_store_value } from 'svelte/internal';
import { exercices_idx } from '../stores/themes.js';
export async function load({ url, stuff }) {
const eidx = get_store_value(exercices_idx);
const exercice = eidx[url.searchParams.get("eid")]?eidx[url.searchParams.get("eid")]:null;
return {
props: {
refresh_issues: stuff.refresh_issues,
exercice: exercice,
fillIssue: exercice !== null || url.searchParams.get("fill-issue") !== null,
}
};
}
</script>
<script>
import {
Alert,
@ -30,22 +10,20 @@
Table,
} from 'sveltestrap';
import DateFormat from '../components/DateFormat.svelte';
import DateFormat from '../../components/DateFormat.svelte';
import { issues, issues_nb_responses, issues_known_responses } from '../stores/issues.js';
import { settings } from '../stores/settings.js';
import { issues, issues_nb_responses, issues_known_responses } from '../../stores/issues.js';
import { settings } from '../../stores/settings.js';
import FormIssue from '../components/FormIssue.svelte';
import FormIssue from '../../components/FormIssue.svelte';
export let refresh_issues = null;
export let exercice = null;
export let fillIssue = false;
export let data;
let issue = {};
issues_known_responses.set($issues_nb_responses);
function newIssue() {
fillIssue = true;
data.fillIssue = true;
}
let sberr = "";
@ -53,7 +31,7 @@
let messageClass = "success";
function waitDiff(curissues, i) {
refresh_issues((issues) => {
issues.refresh((issues) => {
if (i > 0 && (!issues || issues.length <= curissues)) {
setTimeout(waitDiff, 850, curissues, i-1);
}
@ -61,9 +39,9 @@
}
function respondTo(_issue) {
exercice = null;
data.exercice = null;
issue = {id: _issue.id, description: ''};
fillIssue = true;
data.fillIssue = true;
}
async function submit_issue(event) {
@ -85,8 +63,8 @@
messageClass = 'success';
message = data.errmsg;
issue = { };
exercice = null;
fillIssue = false;
data.exercice = null;
data.fillIssue = false;
const currentissues = get_store_value(issues);
waitDiff(currentissues.length, 7);
@ -120,7 +98,7 @@
</Alert>
{/if}
{#if fillIssue}
{#if data.fillIssue}
<Card class="border-warning mt-3 mb-5">
<CardHeader class="bg-warning text-light">
<Icon name="file-earmark-plus" />
@ -135,7 +113,7 @@
<p class="card-text">Rapprochez-vous d'un membre de l'équipe afin d'obtenir de l'aide.</p>
{:else}
<FormIssue
{exercice}
exercice={data.exercice}
bind:issue={issue}
on:submit={submit_issue}
/>
@ -153,7 +131,7 @@
<th>Géré par</th>
<th>Messages</th>
<th>
{#if !fillIssue}
{#if !data.fillIssue}
<Button sm color="warning" on:click={newIssue}>
<Icon name="file-earmark-plus" />
</Button>

View file

@ -10,11 +10,11 @@
Row,
} from 'sveltestrap';
import { my } from '../stores/my.js';
import { rank } from '../stores/teams.js';
import { challengeInfo } from '../stores/challengeinfo.js';
import { my } from '../../stores/my.js';
import { rank } from '../../stores/teams.js';
import { challengeInfo } from '../../stores/challengeinfo.js';
import CardTheme from '../components/CardTheme.svelte';
import CardTheme from '../../components/CardTheme.svelte';
let search = "";
</script>

View file

@ -1,13 +1,3 @@
<script context="module">
export async function load({ stuff }) {
return {
props: {
refresh_my: stuff.refresh_my,
}
}
}
</script>
<script>
import {
Alert,
@ -21,13 +11,11 @@
import { goto } from '$app/navigation';
import { my } from '../stores/my.js';
import { settings } from '../stores/settings.js';
import { my } from '../../stores/my.js';
import { settings } from '../../stores/settings.js';
import RegistrationFormCreateTeam from '../components/RegistrationFormCreateTeam.svelte';
import RegistrationFormJoinTeam from '../components/RegistrationFormJoinTeam.svelte';
export let refresh_my;
import RegistrationFormCreateTeam from '../../components/RegistrationFormCreateTeam.svelte';
import RegistrationFormJoinTeam from '../../components/RegistrationFormJoinTeam.svelte';
let form = { };
let partR = false;
@ -36,7 +24,7 @@
let message;
function gotoHomeOnDiff(i) {
refresh_my((my) => {
my.refresh((my) => {
if (my && my.team_id) {
goto('.');
} else if (i > 0) {

View file

@ -6,8 +6,8 @@
Icon,
} from 'sveltestrap';
import { challengeInfo } from '../stores/challengeinfo.js';
import { settings } from '../stores/settings.js';
import { challengeInfo } from '../../stores/challengeinfo.js';
import { settings } from '../../stores/settings.js';
</script>
<Container class="my-3">

View file

@ -0,0 +1,5 @@
export async function load({ params }) {
return {
tag: params.tag,
};
}

View file

@ -1,13 +1,3 @@
<script context="module">
export async function load({ params }) {
return {
props: {
tag: params.tag,
}
};
}
</script>
<script>
import {
Alert,
@ -22,11 +12,11 @@
import { goto } from '$app/navigation';
import { themes } from '../../stores/themes.js';
import { themes } from '../../../stores/themes.js';
import CardTheme from '../../components/CardTheme.svelte';
import CardTheme from '../../../components/CardTheme.svelte';
export let tag = "";
export let data;
let exercices = [];
$: {
@ -34,7 +24,7 @@
for (let th in $themes) {
for (let ex in $themes[th].exercices) {
if ($themes[th].exercices[ex].tags.indexOf(tag) >= 0) {
if ($themes[th].exercices[ex].tags.indexOf(data.tag) >= 0) {
tmp_exercices.push({theme: $themes[th], exercice: $themes[th].exercices[ex], index: th + "," + ex});
}
}
@ -46,7 +36,7 @@
<Container class="mt-3">
<h1 class="text-dark">
Challenges <em>{tag}</em>
Challenges <em>{data.tag}</em>
</h1>
{#if exercices.length}

View file

@ -5,6 +5,11 @@ function createChallengeStore() {
return {
subscribe,
refresh: async (cb) => {
challengeInfo.update(await fetch('challenge.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: (res_challenge, cb) => {
if (res_challenge.status === 200) {
res_challenge.json().then((challenge) => {

View file

@ -0,0 +1 @@
export let stop_refresh = false;

View file

@ -1,32 +1,53 @@
import { derived, writable } from 'svelte/store';
import { stop_refresh } from './common';
let refresh_interval_issues = null;
function createIssuesStore() {
const { subscribe, set, update } = writable({issues: [], issues_idx: {}, issues_nb_responses: 0, issues_need_info: 0});
function updateFunc (res_issues, cb=null) {
if (res_issues.status === 200) {
res_issues.json().then((issues) => {
const issues_idx = {};
let issues_nb_responses = 0;
let issues_need_info = 0;
issues.forEach(function(issue, k) {
issues_idx[issue.id] = issue;
issues_nb_responses += issue.texts.length;
if (issue.state == 'need-info') issues_need_info++;
issues[k].texts.reverse();
})
update((i) => (Object.assign(i, {issues, issues_idx, issues_nb_responses, issues_need_info})));
if (cb) {
cb(issues, issues_idx, issues_nb_responses, issues_need_info);
}
});
} else if (res_issues.status === 404) {
update((i) => ({issues: [], issues_idx: {}, issues_nb_responses: 0, issues_need_info: 0}));
}
}
return {
subscribe,
update: (res_issues, cb=null) => {
if (res_issues.status === 200) {
res_issues.json().then((issues) => {
const issues_idx = {};
let issues_nb_responses = 0;
let issues_need_info = 0;
issues.forEach(function(issue, k) {
issues_idx[issue.id] = issue;
issues_nb_responses += issue.texts.length;
if (issue.state == 'need-info') issues_need_info++;
issues[k].texts.reverse();
})
update((i) => (Object.assign(i, {issues, issues_idx, issues_nb_responses, issues_need_info})));
if (cb) {
cb(issues, issues_idx, issues_nb_responses, issues_need_info);
}
});
} else if (res_issues.status === 404) {
update((i) => ({issues: [], issues_idx: {}, issues_nb_responses: 0, issues_need_info: 0}));
refresh: async (cb=null, interval=null) => {
if (refresh_interval_issues)
clearInterval(refresh_interval_issues);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_issues = setInterval(refresh_issues, interval);
updateFunc(await fetch('issues.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: updateFunc,
};
}

View file

@ -1,42 +1,63 @@
import { writable } from 'svelte/store';
import { stop_refresh } from './common';
let refresh_interval_my = null;
function createMyStore() {
const { subscribe, set, update } = writable(null);
function updateFunc(res_my, cb=null) {
if (res_my.status === 200) {
res_my.json().then((my) => {
for (let k in my.exercices) {
my.exercices[k].id = k;
if (my.exercices[k].flags) {
let nb = 0;
for (let j in my.exercices[k].flags) {
if (!my.exercices[k].flags[j].found)
nb += 1;
}
my.exercices[k].non_found_flags = nb;
}
if (my.team_id === 0 && my.exercices[k].hints) {
for (let j in my.exercices[k].hints) {
my.exercices[k].hints[j].hidden = true;
}
}
}
update((m) => (Object.assign(m?m:{}, my)));
if (cb) {
cb(my);
}
});
} else if (res_my.status === 404) {
update((m) => (null));
}
}
return {
subscribe,
update: (res_my, cb=null) => {
if (res_my.status === 200) {
res_my.json().then((my) => {
for (let k in my.exercices) {
my.exercices[k].id = k;
if (my.exercices[k].flags) {
let nb = 0;
for (let j in my.exercices[k].flags) {
if (!my.exercices[k].flags[j].found)
nb += 1;
}
my.exercices[k].non_found_flags = nb;
}
if (my.team_id === 0 && my.exercices[k].hints) {
for (let j in my.exercices[k].hints) {
my.exercices[k].hints[j].hidden = true;
}
}
}
update((m) => (Object.assign(m?m:{}, my)));
if (cb) {
cb(my);
}
});
} else if (res_my.status === 404) {
update((m) => (null));
refresh: async (cb=null, interval=null) => {
if (refresh_interval_my)
clearInterval(refresh_interval_my);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 24000;
}
if (stop_refresh) {
return;
}
refresh_interval_my = setInterval(refresh_my, interval);
updateFunc(await fetch('my.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: updateFunc,
};
}

View file

View file

@ -1,42 +1,82 @@
import { readable, writable } from 'svelte/store';
import { stop_refresh } from './common';
let refresh_interval_settings = null;
function createSettingsStore() {
const { subscribe, set, update } = writable({});
function updateFunc(res_settings, cb) {
const recvTime = (new Date()).getTime();
if (res_settings.status === 200) {
res_settings.json().then((settings) => {
if (settings.start)
settings.start = new Date(settings.start);
if (settings.end)
settings.end = new Date(settings.end);
if (settings.generation)
settings.generation = new Date(settings.generation);
if (settings.activateTime)
settings.activateTime = new Date(settings.activateTime);
if (!settings.disablesubmitbutton)
settings.disablesubmitbutton = null;
settings.recvTime = recvTime;
const x_fic_time = res_settings.headers.get("x-fic-time");
if (x_fic_time) {
settings.currentTime = Math.floor(x_fic_time * 1000);
} else {
settings.currentTime = settings.recvTime;
}
update((s) => (Object.assign({}, settings)));
if (cb) {
cb(settings);
}
});
}
}
return {
subscribe,
update: (res_settings, cb) => {
const recvTime = (new Date()).getTime();
if (res_settings.status === 200) {
res_settings.json().then((settings) => {
if (settings.start)
settings.start = new Date(settings.start);
if (settings.end)
settings.end = new Date(settings.end);
if (settings.generation)
settings.generation = new Date(settings.generation);
if (settings.activateTime)
settings.activateTime = new Date(settings.activateTime);
if (!settings.disablesubmitbutton)
settings.disablesubmitbutton = null;
settings.recvTime = recvTime;
const x_fic_time = res_settings.headers.get("x-fic-time");
if (x_fic_time) {
settings.currentTime = Math.floor(x_fic_time * 1000);
} else {
settings.currentTime = settings.recvTime;
}
update((s) => (Object.assign({}, settings)));
if (cb) {
cb(settings);
}
});
refresh: async (cb=null, interval=null) => {
if (refresh_interval_settings)
clearInterval(refresh_interval_settings);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_settings = setInterval(refresh_settings, interval);
if (!cb) {
// Before we start, update settings more frequently.
cb = function(stgs) {
const srv_cur = new Date(Date.now() + (stgs.currentTime - stgs.recvTime));
if (settings.start > srv_cur) {
const startIn = settings.start - srv_cur;
if (startIn > 15000) {
setTimeout(refresh_settings, Math.floor(Math.random() * 10000) + 2400)
} else if (startIn > 1500) {
setTimeout(refresh_settings, startIn - 1000 - Math.floor(Math.random() * 500))
} else {
// On scheduled start time, refresh my.json file
setTimeout(refresh_my, startIn + Math.floor(Math.random() * 200))
}
}
};
}
updateFunc(await fetch('settings.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: updateFunc,
}
}

View file

@ -1,30 +1,51 @@
import { derived, writable } from 'svelte/store';
import { stop_refresh } from './common';
let refresh_interval_teams = null;
function createTeamsStore() {
const { subscribe, set, update } = writable({teams:{}, teams_count: 0, rank: []});
function updateFunc(res_teams, cb=null) {
if (res_teams.status === 200) {
res_teams.json().then((teams) => {
const teams_count = Object.keys(teams).length
const rank = [];
for (const tid in teams) {
teams[tid].id = Number(tid);
rank.push(teams[tid]);
}
rank.sort((a, b) => (a.rank > b.rank ? 1 : (a.rank == b.rank ? 0 : -1)));
update((t) => (Object.assign(t, {teams, teams_count, rank})));
if (cb) {
cb(teams, teams_count, rank);
}
});
}
}
return {
subscribe,
update: (res_teams, cb=null) => {
if (res_teams.status === 200) {
res_teams.json().then((teams) => {
const teams_count = Object.keys(teams).length
const rank = [];
for (const tid in teams) {
teams[tid].id = Number(tid);
rank.push(teams[tid]);
}
rank.sort((a, b) => (a.rank > b.rank ? 1 : (a.rank == b.rank ? 0 : -1)));
update((t) => (Object.assign(t, {teams, teams_count, rank})));
if (cb) {
cb(teams, teams_count, rank);
}
});
refresh: async (cb=null, interval=null) => {
if (refresh_interval_teams)
clearInterval(refresh_interval_teams);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_teams = setInterval(refresh_teams, interval);
updateFunc(await fetch('teams.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: updateFunc,
};
}

View file

@ -1,52 +1,73 @@
import { derived, writable } from 'svelte/store';
import { stop_refresh } from './common';
let refresh_interval_themes = null;
function createThemesStore() {
const { subscribe, set, update } = writable({themes: null, exercices_idx: {}, max_solved: 0});
return {
subscribe,
update: async (res_themes, cb=null) => {
if (res_themes.status === 200) {
const themes = await res_themes.json();
async function updateFunc (res_themes, cb=null) {
if (res_themes.status === 200) {
const themes = await res_themes.json();
let max_solved = 0;
const exercices_idx = {};
let max_solved = 0;
const exercices_idx = {};
for (let key in themes) {
const theme = themes[key];
for (let key in themes) {
const theme = themes[key];
if (theme.solved > max_solved) {
max_solved = theme.solved;
if (theme.solved > max_solved) {
max_solved = theme.solved;
}
themes[key].exercice_count = Object.keys(theme.exercices).length;
themes[key].exercice_coeff_max = 0;
themes[key].max_gain = 0;
let last_exercice = null;
for (let k in theme.exercices) {
const exercice = theme.exercices[k];
themes[key].max_gain += exercice.gain;
if (themes[key].exercice_coeff_max < exercice.curcoeff) {
themes[key].exercice_coeff_max = exercice.curcoeff;
}
themes[key].exercice_count = Object.keys(theme.exercices).length;
themes[key].exercice_coeff_max = 0;
themes[key].max_gain = 0;
let last_exercice = null;
for (let k in theme.exercices) {
const exercice = theme.exercices[k];
if (last_exercice != null)
themes[key].exercices[last_exercice].next = k;
last_exercice = k;
themes[key].max_gain += exercice.gain;
if (themes[key].exercice_coeff_max < exercice.curcoeff) {
themes[key].exercice_coeff_max = exercice.curcoeff;
}
if (last_exercice != null)
themes[key].exercices[last_exercice].next = k;
last_exercice = k;
exercice.id = k;
exercices_idx[k] = exercice;
}
}
update((t) => (Object.assign(t, {themes, exercices_idx, max_solved})));
if (cb) {
cb(themes, exercices_idx, max_solved);
}
exercice.id = k;
exercices_idx[k] = exercice;
}
}
update((t) => (Object.assign(t, {themes, exercices_idx, max_solved})));
if (cb) {
cb(themes, exercices_idx, max_solved);
}
}
}
return {
subscribe,
refresh: async (cb=null, interval=null) => {
if (refresh_interval_themes)
clearInterval(refresh_interval_themes);
if (interval === null) {
interval = Math.floor(Math.random() * 24000) + 32000;
}
if (stop_refresh) {
return;
}
refresh_interval_themes = setInterval(refresh_themes, interval);
await updateFunc(await fetch('themes.json', {headers: {'Accept': 'application/json'}}), cb);
},
update: updateFunc,
};
}

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Challenge Forensic</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<meta name="author" content="EPITA Laboratoire SRS">
<meta name="robots" content="all">
<link href="../../static/main.css" rel="stylesheet">
<link href="../../theme/styles.css" rel="stylesheet">
<style>
.niceborder {
border-bottom-color: #ee5f5b !important;
}
</style>
</head>
<body class="theme-body">
<div class="theme-navbar niceborder">
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/fic.png" alt="Forum International de la Cybersécurité">
</div>
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/epita.png" alt="Épita">
</div>
</div>
<div class="container dex-container" style="margin-top:20px;">
<div class="jumbotron theme-panel niceborder">
<h1>Page introuvable <small>Erreur 404</small></h1>
<hr>
<p class="lead">
La page &agrave; laquelle vous tentez d'acc&eacute;der n'existe pas ou l'adresse que vous avez tap&eacute;e est incorrecte.
</p>
<p>
Si le problème persiste, <a href="mailto:root@srs.epita.fr">contactez un administrateur</a>.
</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1 @@
{"errmsg": "La page à laquelle vous tentez d'accéder n'existe pas ou l'adresse que vous avez tapée est incorrecte."}

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Challenge Forensic</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<meta name="author" content="EPITA Laboratoire SRS">
<meta name="robots" content="all">
<link href="../../static/main.css" rel="stylesheet">
<link href="../../theme/styles.css" rel="stylesheet">
<style>
.niceborder {
border-bottom-color: #ee5f5b !important;
}
</style>
</head>
<body class="theme-body">
<div class="theme-navbar niceborder">
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/fic.png" alt="Forum International de la Cybersécurité">
</div>
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/epita.png" alt="Épita">
</div>
</div>
<div class="container dex-container" style="margin-top:20px;">
<div class="jumbotron theme-panel niceborder">
<h1>Requête trop grosse <small>Erreur 413</small></h1>
<hr>
<p class="lead">
La quantité de données que vous souhaitez envoyer au serveur est trop importante pour qu'il accepte de la traiter.
</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1 @@
{"errmsg": "La quantité de données que vous souhaitez envoyer au serveur est trop importante pour qu'il accepte de la traiter."}

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Challenge Forensic</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<meta name="author" content="EPITA Laboratoire SRS">
<meta name="robots" content="all">
<link href="../../static/main.css" rel="stylesheet">
<link href="../../theme/styles.css" rel="stylesheet">
<style>
.niceborder {
border-bottom-color: #ee5f5b !important;
}
</style>
</head>
<body class="theme-body">
<div class="theme-navbar niceborder">
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/fic.png" alt="Forum International de la Cybersécurité">
</div>
<div class="theme-navbar__logo-wrap">
<img class="theme-navbar__logo" src="/img/epita.png" alt="Épita">
</div>
</div>
<div class="container dex-container" style="margin-top:20px;">
<div class="jumbotron theme-panel niceborder">
<h1>Erreur interne <small>Erreur 500</small></h1>
<hr>
<p class="lead">
Notre serveur est actuellement dans l'incapacit&eacute; de r&eacute;pondre &agrave; votre requ&ecirc;te.<br>Veuillez recommencer dans quelques instants.
</p>
<p>
Si le problème persiste, <a href="mailto:root@srs.epita.fr">contactez un administrateur</a>.
</p>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1 @@
{"errmsg": "Notre serveur est actuellement dans l'incapacité de répondre à votre requête. \nVeuillez recommencer dans quelques instants."}