Using the https://vanjs.org/ to render html and javascript client build for gun.js
Features:
- server minimalist
- node js
- user ui
- login
- register
- forgot
- change passphrase
- forgot hint
Using the https://vanjs.org/ to render html and javascript client build for gun.js
Features:
{ | |
"name": "node_van_js", | |
"version": "1.0.0", | |
"description": "", | |
"main": "server.js", | |
"type": "module", | |
"scripts": { | |
"start": "node server.js", | |
"dev": "node server.js", | |
"dev0": "nodemon server.js" | |
}, | |
"keywords": [], | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"gun": "^0.2020.1239", | |
"mini-van-plate": "^0.4.0" | |
} | |
} |
// https://stackoverflow.com/questions/16333790/node-js-quick-file-server-static-files-over-http | |
import * as url from 'url'; | |
//import fs from 'fs'; | |
import fs, { existsSync } from 'node:fs'; | |
//import fs from 'node:fs'; | |
import path from 'node:path'; | |
import http from 'http'; | |
import Gun from 'gun'; | |
import van from "mini-van-plate/van-plate"; | |
const {a, div, body, script, button, input, li, p, ul} = van.tags | |
const __filename = url.fileURLToPath(import.meta.url); | |
const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); | |
//console.log(Gun.serve(__dirname)); | |
function web_hander(req, res){ | |
//console.log(`${req.method} ${req.url}`); | |
// parse URL | |
const parsedUrl = url.parse(req.url); | |
// extract URL path | |
let pathname = `.${parsedUrl.pathname}`; | |
// based on the URL path, extract the file extension. e.g. .js, .doc, ... | |
const ext = path.parse(pathname).ext; | |
// maps file extension to MIME typere | |
const map = { | |
'.ico': 'image/x-icon', | |
'.html': 'text/html', | |
'.js': 'text/javascript', | |
'.json': 'application/json', | |
'.css': 'text/css', | |
'.png': 'image/png', | |
'.jpg': 'image/jpeg', | |
'.wav': 'audio/wav', | |
'.mp3': 'audio/mpeg', | |
'.svg': 'image/svg+xml', | |
'.pdf': 'application/pdf', | |
'.doc': 'application/msword' | |
}; | |
//existsSync(pathname, function (exist) { | |
let fileExist = existsSync(pathname); | |
if (fileExist){ | |
if(!fileExist) { | |
// if the file is not found, return 404 | |
res.statusCode = 404; | |
res.end(`File ${pathname} not found!`); | |
return; | |
} | |
// if is a directory search for index file matching the extension | |
if (fs.statSync(pathname).isDirectory()) pathname += '/index' + ext; | |
// read file from file system | |
fs.readFile(pathname, function(err, data){ | |
if(err){ | |
res.statusCode = 500; | |
res.end(`Error getting the file: ${err}.`); | |
} else { | |
// if the file is found, set Content-type and send data | |
res.setHeader('Content-type', map[ext] || 'text/plain' ); | |
res.end(data); | |
} | |
}); | |
} | |
if(req.url == "/"){ | |
res.writeHead(200, {'Content-Type': 'text/html'}); | |
//res.write('Hello World!'); | |
//res.end(); | |
res.end(van.html( | |
body( | |
//p("Your user-agent is: ", req.headers["user-agent"] ?? "Unknown"), | |
//p("Hello"), | |
//ul( | |
//li("World"), | |
//li(a({href: "https://vanjs.org/"}, "VanJS")), | |
//), | |
script({type:"module",src:"/user.js"}), | |
), | |
)); | |
} | |
} | |
function main(){ | |
let PORT = 3000; | |
//const server = http.createServer(Gun.serve(__dirname)); | |
const server = http.createServer(web_hander); | |
server.listen(PORT); | |
var gun = Gun({web: server}); | |
gun.on("hi", peer => { | |
//peer connect | |
//console.log('connect peer to',peer); | |
console.log("peer connect!"); | |
}); | |
gun.on("bye", peer => { | |
// peer disconnect | |
//console.log('disconnected from', peer); | |
console.log("disconnected from peer!"); | |
}); | |
console.log(`node server... http://localhost:${PORT}`); | |
console.log('Relay peer started on port ' + PORT + ' with /gun'); | |
} | |
main(); |
import van from "https://cdn.jsdelivr.net/gh/vanjs-org/van/public/van-1.2.0.min.js"; | |
//import Gun from "https://cdn.jsdelivr.net/npm/gun/gun.js"; | |
import GUN from "https://cdn.skypack.dev/gun"; | |
import SEA from "https://cdn.skypack.dev/gun/sea"; | |
const {span, br, div,label, body, script, button, input, li, p, ul, a} = van.tags; | |
const gun = GUN(location.origin+"/gun"); | |
//make sure it connected | |
gun.on("hi", peer => { | |
//peer connect | |
//console.log('connect peer to',peer); | |
console.log("peer connect!"); | |
}); | |
gun.on("bye", peer => { | |
// peer disconnect | |
//console.log('disconnected from', peer); | |
console.log("disconnected from peer!"); | |
}); | |
//=============================================== | |
// ACCESS PAGE | |
//=============================================== | |
const LoginEL = () =>{ | |
const alias = van.state('test'); | |
const passphrase = van.state('12345678'); | |
function click_login(){ | |
const user = gun.user(); | |
user.auth(alias.val, passphrase.val, (ack)=>{ | |
if(ack.err){ | |
console.log("ERROR: ", ack.err); | |
return; | |
} | |
//console.log(ack) | |
document.getElementById("login").remove(); | |
van.add(document.body, HomeEL()) | |
}); | |
} | |
function click_register(){ | |
console.log("Register") | |
const user = gun.user(); | |
//user.create(alias, pass, cb, opt) | |
user.create(alias.val, passphrase.val,(ack)=>{ | |
if(ack.err){ | |
console.log("ERROR: ", ack.err); | |
return; | |
} | |
console.log(ack) | |
}) | |
} | |
function click_forgot(){ | |
document.getElementById("login").remove(); | |
van.add(document.body, ForgotEL()) | |
} | |
return div( | |
{id:"login"}, | |
label('Alias:'), | |
input({placeholder:"User Name",value:alias, oninput: e=>alias.val = e.target.value}), | |
label('Passphrase:'), | |
input({placeholder:"Password / Passphrase",value:passphrase, oninput: e=>passphrase.val = e.target.value}), | |
button({onclick:()=>click_login()},'Login'), | |
button({onclick:()=>click_register()},'Register'), | |
button({onclick:()=>click_forgot()},'Forgot'), | |
) | |
}; | |
const ForgotEL = ()=>{ | |
const alias = van.state('test'); | |
const question1 = van.state(''); | |
const question2 = van.state(''); | |
const hint = van.state(''); | |
async function click_get_hint(){ | |
console.log(gun); | |
console.log(alias.val); | |
console.log('alias/'+alias.val) | |
let graph = await gun.get('~@'+alias.val) | |
console.log(graph) | |
let publickey; | |
for(let obj in graph){//object | |
//console.log(obj); | |
//property name for public key | |
if(SEA.opt.pub(obj)){//check if alias pub key | |
publickey = SEA.opt.pub(obj); | |
break; | |
} | |
} | |
if(!publickey){ | |
console.log("PUB KEY NULL!"); | |
return; | |
} | |
console.log("Alias Pub: ", publickey); | |
let to = gun.user(publickey);//get user alias graph | |
let _hint = await to.get('hint').then();//get encrypt hint key graph | |
let dec = await Gun.SEA.work(question1.val,question2.val);//get fquestion1 and fquestion2 string to mix key | |
_hint = await Gun.SEA.decrypt(_hint,dec);//get hint and key decrypt message | |
if(_hint){ | |
hint.val = _hint; | |
}else{ | |
console.log("FAIL HINT") | |
} | |
} | |
function click_back(){ | |
document.getElementById("forgot").remove(); | |
van.add(document.body, LoginEL()) | |
} | |
return div( | |
{id:'forgot'}, | |
label('Alias:'), | |
input({placeholder:"User Name",value:alias, oninput: e=>alias.val = e.target.value}), | |
br(), | |
label('Question #1:'), | |
input({placeholder:"Question No.1",value:question1, oninput: e=>question1.val = e.target.value}), | |
br(), | |
label('Question #2:'), | |
input({placeholder:"Question No.2",value:question2, oninput: e=>question2.val = e.target.value}), | |
br(), | |
label('Hint:'), | |
input({placeholder:"Hint",value:hint, oninput: e=>hint.val = e.target.value}), | |
br(), | |
button({onclick:()=>click_get_hint()},'Get Hint'), | |
button({onclick:()=>click_back()},'Back'), | |
) | |
} | |
//document.body.appendChild(LoginEL()); | |
van.add(document.body, LoginEL()) | |
//=============================================== | |
// HOME PAGE | |
//=============================================== | |
const HomeEL = () =>{ | |
const view = van.state('account'); | |
function click_logout(){ | |
const user = gun.user(); | |
user.leave(); | |
document.getElementById("home").remove(); | |
van.add(document.body, LoginEL()) | |
} | |
return div( | |
{id:"home"}, | |
div( | |
button({onclick:()=>view.val='account'},'Account'), | |
button({onclick:()=>view.val='message'},'Message'), | |
button({onclick:()=>view.val='chat'},'Chat'), | |
button({onclick:()=>click_logout()},'Logout'), | |
PubDisplayEL() | |
), | |
van.derive(()=>{ | |
if(view.val == 'account'){ | |
return AccountEL(); | |
} | |
if(view.val == 'message'){ | |
return div( | |
label('Message') | |
) | |
} | |
if(view.val == 'chat'){ | |
return div( | |
label('Chat') | |
) | |
} | |
}) | |
); | |
}; | |
const AccountEL = () =>{ | |
const view = van.state('profile'); | |
return div( | |
{id:'account'}, | |
div( | |
button({onclick:()=>view.val='profile'},'Profile'), | |
button({onclick:()=>view.val='changepassphrase'},'Change Passphrase'), | |
button({onclick:()=>view.val='changequestions'},'Change Questions'), | |
button({onclick:()=>view.val='certs'},'Certs'), | |
), | |
van.derive(()=>{ | |
if(view.val == 'profile'){ | |
return ProfileEL() | |
} | |
if(view.val == 'changepassphrase'){ | |
return ChnagePassphraseEL() | |
} | |
if(view.val == 'changequestions'){ | |
return ChnageQuestionsEL() | |
} | |
if(view.val == 'certs'){ | |
return CertsEL() | |
} | |
}), | |
) | |
} | |
const ProfileEL = () =>{ | |
const alias = van.state('Guest'); | |
const pub = van.state('Public Key'); | |
const user = gun.user(); | |
//console.log(user); | |
if(user.is.alias){ | |
alias.val = user.is.alias; | |
pub.val = user.is.pub; | |
} | |
return div({id:'profile'}, | |
label('Profile'), | |
br(), | |
label('Alias:'+alias.val), | |
br(), | |
label('Pub: '+pub.val), | |
) | |
}; | |
const PubDisplayEL = ()=>{ | |
const alias = van.state('Guest'); | |
const pub = van.state('Public Key'); | |
const user = gun.user(); | |
if(user?.is?.alias){ | |
alias.val = user.is.alias; | |
pub.val = user.is.pub; | |
} | |
function click_copy(){ | |
navigator.clipboard.writeText(pub.val); | |
//alert("Copied the text: " + pub.val); | |
} | |
return span( | |
label(' Alias:'), | |
label('[ '+alias.val+' ]'), | |
label({onclick:()=>click_copy()},' [Pub Key Copy]: '), | |
input({value:pub.val}) | |
) | |
} | |
const ChnagePassphraseEL = () =>{ | |
const oldPassphrase = van.state(''); | |
const newPassphrase = van.state(''); | |
function click_apply(){ | |
//console.log(oldPassphrase.val + " > " + newPassphrase.val); | |
const user = gun.user(); | |
user.auth(user.is.alias,oldPassphrase.val,(ack)=>{ | |
if(ack.err){ | |
console.log("ERROR: ",ack.err) | |
return; | |
} | |
console.log(ack); | |
//console.log("PASSWORD CHANGE...") | |
},{change:newPassphrase.val}) | |
} | |
return div({id:'changepassphrase'}, | |
label('Change Passphrase'), | |
br(), | |
label('Old passphrase:'), | |
input({value:oldPassphrase, oninput:e=>oldPassphrase.val=e.target.value}), | |
br(), | |
label('New passphrase:'), | |
input({value:newPassphrase, oninput:e=>newPassphrase.val=e.target.value}), | |
button({onclick:()=>click_apply()},'Apply'), | |
) | |
}; | |
const ChnageQuestionsEL = () =>{ | |
const question1 = van.state(''); | |
const question2 = van.state(''); | |
const hint = van.state(''); | |
async function click_apply_hint(){ | |
//console.log(oldPassphrase.val + " > " + newPassphrase.val); | |
const user = gun.user(); | |
//console.log(user); | |
const pair = user._.sea; | |
let enc = await SEA.encrypt(question1.val, pair); | |
//console.log("question1:",enc); | |
user.get('question1').put(enc); | |
let enc1 = await SEA.encrypt(question2.val, pair); | |
//console.log("question2:",enc1); | |
user.get('question2').put(enc1); | |
let enc2 = await SEA.work(question1.val, question2.val); | |
let data = await SEA.encrypt(hint.val, enc2); | |
//console.log("HINT:",data) | |
user.get('hint').put(data); | |
} | |
async function click_get_hint(){ | |
//console.log(oldPassphrase.val + " > " + newPassphrase.val); | |
const user = gun.user(); | |
const pair = user._.sea; | |
let q1 = await user.get('question1').then(); | |
//console.log("q1: ", q1); | |
q1 = await SEA.decrypt(q1, pair); | |
//console.log('q1: ', q1); | |
question1.val = q1; | |
let q2 = await user.get('question2').then(); | |
//console.log("q2: ", q2); | |
q2 = await SEA.decrypt(q2, pair); | |
question2.val = q2; | |
//console.log('q2: ', q2); | |
let sec = await SEA.work(q1, q2); | |
let hint0 = await user.get('hint').then(); | |
//console.log("hint: ", hint); | |
hint0 = await SEA.decrypt(hint0, sec); | |
//console.log(hint0) | |
hint.val = hint0; | |
} | |
return div({id:'changequestions'}, | |
label('Change Questions'), | |
br(), | |
label('Question #1:'), | |
input({value:question1, oninput:e=>question1.val=e.target.value}), | |
br(), | |
label('Question #2:'), | |
input({value:question2, oninput:e=>question2.val=e.target.value}), | |
br(), | |
label('Hint:'), | |
input({value:hint, oninput:e=>hint.val=e.target.value}), | |
button({onclick:()=>click_get_hint()},'Get'), | |
button({onclick:()=>click_apply_hint()},'Apply'), | |
) | |
}; | |
const CertsEL = () =>{ | |
return div({id:'crets'}, | |
label('Crets'), | |
) | |
}; |