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:
Generated
+70
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@uswriting/exiftool": "1.0.9",
|
||||
"altcha-lib": "^2.7.0",
|
||||
"axios": "^1.20.0",
|
||||
"dayjs": "^1.11.23",
|
||||
"hash-wasm": "^4.12.0",
|
||||
@@ -2071,6 +2072,75 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/altcha-lib": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/altcha-lib/-/altcha-lib-2.7.0.tgz",
|
||||
"integrity": "sha512-gnysjfIpeT6k3RLaWRd7xVvfs8U0a5DHIS95Xgwn8znKq5uIj6c/oTH8DtcdyJS/ZV3I5kYpyurvmJFb0eFWvA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"altcha-lib": "bin/cli.mjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fastify/cookie": "^11.0.0",
|
||||
"@fastify/cors": "^11.0.0",
|
||||
"@fastify/formbody": "^8.0.0 || ^9.0.0",
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
||||
"@nestjs/platform-express": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
||||
"@nestjs/platform-fastify": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
||||
"@sveltejs/kit": "^2.0.0 || ^3.0.0",
|
||||
"cookie-parser": "^1.0.0",
|
||||
"cors": "^2.0.0",
|
||||
"express": "^4.0.0 || ^5.0.0",
|
||||
"fastify": "^5.0.0",
|
||||
"h3": "^2.0.0",
|
||||
"hono": "^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@fastify/cookie": {
|
||||
"optional": true
|
||||
},
|
||||
"@fastify/cors": {
|
||||
"optional": true
|
||||
},
|
||||
"@fastify/formbody": {
|
||||
"optional": true
|
||||
},
|
||||
"@nestjs/common": {
|
||||
"optional": true
|
||||
},
|
||||
"@nestjs/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@nestjs/platform-express": {
|
||||
"optional": true
|
||||
},
|
||||
"@nestjs/platform-fastify": {
|
||||
"optional": true
|
||||
},
|
||||
"@sveltejs/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"cookie-parser": {
|
||||
"optional": true
|
||||
},
|
||||
"cors": {
|
||||
"optional": true
|
||||
},
|
||||
"express": {
|
||||
"optional": true
|
||||
},
|
||||
"fastify": {
|
||||
"optional": true
|
||||
},
|
||||
"h3": {
|
||||
"optional": true
|
||||
},
|
||||
"hono": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@uswriting/exiftool": "1.0.9",
|
||||
"altcha-lib": "^2.7.0",
|
||||
"axios": "^1.20.0",
|
||||
"dayjs": "^1.11.23",
|
||||
"hash-wasm": "^4.12.0",
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
Vendored
+2
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user