🚧 Maybe fix for overlapping text in old-school mode

This commit is contained in:
Mawoka
2024-04-07 21:22:52 +02:00
parent 5f3f248139
commit 33d0ac31be
4 changed files with 130 additions and 84 deletions
+2 -2
View File
@@ -210,7 +210,7 @@ SPDX-License-Identifier: MPL-2.0
d="m4.5 15.75 7.5-7.5 7.5 7.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
/>
</svg>
</div>
<div
@@ -240,7 +240,7 @@ SPDX-License-Identifier: MPL-2.0
d="m19.5 8.25-7.5 7.5-7.5-7.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
/>
</svg>
</div>
</div>
+27 -25
View File
@@ -234,33 +234,35 @@ SPDX-License-Identifier: MPL-2.0
</div>
{/await}
{:else if question.type === QuizQuestionType.TEXT}
<span
class="fixed top-0 bg-red-500 h-8 transition-all"
style="width: {(100 / parseInt(question.time)) * parseInt(timer_res)}vw"
/>
<div class="flex justify-center mt-10">
<p class="text-black dark:text-white">Enter your answer</p>
</div>
<div class="flex justify-center m-2">
<input
type="text"
bind:value={text_input}
disabled={selected_answer}
class="bg-gray-50 focus:ring text-gray-900 rounded-lg focus:ring-blue-500 block w-full p-2 dark:bg-gray-700 dark:text-white dark:focus:ring-blue-500 outline-none transition text-center disabled:opacity-50 disabled:cursor-not-allowed"
<div>
<span
class="fixed top-0 bg-red-500 h-8 transition-all"
style="width: {(100 / parseInt(question.time)) * parseInt(timer_res)}vw"
/>
</div>
<div class="flex justify-center mt-2">
<div class="w-1/3">
<BrownButton
type="button"
<div class="flex justify-center mt-10">
<p class="text-black dark:text-white">Enter your answer</p>
</div>
<div class="flex justify-center m-2">
<input
type="text"
bind:value={text_input}
disabled={selected_answer}
on:click={() => {
selectAnswer(text_input);
}}
>
{$t('words.submit')}
</BrownButton>
class="bg-gray-50 focus:ring text-gray-900 rounded-lg focus:ring-blue-500 block w-full p-2 dark:bg-gray-700 dark:text-white dark:focus:ring-blue-500 outline-none transition text-center disabled:opacity-50 disabled:cursor-not-allowed"
/>
</div>
<div class="flex justify-center mt-2">
<div class="w-1/3">
<BrownButton
type="button"
disabled={selected_answer}
on:click={() => {
selectAnswer(text_input);
}}
>
{$t('words.submit')}
</BrownButton>
</div>
</div>
</div>
{:else if question.type === QuizQuestionType.RANGE}
@@ -36,7 +36,7 @@ SPDX-License-Identifier: MPL-2.0
if (!browser || user_data?.require_password === undefined) {
return;
}
const pw = require_password()
const pw = require_password();
const res = await fetch('/api/v1/users/2fa/require_password', {
method: 'POST',
headers: {
@@ -48,13 +48,20 @@ SPDX-License-Identifier: MPL-2.0
};
const require_password = (): string => {
return prompt("Please enter your password to continue")
}
return prompt('Please enter your password to continue');
};
const add_security_key = async () => {
const pw = require_password()
const res1 = await fetch('/api/v1/users/webauthn/add_key_init', {method: "POST", body: JSON.stringify({password: pw}), headers: { 'Content-Type': 'application/json' }});
if (res1.status === 401) {alert("Password probably wrong"); return}
const pw = require_password();
const res1 = await fetch('/api/v1/users/webauthn/add_key_init', {
method: 'POST',
body: JSON.stringify({ password: pw }),
headers: { 'Content-Type': 'application/json' }
});
if (res1.status === 401) {
alert('Password probably wrong');
return;
}
if (!res1.ok) {
throw Error('Response not ok');
}
@@ -81,34 +88,62 @@ SPDX-License-Identifier: MPL-2.0
};
const remove_security_key = async (key_id: number) => {
const pw = require_password()
const res = await fetch(`/api/v1/users/webauthn/key/${key_id}`, { method: 'DELETE', body: JSON.stringify({password: pw}), headers: { 'Content-Type': 'application/json' } });
if (res.status === 401) {alert("Password probably wrong"); return}
const pw = require_password();
const res = await fetch(`/api/v1/users/webauthn/key/${key_id}`, {
method: 'DELETE',
body: JSON.stringify({ password: pw }),
headers: { 'Content-Type': 'application/json' }
});
if (res.status === 401) {
alert('Password probably wrong');
return;
}
data = get_data();
};
const disable_totp = async () => {
const pw = require_password()
const res = await fetch(`/api/v1/users/2fa/totp`, { method: 'DELETE', body: JSON.stringify({password: pw}), headers: { 'Content-Type': 'application/json' } });
if (res.status === 401) {alert("Password probably wrong"); return}
const pw = require_password();
const res = await fetch(`/api/v1/users/2fa/totp`, {
method: 'DELETE',
body: JSON.stringify({ password: pw }),
headers: { 'Content-Type': 'application/json' }
});
if (res.status === 401) {
alert('Password probably wrong');
return;
}
data = get_data();
};
const enable_totp = async () => {
const pw = require_password()
const res = await fetch('/api/v1/users/2fa/totp', { method: 'POST', body: JSON.stringify({password: pw}), headers: { 'Content-Type': 'application/json' } });
if (res.status === 401) {alert("Password probably wrong"); return}
const pw = require_password();
const res = await fetch('/api/v1/users/2fa/totp', {
method: 'POST',
body: JSON.stringify({ password: pw }),
headers: { 'Content-Type': 'application/json' }
});
if (res.status === 401) {
alert('Password probably wrong');
return;
}
data = get_data();
totp_data = await res.json();
};
const get_backup_code = async () => {
const pw = require_password()
const pw = require_password();
if (!confirm('If you continue, your old backup-code will be removed.')) {
return;
}
const res = await fetch('/api/v1/users/2fa/backup_code', { method: 'POST', body: JSON.stringify({password: pw}), headers: { 'Content-Type': 'application/json' } });
if (res.status === 401) {alert("Password probably wrong"); return}
const res = await fetch('/api/v1/users/2fa/backup_code', {
method: 'POST',
body: JSON.stringify({ password: pw }),
headers: { 'Content-Type': 'application/json' }
});
if (res.status === 401) {
alert('Password probably wrong');
return;
}
backup_code = (await res.json()).code;
};
</script>
@@ -130,7 +165,12 @@ SPDX-License-Identifier: MPL-2.0
</div>
<div class="h-full w-full">
<h2 class="text-center text-2xl">{$t('security_settings.activate_2fa')}</h2>
<div class="flex h-full w-full justify-center flex-col" class:pointer-events-none={!totp_activated} class:grayscale={!totp_activated} class:opacity-50={!totp_activated}>
<div
class="flex h-full w-full justify-center flex-col"
class:pointer-events-none={!totp_activated}
class:grayscale={!totp_activated}
class:opacity-50={!totp_activated}
>
<div class="m-auto">
{#if user_data.require_password}
<div class="flex items-center space-x-2">
+42 -38
View File
@@ -3,7 +3,7 @@ SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
SPDX-License-Identifier: MPL-2.0
-->
<script lang='ts'>
<script lang="ts">
import { onMount } from 'svelte';
import '$lib/hljs.css';
@@ -16,12 +16,12 @@ SPDX-License-Identifier: MPL-2.0
<svelte:head>
<title>ClassQuiz/docs - Self-Host</title>
<meta
name='description'
content='How to self-host ClassQuiz, the open-source quiz-application'
name="description"
content="How to self-host ClassQuiz, the open-source quiz-application"
/>
</svelte:head>
<article
class='prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-slate px-4 dark:prose-invert'
class="prose prose-sm sm:prose lg:prose-lg xl:prose-xl mx-auto mt-10 prose-slate px-4 dark:prose-invert"
>
<h1>Self-Hosting</h1>
<p>Since ClassQuiz is open-source, it can also be self-hosted.</p>
@@ -36,40 +36,42 @@ SPDX-License-Identifier: MPL-2.0
<h3>Software</h3>
<ul>
<li>
<a href='https://docker.com' target='_blank'>Docker</a> (<a
href='https://docs.docker.com/compose/install/linux/'
target='_blank'>Compose</a
>)
<a href="https://docker.com" target="_blank">Docker</a> (<a
href="https://docs.docker.com/compose/install/linux/"
target="_blank">Compose</a
>)
</li>
<li><a href='https://git-scm.com/' target='_blank'>Git</a></li>
<li><a href="https://git-scm.com/" target="_blank">Git</a></li>
<li>
A <a href='https://redis.com' target='_blank'>Redis</a>-Server
A <a href="https://redis.com" target="_blank">Redis</a>-Server
</li>
</ul>
<h3>3rd-Parties</h3>
<h4>Optional</h4>
<ul>
<li><a href='https://sentry.io'>Sentry (Error-Logging)</a></li>
<li><a href="https://sentry.io">Sentry (Error-Logging)</a></li>
<li>
<a href='https://console.cloud.google.com/apis/dashboard'
>Google-Credentials (Sign-In)</a
<a href="https://console.cloud.google.com/apis/dashboard"
>Google-Credentials (Sign-In)</a
>
</li>
<li><a href='https://github.com/settings/developers'>GitHub-Credentials (Sign-In)</a></li>
<li><a href='https://hcaptcha.com'>hCaptcha</a> <b>OR</b> <a href='https://www.google.com/recaptcha/about/'>ReCaptcha</a></li>
<li><a href="https://github.com/settings/developers">GitHub-Credentials (Sign-In)</a></li>
<li>
<a href="https://hcaptcha.com">hCaptcha</a> <b>OR</b>
<a href="https://www.google.com/recaptcha/about/">ReCaptcha</a>
</li>
</ul>
<h2>Installation</h2>
<p>At first, clone the repo:</p>
<pre><code class='language-bash'
>git clone https://github.com/mawoka-myblock/classquiz && cd ClassQuiz</code
></pre>
<pre><code class="language-bash"
>git clone https://github.com/mawoka-myblock/classquiz && cd ClassQuiz</code
></pre>
<p>
Now, you'll configure your frontend. You'll have to change the following in <code
>frontend/Dockerfile</code
>:
>frontend/Dockerfile</code
>:
</p>
<ul>
<li><code>VITE_MAPBOX_ACCESS_TOKEN</code>: A Mapbox-token which is optional.</li>
@@ -93,7 +95,7 @@ SPDX-License-Identifier: MPL-2.0
<h3>Storage Provider</h3>
<p>
You'll have to set up a storage provider for some pictures (these getting imported from
Kahoot!). For now, you can use <a href='https://min.io/' target='_blank'>Minio (S3)</a> or
Kahoot!). For now, you can use <a href="https://min.io/" target="_blank">Minio (S3)</a> or
the local filesystem. Please not that I'd recommend Minio for larger instances, since it can
be scaled and the media doesn't have to streamed through the (comparatively) slow ClassQuiz
server. Now, that you've decided on a storage backend, you can set the
@@ -124,9 +126,9 @@ SPDX-License-Identifier: MPL-2.0
</p>
<h4>Google</h4>
<p>
First, go to <a href='https://console.cloud.google.com/apis/dashboard'
>console.cloud.google.com/apis/dashboard</a
> and create a new project and select it. Then, go to the "OAuth consent screen" and set it up.
First, go to <a href="https://console.cloud.google.com/apis/dashboard"
>console.cloud.google.com/apis/dashboard</a
> and create a new project and select it. Then, go to the "OAuth consent screen" and set it up.
Next, go to the "Credentials"-tab and click on "Create Credentials" and create a new "OAuth Client
ID". This ID should be from the application-type "Web application". Afterwards, add a new "Authorised
JavaScript origin", which is just the base-domain (with https) of your ClassQuiz-installation.
@@ -137,9 +139,9 @@ SPDX-License-Identifier: MPL-2.0
<h4>GitHub</h4>
<p>
First, go to <a href='https://github.com/settings/developers'
>github.com/settings/developers</a
> and create a "new OAuth App". The "Authorization callback URL" has the following schema:
First, go to <a href="https://github.com/settings/developers"
>github.com/settings/developers</a
> and create a "new OAuth App". The "Authorization callback URL" has the following schema:
</p>
<pre><code>https://[BASE_URL]/api/v1/users/oauth/github/auth</code></pre>
<p>
@@ -147,21 +149,23 @@ SPDX-License-Identifier: MPL-2.0
later, together with your client-id.
</p>
<h4>Custom OpenID auth</h4>
<p>A login using a custom OpenID provider is also possible. For that, adjust the settings in the docker-compose.yml
and configure the following scopes: "openid email profile". The follwoing redirect-url should be used:</p>
<p>
A login using a custom OpenID provider is also possible. For that, adjust the settings in
the docker-compose.yml and configure the following scopes: "openid email profile". The
follwoing redirect-url should be used:
</p>
<pre><code>https://[BASE_URL]/api/v1/users/oauth/custom/auth</code></pre>
You'll also need to tell the frontend by editing the <code>frontend/Dockerfile</code>. Add the following line at the
top, where all the other ENV's are as well:
You'll also need to tell the frontend by editing the<code>frontend/Dockerfile</code>. Add the
following line at the top, where all the other ENV's are as well:
<pre><code>ENV VITE_CUSTOM_OAUTH_NAME=[SOME_DISPLAY_NAME_FOR_THE_PROVIDER]</code></pre>
<h3>Docker-Compose File</h3>
Please go through the <code>docker-compose.yml</code> thoroughly and fill out all the details, which should be
self-explanatory.
Please go through the<code>docker-compose.yml</code> thoroughly and fill out all the details,
which should be self-explanatory.
<p>Run the following command to generate and set the secret up automatically</p>
<pre><code class='language-bash'
>sed -i "s/TOP_SECRET/$(openssl rand -hex 32)/g" docker-compose.yml</code
></pre>
<pre><code class="language-bash"
>sed -i "s/TOP_SECRET/$(openssl rand -hex 32)/g" docker-compose.yml</code
></pre>
<p>Now build and deploy:</p>
<pre><code>docker compose build && docker compose up -d</code></pre>
<p><b>ClassQuiz needs HTTPS/SSL to work properly!</b></p>