docs: bridges obfs4 Tor et dépannage signet; add netcat for diagnostics; clarify CONFIGURATION/TESTING; update CHANGELOG

This commit is contained in:
Debian 2025-08-26 06:47:55 +00:00
parent a34b4fbc6d
commit dcc032df48
16268 changed files with 2762658 additions and 800 deletions

102
.cursorignore Normal file
View File

@ -0,0 +1,102 @@
# Fichiers et répertoires à ignorer pour lindexation Cursor
# Dépendances frontend / JS
node_modules/
**/node_modules/**
# Artefacts de build et caches courants
dist/
build/
out/
.next/
coverage/
.cache/
.turbo/
.parcel-cache/
.eslintcache
# Logs et temporaires
*.log
*.tmp
*.swp
*.swo
npm-debug.log*
yarn-debug.log*
pnpm-debug.log*
# Environnements et secrets (ne pas indexer)
.env
.env.*
*.pem
*.key
*.crt
# Artefacts tests
tests/logs/
tests/reports/
tests/**/tmp/
# Répertoires développeur
.nvm/
.venv/
.idea/
.vscode/
# Système
.DS_Store
Thumbs.db
# Rust (si présent localement)
target/
# Docker (artefacts locaux éventuels)
docker-compose.override.yml
docker-compose.*.yml.local
*.docker.tar
docker-image-*.tar
docker-save-*.tar
.docker/
.docker-compose/
docker-data/
docker-volumes/
docker-logs/
# ihm_client spécifiques
ihm_client/.vercel/
ihm_client/.pnpm-store/
ihm_client/.yarn/**
# Archives et sauvegardes (ne pas indexer)
*.zip
*.tar
*.tar.gz
*.tgz
*.gz
*.bz2
*.xz
*.zst
*.7z
*.rar
*.iso
*.bak
*.backup
*.old
*.orig
# Répertoire darchives du projet
archive/
# Dumps SQL et exports de bases de données
*.sql
*.sql.gz
*.dump
*.dmp
*.sqlite
*.sqlite3
*.db
*.db3
*.bak.sql
sql_dumps/
db_dumps/
database_dumps/
backups/sql/

View File

@ -8,21 +8,43 @@ et ce projet adhère au [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Unreleased]
### Added
- Script damorçage `scripts/bootstrap.sh` pour installer git, Docker, Docker Compose, Node.js/npm (nvm) et ajouter lutilisateur au groupe docker
- Test de prérequis `tests/external/test_tooling_versions.sh` pour vérifier les versions des outils
- Infrastructure de tests complète avec organisation par catégorie
- Scripts d'exécution automatisés pour les tests
- Documentation technique complète (Architecture, API)
- Guide de contribution et code de conduite
- Scripts de maintenance et nettoyage automatique
- Reverse proxy central `reverse_proxy` (Nginx) servant `ihm_client` et proxifiant `sdk_relay` (HTTPS/WSS)
- Script `scripts/generate_certs.sh` pour certificats auto-signés sécurisés (droits durcis)
- Script `scripts/build_ui_local.sh` pour construire lUI localement (gestion des permissions `dist/`)
- Script `scripts/cleanup_legacy.sh` pour archiver les fichiers devenus obsolètes
- Fichier `proxy/nginx.conf` consolidant la configuration publique (CSP, MIME, WS, CORS)
- Service Tor reconstruit localement (`tor/Dockerfile`) avec support `obfs4proxy` et healthcheck
### Changed
- Mise à jour de `.cursorignore` pour exclure dépendances, artefacts de build, logs et secrets
- Ajout dexclusions `.cursorignore` pour artefacts Docker et dossiers spécifiques `ihm_client`
- Ajout dexclusions `.cursorignore` pour archives et sauvegardes (zip, tar, gz, bz2, xz, zst, 7z, rar, iso, bak)
- Exclusion du répertoire `archive/` dans `.cursorignore`
- Ajout dexclusions `.cursorignore` pour dumps SQL et exports de bases (sql, dump, sqlite, db)
- Réorganisation complète de la structure des tests
- Amélioration de la documentation avec guides détaillés
- Optimisation des scripts de démarrage et redémarrage
- `docker-compose.yml` modernisé (suppression de la clé `version:`) et services internes (ports supprimés)
- Sortie dNginx hors `ihm_client` et centralisation dans `reverse_proxy`
- Documentation mise à jour (Architecture, Configuration, Installation, Usage, Testing)
- `tor/torrc` actualisé (ControlPort local, logs stdout, bridges obfs4 optionnels)
- Activation et test des bridges obfs4 fournis par le Tor Project ; ajout de recommandations de diagnostic (netcat, tests SOCKS, distinction onion public vs pair signet)
### Fixed
- Correction des problèmes de connectivité entre services
- Amélioration de la gestion des erreurs dans les tests
- Correction des configurations Docker
- Résolution des erreurs TypeScript liées à `pkg/sdk_client` en ajoutant `scripts/setup-remote-deps.sh` et mise à jour du build UI
- Correction des erreurs de MIME et de download côté Nginx via `proxy/nginx.conf`
- Correction `EACCES` de build UI via gestion des permissions `ihm_client/dist`
- Diagnostic et correction outillage réseau: installation de `netcat-openbsd` sur lhôte et dans `bitcoin-signet` pour tests SOCKS/TCP
## [1.0.0] - 2024-12-19

View File

@ -50,10 +50,14 @@ Client → sdk_relay → Bitcoin Core
git clone git@git.4nkweb.com:4nk/4NK_node.git
cd 4NK_node
# 2. Démarrer tous les services
# 2. Amorcer lenvironnement (git, Docker, Compose, Node/npm)
./scripts/bootstrap.sh
# Astuce: se déconnecter/reconnecter pour activer le groupe docker
# 3. Démarrer tous les services
./restart_4nk_node.sh
# 3. Vérifier le statut
# 4. Vérifier le statut
docker ps
```

View File

@ -9,7 +9,9 @@ RUN apk update && apk add --no-cache \
build-base \
python3 \
make \
g++
g++ \
curl \
ca-certificates
# Copie des fichiers de dépendances
COPY package*.json ./
@ -20,6 +22,15 @@ RUN npm install
# Copie du code source
COPY . .
# Préparation des dépendances wasm (pkg/sdk_client)
ARG SDK_CLIENT_PKG_URL=""
ARG SDK_CLIENT_PKG_TARBALL=""
ARG SDK_CLIENT_PKG_BASE="https://git.4nkweb.com/4nk/ihm_client/raw/branch/docker-support/pkg"
ENV SDK_CLIENT_PKG_URL=${SDK_CLIENT_PKG_URL}
ENV SDK_CLIENT_PKG_TARBALL=${SDK_CLIENT_PKG_TARBALL}
ENV SDK_CLIENT_PKG_BASE=${SDK_CLIENT_PKG_BASE}
RUN chmod +x ./scripts/setup-remote-deps.sh && npm run build_wasm
# Build de l'application
RUN npm run build
@ -27,7 +38,7 @@ RUN npm run build
FROM nginx:alpine
# Installation de Node.js pour les scripts de démarrage
RUN apk update && apk add --no-cache nodejs npm
RUN apk update && apk add --no-cache nodejs npm wget
# Copie des fichiers buildés
COPY --from=builder /app/dist /usr/share/nginx/html
@ -35,6 +46,7 @@ COPY --from=builder /app/package*.json /app/
# Copie de la configuration nginx optimisée pour 4NK_node
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN sed -i 's|types_hash_max_size .*;|types_hash_max_size 4096;|' /etc/nginx/nginx.conf || true
# Script de démarrage
COPY start.sh /start-4nk-node.sh

View File

@ -80,13 +80,12 @@ echo "🔄 L'interface utilisateur se connectera directement à Blindbit"
echo "⚙️ Génération de la configuration dynamique..."
# Créer un fichier de configuration JavaScript pour l'application
cat > /usr/share/nginx/html/config.js << EOF
# Pour l'accès via reverse proxy public, utiliser des chemins relatifs /ws et /api
cat > /usr/share/nginx/html/config.js << 'EOF'
window.ENV_CONFIG = {
SDK_RELAY_WS_URL: '$SDK_RELAY_WS_URL',
SDK_RELAY_HTTP_URL: '$SDK_RELAY_HTTP_URL',
BITCOIN_RPC_URL: '$BITCOIN_RPC_URL',
BLINDBIT_URL: '$BLINDBIT_URL',
ENVIRONMENT: '4nk-node'
SDK_RELAY_WS_URL: '/ws',
SDK_RELAY_HTTP_URL: '/api',
ENVIRONMENT: '4nk-node'
};
EOF

@ -0,0 +1 @@
Subproject commit 09b45d14888724760cde0ae89cce6c42e4a59b8f

View File

@ -19,6 +19,7 @@ rpcdoccheck=1
# Paramètres ZMQ
zmqpubhashblock=tcp://0.0.0.0:29000
zmqpubrawblock=tcp://0.0.0.0:29000
zmqpubrawtx=tcp://0.0.0.0:29000
[signet]

19
certs/server.crt Normal file
View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIUJboCRW/xrzBSG2jYDSdNfURIzd4wDQYJKoZIhvcNAQEL
BQAwGDEWMBQGA1UEAwwNOTIuMjQzLjI3LjE2MDAeFw0yNTA4MjYwMTQxNTRaFw0y
NjA4MjYwMTQxNTRaMBgxFjAUBgNVBAMMDTkyLjI0My4yNy4xNjAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDje8YeBKYLCYnUSOR3mQIX3j2JNbIlHrz9
XV5egpEmHArbuh1UV4qsYL8vXv8DuWDSIbFLIvY/QekX++Ug1wMVDQsp+LHWbRp2
StsvkLCfyyVU9tfQ90fy4ylILdVVFaptskgKmY5JyVkJJ/s7qFvOeG10r4DfIA5W
LisWZv3WQtS1ShPxLIDn6/2I6sNA5sxmOo+szud9mC7RPC5V2hD1mnZaB5o7Ovvd
ZfDXfk+cE0qO1rly+CDRSw1YNXmpzpeO8/sRDmMvfA51S/U59NK43C5PgtJ4dunm
XWsVrIzt7QjeF+ANldHBGF8jh88gKxRTYJQNYTwIfhL57ipTokfpAgMBAAGjUzBR
MB0GA1UdDgQWBBRM/sctXRsTyTY0JwP58SmsoM8+FjAfBgNVHSMEGDAWgBRM/sct
XRsTyTY0JwP58SmsoM8+FjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQAPAV2VNg+V67gKamXCNkgJCCJlC1PrSEdD3W5/SKMQwtORSrYV7OjOpvBb
msZ3a769PPOcvV72p35fturzWnave/WHC5sM+2WqZmSQCfoV+d8sHJGB84rDnLV2
zl4sKcwSMwUMZhwxPvSYV+d5ZxQ2SfbQ8ZwTiNDLsWGB8K0E1nK/4SbPRIEfAu5U
m29RkByl74T1kgTHinu0F/eNWQJsV5aQIkD2hhTnazmf0rC9MI4dRlGF1Ycup7xT
FdiBDfkjPkt6YlvA8Mk9aEz/Mm2bh7flmEw2DD9xho6WZfI6974HKQwsngJgb4be
JDIzbS+JLEjB7lR1IuXTN83ZUg1l
-----END CERTIFICATE-----

28
certs/server.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDje8YeBKYLCYnU
SOR3mQIX3j2JNbIlHrz9XV5egpEmHArbuh1UV4qsYL8vXv8DuWDSIbFLIvY/QekX
++Ug1wMVDQsp+LHWbRp2StsvkLCfyyVU9tfQ90fy4ylILdVVFaptskgKmY5JyVkJ
J/s7qFvOeG10r4DfIA5WLisWZv3WQtS1ShPxLIDn6/2I6sNA5sxmOo+szud9mC7R
PC5V2hD1mnZaB5o7OvvdZfDXfk+cE0qO1rly+CDRSw1YNXmpzpeO8/sRDmMvfA51
S/U59NK43C5PgtJ4dunmXWsVrIzt7QjeF+ANldHBGF8jh88gKxRTYJQNYTwIfhL5
7ipTokfpAgMBAAECggEALKQ0i92DrDTR/HvVgfbf50kwsEV+UTSIB/yTsV5UF7N3
GgmbCdsoFo3h1u0AhH2Tl4kPHtyckGthz9gZ7ejj7PSjVTg9xgOhJsen3dy6HJGk
INlUHuj4EJ0tk8GS1OCsklxjMNlTh/1gYKCc9+chJRKTAEwUsTS8O4NR51KMy8fw
2yzDQTuIFO3Fk9F5QvHrw1cE8iylxutS1Y9K8JCKP2nsWIcwEpXtiemKFZKODLzj
7HJLSdUgIX4xIlxbBgUZgPpRg3HKI6JAldVKQyXdQfk+8rqDSNu6q3jrL0GkctOM
e6o2l/3yi+MbiZ/uhLQYfEMcpIYiz/cJ7X3ShK0cAQKBgQD9mlpwtc2zuJmVSE/5
c6B8amKq+NcnWU4KtpbNPWGlGirFurT9pic2beHI+o6Ka1ArmdphP5xI9V+YQHyv
Xmdew8mZ11qxodBk0a1KCCY+5TGeZ1EC70MrAZtm3to5PINCco5yLvjJzHt5I8ve
8L+xfGe3ajJ6uYbwkPP0TutsaQKBgQDlojgQWhus1HAdk5F4mbd6nYmAUUlDWFwF
UCscqiBsp4AYt53C8a0J0B1EIEhqVmyFOQ6VOi2Qgbkf7W7+hKXfcBpi3MJApJ0H
K/PcXxavibqB+VLfc+Ip9ZX+PTbu8Zt42HGjK+TwCGfNj48wSXulE7DY0qkqPxBq
hA2oe6KPgQKBgEt3GR6dGx81+Y3wvMuwWrtrNP1Hm068RnrtpqZgc6Qby0qXqrAo
N1b9D8kstin+kRbIa7GwqiMT0WSPHAtbfks229EJwpVFX6wAsR5smmTw8vj+KZ9D
76rmiYXtHucVWMH3MOhNjf8O+FcuDcbDwWdha8OquGbIupzvpYi3y1qxAoGAPTEV
L4ZTiaKyna8NPM35jrscQQ1oMIIDQ4cxddn/+fRItk13xNMSAWNr6ROROIT/NiEW
Ob3fFnr7Ef77bOd8LCZ7YYziVseG8LpNqZPNP8m74ZbG9rSyt+uxpKY7VUEc5P8I
iSrRPwV+Y9C1n3B1em/c2GqKma9keH3oBdWsZAECgYBHvF+l9m3FE8JW0gOvcpDu
6ny1K8FcQhtuUFM+yeNTZCQoBkzw33DSO3GQJvO5qp6SDGAHVLrV7sshgc699gz8
u9T2zUEHMK7b3VV90Cwl/SzDCyrK23TnJJQTf/lzdRwOxgrymxSaLXi9Dla8Z1a4
8eSPkI1JPI4SWVxVvZXkUg==
-----END PRIVATE KEY-----

View File

@ -1,8 +1,7 @@
version: '3.8'
services:
tor:
image: dperson/torproxy:latest
build: ./tor
image: 4nk_node-tor
container_name: tor-proxy
networks:
btcnet:
@ -12,6 +11,14 @@ services:
- "9050:9050" # Port SOCKS
- "9051:9051" # Port de contrôle
restart: unless-stopped
volumes:
- ./tor/torrc:/etc/tor/torrc:ro
command: ["tor", "-f", "/etc/tor/torrc"]
healthcheck:
test: ["CMD-SHELL", "nc -z 127.0.0.1 9050"]
interval: 30s
timeout: 5s
retries: 3
bitcoin:
build: ./bitcoin
@ -21,10 +28,7 @@ services:
volumes:
- bitcoin_data:/home/bitcoin/.bitcoin
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
ports:
- "38333:38333" # signet p2p
- "18443:18443" # signet rpc
- "29000:29000" # zmq
# ports supprimés: service interne uniquement
networks:
btcnet:
aliases:
@ -48,8 +52,7 @@ services:
- blindbit_data:/data
- ./blindbit/blindbit.toml:/data/blindbit.toml
- bitcoin_data:/home/bitcoin/.bitcoin
ports:
- "8000:8000"
# ports supprimés: service interne uniquement
networks:
btcnet:
aliases:
@ -71,9 +74,7 @@ services:
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
- sdk_relay_1_data:/home/bitcoin/.4nk
- ./sdk_relay/.conf.docker.relay1:/home/bitcoin/.conf.docker
ports:
- "8090:8090"
- "8091:8091"
# ports supprimés: service utilisé via reverse proxy interne
networks:
btcnet:
aliases:
@ -102,7 +103,7 @@ services:
curl -s --connect-timeout 5 http://bitcoin:18443 &&
echo 'Bitcoin accessible via curl' &&
echo 'Starting sdk_relay_1:' &&
/usr/local/bin/sdk_relay --config .conf"
/usr/local/bin/sdk_relay serve --config .conf --ws 0.0.0.0:8090 --http 0.0.0.0:8091"
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 30s
@ -125,9 +126,7 @@ services:
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
- sdk_relay_2_data:/home/bitcoin/.4nk
- ./sdk_relay/.conf.docker.relay2:/home/bitcoin/.conf.docker
ports:
- "8092:8090"
- "8093:8091"
# ports supprimés: service utilisé via reverse proxy interne
networks:
btcnet:
aliases:
@ -156,7 +155,7 @@ services:
curl -s --connect-timeout 5 http://bitcoin:18443 &&
echo 'Bitcoin accessible via curl' &&
echo 'Starting sdk_relay_2:' &&
/usr/local/bin/sdk_relay --config .conf"
/usr/local/bin/sdk_relay serve --config .conf --ws 0.0.0.0:8090 --http 0.0.0.0:8091"
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 30s
@ -179,9 +178,7 @@ services:
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
- sdk_relay_3_data:/home/bitcoin/.4nk
- ./sdk_relay/.conf.docker.relay3:/home/bitcoin/.conf.docker
ports:
- "8094:8090"
- "8095:8091"
# ports supprimés: service utilisé via reverse proxy interne
networks:
btcnet:
aliases:
@ -210,7 +207,7 @@ services:
curl -s --connect-timeout 5 http://bitcoin:18443 &&
echo 'Bitcoin accessible via curl' &&
echo 'Starting sdk_relay_3:' &&
/usr/local/bin/sdk_relay --config .conf"
/usr/local/bin/sdk_relay serve --config .conf --ws 0.0.0.0:8090 --http 0.0.0.0:8091"
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 30s
@ -219,32 +216,30 @@ services:
start_period: 60s
ihm_client:
build:
context: ./ihm_client
dockerfile: Dockerfile
image: busybox:latest
container_name: 4nk-ihm-client
ports:
- "8080:80"
environment:
- SDK_RELAY_WS_URL=ws://sdk_relay_1:8090
- SDK_RELAY_HTTP_URL=http://sdk_relay_1:8091
- BITCOIN_RPC_URL=http://bitcoin:18443
- BLINDBIT_URL=http://blindbit:8000
command: sh -c "sleep infinity"
volumes:
- ihm_client_logs:/var/log/nginx
- ./ihm_client/dist:/usr/share/nginx/html:ro
networks:
- btcnet
reverse_proxy:
image: nginx:alpine
container_name: 4nk-reverse-proxy
depends_on:
- sdk_relay_1
- sdk_relay_2
- sdk_relay_3
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--timeout=5", "--spider", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
- ./ihm_client/dist:/usr/share/nginx/html:ro
ports:
- "80:80"
- "443:443"
networks:
btcnet:
aliases:
- reverse_proxy
volumes:
bitcoin_data:
@ -257,8 +252,6 @@ volumes:
name: 4nk_node_sdk_relay_2_data
sdk_relay_3_data:
name: 4nk_node_sdk_relay_3_data
ihm_client_logs:
driver: local
networks:
btcnet:

View File

@ -35,6 +35,18 @@ L'infrastructure 4NK Node est composée de plusieurs services interconnectés qu
└─────────────────┘
```
### Reverse proxy public (Nginx)
Un reverse proxy dédié assure désormais l'exposition publique unique de l'infrastructure :
- Sert les assets statiques de linterface `ihm_client` générés dans `ihm_client/dist`
- Termine TLS en 443 (auto-signé par défaut) et force la redirection HTTP→HTTPS
- Proxifie les routes applicatives :
- `/` → UI statique
- `/api/``sdk_relay_1` (HTTP)
- `/ws/``sdk_relay_1` (WebSocket, upgrade)
- Tous les autres services restent strictement internes au réseau Docker `btcnet`
## Composants Principaux
### 1. Bitcoin Core (Nœud Signet)
@ -230,33 +242,33 @@ pub struct MeshConnection {
### Configuration Docker
Le système supporte la configuration de plusieurs relais via Docker :
Le système supporte la configuration de plusieurs relais via Docker, avec une publication unique via reverse proxy :
```yaml
# Publication unique via reverse proxy
services:
reverse_proxy:
image: nginx:alpine
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
- ./ihm_client/dist:/usr/share/nginx/html:ro
ports:
- "80:80"
- "443:443"
networks:
- btcnet
# Relais internes seulement (pas de ports exposés)
sdk_relay_1:
container_name: sdk_relay_1
ports:
- "8090:8090"
- "8091:8091"
environment:
- ENABLE_SYNC_TEST=1
networks:
- btcnet
sdk_relay_2:
container_name: sdk_relay_2
ports:
- "8092:8090"
- "8093:8091"
environment:
- ENABLE_SYNC_TEST=1
networks:
- btcnet
sdk_relay_3:
container_name: sdk_relay_3
ports:
- "8094:8090"
- "8095:8091"
environment:
- ENABLE_SYNC_TEST=1
networks:
- btcnet
```
### Configuration par Relais
@ -323,7 +335,7 @@ Client ──── WebSocket ──── SDK Relay ──── Bitcoin Core R
### 1. Isolation Réseau
- **Réseau privé :** `btcnet` pour la communication inter-services
- **Ports exposés :** Seulement les ports nécessaires
- **Ports exposés :** Uniquement 80/443 sur `reverse_proxy`; tous les autres services sont internes (aucune publication de ports)
- **Volumes :** Données persistantes isolées
### 2. Authentification

View File

@ -46,11 +46,10 @@ sed -i 's/4nk_default/4nk-network/g' docker-compose.yml
#### Configuration de Pare-feu
```bash
# Autoriser les ports nécessaires
sudo ufw allow 18443/tcp # Bitcoin Core RPC
sudo ufw allow 8090/tcp # sdk_relay WebSocket
sudo ufw allow 8000/tcp # Blindbit API
sudo ufw allow 9050/tcp # Tor SOCKS
# Exposition publique centralisée via reverse proxy uniquement
sudo ufw allow 80/tcp # HTTP (redirection)
sudo ufw allow 443/tcp # HTTPS (UI + API + WS)
sudo ufw enable
sudo ufw enable
# Vérifier les règles
@ -390,51 +389,99 @@ certificate_verification = true
## 🔧 Configuration Tor
### 1. Configuration de Base
### 1. Image et service Tor
Fichier : `tor/torrc`
- Le service Tor est désormais construit localement depuis `tor/Dockerfile` et monté avec `tor/torrc`.
- Le conteneur tourne en utilisateur `debian-tor` et expose uniquement le SOCKS (9050) et, en interne, un ControlPort local (127.0.0.1:9051).
Extrait `docker-compose.yml` (service `tor`):
```yaml
services:
tor:
build: ./tor
image: 4nk_node-tor
container_name: tor-proxy
ports:
- "9050:9050"
- "9051:9051"
volumes:
- ./tor/torrc:/etc/tor/torrc:ro
command: ["tor", "-f", "/etc/tor/torrc"]
healthcheck:
test: ["CMD-SHELL", "nc -z 127.0.0.1 9050"]
interval: 30s
timeout: 5s
retries: 3
networks:
- btcnet
```
### 2. Fichier `tor/torrc`
Configuration de base (extrait):
```ini
# Configuration Tor
SocksPort 9050
ControlPort 9051
SocksPort 0.0.0.0:9050
ControlPort 127.0.0.1:9051
DataDirectory /var/lib/tor
PidFile /var/run/tor/tor.pid
# Logs
Log notice file /var/log/tor/notices.log
Log info file /var/log/tor/info.log
# Sécurité
CookieAuthentication 1
Log notice stdout
ClientUseIPv6 1
SafeLogging 1
ReducedConnectionPadding 1
SocksPolicy accept 0.0.0.0/0
AutomapHostsOnResolve 1
```
### 2. Configuration Avancée
### 3. Bridges obfs4 (optionnels)
#### Performance
Pour contourner des filtrages réseaux, activer les bridges obfs4 dans `tor/torrc`:
```ini
# Optimisation réseau
MaxCircuitDirtiness 600
MaxClientCircuitsPending 32
EnforceDistinctSubnets 1
# Cache
MaxMemInQueues 64 MB
UseBridges 1
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy
Bridge obfs4 81.64.0.218:6697 53E6469DC06BED50543AED0311D66082F4B66676 cert=zOKy+MnZ4wWbKcENcyaElPu62PEaXdE/c802ssuzCIDa2aIC1+J4LyfPhAwSiLaAo/I/bg iat-mode=0
Bridge obfs4 198.98.53.149:443 886CA31F71272FC8B3808C601FA3ABB8A2905DB4 cert=D+zypuFdMpP8riBUbInxIguzqClR0JKkP1DbkKz5es1+OP2Fao8jiXyM+B/+DYA2ZFy6UA iat-mode=0
```
#### Sécurité
Limage Tor installe `obfs4proxy`. Après modification:
```ini
# Authentification
CookieAuthentication 1
ControlPort 9051
# Limites
MaxConnections 1000
MaxConnectionsEntry 100
```bash
sudo docker compose build tor
sudo docker compose up -d tor
```
Vérifications:
```bash
sudo docker compose ps tor
sudo docker logs tor-proxy --tail=40
sudo docker exec tor-proxy nc -z 127.0.0.1 9050 && echo SOCKS:OK
```
#### Notes et recommandations (Tor/bridges)
- Le `ControlPort 127.0.0.1:9051` est utilisé pour le diagnostic interne. Ne pas lexposer publiquement. Une authentification peut être activée si un contrôle à distance est requis.
- Les bridges obfs4 cidessus proviennent de la page de référence du Tor Project. En cas déchec répété (messages « general SOCKS server failure » côté Tor), ajouter 23 bridges supplémentaires depuis la même source et redémarrer le service Tor.
- Les tests de connectivité doivent distinguer:
- Accès SOCKS vers des services onion « publics » (ex. DuckDuckGo) pour valider le proxy Tor.
- Accès SOCKS vers lonion cible du signet pour valider la reachability du pair.
- À la date dédition, le proxy SOCKS fonctionne (onion publics accessibles), mais le pair signet `.onion:38333` est injoignable (host unreachable). La configuration du signet (signetchallenge et addnode) doit rester inchangée, conformément à la contrainte fonctionnelle.
#### Outils de diagnostic réseau
- Installer `netcat-openbsd` sur lhôte et dans les conteneurs pertinents pour faciliter les vérifications:
- Hôte: `sudo apt-get install -y netcat-openbsd`
- Conteneur Bitcoin (root): `apt-get update && apt-get install -y --no-install-recommends netcat-openbsd`
- Exemples de vérifications:
- `nc -vz -w 10 -x 127.0.0.1:9050 -X 5 <onion> 80`
- `nc -vz -w 10 -x 127.0.0.1:9050 -X 5 <onion_signet> 38333`
#### DNS externes (ex. dev.4nkweb.com)
- Si un nom de domaine (ex. `dev.4nkweb.com`) doit pointer vers un service, sassurer quun enregistrement DNS A/AAAA existe. En labsence de résolution, les tests échoueront côté HTTP/HTTPS et TCP.
- En phase de test, une entrée temporaire peut être ajoutée dans `/etc/hosts` si lIP est connue.
## 🔧 Configuration Docker Compose
### 1. Configuration de Base
@ -442,112 +489,34 @@ MaxConnectionsEntry 100
Fichier : `docker-compose.yml`
```yaml
version: '3.8'
# Compose modernisé (sans clé version), publication unique via reverse proxy
services:
tor:
image: dperson/torproxy:latest
container_name: tor-proxy
reverse_proxy:
image: nginx:alpine
depends_on:
- sdk_relay_1
volumes:
- ./proxy/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
- ./ihm_client/dist:/usr/share/nginx/html:ro
ports:
- "80:80"
- "443:443"
networks:
btcnet:
aliases:
- tor
ports:
- "9050:9050"
- "9051:9051"
restart: unless-stopped
- reverse_proxy
# Tous les autres services sont internes (pas de ports exposés)
bitcoin:
build: ./bitcoin
container_name: bitcoin-signet
depends_on:
- tor
volumes:
- bitcoin_data:/home/bitcoin/.bitcoin
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
ports:
- "38333:38333"
- "18443:18443"
- "29000:29000"
networks:
btcnet:
aliases:
- bitcoin
environment:
- TOR_HOST=tor
- TOR_PORT=9050
restart: unless-stopped
healthcheck:
test: ["CMD", "bitcoin-cli", "-conf=/home/bitcoin/.bitcoin/bitcoin.conf", "getblockchaininfo"]
interval: 30s
timeout: 10s
retries: 3
- btcnet
blindbit:
build: ./blindbit
container_name: blindbit-oracle
depends_on:
- bitcoin
volumes:
- blindbit_data:/data
- ./blindbit/blindbit.toml:/data/blindbit.toml
- bitcoin_data:/home/bitcoin/.bitcoin
ports:
- "8000:8000"
networks:
btcnet:
aliases:
- blindbit
restart: unless-stopped
- btcnet
sdk_relay_1:
build:
context: ..
dockerfile: 4NK_node/sdk_relay/Dockerfile
container_name: sdk_relay_1
depends_on:
bitcoin:
condition: service_healthy
blindbit:
condition: service_started
volumes:
- bitcoin_data:/home/bitcoin/.bitcoin
- ./bitcoin/bitcoin.conf:/home/bitcoin/.bitcoin/bitcoin.conf
- sdk_relay_1_data:/home/bitcoin/.4nk
- ./sdk_relay/.conf.docker.relay1:/home/bitcoin/.conf.docker
- ./sdk_relay/external_nodes.conf:/home/bitcoin/.4nk/external_nodes.conf
ports:
- "8090:8090"
- "8091:8091"
networks:
btcnet:
aliases:
- sdk_relay_1
environment:
- RUST_LOG=debug,bitcoincore_rpc=trace
- HOME=/home/bitcoin
- BITCOIN_COOKIE_PATH=/home/bitcoin/.bitcoin/signet/.cookie
- ENABLE_SYNC_TEST=1
restart: on-failure:3
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 30s
timeout: 15s
retries: 3
start_period: 60s
volumes:
bitcoin_data:
name: 4nk_node_bitcoin_data
blindbit_data:
name: 4nk_node_blindbit_data
sdk_relay_1_data:
name: 4nk_node_sdk_relay_1_data
networks:
btcnet:
name: 4nk_node_btcnet
driver: bridge
- btcnet
```
### 2. Configuration Avancée
@ -601,34 +570,12 @@ services:
### 1. Certificat Auto-Signé
```bash
# Générer un certificat auto-signé
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
# Générer et protéger des certificats auto-signés
./scripts/generate_certs.sh
# Configurer nginx comme proxy SSL
cat > nginx.conf << EOF
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate cert.pem;
ssl_certificate_key key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
location / {
proxy_pass http://localhost:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
# Les certificats sont montés par le reverse proxy :
# - certs/server.crt -> /etc/nginx/certs/server.crt
# - certs/server.key -> /etc/nginx/certs/server.key
```
### 2. Certificat Let's Encrypt

View File

@ -21,7 +21,16 @@ Guide complet pour installer et configurer l'infrastructure 4NK Node.
## 🚀 Installation
### 1. Installation de Docker
### 1. Amorçage automatique (recommandé)
Exécuter le script damorçage qui installe git, Docker, Docker Compose, Node.js/npm (via nvm, dernière LTS) et ajoute lutilisateur au groupe docker dès le début.
```bash
./scripts/bootstrap.sh
# Se déconnecter/reconnecter ensuite ou `newgrp docker` pour activer le groupe docker
```
### 2. Installation de Docker (manuel)
#### Ubuntu/Debian
@ -70,7 +79,7 @@ sudo systemctl enable docker
sudo usermod -aG docker $USER
```
### 2. Configuration SSH (Recommandé)
### 3. Configuration SSH (recommandé)
```bash
# Générer une clé SSH
@ -91,7 +100,7 @@ cat ~/.ssh/id_ed25519_4nk.pub
2. Coller la clé publique
3. Cliquer sur "Add key"
### 3. Clonage du Repository
### 4. Clonage du repository
```bash
# Cloner avec SSH (recommandé)
@ -103,7 +112,7 @@ cd 4NK_node
# cd 4NK_node
```
### 4. Vérification de l'Installation
### 5. Vérification de l'installation
```bash
# Vérifier Docker
@ -117,7 +126,7 @@ ssh -T git@git.4nkweb.com
ls -la
```
## 🔧 Configuration Initiale
## 🔧 Configuration initiale
### 1. Configuration des Variables d'Environnement
@ -147,7 +156,23 @@ RELAY_3_PORTS=8094:8090,8095:8091
EOF
```
### 2. Configuration Bitcoin Core
### 2. Préparation de lUI (ihm_client)
```bash
# Construire lUI localement et produire ./ihm_client/dist
chmod +x scripts/build_ui_local.sh
./scripts/build_ui_local.sh
```
### 3. Génération des certificats
```bash
# Générer les certificats auto-signés et appliquer les bons droits
chmod +x scripts/generate_certs.sh
./scripts/generate_certs.sh
```
### 4. Configuration Bitcoin Core
```bash
# Vérifier la configuration Bitcoin
@ -172,7 +197,26 @@ server=1
listen=1
```
### 3. Configuration Blindbit
### 5. Configuration Tor (option bridges)
Si votre réseau nécessite des bridges obfs4, ajoutez-les dans `tor/torrc`:
```ini
UseBridges 1
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy
# Exemple
Bridge obfs4 81.64.0.218:6697 53E6469DC06BED50543AED0311D66082F4B66676 cert=zOKy+MnZ4wWbKcENcyaElPu62PEaXdE/c802ssuzCIDa2aIC1+J4LyfPhAwSiLaAo/I/bg iat-mode=0
Bridge obfs4 198.98.53.149:443 886CA31F71272FC8B3808C601FA3ABB8A2905DB4 cert=D+zypuFdMpP8riBUbInxIguzqClR0JKkP1DbkKz5es1+OP2Fao8jiXyM+B/+DYA2ZFy6UA iat-mode=0
```
Puis reconstruire et (re)démarrer:
```bash
sudo docker compose build tor
sudo docker compose up -d tor
```
### 6. Configuration Blindbit
```bash
# Vérifier la configuration Blindbit
@ -194,7 +238,7 @@ max_parallel_tweak_computations = 4
max_parallel_requests = 4
```
### 4. Configuration des Relais
### 7. Configuration des Relais
```bash
# Vérifier les configurations des relais
@ -227,8 +271,8 @@ relay_id=relay-1 # Changer pour chaque relay
### 1. Démarrage Complet
```bash
# Démarrer tous les services
./restart_4nk_node.sh
# Démarrer linfrastructure (reverse proxy inclus)
sudo docker compose up -d --build
# Vérifier le statut
docker ps
@ -237,21 +281,21 @@ docker ps
### 2. Démarrage Séquentiel (Debug)
```bash
# Démarrer Tor
./restart_4nk_node.sh -t
# Démarrer Tor (si utilisé)
sudo docker compose up -d tor
# Démarrer Bitcoin Core
./restart_4nk_node.sh -b
sudo docker compose up -d bitcoin
# Attendre la synchronisation Bitcoin (10-30 minutes)
echo "Attendre la synchronisation Bitcoin..."
docker logs bitcoin-signet | grep "progress"
# Démarrer Blindbit
./restart_4nk_node.sh -l
sudo docker compose up -d blindbit
# Démarrer les relais
./restart_4nk_node.sh -r
# Démarrer les relais et le reverse proxy
sudo docker compose up -d sdk_relay_1 sdk_relay_2 sdk_relay_3 reverse_proxy
```
### 3. Vérification du Démarrage
@ -263,8 +307,9 @@ docker ps
# Vérifier les logs
docker-compose logs --tail=50
# Vérifier la connectivité
./test_final_sync.sh
# Vérifier laccès public
curl -kI https://<IP_VM>/
curl -kI https://<IP_VM>/api/
```
## 🧪 Tests Post-Installation

View File

@ -91,6 +91,7 @@ Options disponibles :
- `test_final_sync.sh` : Test complet de synchronisation
- `test_sync_logs.sh` : Test des logs de synchronisation
- `test_messages.sh` : Test des messages entre relais
- `scripts/test_ui.sh` : Build UI, déploiement reverse proxy et vérifications HTTPS (headers, index, modules JS)
**Prérequis :**
- Tous les services Docker démarrés (bitcoin, blindbit, sdk_relay)
@ -104,6 +105,12 @@ Options disponibles :
**Tests inclus :**
- `test_connectivity.sh` : Test de connectivité des services
- `test_websocket_messages.py` : Test des messages WebSocket
- Tests externes reverse proxy :
```bash
curl -kI https://<IP_VM>/
curl -kI https://<IP_VM>/api/
npx wscat -c wss://<IP_VM>/ws --no-check
```
**Prérequis :**
- Services Docker démarrés
@ -276,6 +283,64 @@ Structure du rapport :
**Symptôme** : Erreur SSL dans les tests externes
**Solution** : Vérifier les certificats et la configuration SSL
### Dépannage Tor/Bitcoin (signet custom)
#### Vérifier Tor
```bash
# État du conteneur Tor
sudo docker compose ps tor
# Derniers logs Tor
sudo docker logs tor-proxy --tail=60
# Test du port SOCKS interne
sudo docker exec tor-proxy nc -z 127.0.0.1 9050 && echo SOCKS:OK
```
#### Bridges obfs4 et diagnostics
- Les bridges obfs4 peuvent être ajoutés dans `tor/torrc` (cf. docs/CONFIGURATION.md). Après ajout, redémarrer le service Tor.
- En cas déchec « general SOCKS server failure », remplacer/ajouter des bridges et retester.
- Valider la différence entre:
- `nc -vz -w 10 -x 127.0.0.1:9050 -X 5 duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion 80` (référence onion publique)
- `nc -vz -w 10 -x 127.0.0.1:9050 -X 5 <onion_signet> 38333` (pair signet cible)
#### Vérifier Bitcoin Core côté réseau
```bash
# Connexions réseau
sudo docker exec bitcoin-signet bitcoin-cli -signet getnetworkinfo | jq '.connections,.connections_in,.connections_out'
# Pairs et nombre total
sudo docker exec bitcoin-signet bitcoin-cli -signet getpeerinfo | wc -l
```
#### Forcer une tentative de pair (onion existant)
```bash
ONION=$(awk -F= '/^addnode=/{print $2}' bitcoin/bitcoin.conf | head -n1)
sudo docker exec bitcoin-signet bitcoin-cli -signet addnode ${ONION}:38333 onetry
sleep 5
sudo docker exec bitcoin-signet bitcoin-cli -signet getconnectioncount
```
#### Boucle de retry signet (optionnelle)
Un script dédié permet de tenter automatiquement lajout du pair signet `.onion` et de redémarrer `sdk_relay_1` une fois des connexions établies:
```bash
./scripts/retry_signet_sync.sh
```
Les logs sont conservés sous `tests/logs/signet_sync_*.log`.
#### Vérifier louverture des ports du relais et lAPI
```bash
# Ports 8090/8091 dans sdk_relay_1
sudo docker exec sdk_relay_1 sh -lc 'ss -lntp 2>/dev/null || netstat -tlnp 2>/dev/null' | grep -E ':8090 |:8091 ' || echo no-listen
# Test /api via reverse proxy
curl -k -I https://localhost/api/
```
#### Dépendances Python
**Symptôme** : ModuleNotFoundError
**Solution** : Installer les dépendances avec `pip install websockets`

View File

@ -7,8 +7,8 @@ Guide complet pour utiliser l'infrastructure 4NK Node au quotidien.
### 1. Démarrage Rapide
```bash
# Démarrer tous les services
./restart_4nk_node.sh
# Démarrer linfrastructure (reverse proxy inclus)
sudo docker compose up -d --build
# Vérifier le statut
docker ps
@ -17,21 +17,21 @@ docker ps
### 2. Démarrage Séquentiel
```bash
# Démarrer Tor
./restart_4nk_node.sh -t
# Démarrer Tor (si utilisé)
sudo docker compose up -d tor
# Démarrer Bitcoin Core
./restart_4nk_node.sh -b
sudo docker compose up -d bitcoin
# Attendre la synchronisation Bitcoin
echo "Attendre la synchronisation Bitcoin (10-30 minutes)..."
docker logs bitcoin-signet | grep "progress"
# Démarrer Blindbit
./restart_4nk_node.sh -l
sudo docker compose up -d blindbit
# Démarrer les relais
./restart_4nk_node.sh -r
# Démarrer les relais et le reverse proxy
sudo docker compose up -d sdk_relay_1 sdk_relay_2 sdk_relay_3 reverse_proxy
```
### 3. Vérification du Démarrage
@ -99,17 +99,17 @@ docker-compose logs --since="2024-01-01T00:00:00"
docker-compose logs --tail=100
```
## 🌐 Utilisation du Réseau de Relais
## 🌐 Utilisation via reverse proxy
### 1. Configuration des Relais
L'infrastructure utilise 3 relais locaux :
| Relay | Port WebSocket | Port HTTP | Configuration |
|-------|----------------|-----------|---------------|
| **Relay 1** | 8090 | 8091 | `sdk_relay/.conf.docker.relay1` |
| **Relay 2** | 8092 | 8093 | `sdk_relay/.conf.docker.relay2` |
| **Relay 3** | 8094 | 8095 | `sdk_relay/.conf.docker.relay3` |
Les accès externes se font via le reverse proxy unique :
- UI : `https://<IP_VM>/`
- API : `https://<IP_VM>/api/`
- WebSocket : `wss://<IP_VM>/ws/`
### 2. Test de Connectivité des Relais

File diff suppressed because one or more lines are too long

BIN
ihm_client/dist/assets/4nk_image.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
ihm_client/dist/assets/4nk_revoke.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
ihm_client/dist/assets/bgd.webp vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

BIN
ihm_client/dist/assets/camera.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

34
ihm_client/dist/assets/home.js vendored Executable file
View File

@ -0,0 +1,34 @@
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.getElementById(tab.getAttribute('data-tab')).classList.add('active');
});
});
function toggleMenu() {
var menu = document.getElementById('menu');
if (menu.style.display === 'block') {
menu.style.display = 'none';
} else {
menu.style.display = 'block';
}
}
//// Modal
function openModal() {
document.getElementById('modal').style.display = 'flex';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
// Close modal when clicking outside of it
window.onclick = function(event) {
const modal = document.getElementById('modal');
if (event.target === modal) {
closeModal();
}
}

BIN
ihm_client/dist/assets/qr_code.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,6 @@
function getCorrectDOM(componentTag) {
const dom = document?.querySelector(componentTag)?.shadowRoot || document;
return dom;
}
export { getCorrectDOM as g };

9546
ihm_client/dist/index-C1Gp83RD.mjs vendored Normal file

File diff suppressed because one or more lines are too long

13
ihm_client/dist/index.html vendored Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>4NK UI</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/index.js"></script>
</body>
</html>

1
ihm_client/dist/index.js vendored Normal file
View File

@ -0,0 +1 @@
export { D as Database, M as MessageType, S as Services } from './index-C1Gp83RD.mjs';

View File

@ -0,0 +1,14 @@
import { S as Services } from './index-C1Gp83RD.mjs';
import { g as getCorrectDOM } from './document.utils-SxcZpxzG.mjs';
async function initProcessElement(id, zone) {
await getProcesses();
getCorrectDOM("process-4nk-component");
}
async function getProcesses() {
const service = await Services.getInstance();
const processes = await service.getProcesses();
return processes;
}
export { initProcessElement };

View File

@ -0,0 +1,100 @@
const createWorker=()=>new Worker(URL.createObjectURL(new Blob([`class x{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new x(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;f<b+d;f++)for(let g=a;g<a+c;g++)this.set(g,f,!!e)}}
class A{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,b){return this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c}}
class ba{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&255>>c<<c)>>c,
this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var B,C=B||(B={});C.Numeric="numeric";C.Alphanumeric="alphanumeric";C.Byte="byte";C.Kanji="kanji";C.ECI="eci";C.StructuredAppend="structuredappend";var D,E=D||(D={});E[E.Terminator=0]="Terminator";E[E.Numeric=1]="Numeric";E[E.Alphanumeric=2]="Alphanumeric";E[E.Byte=4]="Byte";E[E.Kanji=8]="Kanji";E[E.ECI=7]="ECI";E[E.StructuredAppend=3]="StructuredAppend";let F="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");
function ca(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let e=0;e<b;e++){let f=a.readBits(8);c.push(f)}try{d+=decodeURIComponent(c.map(e=>\`%\${("0"+e.toString(16)).substr(-2)}\`).join(""))}catch(e){}return{bytes:c,text:d}}
function da(a,b){a=new ba(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===D.Terminator)return b;if(d===D.ECI)0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(7)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:B.ECI,assignmentNumber:-1});else if(d===D.Numeric){var e=a,f=[];d="";for(var g=
e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),m=Math.floor(h/10)%10;h%=10;f.push(48+k,48+m,48+h);d+=k.toString()+m.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;f.push(48+e,48+g);d+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");f.push(48+e);d+=e.toString()}b.text+=
d;b.bytes.push(...f);b.chunks.push({type:B.Numeric,text:d})}else if(d===D.Alphanumeric){e=a;f=[];d="";for(g=e.readBits([9,11,13][c]);2<=g;)m=e.readBits(11),k=Math.floor(m/45),m%=45,f.push(F[k].charCodeAt(0),F[m].charCodeAt(0)),d+=F[k]+F[m],g-=2;1===g&&(e=e.readBits(6),f.push(F[e].charCodeAt(0)),d+=F[e]);b.text+=d;b.bytes.push(...f);b.chunks.push({type:B.Alphanumeric,text:d})}else if(d===D.Byte)d=ca(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:B.Byte,bytes:d.bytes,text:d.text});
else if(d===D.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;g<e;g++)k=f.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));b.text+=f;b.bytes.push(...d);b.chunks.push({type:B.Kanji,bytes:d,text:f})}else d===D.StructuredAppend&&b.chunks.push({type:B.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b}
class G{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;
if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new G(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new G(this.field,c)}multiplyPoly(a){if(this.isZero()||
a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let f=0;f<c;f++){let g=b[f];for(let h=0;h<d;h++)e[f+h]=H(e[f+h],this.field.multiply(g,a[h]))}return new G(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);return new G(this.field,
a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach(d=>{b^=d}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=H(this.field.multiply(a,b),this.coefficients[d]);return b}}function H(a,b){return a^b}
class ea{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new G(this,Uint8ClampedArray.from([0]));this.one=new G(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===
a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new G(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}
function fa(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var f=a.one;c.degree()>=d/2;){var g=b;let h=e;b=c;e=f;if(b.isZero())return null;c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let k=c.degree()-b.degree(),m=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(k,m));c=c.addOrSubtract(b.multiplyByMonomial(k,m))}f=f.multiplyPoly(e).addOrSubtract(h);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0);
if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]}
function ha(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new ea(285,256,0);var d=new G(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;g<b;g++){var h=d.evaluateAt(a.exp(g+a.generatorBase));e[e.length-1-g]=h;0!==h&&(f=!0)}if(!f)return c;d=new G(a,e);d=fa(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];g=b.degree();if(1===g)b=[b.getCoefficient(1)];else{e=Array(g);f=0;for(h=1;h<a.size&&f<g;h++)0===b.evaluateAt(h)&&(e[f]=a.inverse(h),f++);b=f!==g?null:e}if(null==b)return null;e=d[1];
f=b.length;d=Array(f);for(g=0;g<f;g++){h=a.inverse(b[g]);let k=1;for(let m=0;m<f;m++)g!==m&&(k=a.multiply(k,H(1,a.multiply(b[m],h))));d[g]=a.multiply(e.evaluateAt(h),a.inverse(k));0!==a.generatorBase&&(d[g]=a.multiply(d[g],h))}for(e=0;e<b.length;e++){f=c.length-1-a.log(b[e]);if(0>f)return null;c[f]^=d[e]}return c}
let I=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},
{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,
34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,
ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},
{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},
{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,
dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},
{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,
ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,
74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,
versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,
dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,
dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},
{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},
{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},
{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},
{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},
{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,
dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,
dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},
{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,
26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},
{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},
{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,
dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,
dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,
dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,
dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],
errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,
versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},
{numBlocks:61,dataCodewordsPerBlock:16}]}]}];function J(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function K(a,b){return b<<1|a}
let ia=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,
dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,
formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,
dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],ja=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y%
2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2];
function ka(a,b,c){c=ja[c.dataMask];let d=a.height;var e=17+4*b.versionNumber;let f=x.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6<b.versionNumber&&(f.setRegion(e-11,0,3,6,!0),f.setRegion(0,e-11,6,3,!0));b=[];h=g=0;e=!0;for(let k=d-1;0<k;k-=2){6===
k&&k--;for(let m=0;m<d;m++){let l=e?d-1-m:m;for(let n=0;2>n;n++){let q=k-n;if(!f.get(q,l)){h++;let r=a.get(q,l);c({y:l,x:q})&&(r=!r);g=g<<1|r;8===h&&(b.push(g),g=h=0)}}}e=!e}return b}
function la(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return I[c-1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=K(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let g=b-9;g>=b-11;g--)d=K(a.get(e,g),d);a=Infinity;let f;for(let g of I){if(g.infoBits===c||g.infoBits===d)return g;b=J(c,g.infoBits);b<a&&(f=g,a=b);b=J(d,g.infoBits);b<a&&(f=g,a=b)}if(3>=a)return f}
function ma(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=K(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=K(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=K(a.get(8,e),c);for(e=d-8;e<d;e++)c=K(a.get(e,8),c);a=Infinity;d=null;for(let {bits:f,formatInfo:g}of ia){if(f===b||f===c)return g;e=J(b,f);e<a&&(d=g,a=e);b!==c&&(e=J(c,f),e<a&&(d=g,a=e))}return 3>=a?d:null}
function na(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(h=>{for(let k=0;k<h.numBlocks;k++)e.push({numDataCodewords:h.dataCodewordsPerBlock,codewords:[]}),f+=h.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<f)return null;a=a.slice(0,f);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var g of e)g.codewords.push(a.shift());if(1<d.ecBlocks.length)for(g=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=0;c<b;c++)e[g+c].codewords.push(a.shift());for(;0<a.length;)for(let h of e)h.codewords.push(a.shift());
return e}function L(a){let b=la(a);if(!b)return null;var c=ma(a);if(!c)return null;a=ka(a,b,c);var d=na(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((e,f)=>e+f.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let e of d){d=ha(e.codewords,e.codewords.length-e.numDataCodewords);if(!d)return null;for(let f=0;f<e.numDataCodewords;f++)c[a++]=d[f]}try{return da(c,b.versionNumber)}catch(e){return null}}
function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let f=a.y-b.y+c.y-d.y;if(0===e&&0===f)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};let g=b.x-c.x;var h=d.x-c.x;let k=b.y-c.y,m=d.y-c.y;c=g*m-h*k;h=(e*m-h*f)/c;e=(g*f-e*k)/c;return{a11:b.x-a.x+h*b.x,a12:b.y-a.y+h*b.y,a13:h,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}
function oa(a,b,c,d){a=M(a,b,c,d);return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}
function pa(a,b){var c=oa({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,f=d.a12*c.a11+d.a22*c.a12+d.a32*c.a13,g=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,m=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,l=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,n=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,q=d.a13*
c.a31+d.a23*c.a32+d.a33*c.a33;c=x.createEmpty(b.dimension,b.dimension);d=(r,u)=>{const p=g*r+m*u+q;return{x:(e*r+h*u+l)/p,y:(f*r+k*u+n)/p}};for(let r=0;r<b.dimension;r++)for(let u=0;u<b.dimension;u++){let p=d(u+.5,r+.5);c.set(u,r,a.get(Math.floor(p.x),Math.floor(p.y)))}return{matrix:c,mappingFunction:d}}let N=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));function O(a){return a.reduce((b,c)=>b+c)}
function qa(a,b,c){let d=N(a,b),e=N(b,c),f=N(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}}
function ra(a,b,c,d){d=(O(P(a,c,d,5))/7+O(P(a,b,d,5))/7+O(P(c,a,d,5))/7+O(P(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(N(a,b)/d);a=Math.round(N(a,c)/d);a=Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}
function Q(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),m=Math.abs(b-h),l=Math.floor(-k/2),n=g<a?1:-1,q=h<b?1:-1,r=!0;for(let u=g,p=h;u!==a+n;u+=n){g=f?p:u;h=f?u:p;if(c.get(g,h)!==r&&(r=!r,e.push({x:g,y:h}),e.length===d+1))break;l+=m;if(0<l){if(p===b)break;p+=q;l-=k}}c=
[];for(f=0;f<d;f++)e[f]&&e[f+1]?c.push(N(e[f],e[f+1])):c.push(0);return c}function P(a,b,c,d){let e=b.y-a.y,f=b.x-a.x;b=Q(a,b,c,Math.ceil(d/2));a=Q(a,{x:a.x-f,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function R(a,b){let c=O(a)/O(b),d=0;b.forEach((e,f)=>{d+=Math.pow(a[f]-e*c,2)});return{averageSize:c,error:d}}
function S(a,b,c){try{let d=P(a,{x:-1,y:a.y},c,b.length),e=P(a,{x:a.x,y:-1},c,b.length),f=P(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=P(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=R(d,b),k=R(e,b),m=R(f,b),l=R(g,b),n=(h.averageSize+k.averageSize+m.averageSize+l.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+m.error*m.error+l.error*l.error)+(Math.pow(h.averageSize-n,2)+Math.pow(k.averageSize-n,2)+Math.pow(m.averageSize-n,2)+
Math.pow(l.averageSize-n,2))/n}catch(d){return Infinity}}function T(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}}
function sa(a){var b=[],c=[];let d=[];var e=[];for(let p=0;p<=a.height;p++){var f=0,g=!1;let t=[0,0,0,0,0];for(let v=-1;v<=a.width;v++){var h=a.get(v,p);if(h===g)f++;else{t=[t[1],t[2],t[3],t[4],f];f=1;g=h;var k=O(t)/7;k=Math.abs(t[0]-k)<k&&Math.abs(t[1]-k)<k&&Math.abs(t[2]-3*k)<3*k&&Math.abs(t[3]-k)<k&&Math.abs(t[4]-k)<k&&!h;var m=O(t.slice(-3))/3;h=Math.abs(t[2]-m)<m&&Math.abs(t[3]-m)<m&&Math.abs(t[4]-m)<m&&h;if(k){let z=v-t[3]-t[4],y=z-t[2];k={startX:y,endX:z,y:p};m=c.filter(w=>y>=w.bottom.startX&&
y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<m.length?m[0].bottom=k:c.push({top:k,bottom:k})}if(h){let z=v-t[4],y=z-t[3];h={startX:y,y:p,endX:z};k=e.filter(w=>y>=w.bottom.startX&&y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<k.length?
k[0].bottom=h:e.push({top:h,bottom:h})}}}b.push(...c.filter(v=>v.bottom.y!==p&&2<=v.bottom.y-v.top.y));c=c.filter(v=>v.bottom.y===p);d.push(...e.filter(v=>v.bottom.y!==p));e=e.filter(v=>v.bottom.y===p)}b.push(...c.filter(p=>2<=p.bottom.y-p.top.y));d.push(...e);c=[];for(var l of b)2>l.bottom.y-l.top.y||(b=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4,e=(l.top.y+l.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[l.top.endX-l.top.startX,l.bottom.endX-l.bottom.startX,l.bottom.y-l.top.y+
1],f=O(f)/f.length,g=S({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((p,t)=>p.score-t.score);l=[];for(b=0;b<Math.min(c.length,5);++b){e=c[b];f=[];for(var n of c)n!==e&&f.push(Object.assign(Object.assign({},n),{score:n.score+Math.pow(n.size-e.size,2)/e.size}));f.sort((p,t)=>p.score-t.score);l.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}l.sort((p,t)=>p.score-t.score);let {topRight:q,topLeft:r,bottomLeft:u}=qa(...l[0].points);
l=U(a,d,q,r,u);n=[];l&&n.push({alignmentPattern:{x:l.alignmentPattern.x,y:l.alignmentPattern.y},bottomLeft:{x:u.x,y:u.y},dimension:l.dimension,topLeft:{x:r.x,y:r.y},topRight:{x:q.x,y:q.y}});l=T(a,q);b=T(a,r);c=T(a,u);(a=U(a,d,l,b,c))&&n.push({alignmentPattern:{x:a.alignmentPattern.x,y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:l.x,y:l.y},dimension:a.dimension});return 0===n.length?null:n}
function U(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ra(d,c,e,a))}catch(l){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(N(d,e)+N(d,c))/2/g;e=1-3/c;let m={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(l=>{const n=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4;l=(l.top.y+l.bottom.y+1)/2;if(a.get(Math.floor(n),Math.floor(l))){var q=S({x:Math.floor(n),y:Math.floor(l)},[1,1,1],a)+N({x:n,y:l},m);return{x:n,y:l,score:q}}}).filter(l=>!!l).sort((l,n)=>l.score-n.score);return{alignmentPattern:15<=
c&&b.length?b[0]:m,dimension:f}}
function V(a){var b=sa(a);if(!b)return null;for(let e of b){b=pa(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let f=d+1;f<c.height;f++)c.get(d,f)!==c.get(f,d)&&(c.set(d,f,!c.get(d,f)),c.set(f,d,!c.get(f,d)));c=L(c)}}if(c)return{binaryData:c.bytes,data:c.text,chunks:c.chunks,version:c.version,location:{topRightCorner:b.mappingFunction(e.dimension,0),topLeftCorner:b.mappingFunction(0,0),bottomRightCorner:b.mappingFunction(e.dimension,e.dimension),bottomLeftCorner:b.mappingFunction(0,
e.dimension),topRightFinderPattern:e.topRight,topLeftFinderPattern:e.topLeft,bottomLeftFinderPattern:e.bottomLeft,bottomRightAlignmentPattern:e.alignmentPattern},matrix:b.matrix}}return null}let ta={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},canOverwriteImage:!0};function W(a,b){Object.keys(b).forEach(c=>{a[c]=b[c]})}
function X(a,b,c,d={}){let e=Object.create(null);W(e,ta);W(e,d);d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var m=0;if(h){var l=new Uint8ClampedArray(a.buffer,m,k);m+=k}l=new A(b,c,l);if(g.useIntegerApproximation)for(var n=0;n<c;n++)for(var q=0;q<b;q++){var r=4*(n*b+q);l.set(q,n,g.red*a[r]+g.green*a[r+1]+
g.blue*a[r+2]+128>>8)}else for(n=0;n<c;n++)for(q=0;q<b;q++)r=4*(n*b+q),l.set(q,n,g.red*a[r]+g.green*a[r+1]+g.blue*a[r+2]);g=Math.ceil(b/8);n=Math.ceil(c/8);q=g*n;if(h){var u=new Uint8ClampedArray(a.buffer,m,q);m+=q}u=new A(g,n,u);for(q=0;q<n;q++)for(r=0;r<g;r++){var p=Infinity,t=0;for(var v=0;8>v;v++)for(let w=0;8>w;w++){let aa=l.get(8*r+w,8*q+v);p=Math.min(p,aa);t=Math.max(t,aa)}v=(p+t)/2;v=Math.min(255,1.11*v);24>=t-p&&(v=p/2,0<q&&0<r&&(t=(u.get(r,q-1)+2*u.get(r-1,q)+u.get(r-1,q-1))/4,p<t&&(v=t)));
u.set(r,q,v)}h?(q=new Uint8ClampedArray(a.buffer,m,k),m+=k,q=new x(q,b)):q=x.createEmpty(b,c);r=null;f&&(h?(a=new Uint8ClampedArray(a.buffer,m,k),r=new x(a,b)):r=x.createEmpty(b,c));for(b=0;b<n;b++)for(a=0;a<g;a++){c=g-3;c=2>a?2:a>c?c:a;h=n-3;h=2>b?2:b>h?h:b;k=0;for(m=-2;2>=m;m++)for(p=-2;2>=p;p++)k+=u.get(c+m,h+p);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)m=8*a+h,p=8*b+k,t=l.get(m,p),q.set(m,p,t<=c),f&&r.set(m,p,!(t<=c))}f=f?{binarized:q,inverted:r}:{binarized:q};let {binarized:z,inverted:y}=f;(f=V(d?
y:z))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||(f=V(d?z:y));return f}X.default=X;let Y="dontInvert",Z={red:77,green:150,blue:29,useIntegerApproximation:!0};
self.onmessage=a=>{let b=a.data.id,c=a.data.data;switch(a.data.type){case "decode":(a=X(c.data,c.width,c.height,{inversionAttempts:Y,greyScaleWeights:Z}))?self.postMessage({id:b,type:"qrResult",data:a.data,cornerPoints:[a.location.topLeftCorner,a.location.topRightCorner,a.location.bottomRightCorner,a.location.bottomLeftCorner]}):self.postMessage({id:b,type:"qrResult",data:null});break;case "grayscaleWeights":Z.red=c.red;Z.green=c.green;Z.blue=c.blue;Z.useIntegerApproximation=c.useIntegerApproximation;
break;case "inversionMode":switch(c){case "original":Y="dontInvert";break;case "invert":Y="onlyInvert";break;case "both":Y="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}
`]),{type:"application/javascript"}));
export { createWorker };

1528
ihm_client/dist/sdk_client-BJ65BQeE.mjs vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

877
ihm_client/dist/style.css vendored Normal file

File diff suppressed because one or more lines are too long

877
ihm_client/dist/style/4nk.css vendored Executable file
View File

@ -0,0 +1,877 @@
:root {
--primary-color
: #3A506B;
/* Bleu métallique */
--secondary-color
: #B0BEC5;
/* Gris acier */
--accent-color
: #D68C45;
/* Cuivre */
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
background-image: url(../assets/bgd.webp);
background-repeat:no-repeat;
background-size: cover;
background-blend-mode :soft-light;
height: 100vh;
}
.message {
margin: 30px 0;
font-size: 14px;
overflow-wrap: anywhere;
}
.message strong{
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
/** Modal Css */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
z-index: 3;
}
.modal-content {
width: 55%;
height: 30%;
background-color: white;
border-radius: 4px;
padding: 20px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.modal-title {
margin: 0;
padding-bottom: 8px;
width: 100%;
font-size: 0.9em;
border-bottom: 1px solid #ccc;
}
.confirmation-box {
/* margin-top: 20px; */
align-content: center;
width: 70%;
height: 20%;
/* padding: 20px; */
font-size: 1.5em;
color: #333333;
top: 5%;
position: relative;
}
/* Confirmation Modal Styles */
#confirmation-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 1000;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.modal-confirmation {
text-align: left;
padding: 10px;
}
.modal-confirmation h3 {
margin-bottom: 15px;
color: var(--primary-color);
font-size: 1.1em;
}
.modal-confirmation p {
margin: 8px 0;
font-size: 0.9em;
line-height: 1.4;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.modal-footer button {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 0.9em;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-secondary {
background: var(--secondary-color);
color: white;
}
/* Responsive adjustments */
@media only screen and (max-width: 600px) {
.modal-content {
width: 95%;
margin: 10px;
padding: 15px;
}
.modal-confirmation h3 {
font-size: 1em;
}
.modal-confirmation p {
font-size: 0.85em;
}
}
.nav-wrapper {
position: fixed;
z-index: 2;
background: radial-gradient(circle, white, var(--primary-color));
/* background-color: #CFD8DC; */
display: flex;
justify-content: flex-end;
align-items: center;
color: #37474F;
height: 9vh;
width: 100vw;
left: 0;
top: 0;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);
.nav-right-icons {
display: flex;
.notification-container {
position: relative;
display: inline-block;
}
.notification-bell, .burger-menu {
z-index: 3;
height: 20px;
width: 20px;
margin-right: 1rem;
}
.notification-badge {
position: absolute;
top: -.7rem;
left: -.8rem;
background-color: red;
color: white;
border-radius: 50%;
padding: 2.5px 6px;
font-size: 0.8em;
font-weight: bold;
}
}
.notification-board {
position: absolute;
width: 20rem;
min-height: 8rem;
background-color: white;
right: 0.5rem;
display: none;
border-radius: 4px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
.notification-element {
padding: .8rem 0;
width: 100%;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.notification-element:not(:last-child) {
border-bottom: 1px solid;
}
}
}
.brand-logo {
height: 100%;
width: 100vw;
align-content: center;
position: relative;
display: flex;
position: absolute;
align-items: center;
justify-content: center;
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.container {
text-align: center;
display: grid;
height: 100vh;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
grid-auto-rows: 10vh 15vh 1fr;
}
.title-container {
grid-column: 2 / 7;
grid-row: 2;
}
.page-container {
grid-column: 2 / 7;
grid-row: 3 ;
justify-content: center;
display: flex;
padding: 1rem;
box-sizing: border-box;
max-height: 60vh;
}
h1 {
font-size: 2em;
margin: 20px 0;
}
@media only screen and (min-width: 600px) {
.tab-container {
display: none;
}
.page-container {
display: flex;
align-items: center;
}
.process-container {
grid-column: 3 / 6;
grid-row: 3 ;
.card {
min-width: 40vw;
}
}
.separator {
width: 2px;
background-color: #78909C;
height: 80%;
margin: 0 0.5em;
}
.tab-content {
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
height: 80%;
}
}
@media only screen and (max-width: 600px) {
.process-container {
grid-column: 2 / 7;
grid-row: 3 ;
}
.container {
grid-auto-rows: 10vh 15vh 15vh 1fr;
}
.tab-container {
grid-column: 1 / 8;
grid-row: 3;
}
.page-container {
grid-column: 2 / 7;
grid-row: 4 ;
}
.separator {
display: none;
}
.tabs {
display: flex;
flex-grow: 1;
overflow: hidden;
z-index: 1;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #E0E4D6;
}
.tab {
flex: 1;
text-align: center;
padding: 10px 0;
cursor: pointer;
font-size: 1em;
color: #6200ea;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.tab.active {
border-bottom: 2px solid #6200ea;
font-weight: bold;
}
.card.tab-content {
display: none;
}
.tab-content.active {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 80%;
}
.modal-content {
width: 80%;
height: 20%;
}
}
.qr-code {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.emoji-display {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#emoji-display-2{
margin-top: 30px;
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 20px;
}
#okButton{
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
margin-top: 10px;
}
.pairing-request {
font-family: "Segoe UI Emoji", "Noto Color Emoji", "Apple Color Emoji", sans-serif;
font-size: 14px;
margin-top: 0px;
}
.sp-address-btn {
margin-bottom: 2em;
cursor: pointer;
background-color: #D0D0D7;
color: white;
border-style: none;
border-radius: 5px;
color: #000;
padding: 2px;
}
.camera-card {
display: flex;
justify-content: center;
align-items: center;
/* height: 200px; */
}
.btn {
display: inline-block;
padding: 10px 20px;
background-color: var(--primary-color);
color: white;
text-align: center;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
.btn:hover {
background-color: #3700b3;
}
.card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
height: 60vh;
justify-content: flex-start;
padding: 1rem;
overflow-y: auto;
}
.card-content {
flex-grow: 1;
flex-direction: column;
display: flex;
justify-content: flex-start;
align-items: center;
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
}
.card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 0px;
}
.card-action {
width: 100%;
}
.menu-content {
display: none;
position: absolute;
top: 3.4rem;
right: 1rem;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
}
.menu-content a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #e0e0e0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.menu-content a:last-child {
border-bottom: none;
}
.qr-code-scanner {
display: none;
}
/* QR READER */
#qr-reader div {
position: inherit;
}
#qr-reader div img{
top: 15px ;
right: 25px;
margin-top: 5px;
}
/* INPUT CSS **/
.input-container {
position: relative;
width: 100%;
background-color: #ECEFF1;
}
.input-field {
width: 36vw;
padding: 10px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
transition: border-color 0.3s;
}
.input-field:focus {
border-bottom: 2px solid #6200ea;
}
.input-label {
position: absolute;
margin-top: -0.5em;
top: 0;
left: 0;
padding: 10px 0;
font-size: 1em;
color: #999;
pointer-events: none;
transition: transform 0.3s, color 0.3s, font-size 0.3s;
}
.input-field:focus + .input-label,
.input-field:not(:placeholder-shown) + .input-label {
transform: translateY(-20px);
font-size: 0.8em;
color: #6200ea;
}
.input-underline {
position: absolute;
bottom: 0;
left: 50%;
width: 0;
height: 2px;
background-color: #6200ea;
transition: width 0.3s, left 0.3s;
}
.input-field:focus ~ .input-underline {
width: 100%;
left: 0;
}
.dropdown-content {
position: absolute;
flex-direction: column;
top: 100%;
left: 0;
width: 100%;
max-height: 150px;
overflow-y: auto;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: none;
z-index: 1;
}
.dropdown-content span {
padding: 10px;
cursor: pointer;
list-style: none;
}
.dropdown-content span:hover {
background-color: #f0f0f0;
}
/** AUTOCOMPLETE **/
select[data-multi-select-plugin] {
display: none !important;
}
.multi-select-component {
width: 36vw;
padding: 5px 0;
font-size: 1em;
border: none;
border-bottom: 1px solid #ccc;
outline: none;
background: transparent;
display: flex;
flex-direction: row;
height: auto;
width: 100%;
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
}
.autocomplete-list {
border-radius: 4px 0px 0px 4px;
}
.multi-select-component:focus-within {
box-shadow: inset 0px 0px 0px 2px #78ABFE;
}
.multi-select-component .btn-group {
display: none !important;
}
.multiselect-native-select .multiselect-container {
width: 100%;
}
.selected-processes {
background-color: white;
padding: 0.4em;
}
.selected-wrapper {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
display: inline-block;
border: 1px solid #d9d9d9;
background-color: #ededed;
white-space: nowrap;
margin: 1px 5px 5px 0;
height: 22px;
vertical-align: top;
cursor: default;
}
.selected-wrapper .selected-label {
max-width: 514px;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
padding-left: 4px;
vertical-align: top;
}
.selected-wrapper .selected-close {
display: inline-block;
text-decoration: none;
font-size: 14px;
line-height: 1.49em;
margin-left: 5px;
padding-bottom: 10px;
height: 100%;
vertical-align: top;
padding-right: 4px;
opacity: 0.2;
color: #000;
text-shadow: 0 1px 0 #fff;
font-weight: 700;
}
.search-container {
display: flex;
flex-direction: row;
}
.search-container .selected-input {
background: none;
border: 0;
height: 20px;
width: 60px;
padding: 0;
margin-bottom: 6px;
-webkit-box-shadow: none;
box-shadow: none;
}
.search-container .selected-input:focus {
outline: none;
}
.dropdown-icon.active {
transform: rotateX(180deg)
}
.search-container .dropdown-icon {
display: inline-block;
padding: 10px 5px;
position: absolute;
top: 5px;
right: 5px;
width: 10px;
height: 10px;
border: 0 !important;
/* needed */
-webkit-appearance: none;
-moz-appearance: none;
/* SVG background image */
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Ctitle%3Edown-arrow%3C%2Ftitle%3E%3Cg%20fill%3D%22%23818181%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23818181%22%3E%3C%2Fpath%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-position: center;
background-size: 10px;
background-repeat: no-repeat;
}
.search-container ul {
position: absolute;
list-style: none;
padding: 0;
z-index: 3;
margin-top: 29px;
width: 100%;
right: 0px;
background: #fff;
border: 1px solid #ccc;
border-top: none;
border-bottom: none;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
}
.search-container ul :focus {
outline: none;
}
.search-container ul li {
display: block;
text-align: left;
padding: 8px 29px 2px 12px;
border-bottom: 1px solid #ccc;
font-size: 14px;
min-height: 31px;
}
.search-container ul li:first-child {
border-top: 1px solid #ccc;
border-radius: 4px 0px 0 0;
}
.search-container ul li:last-child {
border-radius: 4px 0px 0 0;
}
.search-container ul li:hover.not-cursor {
cursor: default;
}
.search-container ul li:hover {
color: #333;
background-color: #f0f0f0;
;
border-color: #adadad;
cursor: pointer;
}
/* Adding scrool to select options */
.autocomplete-list {
max-height: 130px;
overflow-y: auto;
}
/**************************************** Process page card ******************************************************/
.process-card {
min-width: 300px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
min-height: 40vh;
max-height: 60vh;
justify-content: space-between;
padding: 1rem;
overflow-y: auto;
}
.process-card-content {
text-align: left;
font-size: .8em;
position: relative;
left: 2vw;
width: 90%;
.process-title {
font-weight: bold;
padding: 1rem 0;
}
.process-element {
padding: .4rem 0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
&.selected {
background-color: rgba(26, 28, 24, .08);
}
}
.selected-process-zone {
background-color: rgba(26, 28, 24, .08);
}
}
.process-card-description {
padding: 20px;
font-size: 1em;
color: #333;
width: 90%;
}
.process-card-action {
width: 100%;
}

1507
ihm_client/dist/style/account.css vendored Executable file

File diff suppressed because it is too large Load Diff

597
ihm_client/dist/style/chat.css vendored Executable file
View File

@ -0,0 +1,597 @@
/* Styles de base */
:root {
--primary-color: #3A506B;
/* Bleu métallique */
--secondary-color: #B0BEC5;
/* Gris acier */
--accent-color: #D68C45;
/* Cuivre */
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
/* 4NK NAVBAR */
.brand-logo {
text-align: center;
font-size: 1.5em;
font-weight: bold;
}
.nav-wrapper {
position: fixed;
background: radial-gradient(circle, white, var(--primary-color));
display: flex;
justify-content: space-between;
align-items: center;
color: #37474F;
height: 9vh;
width: 100vw;
left: 0;
top: 0;
box-shadow: 0px 8px 10px -5px rgba(0, 0, 0, .2), 0px 16px 24px 2px rgba(0, 0, 0, .14), 0px 6px 30px 5px rgba(0, 0, 0, .12);
}
/* Icônes de la barre de navigation */
.nav-right-icons {
display: flex;
}
.notification-bell,
.burger-menu {
height: 20px;
width: 20px;
margin-right: 1rem;
cursor: pointer;
}
.notification-container {
position: relative;
/* Conserve la position pour le notification-board */
display: inline-flex;
align-items: center;
}
.notification-board {
position: absolute;
/* Position absolue pour le placer par rapport au container */
top: 40px;
right: 0;
background-color: white;
border: 1px solid #ccc;
padding: 10px;
width: 200px;
max-height: 300px;
overflow-y: auto;
/* Scroll si les notifications dépassent la taille */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 10;
/* Définit la priorité d'affichage au-dessus des autres éléments */
display: none;
/* Par défaut, la notification est masquée */
}
.notification-item{
cursor: pointer;
}
.notification-badge {
position: absolute;
top: -18px;
right: 35px;
background-color: red;
color: white;
border-radius: 50%;
padding: 4px 8px;
font-size: 12px;
display: none;
/* S'affiche seulement lorsqu'il y a des notifications */
z-index: 10;
}
/* Par défaut, le menu est masqué */
#menu {
display: none;
/* Menu caché par défaut */
transition: display 0.3s ease-in-out;
}
.burger-menu {
cursor: pointer;
}
/* Icône burger */
#burger-icon {
cursor: pointer;
}
.menu-content {
display: none;
position: absolute;
top: 3.4rem;
right: 1rem;
background-color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
overflow: hidden;
}
.menu-content a {
display: block;
padding: 10px 20px;
text-decoration: none;
color: #333;
border-bottom: 1px solid #e0e0e0;
&:hover {
background-color: rgba(26, 28, 24, .08);
}
}
.menu-content a:last-child {
border-bottom: none;
}
/* Ajustement pour la barre de navigation fixe */
.container {
display: flex;
flex: 1;
height: 90vh;
margin-top: 9vh;
margin-left: -1%;
text-align: left;
width: 100vw;
}
/* Liste des groupes */
.group-list {
width: 25%;
background-color: #1f2c3d;
color: white;
padding: 20px;
box-sizing: border-box;
overflow-y: auto;
border-right: 2px solid #2c3e50;
flex-shrink: 0;
padding-right: 10px;
height: 91vh;
}
.group-list ul {
cursor: pointer;
list-style: none;
padding: 0;
padding-right: 10px;
margin-left: 20px;
}
.group-list li {
margin-bottom: 20px;
padding: 15px;
border-radius: 8px;
background-color: #273646;
cursor: pointer;
transition: background-color 0.3s, box-shadow 0.3s;
}
.group-list li:hover {
background-color: #34495e;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.group-list .member-container {
position: relative;
}
.group-list .member-container button {
margin-left: 40px;
padding: 5px;
cursor: pointer;
background: var(--primary-color);
color: white;
border: 0px solid var(--primary-color);
border-radius: 50px;
position: absolute;
top: -25px;
right: -25px;
}
.group-list .member-container button:hover {
background: var(--accent-color)
}
/* Zone de chat */
.chat-area {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
background-color:#f1f1f1;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
margin: 1% 0% 0.5% 1%;
}
/* En-tête du chat */
.chat-header {
background-color: #34495e;
color: white;
padding: 15px;
font-size: 20px;
font-weight: bold;
border-radius: 10px 10px 0 0;
text-align: center;
}
/* Messages */
.messages {
flex: 1;
padding: 20px;
overflow-y: auto;
background-color: #f1f1f1;
border-top: 1px solid #ddd;
}
.message-container {
display: flex;
margin: 8px;
}
.message-container .message {
align-self: flex-start;
}
.message-container .message.user {
align-self: flex-end;
margin-left: auto;
color: white;
}
.message {
max-width: 70%;
padding: 10px;
border-radius: 12px;
background:var(--secondary-color);
margin: 2px 0;
}
/* Messages de l'utilisateur */
.message.user {
background: #2196f3;
color: white;
}
.message-time {
font-size: 0.7em;
opacity: 0.7;
margin-left: 0px;
margin-top: 5px;
}
/* Amélioration de l'esthétique des messages */
/* .message.user:before {
content: '';
position: absolute;
top: 10px;
right: -10px;
border: 10px solid transparent;
border-left-color: #3498db;
} */
/* Zone de saisie */
.input-area {
padding: 10px;
background-color: #bdc3c7;
display: flex;
align-items: center;
border-radius: 10px;
margin: 1%;
/* Alignement vertical */
}
.input-area input[type="text"] {
flex: 1;
/* Prend l'espace restant */
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.input-area .attachment-icon {
margin: 0 10px;
cursor: pointer;
display: flex;
align-items: center;
}
.input-area button {
padding: 10px;
margin-left: 10px;
background-color: #2980b9;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.input-area button:hover {
background-color: #1f608d;
}
.tabs {
display: flex;
margin: 20px 0px;
gap: 10px;
}
.tabs button {
padding: 10px 20px;
cursor: pointer;
background: var(--primary-color);
color: white;
border: 0px solid var(--primary-color);
margin-right: 5px;
border-radius: 10px;
}
.tabs button:hover {
background: var(--secondary-color);
color: var(--primary-color);
}
/* Signature */
.signature-area {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
background-color:#f1f1f1;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
margin: 1% 0% 0.5% 1%;
transition: all 1s ease 0.1s;
visibility: visible;
}
.signature-area.hidden {
opacity: 0;
visibility: hidden;
display: none;
pointer-events: none;
}
.signature-header {
display: flex;
align-items: center;
justify-content: center;
background-color: var(--primary-color);
color: white;
border-radius: 10px 10px 0 0;
padding-left: 4%;
}
.signature-content {
padding: 10px;
background-color: var(--secondary-color);
color: var(--primary-color);
height: 100%;
border-radius: 10px;
margin: 1%;
display: flex;
flex-direction: column;
align-items: center;
}
.signature-description {
height: 20%;
width: 100%;
margin: 0% 10% 0% 10%;
overflow: auto;
display: flex;
}
.signature-description li {
margin: 1% 0% 1% 0%;
list-style: none;
padding: 2%;
border-radius: 10px;
background-color: var(--primary-color);
color: var(--secondary-color);
width: 20%;
text-align: center;
cursor: pointer;
font-weight: bold;
margin-right: 2%;
overflow: auto;
}
.signature-description li .member-list {
margin-left: -30%;
}
.signature-description li .member-list li {
width: 100%;
}
.signature-description li .member-list li:hover {
background-color: var(--secondary-color);
color: var(--primary-color);
}
.signature-documents {
height: 80%;
width: 100%;
margin: 0% 10% 0% 10%;
overflow: auto;
display: flex;
}
.signature-documents-header {
display: flex;
width: 100%;
height: 15%;
align-items: center;
}
#request-document-button {
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 10px;
padding: 8px;
cursor: pointer;
margin-left: 5%;
font-weight: bold;
}
#request-document-button:hover {
background-color: var(--accent-color);
font-weight: bold;
}
#close-signature {
cursor: pointer;
align-items: center;
margin-left: auto;
margin-right: 2%;
border-radius: 50%;
background-color: var(--primary-color);
color: white;
border: none;
padding: -3%;
margin-top: -5%;
font-size: 1em;
font-weight: bold;
}
#close-signature:hover {
background-color: var(--secondary-color);
color: var(--primary-color);
}
/* REQUEST MODAL */
.request-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background-color: var(--secondary-color);
padding: 20px;
border-radius: 8px;
position: relative;
min-width: 300px;
}
.close-modal {
position: absolute;
top: 10px;
right: 10px;
border: none;
background: none;
font-size: 1.5em;
cursor: pointer;
font-weight: bold;
}
.close-modal:hover {
color: var(--accent-color);
}
.modal-members {
display: flex;
justify-content: space-between;
}
.modal-members ul li{
list-style: none;
}
.file-upload-container {
margin: 10px 0;
}
.file-list {
margin-top: 10px;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px;
margin: 5px 0;
background: var(--background-color-secondary);
border-radius: 4px;
}
.remove-file {
background: none;
border: none;
color: var(--text-color);
cursor: pointer;
padding: 0 5px;
}
.remove-file:hover {
color: var(--error-color);
}
#message-input {
width: 100%;
height: 50px;
resize: none;
padding: 10px;
box-sizing: border-box;
overflow: auto;
max-width: 100%;
border-radius: 10px;
}
/* Responsive */
@media screen and (max-width: 768px) {
.group-list {
display: none;
/* Masquer la liste des groupes sur les petits écrans */
}
.chat-area {
margin: 0;
}
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: var(--primary-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: var(--secondary-color);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent-color);
}

1664
ihm_client/dist/style/signature.css vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
export declare function unpair(): Promise<void>;
export declare function initHeader(): Promise<void>;

View File

@ -0,0 +1 @@
export declare function closeConfirmationModal(): Promise<void>;

View File

@ -0,0 +1,10 @@
export default class QrScannerComponent extends HTMLElement {
videoElement: any;
wrapper: any;
qrScanner: any;
constructor();
connectedCallback(): void;
initializeScanner(): Promise<void>;
onQrCodeScanned(result: any): Promise<void>;
disconnectedCallback(): void;
}

View File

@ -0,0 +1 @@
export declare function initValidationModal(processDiffs: any): Promise<void>;

View File

@ -0,0 +1,14 @@
export interface ValidationRule {
quorum: number;
fields: string[];
min_sig_member: number;
}
/**
* Loads and injects the modal HTML into the document if not already loaded.
*/
export declare function loadValidationRuleModal(templatePath?: string): Promise<void>;
/**
* Opens the modal and lets the user input a ValidationRule.
* Calls the callback with the constructed rule on submit.
*/
export declare function showValidationRuleModal(onSubmit: (rule: ValidationRule) => void): void;

3
ihm_client/dist/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
export { default as Services } from './services/service';
export { default as Database } from './services/database.service';
export { MessageType } from './models/process.model';

View File

@ -0,0 +1,24 @@
import { DocumentSignature } from '~/models/signature.models';
export interface Group {
id: number;
name: string;
description: string;
roles: Array<{
name: string;
members: Array<{
id: string | number;
name: string;
}>;
documents?: Array<any>;
}>;
commonDocuments: Array<{
id: number;
name: string;
visibility: string;
description: string;
createdAt?: string | null;
deadline?: string | null;
signatures?: DocumentSignature[];
status?: string;
}>;
}

View File

@ -0,0 +1,10 @@
export interface Member {
id: string | number;
name: string;
email?: string;
avatar?: string;
processRoles?: Array<{
processId: number | string;
role: string;
}>;
}

13
ihm_client/dist/types/main.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { SignatureComponent } from './pages/signature/signature-component';
import { SignatureElement } from './pages/signature/signature';
import { AccountComponent } from './pages/account/account-component';
import { AccountElement } from './pages/account/account';
export { SignatureComponent, SignatureElement, AccountComponent, AccountElement };
declare global {
interface HTMLElementTagNameMap {
'signature-component': SignatureComponent;
'signature-element': SignatureElement;
'account-component': AccountComponent;
'account-element': AccountElement;
}
}

View File

@ -0,0 +1,118 @@
export declare const ALLOWED_ROLES: string[];
export declare const STORAGE_KEYS: {
pairing: string;
wallet: string;
process: string;
data: string;
};
export declare const defaultRows: {
column1: string;
column2: string;
column3: string;
}[];
export declare const mockNotifications: {
[key: string]: Notification[];
};
export declare const notificationMessages: string[];
export declare const mockDataRows: {
column1: string;
column2: string;
column3: string;
column4: string;
column5: string;
column6: string;
processName: string;
zone: string;
}[];
export declare const mockProcessRows: ({
process: string;
role: string;
notification: {
messages: {
id: number;
read: boolean;
date: string;
message: string;
}[];
unread?: undefined;
total?: undefined;
};
} | {
process: string;
role: string;
notification: {
unread: number;
total: number;
messages: {
id: number;
read: boolean;
date: string;
message: string;
}[];
};
})[];
export declare const mockContracts: {
'Contract #123': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #456': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #789': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #101': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #102': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #103': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #104': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #105': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
'Contract #106': {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
};
};

View File

@ -0,0 +1,38 @@
export interface Row {
column1: string;
column2: string;
column3: string;
}
export interface Contract {
title: string;
date: string;
parties: string[];
terms: string[];
content: string;
}
export interface WalletRow {
column1: string;
column2: string;
column3: string;
}
export interface DataRow {
column1: string;
column2: string;
column3: string;
column4: string;
column5: string;
column6: string;
processName: string;
zone: string;
}
export interface Notification {
message: string;
timestamp: string;
isRead: boolean;
}
export interface NotificationMessage {
id: number;
read: boolean;
date: string;
message: string;
}

View File

@ -0,0 +1,119 @@
export const groupsMock: {
id: number;
name: string;
description: string;
commonDocuments: {
id: number;
name: string;
description: string;
visibility: string;
status: string;
createdAt: null;
deadline: null;
signatures: never[];
}[];
roles: ({
name: string;
members: {
id: number;
name: string;
}[];
documents: ({
id: number;
name: string;
description: string;
visibility: string;
createdAt: string;
deadline: string;
signatures: ({
member: {
id: number;
name: string;
};
signed: boolean;
signedAt: string;
} | {
member: {
id: number;
name: string;
};
signed: boolean;
signedAt?: undefined;
})[];
status?: undefined;
} | {
id: number;
name: string;
description: string;
visibility: string;
createdAt: null;
deadline: null;
signatures: never[];
status?: undefined;
} | {
id: number;
name: string;
description: string;
visibility: string;
status: string;
createdAt: null;
deadline: null;
signatures: never[];
})[];
} | {
name: string;
members: {
id: number;
name: string;
}[];
documents: ({
id: number;
name: string;
description: string;
visibility: string;
createdAt: string;
deadline: string;
signatures: {
member: {
id: number;
name: string;
};
signed: boolean;
signedAt: string;
}[];
status?: undefined;
} | {
id: number;
name: string;
description: string;
visibility: string;
status: string;
createdAt: null;
deadline: null;
signatures: never[];
} | {
id: number;
name: string;
description: string;
visibility: string;
status: string;
createdAt: string;
deadline: string;
signatures: ({
member: {
id: number;
name: string;
};
signed: boolean;
signedAt: string;
} | {
member: {
id: number;
name: string;
};
signed: boolean;
signedAt?: undefined;
})[];
})[];
})[];
}[];

View File

@ -0,0 +1,10 @@
export const membersMock: {
id: number;
name: string;
avatar: string;
email: string;
processRoles: {
processId: number;
role: string;
}[];
}[];

View File

@ -0,0 +1,9 @@
export declare const messagesMock: {
memberId: number;
messages: {
id: number;
sender: string;
text: string;
time: string;
}[];
}[];

View File

@ -0,0 +1,6 @@
import { Device, Process, SecretsStore } from "pkg/sdk_client";
export interface BackUp {
device: Device;
secrets: SecretsStore;
processes: Record<string, Process>;
}

View File

@ -0,0 +1,24 @@
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}
export interface IUser {
id: string;
information?: any;
}
export interface IMessage {
id: string;
message: any;
}
export interface UserDiff {
new_state_merkle_root: string;
field: string;
previous_value: string;
new_value: string;
notify_user: boolean;
need_validation: boolean;
proof: any;
}

View File

@ -0,0 +1,56 @@
export interface IProcess {
id: number;
name: string;
description: string;
icon?: string;
zoneList: IZone[];
}
export interface IZone {
id: number;
name: string;
path: string;
icon?: string;
}
export interface INotification {
id: number;
title: string;
description: string;
sendToNotificationPage?: boolean;
path?: string;
}
export declare enum MessageType {
LISTENING = "LISTENING",
REQUEST_LINK = "REQUEST_LINK",
LINK_ACCEPTED = "LINK_ACCEPTED",
CREATE_PAIRING = "CREATE_PAIRING",
PAIRING_CREATED = "PAIRING_CREATED",
ERROR = "ERROR",
VALIDATE_TOKEN = "VALIDATE_TOKEN",
RENEW_TOKEN = "RENEW_TOKEN",
GET_PAIRING_ID = "GET_PAIRING_ID",
GET_PROCESSES = "GET_PROCESSES",
GET_MY_PROCESSES = "GET_MY_PROCESSES",
PROCESSES_RETRIEVED = "PROCESSES_RETRIEVED",
RETRIEVE_DATA = "RETRIEVE_DATA",
DATA_RETRIEVED = "DATA_RETRIEVED",
DECODE_PUBLIC_DATA = "DECODE_PUBLIC_DATA",
PUBLIC_DATA_DECODED = "PUBLIC_DATA_DECODED",
GET_MEMBER_ADDRESSES = "GET_MEMBER_ADDRESSES",
MEMBER_ADDRESSES_RETRIEVED = "MEMBER_ADDRESSES_RETRIEVED",
CREATE_PROCESS = "CREATE_PROCESS",
PROCESS_CREATED = "PROCESS_CREATED",
UPDATE_PROCESS = "UPDATE_PROCESS",
PROCESS_UPDATED = "PROCESS_UPDATED",
NOTIFY_UPDATE = "NOTIFY_UPDATE",
UPDATE_NOTIFIED = "UPDATE_NOTIFIED",
VALIDATE_STATE = "VALIDATE_STATE",
STATE_VALIDATED = "STATE_VALIDATED",
HASH_VALUE = "HASH_VALUE",
VALUE_HASHED = "VALUE_HASHED",
GET_MERKLE_PROOF = "GET_MERKLE_PROOF",
MERKLE_PROOF_RETRIEVED = "MERKLE_PROOF_RETRIEVED",
VALIDATE_MERKLE_PROOF = "VALIDATE_MERKLE_PROOF",
MERKLE_PROOF_VALIDATED = "MERKLE_PROOF_VALIDATED",
ADD_DEVICE = "ADD_DEVICE",
DEVICE_ADDED = "DEVICE_ADDED"
}

View File

@ -0,0 +1,60 @@
export interface Group {
id: number;
name: string;
description?: string;
roles: {
id?: number;
name: string;
members: {
id: string | number;
name: string;
}[];
documents?: {
id: number;
name: string;
description?: string;
visibility: string;
createdAt: string | null;
deadline: string | null;
signatures: DocumentSignature[];
status?: string;
files?: Array<{
name: string;
url: string;
}>;
}[];
}[];
}
export interface Message {
id: number;
sender: string;
text?: string;
time: string;
type: 'text' | 'file';
fileName?: string;
fileData?: string;
}
export interface MemberMessages {
memberId: string;
messages: Message[];
}
export interface DocumentSignature {
signed: boolean;
member: {
name: string;
};
signedAt?: string;
}
export interface RequestParams {
processId: number;
processName: string;
roleId: number;
roleName: string;
documentId: number;
documentName: string;
}
export interface Notification {
memberId: string;
text: string;
time: string;
}

View File

@ -0,0 +1,12 @@
import { AccountElement } from './account';
declare class AccountComponent extends HTMLElement {
_callback: any;
accountElement: AccountElement | null;
constructor();
connectedCallback(): void;
fetchData(): Promise<void>;
set callback(fn: any);
get callback(): any;
render(): void;
}
export { AccountComponent };

View File

@ -0,0 +1,98 @@
declare global {
interface Window {
initAccount: () => void;
showContractPopup: (contractId: string) => void;
showPairing: () => Promise<void>;
showWallet: () => void;
showData: () => void;
addWalletRow: () => void;
confirmWalletRow: () => void;
cancelWalletRow: () => void;
openAvatarPopup: () => void;
closeAvatarPopup: () => void;
editDeviceName: (cell: HTMLTableCellElement) => void;
showNotifications: (processName: string) => void;
closeNotificationPopup: (event: Event) => void;
markAsRead: (processName: string, messageId: number, element: HTMLElement) => void;
exportRecovery: () => void;
confirmDeleteAccount: () => void;
deleteAccount: () => void;
updateNavbarBanner: (bannerUrl: string) => void;
saveBannerToLocalStorage: (bannerUrl: string) => void;
loadSavedBanner: () => void;
cancelAddRowPairing: () => void;
saveName: (cell: HTMLElement, input: HTMLInputElement) => void;
showProcessNotifications: (processName: string) => void;
handleLogout: () => void;
initializeEventListeners: () => void;
showProcess: () => void;
showProcessCreation: () => void;
showDocumentValidation: () => void;
updateNavbarName: (name: string) => void;
updateNavbarLastName: (lastName: string) => void;
showAlert: (title: string, text?: string, icon?: string) => void;
addRowPairing: () => void;
confirmRowPairing: () => void;
cancelRowPairing: () => void;
deleteRowPairing: (button: HTMLButtonElement) => void;
generateRecoveryWords: () => string[];
exportUserData: () => void;
updateActionButtons: () => void;
showQRCodeModal: (pairingId: string) => void;
}
}
declare class AccountElement extends HTMLElement {
private dom;
constructor();
connectedCallback(): void;
private showAlert;
private confirmDeleteAccount;
private deleteAccount;
private updateNavbarBanner;
private saveBannerToLocalStorage;
private loadSavedBanner;
private closeNotificationPopup;
private markAsRead;
private calculateNotifications;
private exportRecovery;
private generateRecoveryWords;
private exportUserData;
private updateActionButtons;
private getConfirmFunction;
private getCancelFunction;
private addRowPairing;
private updateTableContent;
private confirmRowPairing;
private cancelRowPairing;
private resetButtonContainer;
private deleteRowPairing;
private editDeviceName;
private finishEditing;
private handleAvatarUpload;
private showProcessCreation;
private showDocumentValidation;
private showProcess;
private showProcessNotifications;
private handleLogout;
private showContractPopup;
private hideAllContent;
private showPairing;
private showWallet;
private updateWalletTableContent;
private showData;
private addWalletRow;
private confirmWalletRow;
private cancelWalletRow;
private updateDataTableContent;
private openAvatarPopup;
private setupEventListeners;
private closeAvatarPopup;
private loadAvatar;
private loadUserInfo;
private updateNavbarName;
private updateNavbarLastName;
private updateProfilePreview;
private initializeEventListeners;
private showQRCodeModal;
}
export { AccountElement };

View File

@ -0,0 +1,33 @@
export interface Vin {
txid: string;
vout: number;
prevout: {
scriptpubkey: string;
scriptpubkey_asm: string;
scriptpubkey_type: string;
scriptpubkey_address: string;
value: number;
};
scriptsig: string;
scriptsig_asm: string;
witness: string[];
is_coinbase: boolean;
sequence: number;
}
export interface TransactionInfo {
txid: string;
version: number;
locktime: number;
vin: Vin[];
vout: any[];
size: number;
weight: number;
fee: number;
status: {
confirmed: boolean;
block_height: number;
block_hash: string;
block_time: number;
};
}
export declare function getDocumentValidation(container: HTMLElement): void;

View File

@ -0,0 +1,8 @@
import type { RoleDefinition } from '../../../pkg/sdk_client';
export declare function createKeyValueSection(title: string, id: string, isRoleSection?: boolean): {
element: HTMLDivElement;
getData: () => Record<string, RoleDefinition> | Record<string, string | {
type: string;
data: Uint8Array;
}>;
};

View File

@ -0,0 +1 @@
export declare function getProcessCreation(container: HTMLElement): Promise<void>;

View File

@ -0,0 +1,4 @@
export declare function createProcessTab(container: HTMLElement, processes: {
name: string;
publicData: Record<string, any>;
}[]): HTMLElement;

View File

@ -0,0 +1,8 @@
export declare class LoginComponent extends HTMLElement {
_callback: any;
constructor();
connectedCallback(): void;
set callback(fn: any);
get callback(): any;
render(): void;
}

View File

@ -0,0 +1,4 @@
import QrScannerComponent from '../../components/qrcode-scanner/qrcode-scanner-component';
export { QrScannerComponent };
export declare function initHomePage(): Promise<void>;
export declare function openModal(myAddress: string, receiverAddress: string): Promise<void>;

View File

@ -0,0 +1,10 @@
export declare class ProcessListComponent extends HTMLElement {
_callback: any;
id: string;
zone: string;
constructor();
connectedCallback(): void;
set callback(fn: any);
get callback(): any;
render(): void;
}

View File

@ -0,0 +1 @@
export declare function initProcessElement(id: string, zone: string): Promise<void>;

View File

@ -0,0 +1,12 @@
import { SignatureElement } from './signature';
declare class SignatureComponent extends HTMLElement {
_callback: any;
signatureElement: SignatureElement | null;
constructor();
connectedCallback(): void;
fetchData(): Promise<void>;
set callback(fn: any);
get callback(): any;
render(): void;
}
export { SignatureComponent };

View File

@ -0,0 +1,71 @@
declare global {
interface Window {
toggleUserList: () => void;
switchUser: (userId: string | number) => void;
closeProcessDetails: (groupId: number) => void;
loadMemberChat: (memberId: string | number) => void;
closeRoleDocuments: (roleName: string) => void;
newRequest: (params: RequestParams) => void;
submitRequest: () => void;
closeNewRequest: () => void;
closeModal: (button: HTMLElement) => void;
submitDocumentRequest: (documentId: number) => void;
submitNewDocument: (event: Event) => void;
submitCommonDocument: (event: Event) => void;
signDocument: (documentId: number, processId: number, isCommonDocument: boolean) => void;
confirmSignature: (documentId: number, processId: number, isCommonDocument: boolean) => void;
}
}
import { RequestParams } from '../../models/signature.models';
declare class SignatureElement extends HTMLElement {
private selectedMemberId;
private messagesMock;
private dom;
private notifications;
private notificationBadge;
private notificationBoard;
private notificationBell;
private selectedSignatories;
private allMembers;
private showAlert;
private signDocument;
constructor();
private initMessageEvents;
private initFileUpload;
private calculateDuration;
private canUserAccessDocument;
private canUserSignDocument;
private closeProcessDetails;
private removeNotification;
private renderNotifications;
private updateNotificationBadge;
private addNotification;
private sendMessage;
private showProcessDetails;
private scrollToBottom;
private loadMemberChat;
private toggleMembers;
private toggleRoles;
private loadGroupList;
private toggleUserList;
private switchUser;
private updateCurrentUserDisplay;
private generateAutoReply;
private sendFile;
private fileList;
private getFileList;
private showRoleDocuments;
private closeRoleDocuments;
private handleFiles;
private newRequest;
private closeModal;
private submitNewDocument;
private submitCommonDocument;
private submitRequest;
private closeNewRequest;
private submitDocumentRequest;
private confirmSignature;
private initializeEventListeners;
connectedCallback(): void;
}
export { SignatureElement };

5
ihm_client/dist/types/router.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import '../public/style/4nk.css';
export declare let currentRoute: string;
export declare function navigate(path: string): Promise<void>;
export declare function init(): Promise<void>;
export declare function registerAllListeners(): Promise<void>;

View File

@ -0,0 +1,43 @@
export declare class Database {
private static instance;
private db;
private dbName;
private dbVersion;
private serviceWorkerRegistration;
private messageChannel;
private messageChannelForGet;
private serviceWorkerCheckIntervalId;
private storeDefinitions;
private constructor();
static getInstance(): Promise<Database>;
private init;
getDb(): Promise<IDBDatabase>;
getStoreList(): {
[key: string]: string;
};
registerServiceWorker(path: string): Promise<void>;
private waitForServiceWorkerActivation;
private checkForUpdates;
private handleServiceWorkerMessage;
private handleDownloadList;
private handleAddObjectResponse;
private handleGetObjectResponse;
addObject(payload: {
storeName: string;
object: any;
key: any;
}): Promise<void>;
batchWriting(payload: {
storeName: string;
objects: {
key: any;
object: any;
}[];
}): Promise<void>;
getObject(storeName: string, key: string): Promise<any | null>;
dumpStore(storeName: string): Promise<Record<string, any>>;
deleteObject(storeName: string, key: string): Promise<void>;
clearStore(storeName: string): Promise<void>;
requestStoreByIndex(storeName: string, indexName: string, request: string): Promise<any[]>;
}
export default Database;

View File

@ -0,0 +1,28 @@
import type { RoleDefinition } from 'pkg/sdk_client';
interface ConfirmationModalOptions {
title: string;
content: string;
confirmText?: string;
cancelText?: string;
}
export default class ModalService {
private static instance;
private stateId;
private processId;
private constructor();
private paired_addresses;
private modal;
static getInstance(): Promise<ModalService>;
openLoginModal(myAddress: string, receiverAddress: string): void;
injectModal(members: any[]): Promise<void>;
injectCreationModal(members: any[]): Promise<void>;
injectWaitingModal(): Promise<void>;
injectValidationModal(processDiff: any): Promise<void>;
closeValidationModal(): Promise<void>;
openPairingConfirmationModal(roleDefinition: Record<string, RoleDefinition>, processId: string, stateId: string): Promise<void>;
confirmLogin(): void;
closeLoginModal(): Promise<void>;
showConfirmationModal(options: ConfirmationModalOptions, fullscreen?: boolean): Promise<boolean>;
closeConfirmationModal(): Promise<void>;
}
export {};

View File

@ -0,0 +1,167 @@
import type { ApiReturn, Device, Member, MerkleProofResult, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
import { BackUp } from '~/models/backup.model';
export declare const U32_MAX = 4294967295;
export default class Services {
private static initializing;
private static instance;
private processId;
private stateId;
private sdkClient;
private processesCache;
private myProcesses;
private notifications;
private subscriptions;
private database;
private routingInstance;
private relayAddresses;
private membersList;
private currentBlockHeight;
private constructor();
static getInstance(): Promise<Services>;
init(): Promise<void>;
setProcessId(processId: string | null): void;
setStateId(stateId: string | null): void;
getProcessId(): string | null;
getStateId(): string | null;
/**
* Calls `this.addWebsocketConnection` for each `wsurl` in relayAddresses.
* Waits for at least one handshake message before returning.
*/
connectAllRelays(): Promise<void>;
addWebsocketConnection(url: string): Promise<void>;
/**
* Add or update a key/value pair in relayAddresses.
* @param wsurl - The WebSocket URL (key).
* @param spAddress - The SP Address (value).
*/
updateRelay(wsurl: string, spAddress: string): void;
/**
* Retrieve the spAddress for a given wsurl.
* @param wsurl - The WebSocket URL to look up.
* @returns The SP Address if found, or undefined if not.
*/
getSpAddress(wsurl: string): string | undefined;
/**
* Get all key/value pairs from relayAddresses.
* @returns An array of objects containing wsurl and spAddress.
*/
getAllRelays(): {
wsurl: string;
spAddress: string;
}[];
/**
* Print all key/value pairs for debugging.
*/
printAllRelays(): void;
isPaired(): boolean;
unpairDevice(): Promise<void>;
getSecretForAddress(address: string): Promise<string | null>;
getAllSecrets(): Promise<SecretsStore>;
getAllDiffs(): Promise<Record<string, UserDiff>>;
getDiffByValue(value: string): Promise<UserDiff | null>;
private getTokensFromFaucet;
checkConnections(members: Member[]): Promise<void>;
connectAddresses(addresses: string[]): Promise<ApiReturn>;
private ensureSufficientAmount;
private waitForAmount;
createPairingProcess(userName: string, pairWith: string[]): Promise<ApiReturn>;
private isFileBlob;
private splitData;
createProcess(privateData: Record<string, any>, publicData: Record<string, any>, roles: Record<string, RoleDefinition>): Promise<ApiReturn>;
updateProcess(process: Process, privateData: Record<string, any>, publicData: Record<string, any>, roles: Record<string, RoleDefinition> | null): Promise<ApiReturn>;
createPrdUpdate(processId: string, stateId: string): Promise<ApiReturn>;
createPrdResponse(processId: string, stateId: string): Promise<ApiReturn>;
approveChange(processId: string, stateId: string): Promise<ApiReturn>;
rejectChange(processId: string, stateId: string): Promise<ApiReturn>;
resetDevice(): Promise<void>;
sendNewTxMessage(message: string): void;
sendCommitMessage(message: string): void;
sendCipherMessages(ciphers: string[]): void;
sendFaucetMessage(message: string): void;
parseCipher(message: string): Promise<void>;
parseNewTx(newTxMsg: string): Promise<void>;
handleApiReturn(apiReturn: ApiReturn): Promise<void>;
openPairingConfirmationModal(processId: string): Promise<void>;
confirmPairing(): Promise<void>;
updateDevice(): Promise<void>;
pairDevice(): Promise<void>;
getAmount(): BigInt;
getDeviceAddress(): string;
dumpDeviceFromMemory(): Device;
dumpNeuteredDevice(): Device | null;
getPairingProcessId(): string;
saveDeviceInDatabase(device: Device): Promise<void>;
getDeviceFromDatabase(): Promise<Device | null>;
getMemberFromDevice(): Promise<string[] | null>;
isChildRole(parent: any, child: any): boolean;
rolesContainsUs(roles: Record<string, RoleDefinition>): boolean;
rolesContainsMember(roles: Record<string, RoleDefinition>, pairingProcessId: string): boolean;
dumpWallet(): Promise<any>;
createFaucetMessage(): any;
createNewDevice(): Promise<string>;
restoreDevice(device: Device): void;
updateDeviceBlockHeight(): Promise<void>;
private removeProcess;
batchSaveProcessesToDb(processes: Record<string, Process>): Promise<void>;
saveProcessToDb(processId: string, process: Process): Promise<void>;
saveBlobToDb(hash: string, data: Blob): Promise<void>;
getBlobFromDb(hash: string): Promise<Blob | null>;
saveDataToStorage(hash: string, data: Blob, ttl: number | null): Promise<void>;
fetchValueFromStorage(hash: string): Promise<any | null>;
testDataInStorage(hash: string): Promise<Record<string, boolean | null> | null>;
saveDiffsToDb(diffs: UserDiff[]): Promise<void>;
getProcess(processId: string): Promise<Process | null>;
getProcesses(): Promise<Record<string, Process>>;
restoreProcessesFromBackUp(processes: Record<string, Process>): Promise<void>;
restoreProcessesFromDB(): Promise<void>;
clearSecretsFromDB(): Promise<void>;
restoreSecretsFromBackUp(secretsStore: SecretsStore): Promise<void>;
restoreSecretsFromDB(): Promise<void>;
decodeValue(value: number[]): any | null;
decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null>;
getNotifications(): any[] | null;
setNotifications(notifications: any[]): void;
importJSON(backup: BackUp): Promise<void>;
createBackUp(): Promise<BackUp | null>;
device1: boolean;
device2Ready: boolean;
resetState(): void;
handleHandshakeMsg(url: string, parsedMsg: any): Promise<void>;
private lookForStateId;
/**
* Waits for at least one handshake message to be received from any connected relay.
* This ensures that the relay addresses are fully populated and the member list is updated.
* @returns A promise that resolves when at least one handshake message is received.
*/
private waitForHandshakeMessage;
/**
* Retourne la liste de tous les membres ordonnés par leur process id
* @returns Un tableau contenant tous les membres
*/
getAllMembersSorted(): Record<string, Member>;
getAllMembers(): Record<string, Member>;
getAddressesForMemberId(memberId: string): string[] | null;
compareMembers(memberA: string[], memberB: string[]): boolean;
handleCommitError(response: string): Promise<void>;
getRoles(process: Process): Record<string, RoleDefinition> | null;
getPublicData(process: Process): Record<string, any> | null;
getProcessName(process: Process): string | null;
getMyProcesses(): Promise<string[] | null>;
requestDataFromPeers(processId: string, stateIds: string[], roles: Record<string, RoleDefinition>[]): Promise<void>;
hexToBlob(hexString: string): Blob;
hexToUInt8Array(hexString: string): Uint8Array;
blobToHex(blob: Blob): Promise<string>;
getHashForFile(commitedIn: string, label: string, fileBlob: {
type: string;
data: Uint8Array;
}): string;
getMerkleProofForFile(processState: ProcessState, attributeName: string): MerkleProofResult;
validateMerkleProof(proof: MerkleProofResult, hash: string): boolean;
getLastCommitedState(process: Process): ProcessState | null;
getLastCommitedStateIndex(process: Process): number | null;
getUncommitedStates(process: Process): ProcessState[];
getStateFromId(process: Process, stateId: string): ProcessState | null;
getNextStateAfterId(process: Process, stateId: string): ProcessState | null;
isPairingProcess(roles: Record<string, RoleDefinition>): boolean;
updateMemberPublicName(process: Process, newName: string): Promise<ApiReturn>;
}

View File

@ -0,0 +1,4 @@
import { AxiosResponse } from 'axios';
export declare function storeData(servers: string[], key: string, value: Blob, ttl: number | null): Promise<AxiosResponse | null>;
export declare function retrieveData(servers: string[], key: string): Promise<ArrayBuffer | null>;
export declare function testData(servers: string[], key: string): Promise<Record<string, boolean | null> | null>;

View File

@ -0,0 +1,17 @@
interface TokenPair {
accessToken: string;
refreshToken: string;
}
export default class TokenService {
private static instance;
private readonly SECRET_KEY;
private readonly ACCESS_TOKEN_EXPIRATION;
private readonly REFRESH_TOKEN_EXPIRATION;
private readonly encoder;
private constructor();
static getInstance(): Promise<TokenService>;
generateSessionToken(origin: string): Promise<TokenPair>;
validateToken(token: string, origin: string): Promise<boolean>;
refreshAccessToken(refreshToken: string, origin: string): Promise<string | null>;
}
export {};

View File

@ -0,0 +1 @@
export declare function getCorrectDOM(componentTag: string): Node;

View File

@ -0,0 +1,4 @@
export declare function interpolate(template: string, data: {
[key: string]: string;
}): string;
export declare function getCorrectDOM(componentTag: string): Node;

View File

@ -0,0 +1,12 @@
declare class MessageStore {
private readonly STORAGE_KEY;
private messages;
constructor();
private loadFromLocalStorage;
getMessages(): any[];
setMessages(messages: any[]): void;
private saveToLocalStorage;
addMessage(memberId: string | number, message: any): void;
}
export declare const messageStore: MessageStore;
export {};

View File

@ -0,0 +1,23 @@
interface INotification {
id: number;
title: string;
description: string;
time?: string;
memberId?: string;
}
declare class NotificationStore {
private static instance;
private notifications;
private constructor();
static getInstance(): NotificationStore;
addNotification(notification: INotification): void;
removeNotification(index: number): void;
getNotifications(): INotification[];
private saveToLocalStorage;
private loadFromLocalStorage;
private updateUI;
private renderNotificationBoard;
refreshNotifications(): void;
}
export declare const notificationStore: NotificationStore;
export {};

View File

@ -0,0 +1,5 @@
export declare function splitPrivateData(data: Record<string, any>, privateFields: string[]): {
privateData: Record<string, any>;
publicData: Record<string, any>;
};
export declare function isValid32ByteHex(value: string): boolean;

View File

@ -0,0 +1,8 @@
export declare function copyToClipboard(fullAddress: string): Promise<void>;
export declare function generateEmojiList(): string[];
export declare function addressToEmoji(text: string): Promise<string>;
export declare function displayEmojis(text: string): Promise<void>;
export declare function initAddressInput(): void;
export declare function prepareAndSendPairingTx(): Promise<void>;
export declare function generateQRCode(spAddress: string): Promise<void>;
export declare function generateCreateBtn(): Promise<void>;

View File

@ -0,0 +1,2 @@
export declare function cleanSubscriptions(): void;
export declare function addSubscription(element: Element | Document, event: any, eventHandler: EventListenerOrEventListenerObject): void;

5
ihm_client/dist/types/websockets.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import type { AnkFlag } from 'pkg/sdk_client';
export declare function initWebsocket(url: string): Promise<void>;
export declare function sendMessage(flag: AnkFlag, message: string): void;
export declare function getUrl(): string;
export declare function close(): void;

View File

@ -1,96 +0,0 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gestion des fichiers statiques
location / {
try_files $uri $uri/ /index.html;
# Headers de sécurité
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
# Proxy vers sdk_relay WebSocket
location /ws/ {
proxy_pass http://sdk_relay_1:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}
# Proxy vers sdk_relay HTTP API
location /api/ {
proxy_pass http://sdk_relay_1:8091/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "Authorization,Content-Type,Accept,X-Requested-With" always;
# Gestion des requêtes OPTIONS
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE";
add_header Access-Control-Allow-Headers "Authorization,Content-Type,Accept,X-Requested-With";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
}
# Proxy vers Bitcoin Core RPC (si nécessaire)
location /bitcoin/ {
proxy_pass http://bitcoin:18443/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Authentification basique pour Bitcoin RPC
auth_basic "Bitcoin RPC";
auth_basic_user_file /etc/nginx/.htpasswd;
}
# Proxy vers Blindbit (si nécessaire)
location /blindbit/ {
proxy_pass http://blindbit:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Cache pour les assets statiques
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# Gestion des erreurs
error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# Logs
access_log /var/log/nginx/ihm_client_access.log;
error_log /var/log/nginx/ihm_client_error.log;
}

1
ihm_client/node_modules/.bin/acorn generated vendored Symbolic link
View File

@ -0,0 +1 @@
../acorn/bin/acorn

1
ihm_client/node_modules/.bin/ansi-html generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ansi-html-community/bin/ansi-html

1
ihm_client/node_modules/.bin/browserslist generated vendored Symbolic link
View File

@ -0,0 +1 @@
../browserslist/cli.js

1
ihm_client/node_modules/.bin/ejs generated vendored Symbolic link
View File

@ -0,0 +1 @@
../ejs/bin/cli.js

1
ihm_client/node_modules/.bin/envinfo generated vendored Symbolic link
View File

@ -0,0 +1 @@
../envinfo/dist/cli.js

1
ihm_client/node_modules/.bin/esbuild generated vendored Symbolic link
View File

@ -0,0 +1 @@
../esbuild/bin/esbuild

1
ihm_client/node_modules/.bin/flat generated vendored Symbolic link
View File

@ -0,0 +1 @@
../flat/cli.js

1
ihm_client/node_modules/.bin/glob generated vendored Symbolic link
View File

@ -0,0 +1 @@
../glob/dist/esm/bin.mjs

1
ihm_client/node_modules/.bin/he generated vendored Symbolic link
View File

@ -0,0 +1 @@
../he/bin/he

1
ihm_client/node_modules/.bin/html-minifier-terser generated vendored Symbolic link
View File

@ -0,0 +1 @@
../html-minifier-terser/cli.js

1
ihm_client/node_modules/.bin/import-local-fixture generated vendored Symbolic link
View File

@ -0,0 +1 @@
../import-local/fixtures/cli.js

1
ihm_client/node_modules/.bin/is-docker generated vendored Symbolic link
View File

@ -0,0 +1 @@
../is-docker/cli.js

1
ihm_client/node_modules/.bin/is-inside-container generated vendored Symbolic link
View File

@ -0,0 +1 @@
../is-inside-container/cli.js

1
ihm_client/node_modules/.bin/jake generated vendored Symbolic link
View File

@ -0,0 +1 @@
../jake/bin/cli.js

1
ihm_client/node_modules/.bin/jsesc generated vendored Symbolic link
View File

@ -0,0 +1 @@
../jsesc/bin/jsesc

1
ihm_client/node_modules/.bin/json5 generated vendored Symbolic link
View File

@ -0,0 +1 @@
../json5/lib/cli.js

1
ihm_client/node_modules/.bin/mime generated vendored Symbolic link
View File

@ -0,0 +1 @@
../mime/cli.js

Some files were not shown because too many files have changed in this diff Show More