The app links to its source, from an admin setting (#6228)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Android / Core and FFI clippy and tests (push) Skipped
Android / Kotlin + Rust (APK) (push) Skipped
Android / Build the server image (push) Skipped
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 9s
CI & Build / Python tests (push) Successful in 14s
CI & Build / integration (push) Successful in 1m45s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 2m17s
CI & Build / Build & push image (push) Successful in 42s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m25s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m10s
Desktop (Tauri) / Update manifest (push) Successful in 3s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Android / Core and FFI clippy and tests (push) Skipped
Android / Kotlin + Rust (APK) (push) Skipped
Android / Build the server image (push) Skipped
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 9s
CI & Build / Python tests (push) Successful in 14s
CI & Build / integration (push) Successful in 1m45s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 2m17s
CI & Build / Build & push image (push) Successful in 42s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m25s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m10s
Desktop (Tauri) / Update manifest (push) Successful in 3s
#6228 "The app offers its source: a Source link from an admin setting (AGPL §13)". The AGPL requires a modified copy run for others over a network to offer them its source; Forge's #2282 is the shape followed. - Settings → General gains "Source code" (`source_url`), defaulting to the public repo so a stock install makes the offer. Empty hides it. - It is an http(s)-only URL setting, refused otherwise on save, and `safe_link` refuses anything else again when it is published, because the web renders it as an href. - /api/config carries it; the web shows "Source" beside the build number at the foot of the sidebar. The offline desktop's config has no such field, so the link stays hidden there. - ci.yml's client-bake comments no longer call the forge private; the repo has been public since #2091. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -471,13 +471,20 @@ async function signOut() {
|
||||
</nav>
|
||||
|
||||
<!-- The build, at the foot of the rail (`mt-auto`). `select-all` because it
|
||||
gets copied into bug reports. -->
|
||||
<p
|
||||
class="mt-auto select-all px-3 pt-6 text-[11px] text-neutral-400 dark:text-neutral-500"
|
||||
:title="buildLabel"
|
||||
>
|
||||
{{ buildVersion }}
|
||||
</p>
|
||||
gets copied into bug reports. Beside it, the AGPL source offer, when the
|
||||
server names a source address. -->
|
||||
<div class="mt-auto flex items-baseline gap-2 px-3 pt-6 text-[11px] text-neutral-400 dark:text-neutral-500">
|
||||
<p class="select-all" :title="buildLabel">{{ buildVersion }}</p>
|
||||
<a
|
||||
v-if="config.sourceUrl"
|
||||
:href="config.sourceUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="hover:text-neutral-600 hover:underline dark:hover:text-neutral-300"
|
||||
title="Source code, under the GNU AGPL v3"
|
||||
>Source</a
|
||||
>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- tabindex="-1" so the skip link moves focus here, not just the viewport. -->
|
||||
|
||||
@@ -46,6 +46,9 @@ export interface PublicConfig {
|
||||
// The shortest password an account may have (the server's MIN_PASSWORD_LEN).
|
||||
// Absent on an older server and on the offline desktop, which has no accounts.
|
||||
min_password_length?: number;
|
||||
// Where this server's source code is, for the AGPL source offer; "" hides the link.
|
||||
// Absent on an older server and on the offline desktop.
|
||||
source_url?: string;
|
||||
// Every client this server holds, keyed by platform id. Absent on a server that
|
||||
// holds none, and absent on the desktop's own offline config — the Tauri build
|
||||
// answers `config_get` locally and has no clients to hand out.
|
||||
@@ -68,6 +71,7 @@ export const useConfigStore = defineStore("config", () => {
|
||||
const passwordResetByEmail = ref(false);
|
||||
// The server's own number; 8 only until /api/config answers, or if it never does.
|
||||
const minPasswordLength = ref(8);
|
||||
const sourceUrl = ref("");
|
||||
const loaded = ref(false);
|
||||
|
||||
// Why the server would refuse this password, said before asking it; null when it
|
||||
@@ -90,6 +94,7 @@ export const useConfigStore = defineStore("config", () => {
|
||||
clients.value = cfg.clients ?? {};
|
||||
passwordResetByEmail.value = cfg.password_reset_by_email ?? false;
|
||||
minPasswordLength.value = cfg.min_password_length ?? 8;
|
||||
sourceUrl.value = cfg.source_url ?? "";
|
||||
} catch {
|
||||
// Keep defaults if the config endpoint is unreachable.
|
||||
} finally {
|
||||
@@ -111,6 +116,7 @@ export const useConfigStore = defineStore("config", () => {
|
||||
clients,
|
||||
passwordResetByEmail,
|
||||
minPasswordLength,
|
||||
sourceUrl,
|
||||
passwordRefusal,
|
||||
loaded,
|
||||
load,
|
||||
|
||||
Reference in New Issue
Block a user