Add client ALTCHA proof-of-work and Vite env flags for uploads.

Expose enabled/max-number settings and wire altcha-lib for per-upload challenges.
This commit is contained in:
2026-10-23 18:34:09 +02:00
parent 805665745b
commit fe643d9831
6 changed files with 152 additions and 1 deletions
+6
View File
@@ -10,10 +10,16 @@ const env = (key: string): string => {
const envInt = (key: string): number => parseInt(env(key), 10);
const isEnabled = (key: string): boolean => env(key) === 'true';
export const config = {
baseUrl: env('VITE_API_BASE_URL'),
clearnetDomain: env('VITE_CLEARNET_DOMAIN'),
onionHostname: env('VITE_ONION_HOSTNAME'),
altcha: {
enabled: isEnabled('VITE_ALTCHA_ENABLED'),
maxNumber: envInt('VITE_ALTCHA_MAX_NUMBER')
},
validation: {
upload: {
maxQueueSize: envInt('VITE_UPLOAD_MAX_QUEUE_SIZE'),
+70
View File
@@ -0,0 +1,70 @@
import { solveChallengeWorkers } from 'altcha-lib/v1';
import type { Challenge, Payload } from 'altcha-lib/v1/types';
import { defineStore } from 'pinia';
import { config } from '@/config';
import { api } from '@/plugins/axios';
import { bytesToBase64 } from '@/utils/encoding/bytesToBase64';
import AltchaSolveWorker from 'altcha-lib/v1/worker?worker';
export const useAltchaStore = defineStore('altcha', () => {
const runProofOfWork = async (): Promise<string | undefined> => {
if (!config.altcha.enabled) {
return undefined;
}
const challenge = await fetchChallenge();
return encodeSolvedChallenge(challenge);
};
const fetchChallenge = async (): Promise<Challenge> => {
const { data } = await api.get<Challenge>('/altcha/challenge');
return data;
};
const encodeSolvedChallenge = async ({
algorithm,
challenge: challengeValue,
salt,
signature,
maxnumber
}: Challenge): Promise<string> => {
const { maxNumber: fallbackMaxNumber } = config.altcha;
const maxNumber = maxnumber ?? fallbackMaxNumber;
const cuncurrency = Math.min(8, Math.max(1, navigator.hardwareConcurrency ?? 4));
const solution = await solveChallengeWorkers(
() => new AltchaSolveWorker(),
cuncurrency,
challengeValue,
salt,
algorithm,
maxNumber
);
if (!solution) {
throw new Error('ALTCHA proof of work could not be solved');
}
const payload: Payload = {
algorithm,
challenge: challengeValue,
number: solution.number,
salt,
signature
};
const json = JSON.stringify(payload);
const textEncoder = new TextEncoder();
const bytes = textEncoder.encode(json);
return bytesToBase64(bytes);
};
return {
runProofOfWork
};
});
+2
View File
@@ -4,6 +4,8 @@ interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string;
readonly VITE_CLEARNET_DOMAIN: string;
readonly VITE_ONION_HOSTNAME: string;
readonly VITE_ALTCHA_ENABLED: string;
readonly VITE_ALTCHA_MAX_NUMBER: string;
readonly VITE_UPLOAD_MAX_QUEUE_SIZE: string;
readonly VITE_VALIDATION_UPLOAD_MAX_VIEWS_MAX: string;
readonly VITE_VALIDATION_UPLOAD_MAX_IMAGE_BYTES: string;