🚧 Maybe fix for overlapping text in old-school mode
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user