chore: sync from 4NK_env superproject 2025-10-01T16:23:14Z
All checks were successful
Docker Build and Push (ext) / build (push) Successful in 1m23s

This commit is contained in:
4NK CI Bot 2025-10-01 16:23:14 +00:00
parent 8c11c59681
commit 1a62a955f5
62 changed files with 16997 additions and 86 deletions

108
.gitignore vendored
View File

@ -1,40 +1,82 @@
# Secrets et fichiers sensibles # 4NK Environment - Git Ignore
.env # ============================
.env.*
!.env.example
!.env.exemple
*.key
*.pem
secrets/
# Logs # Dossiers de sauvegarde des scripts
logs/ **/backup/
*.log **/*backup*
# Node.js **/.cargo/
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.cargo/
Cargo.lock
*/.cargo/
*/Cargo.lock
# Build
dist/
build/
# IDE
.vscode/ # Fichiers temporaires
.idea/ **/*.tmp*
*.swp **/*.temp*
*.swo **/*.log*
**/*.pid*
# Fichiers de configuration locale
**/*.env*
**/*.conf*
**/*.yaml*
**/*.yml*
**/*.ini*
**/*.json*
**/*.toml*
**/*.lock*
# Données et logs
**/*.logs*
**/*.data
*.db
*.sqlite
# Certificats et clés
**/*.key
**/*.pem
**/*.crt
**/*.p12
**/*.pfx
ssl/
certs/
# Docker
**/*.docker*
# Cache et build
**/*.node_modules/
**/*.dist/
**/*build/
**/*target/
**/*.*.o
**/*.so
**/*.dylib
# IDE et éditeurs
**/*.vscode/
**/*.idea/
**/*.swp
**/*.swo
**/*~
# OS # OS
.DS_Store **/*.DS_Store
Thumbs.db **/*Thumbs.db
**/*tmp*
# Temporary files # Git
tmp/ **/*.git/
*.tmp.cursor-server **/*.orig*
# Backup des projets existants
**/*backup*
**/*wallet*
**/*keys*
# Supervisor
supervisor-logs/
**/*node_modules*
**/*cursor*

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
dist/assets/bgd.webp vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

BIN
dist/assets/camera.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

34
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
dist/assets/qr_code.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

111
dist/authorized-client.html vendored Normal file
View File

@ -0,0 +1,111 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Redirection en cours…</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 2rem; color: #0a0a0a; }
.box { max-width: 720px; margin: 10vh auto; padding: 1.5rem; border: 1px solid #e5e7eb; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
.muted { color: #6b7280; font-size: .95rem; }
.error { color: #b91c1c; }
.ok { color: #065f46; }
code { background: #f3f4f6; padding: .2rem .35rem; border-radius: 6px; }
a { color: #006BE0; text-decoration: none; }
</style>
</head>
<body>
<div class="box">
<h1>Connexion IdNot</h1>
<p class="muted" id="status">Traitement du code d'autorisation…</p>
<pre class="muted" id="details" style="white-space: pre-wrap"></pre>
</div>
<script>
(function () {
function getQueryParam(name) {
const params = new URLSearchParams(location.search);
return params.get(name);
}
function setCookie(name, value, days) {
const expires = new Date(Date.now() + days * 864e5).toUTCString();
// Domaine implicite: dev4.4nkweb.com (hébergement de cette page)
document.cookie = name + '=' + encodeURIComponent(value) + '; Path=/; Expires=' + expires + '; SameSite=None; Secure';
}
async function main() {
const code = getQueryParam('code');
const statusEl = document.getElementById('status');
const detailsEl = document.getElementById('details');
if (!code) {
statusEl.textContent = 'Aucun code reçu dans la redirection IdNot.';
statusEl.className = 'error';
detailsEl.textContent = 'Paramètre attendu: ?code=…\nRetour à l\'espace: https://dev4.4nkweb.com/lecoffre/';
return;
}
try {
const resp = await fetch('https://dev4.4nkweb.com/api/v1/idnot/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Request-ID': 'bridge_' + Math.random().toString(36).slice(2)
},
body: JSON.stringify({ code })
});
const text = await resp.text();
let data;
try { data = JSON.parse(text); } catch (_) { data = null; }
if (!resp.ok) {
statusEl.textContent = 'Connexion refusée (' + resp.status + ').';
statusEl.className = 'error';
detailsEl.textContent = (data && data.error && data.error.message) ? data.error.message : text;
// Redirige néanmoins vers le front avec état d\'erreur afin d\'afficher un message utilisateur.
setTimeout(function(){ location.replace('https://dev4.4nkweb.com/lecoffre/authorized-bridge#error=' + encodeURIComponent(String(resp.status))); }, 600);
return;
}
// Attendu: { idNotUser, authToken }
if (!data || !data.authToken) {
statusEl.textContent = 'Réponse invalide du serveur.';
statusEl.className = 'error';
detailsEl.textContent = text;
setTimeout(function(){ location.replace('https://dev4.4nkweb.com/lecoffre/authorized-bridge#error=invalid_response'); }, 800);
return;
}
// Stocker le jeton pour le domaine dev4 (utilisé par le front)
setCookie('leCoffreAccessToken', data.authToken, 1);
statusEl.textContent = 'Connexion réussie, redirection…';
statusEl.className = 'ok';
detailsEl.textContent = '';
// Redirection vers le front avec token en hash en sauvegarde
location.replace('https://dev4.4nkweb.com/lecoffre/authorized-bridge#token=' + encodeURIComponent(data.authToken));
} catch (e) {
statusEl.textContent = 'Erreur réseau lors de la connexion.';
statusEl.className = 'error';
detailsEl.textContent = String(e && e.message || e);
setTimeout(function(){ location.replace('https://dev4.4nkweb.com/lecoffre/authorized-bridge#error=network'); }, 1000);
}
}
main();
})();
</script>
</body>
</html>

9139
dist/index.js vendored Normal file

File diff suppressed because it is too large Load Diff

1885
dist/sdk_client-6DvC0K4R.mjs vendored Normal file

File diff suppressed because one or more lines are too long

877
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
dist/style/account.css vendored Executable file

File diff suppressed because it is too large Load Diff

597
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
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
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
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,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;
}

56
dist/types/models/process.model.d.ts vendored Normal file
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"
}

60
dist/types/models/signature.models.d.ts vendored Normal file
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 };

98
dist/types/pages/account/account.d.ts vendored Normal file
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 { RoleDefinition } from '../../../pkg/sdk_client.js';
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>;

4
dist/types/pages/account/process.d.ts vendored Normal file
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;
}

4
dist/types/pages/home/home.d.ts vendored Normal file
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
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;

28
dist/types/services/modal.service.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
import { RoleDefinition } from '../../pkg/sdk_client.js';
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 {};

171
dist/types/services/service.d.ts vendored Normal file
View File

@ -0,0 +1,171 @@
import { ApiReturn, Device, Member, MerkleProofResult, Process, ProcessState, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client.js';
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(process: Process, stateId?: string | null): 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>;
/**
* Déclenche un transfert automatique de fonds du wallet mining vers le relay
*/
private triggerAutomaticFundsTransfer;
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(processId: string, spAddressList: string[]): 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, storages: string[], data: Blob, ttl: number | null): Promise<void>;
fetchValueFromStorage(hash: string): Promise<ArrayBuffer | null>;
getDiffByValueFromDb(hash: string): Promise<UserDiff | 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(url: string, key: string): Promise<boolean | null>;

18
dist/types/services/token.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
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>;
private getOrCreateSecret;
generateSessionToken(origin: string): Promise<TokenPair>;
validateToken(token: string, origin: string): Promise<boolean>;
refreshAccessToken(refreshToken: string, origin: string): Promise<string | null>;
}
export {};

1
dist/types/utils/document.utils.d.ts vendored Normal file
View File

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

4
dist/types/utils/html.utils.d.ts vendored Normal file
View File

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

12
dist/types/utils/messageMock.d.ts vendored Normal file
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 {};

5
dist/types/utils/service.utils.d.ts vendored Normal file
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
dist/types/websockets.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import { AnkFlag } from '.././pkg/sdk_client.js';
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,48 +0,0 @@
server {
listen 80;
server_name localhost;
# Redirection des requêtes HTTP vers Vite
location / {
proxy_pass http://localhost:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location /ws/ {
proxy_pass http://dev4.4nkweb.com: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_set_header X-NginX-Proxy true;
proxy_read_timeout 86400;
}
location /storage/ {
rewrite ^/storage(/.*)$ $1 break;
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location /api/ {
proxy_pass http://localhost: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;
}
}

View File

@ -1,5 +1,5 @@
import { ProcessState } from '../../../pkg/sdk_client.js'; import { ProcessState } from '../../../pkg/sdk_client.js';
import Services from '../../services/service'; import Services from '../../services/service.js';
interface State { interface State {
file: File | null; file: File | null;

View File

@ -1,5 +1,5 @@
import { ValidationRule, RoleDefinition } from '../../../pkg/sdk_client.js'; import { ValidationRule, RoleDefinition } from '../../../pkg/sdk_client.js';
import { showValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal'; import { showValidationRuleModal } from '../../components/validation-rule-modal/validation-rule-modal.js';
export function createKeyValueSection(title: string, id: string, isRoleSection = false) { export function createKeyValueSection(title: string, id: string, isRoleSection = false) {
const section = document.createElement('div'); const section = document.createElement('div');

View File

@ -1,5 +1,5 @@
import '../public/style/4nk.css'; import '../public/style/4nk.css';
import { initHeader } from '../src/components/header/header'; import { initHeader } from './components/header/header';
/*import { initChat } from '../src/pages/chat/chat';*/ /*import { initChat } from '../src/pages/chat/chat';*/
import Database from './services/database.service'; import Database from './services/database.service';
import Services from './services/service'; import Services from './services/service';

View File

@ -1,5 +1,5 @@
import { AnkFlag } from '.././pkg/sdk_client.js'; import { AnkFlag } from '../pkg/sdk_client.js';
import Services from './services/service'; import Services from './services/service.js';
let ws: WebSocket; let ws: WebSocket;
let messageQueue: string[] = []; let messageQueue: string[] = [];