license: relicense to AGPL-3.0-only with commercial dual-license
- LICENSE -> verbatim GNU AGPL-3.0; add LICENSE-COMMERCIAL.md + SECURITY.md - CLA -> exclusive-license grant (contributors keep copyright); add README anti-rugpull covenant + relicense record - NOTICE / README / CONTRIBUTING / CLAUDE.md / package.json x5 updated; contact routed through GitHub (no email placeholders) - AGPL section 13 source offer: operator-configurable BACKSPACE_SOURCE_URL + build-injected commit; sourceCodeUrl+commit on /api/instance/info; SourceCodeLink on login/register/settings/desktop; docs + .env.example updated
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useAuthStore } from '../../stores/authStore';
|
||||
import { RateLimitError } from '../../api/client';
|
||||
import { api, RateLimitError } from '../../api/client';
|
||||
import type { InstanceInfoResponse } from '@backspace/shared';
|
||||
import { SourceCodeLink } from '../ui/SourceCodeLink';
|
||||
|
||||
export function LoginPage() {
|
||||
const [username, setUsername] = useState('');
|
||||
@@ -14,6 +16,17 @@ export function LoginPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const redirect = searchParams.get('redirect');
|
||||
|
||||
// AGPL § 13: anonymous users must be able to reach the source of the running
|
||||
// version. Fetched from the unauthenticated public info endpoint.
|
||||
const [instanceInfo, setInstanceInfo] = useState<InstanceInfoResponse | null>(null);
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
api.instance.info()
|
||||
.then((info) => { if (!cancelled) setInstanceInfo(info); })
|
||||
.catch(() => { /* Non-critical — link is simply omitted if unreachable. */ });
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (retryAfter <= 0) return;
|
||||
const timer = setInterval(() => {
|
||||
@@ -129,6 +142,12 @@ export function LoginPage() {
|
||||
</Link>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
{instanceInfo && (
|
||||
<div className="mt-6 pt-4 border-t border-white/[0.04] flex justify-center">
|
||||
<SourceCodeLink sourceCodeUrl={instanceInfo.sourceCodeUrl} version={instanceInfo.version} commit={instanceInfo.commit} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { AvatarColor, CheckInviteResponse, InstanceInfoResponse } from '@ba
|
||||
import { api, RateLimitError } from '../../api/client';
|
||||
import { useTransferStore } from '../../stores/transferStore';
|
||||
import { waitForTransferAttachment } from '../../utils/waitForTransfer';
|
||||
import { SourceCodeLink } from '../ui/SourceCodeLink';
|
||||
|
||||
// Single-source regex for extracting a bare invite token from a pasted full URL.
|
||||
// Token format: 22 chars base64url ([A-Za-z0-9_-]).
|
||||
@@ -727,6 +728,13 @@ export function RegisterPage() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* AGPL § 13: source offer for anonymous visitors, shown on both steps. */}
|
||||
{instanceInfo && (
|
||||
<div className="mt-6 pt-4 border-t border-white/[0.04] flex justify-center">
|
||||
<SourceCodeLink sourceCodeUrl={instanceInfo.sourceCodeUrl} version={instanceInfo.version} commit={instanceInfo.commit} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user