IPFS FileUpload & G1lib crypto try

This commit is contained in:
fred 2022-11-27 18:08:44 +01:00
parent 3699d5bfff
commit a53223f887
8 changed files with 536 additions and 275 deletions

43
tools/ipfs_P2P_forward.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
################################################################################
# Author: Fred (support@qo-op.com)
# Version: 0.2
# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/)
################################################################################
# Activate SUPPORT MODE: open ssh over IPFS
MY_PATH="`dirname \"$0\"`" # relative
MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized
########################################################################
YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running"
IPFSNODEID=$(cat ~/.ipfs/config | jq -r .Identity.PeerID) || er+=" ipfs id problem"
[[ "$YOU" == "" || "$IPFSNODEID" == "" ]] && echo "ERROR : $er " && exit 1
########################################################################
# Make Station publish SSH port on "/x/ssh-$(hostname)"
zuid="$(hostname)"
if [[ $zuid ]]
then
if [[ ! $(cat ~/.ssh/authorized_keys | grep "fred@ONELOVE") ]]
then
echo "# ADD fred@ONELOVE to ~/.ssh/authorized_keys"
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFLHW8P88C/B7622yXzdAn1ZcTBfE1A4wMqajBwAoHwUVTOUaYfvkiSxbzb5H9dPTAXhQU6ZfuLa70kTo1m2b9TKH0tD6hR3RiKJ0NIjCHYEypcPGpLmHaZWnBKPq3IUU24qFVdUJxnTkDdFUszYMNoV4nqlXY/ZYdNpic8L1jPPyfOLLfPFkuSxagyQj4FGJq77UQE5j+skMJS3ISkazNTLqOCGLFJ5qtBC11BvQaCJ4cQ2Ss7ejPYhpx16NLJfg9VtG4dv9ZebEIl2pf7niiQGSPrDMFWHuQcGAuHt/patr0BcvfvD3Gv+qNsVfAJCNZ2U5NHEMKIhgj1ilNPEw7 fred@ONELOVE" >> ~/.ssh/authorized_keys
fi
echo "Lanching /x/ssh-$zuid"
[[ ! $(ipfs p2p ls | grep "/x/ssh-$zuid") ]] && ipfs p2p listen /x/ssh-$zuid /ip4/127.0.0.1/tcp/22
# echo "echo \"ssh-$zuid local port please?\"; read lport; ipfs p2p forward /x/ssh-$zuid /ip4/127.0.0.1/tcp/$lport /p2p/$IPFSNODEID" >> ~/.zen/tmp/$IPFSNODEID/astroport/port
fi
ipfs p2p ls
## CONNECT WITH COMMAND
## ipfs cat /ipns/$IPFSNODEID/.$IPFSNODEID/x_ssh-$zuid.sh | bash
rm ~/.zen/tmp/$IPFSNODEID/x_ssh-$zuid.sh >/dev/null 2>&1
if [[ ! -f ~/.zen/tmp/$IPFSNODEID/x_ssh-$zuid.sh ]]; then
PORT=12345
[ ${PORT} -eq 12345 ] && PORT=$((PORT+${RANDOM:0:3})) || PORT=$((PORT-${RANDOM:0:3}))
echo "[[ ! \$(ipfs p2p ls | grep x/ssh-$zuid) ]] && ipfs --timeout=5s ping -n 1 /p2p/$IPFSNODEID && ipfs p2p forward /x/ssh-$zuid /ip4/127.0.0.1/tcp/$PORT /p2p/$IPFSNODEID && ssh $USER@127.0.0.1 -p $PORT" > ~/.zen/tmp/$IPFSNODEID/x_ssh-$zuid.sh
fi
cat ~/.zen/tmp/$IPFSNODEID/x_ssh-$zuid.sh
## THIS PORT FORWARDING HUB COULD BE MADE MORE CONTROLABLE USING FRIENDSHIP LEVEL & IPFS BALISES

92
www/crypto.js Normal file
View File

@ -0,0 +1,92 @@
export {b58,b16, saltPass2seed, seed2keyPair,idSecPass2rawAll, raw2b58, idSecPass2cleanKeys}
import {nacl} from "./vendors/nacl.js";
async function idSecPass2rawAll(idSec,pass) {
const rawSeed = await saltPass2seed(idSec,pass);
const keyPair = seed2keyPair(rawSeed);
return {
seed:rawSeed,
publicKey:keyPair.publicKey,
secretKey:keyPair.secretKey
}
}
function raw2b58(raws){
const res = {};
for(let r in raws) res[r] = b58.encode(raws[r]);
return res;
}
async function idSecPass2cleanKeys(idSec,pass){
const raw = await idSecPass2rawAll(idSec,pass);
return Object.assign(raw2b58(raw),{idSec,password:pass});
}
function seed2keyPair(seed){
return nacl.sign.keyPair.fromSeed(seed);
}
import scrypt from "./vendors/scrypt.js";
async function saltPass2seed(idSec,pass) {
const options = {
logN: 12,
r: 16,
p: 1,
//dkLen: 32,
encoding: 'binary'
};
return await scrypt(pass.normalize('NFKC'), idSec.normalize('NFKC'), options);
}
//inspired by bs58 and base-x module
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const b58 = basex(ALPHABET);
const b16 = basex('0123456789abcdef');
function basex (ALPHABET) {
const ALPHABET_MAP = {};
const BASE = ALPHABET.length;
const LEADER = ALPHABET.charAt(0);
// pre-compute lookup table
for (let z = 0; z < ALPHABET.length; z++) {
let x = ALPHABET.charAt(z);
if (ALPHABET_MAP[x] !== undefined) throw new TypeError(x + ' is ambiguous');
ALPHABET_MAP[x] = z;
}
function encode (source) {
if (source.length === 0) return '';
const digits = [0];
for (let i = 0; i < source.length; ++i) {
let carry = source[i];
for (let j = 0; j < digits.length; ++j) {
carry += digits[j] << 8;
digits[j] = carry % BASE;
carry = (carry / BASE) | 0;
}
while (carry > 0) { digits.push(carry % BASE); carry = (carry / BASE) | 0; }
}
let string = '';
for (let k = 0; source[k] === 0 && k < source.length - 1; ++k) string += LEADER; // deal with leading zeros
for (let q = digits.length - 1; q >= 0; --q) string += ALPHABET[digits[q]]; // convert digits to a string
return string;
}
function decodeUnsafe (string) {
if (typeof string !== 'string') throw new TypeError('Expected String');
if (string.length === 0) return new Uint8Array(0);
const bytes = [0];
for (let i = 0; i < string.length; i++) {
const value = ALPHABET_MAP[string[i]];
if (value === undefined) return ;
let carry = value;
for (let j = 0; j < bytes.length; ++j) {
carry += bytes[j] * BASE;
bytes[j] = carry & 0xff;
carry >>= 8;
}
while (carry > 0) { bytes.push(carry & 0xff); carry >>= 8; }
}
for (let k = 0; string[k] === LEADER && k < string.length - 1; ++k) bytes.push(0); // deal with leading zeros
return new Uint8Array(bytes.reverse());
}
function decode (string) {
const buffer = decodeUnsafe(string);
if (buffer) return buffer;
throw new Error('Non-base' + BASE + ' character')
}
return { encode, decodeUnsafe, decode }
}

1
www/crypto.mjs Normal file

File diff suppressed because one or more lines are too long

70
www/dunikey.html Normal file
View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>idSec+password or seed or keys => file.dunikey</title>
<link rel="icon" type="image/png" sizes="16x16" href="img/favicon.png">
<link rel="canonical" href="https://gsper.duniter.io/dunikey.html"/>
<meta property="og:type" content="website" />
<meta property="og:locale" content="fr_FR" />
<meta property="og:title" content="idSec+password or seed or keys => file.dunikey" />
<meta property="og:site_name" content="gsper.duniter.io" />
<meta property="og:url" content="https://gsper.duniter.io/dunikey.html" />
<meta property="og:image" content="https://gsper.duniter.io/img/gsper-logo.png" />
<meta property="og:description" content="Converti vos secrets d'authentification pour générer un fichier d'authentification file.dunikey" />
<style>
* {font-family: sans-serif; box-sizing: border-box; margin: 0; padding: 0}
h1{font-weight: normal; width: 100%; text-align: center; line-height: 70px;}
fieldset {margin: 10px;}
.logic {margin-left: 50px;}
input{border: 0; padding: 5px;width: 100%; line-height: 60px; font-size: 16px;}
button {width: 100%; height: 32px}
.gift {position: absolute; top: 10px; left: 10px;}
.gift a{text-decoration-color: rgba(0%,0%,100%,0.2); color: rgba(0%,0%,100%,0.7);}
</style>
</head>
<body>
<h1>DunikeyMaker</h1>
<fieldset>
<fieldset>
<input id="idSec" type="password" placeholder="Identifiant secret"/>
<input id="pass" type="password" placeholder="Mot de passe"/>
</fieldset>
<div class="logic">ou</div>
<fieldset>
<input id="seed" type="text" placeholder="Seed"/>
</fieldset>
<div class="logic">ou</div>
<fieldset>
<input id="secretkey" type="text" placeholder="Clef privée (ou secrète)"/>
<input id="pubkey" type="text" placeholder="Clef publique"/>
</fieldset>
</fieldset>
<div class="logic">et</div>
<fieldset>
<input id="filename" type="text" placeholder="Nom du fichier à générer"/>
<button id="compute">Générer le fichier .dunikey.yml</button>
</fieldset>
<div class="gift">
<a href="https://cesium.g1.1000i100.fr/#/app/wot/2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT/1000i100" target="_blank">Ǧ'spère vous avoir été utile.<br/>
Soutenez mon travail avec un don !<br/>
👤 : 1000i100 🔑 : 2sZF6j2P
</a>
</div>
<footer>
<a href="https://git.duniter.org/tools/gsper" id="sourceRibbon" title="Fork me !">
<span>AGPL-3.0</span>
<span>Logiciel Libre</span>
<span>Remonter aux sources</span>
<style>
#sourceRibbon {position: fixed; top: 15px; right: -110px; z-index: 500; transform: rotate(45deg); text-align: center;width: 300px; box-shadow: 0 0 5px #000; text-decoration: none;}
#sourceRibbon span {background-color: #0A0; color: white; font-family: sans-serif; display: block; padding: 1px 30px; font-size: 9pt; margin: -1px; text-shadow: 0 0 3px rgba(0,0,0,.5);}
#sourceRibbon span + span {background-color: #06A; font-size: 11pt;}
#sourceRibbon span + span + span {background-color: #E80; font-size: 10pt;}
</style>
</a>
</footer>
<script type="module" src="dunikey.js"></script>
</body>
</html>

54
www/dunikey.js Normal file
View File

@ -0,0 +1,54 @@
import * as dKey from "./crypto.js";
addEventsListeners([
document.getElementById("idSec"),
document.getElementById("pass"),
], "keyup change",
async function () {
const idSec = document.getElementById("idSec").value;
const pass = document.getElementById("pass").value;
const keys = await dKey.idSecPass2cleanKeys(idSec, pass);
document.getElementById("seed").value = keys.seed;
document.getElementById("secretkey").value = keys.secretKey;
document.getElementById("pubkey").value = keys.publicKey;
});
addEventsListeners([
document.getElementById("seed"),
], "keyup change",
async function () {
let rawSeed;
const seed = document.getElementById("seed").value;
console.log(seed.length);
if (seed.length === 64) rawSeed = dKey.b16.decode(seed);
else rawSeed = dKey.b58.decode(seed);
const keys = await dKey.seed2keyPair(rawSeed);
document.getElementById("secretkey").value = dKey.b58.encode(keys.secretKey);
document.getElementById("pubkey").value = dKey.b58.encode(keys.publicKey);
});
addEventsListeners([
document.getElementById("compute"),
], "click",
async function () {
const filename = document.getElementById("filename").value;
const secretkey = document.getElementById("secretkey").value;
const pubkey = document.getElementById("pubkey").value;
exportAsFile(filename, `pub: ${pubkey}\nsec: ${secretkey}`);
});
function exportAsFile(fileName, data) {
const a = document.createElement('a');
a.setAttribute('download', fileName + '.dunikey.yml');
a.setAttribute('href', 'data:text/yaml;charset=utf-8,' + encodeURIComponent(data));
clickOn(a);
}
function clickOn(element) {
const event = new MouseEvent('click', {'view': window, 'bubbles': true, 'cancelable': true});
element.dispatchEvent(event);
}
function addEventsListeners(triggerNodes, events, functions) {
if (!triggerNodes.length) triggerNodes = [triggerNodes];
if (typeof events !== "object") events = events.split(" ");
if (typeof functions !== "object") functions = [functions];
for (let n of triggerNodes) events.forEach(e => functions.forEach(f => n.addEventListener(e, f)));
}

BIN
www/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,275 +0,0 @@
<html class=" rrghhqet idc0_344 cadefrffwk ftnryedt"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Upload files to Astroport</title>
<style>
a, a:hover, a:visited, a:active {
color: #fff;
text-decoration: none;
}
body {
border: 0;
margin: 0;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
background-color: #041d2f;
color: #fff;
display: flex;
flex-direction: column;
min-height: 100vh;
}
input[type=text] {
width: 500px;
height: 50px;
text-align: center;
margin: 5px;
}
input[type=file] {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
input[type=file] + label {
display: inline-block;
font-size: 1.25em;
font-weight: 700;
color: #333;
background-color: #fff;
border-radius: 5px;
padding: 20px;
}
input[type=file]:focus + label,
input[type=file] + label:hover {
background-color: #fff;
}
label {
cursor: pointer;
}
button {
width: 500px;
height: 50px;
text-align: center;
margin: 5px;
cursor: pointer;
}
.content {
flex: 1;
margin: 10px;
text-align: center;
}
.informations {
font-size: 24px;
}
.image {
display: block;
margin: 0 auto;
height: 300px;
margin-bottom: 40px;
}
.wrapWithDirectoryContainer {
font-size: 16px;
}
.footer {
text-align: center;
margin: 10px;
}
</style>
<script src="ipfs.js"></script>
</head>
<body>
<div class="content">
<img src="./logo-astroport.png" class="image">
<div id="informations" class="informations">
Téléverser un fichier vers Astroport
</div>
<br>
<br>
<form>
<input id="file" type="file">
<label for="file">Choose a file</label>
<br>
<br>
<div class="wrapWithDirectoryContainer">
<input id="wrapWithDirectory" type="checkbox">
<label for="wrapWithDirectory">Wrap with a directory (recommended)</label>
</div>
</form>
</div>
<script>
function cacheIt (appName, hash, nodeId = '') {
var gatewayProtocol = 'http';
var gatewayDomain = 'ipfs.localhost';
var gatewayPort = '1234';
var salt = 'totodu56';
var pepper = 'totodu56';
var query = 'salt='+ salt
query += '&pepper='+ pepper
query += '&' + appName
query += '&nodeid=' + nodeId
query += '&dataid=' + hash;
var fullURL = 'http://'+ gatewayDomain +':'+ gatewayPort + '/?' + query;
console.log(fullURL)
const controller = new AbortController()
const timeoutId = setTimeout( () => {
controller.abort()
}, 15000)
var fetchOpts = {
method: 'GET',
headers: {
'Accept': 'text/html'
// ,'Content-Type': 'text/html'
// // ,'Access-Control-Allow-Origin': '*',
// ,'Origin': 'la-bureautique'
// ,'Referrer-Policy': 'unsafe-url'
// ,'Redirect': 'manual'
},
signal: controller.signal
}
fetch(fullURL, fetchOpts)
.then(reponse => {
return reponse.text()
})
.then(html => {
// console.log(html)
var regex = /url='([^']+)/i;
var redirectURL = html.match(regex)[1]
return redirectURL
})
.then(url => {
console.log(url)
})
// JSON.stringify(json)
}
async function ipfs_init() {
const node = await window.IpfsCore.create(
{
config: {
Addresses: {
Swarm: [
'/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
'/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
]
}
},
"Discovery": {
"MDNS": {
"Enabled": false,
"Interval": 10
},
"webRTCStar": {
"Enabled": true
}
},
"Bootstrap": [
"/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic",
"/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6"
]
}
);
let nodeId = await node.id();
console.log('node id :', nodeId)
nodeId = nodeId.id
console.log(nodeId)
const version = await node.version();
console.log('Version:', version.version);
async function ipfs_upload(file, wrapWithDirectory) {
const fileAdded = await node.add(
{
path: file.name,
content: file
},
{
wrapWithDirectory,
progress: bytesAdded => document.getElementById('informations').innerHTML = `Sending... ${Math.round((bytesAdded / file.size) * 100)}%`
}
);
const cid = fileAdded.cid.toString();
var salt = 'totodu56';
var pepper = 'totodu56';
document.getElementById('informations').innerHTML = [
'Prêt à enregistrer !',
'',
'Son Hash (CID) est :',
`<a href="http://libra.copylaradio.com:1234/?salt=${salt}&pepper=${pepper}&testcraft=on&nodeid=${nodeId}&dataid=${cid}" target="reboot">${cid}</a>`,
'(cliquez pour demander enregistrement)'
].join('<br />');
cacheIt('testcraft=on', cid, nodeId)
}
document.getElementById('file').disabled = false;
document.getElementById('file').addEventListener('change', async event => {
event.preventDefault();
document.getElementById('file').disabled = true;
const fileInput = document.getElementById('file');
if (!fileInput.files.length) {
alert('You have to select a file first');
} else {
const wrapWithDirectory = document.getElementById('wrapWithDirectory').checked;
await ipfs_upload(fileInput.files[0], wrapWithDirectory)
.catch(error => alert(error.toString()));
}
document.getElementById('file').disabled = false;
});
}
ipfs_init();
</script>
</body></html>

1
www/index.html Symbolic link
View File

@ -0,0 +1 @@
dunikey.html

View File

@ -0,0 +1,275 @@
<html class=" rrghhqet idc0_344 cadefrffwk ftnryedt"><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Upload files to Astroport</title>
<style>
a, a:hover, a:visited, a:active {
color: #fff;
text-decoration: none;
}
body {
border: 0;
margin: 0;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
background-color: #041d2f;
color: #fff;
display: flex;
flex-direction: column;
min-height: 100vh;
}
input[type=text] {
width: 500px;
height: 50px;
text-align: center;
margin: 5px;
}
input[type=file] {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
input[type=file] + label {
display: inline-block;
font-size: 1.25em;
font-weight: 700;
color: #333;
background-color: #fff;
border-radius: 5px;
padding: 20px;
}
input[type=file]:focus + label,
input[type=file] + label:hover {
background-color: #fff;
}
label {
cursor: pointer;
}
button {
width: 500px;
height: 50px;
text-align: center;
margin: 5px;
cursor: pointer;
}
.content {
flex: 1;
margin: 10px;
text-align: center;
}
.informations {
font-size: 24px;
}
.image {
display: block;
margin: 0 auto;
height: 300px;
margin-bottom: 40px;
}
.wrapWithDirectoryContainer {
font-size: 16px;
}
.footer {
text-align: center;
margin: 10px;
}
</style>
<script src="ipfs.js"></script>
</head>
<body>
<div class="content">
<img src="./logo-astroport.png" class="image">
<div id="informations" class="informations">
Téléverser un fichier vers Astroport
</div>
<br>
<br>
<form>
<input id="file" type="file">
<label for="file">Choose a file</label>
<br>
<br>
<div class="wrapWithDirectoryContainer">
<input id="wrapWithDirectory" type="checkbox">
<label for="wrapWithDirectory">Wrap with a directory (recommended)</label>
</div>
</form>
</div>
<script>
function cacheIt (appName, hash, nodeId = '') {
var gatewayProtocol = 'http';
var gatewayDomain = 'ipfs.localhost';
var gatewayPort = '1234';
var salt = 'totodu56';
var pepper = 'totodu56';
var query = 'salt='+ salt
query += '&pepper='+ pepper
query += '&' + appName
query += '&nodeid=' + nodeId
query += '&dataid=' + hash;
var fullURL = 'http://'+ gatewayDomain +':'+ gatewayPort + '/?' + query;
console.log(fullURL)
const controller = new AbortController()
const timeoutId = setTimeout( () => {
controller.abort()
}, 15000)
var fetchOpts = {
method: 'GET',
headers: {
'Accept': 'text/html'
// ,'Content-Type': 'text/html'
// // ,'Access-Control-Allow-Origin': '*',
// ,'Origin': 'la-bureautique'
// ,'Referrer-Policy': 'unsafe-url'
// ,'Redirect': 'manual'
},
signal: controller.signal
}
fetch(fullURL, fetchOpts)
.then(reponse => {
return reponse.text()
})
.then(html => {
// console.log(html)
var regex = /url='([^']+)/i;
var redirectURL = html.match(regex)[1]
return redirectURL
})
.then(url => {
console.log(url)
})
// JSON.stringify(json)
}
async function ipfs_init() {
const node = await window.IpfsCore.create(
{
config: {
Addresses: {
Swarm: [
'/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
'/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
]
}
},
"Discovery": {
"MDNS": {
"Enabled": false,
"Interval": 10
},
"webRTCStar": {
"Enabled": true
}
},
"Bootstrap": [
"/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic",
"/dns4/wss1.bootstrap.libp2p.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6"
]
}
);
let nodeId = await node.id();
console.log('node id :', nodeId)
nodeId = nodeId.id
console.log(nodeId)
const version = await node.version();
console.log('Version:', version.version);
async function ipfs_upload(file, wrapWithDirectory) {
const fileAdded = await node.add(
{
path: file.name,
content: file
},
{
wrapWithDirectory,
progress: bytesAdded => document.getElementById('informations').innerHTML = `Sending... ${Math.round((bytesAdded / file.size) * 100)}%`
}
);
const cid = fileAdded.cid.toString();
var salt = 'totodu56';
var pepper = 'totodu56';
document.getElementById('informations').innerHTML = [
'Prêt à enregistrer !',
'',
'Son Hash (CID) est :',
`<a href="http://libra.copylaradio.com:1234/?salt=${salt}&pepper=${pepper}&testcraft=on&nodeid=${nodeId}&dataid=${cid}" target="reboot">${cid}</a>`,
'(cliquez pour demander enregistrement)'
].join('<br />');
cacheIt('testcraft=on', cid, nodeId)
}
document.getElementById('file').disabled = false;
document.getElementById('file').addEventListener('change', async event => {
event.preventDefault();
document.getElementById('file').disabled = true;
const fileInput = document.getElementById('file');
if (!fileInput.files.length) {
alert('You have to select a file first');
} else {
const wrapWithDirectory = document.getElementById('wrapWithDirectory').checked;
await ipfs_upload(fileInput.files[0], wrapWithDirectory)
.catch(error => alert(error.toString()));
}
document.getElementById('file').disabled = false;
});
}
ipfs_init();
</script>
</body></html>