parent
2f9a2d8668
commit
9c1165653b
@ -0,0 +1,24 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,88 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
// All rights reserved. |
||||
// Licensed under the Apache License, Version 2.0 |
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||
// at your option. All files in the project carrying such |
||||
// notice may not be copied, modified, or distributed except |
||||
// according to those terms. |
||||
--> |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>NextGraph Auth</title> |
||||
<style> |
||||
.splashing { |
||||
height: 95vh; |
||||
width:100%; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
.noshow { |
||||
display: none !important; |
||||
} |
||||
.nextgraph-app-auth-iframe { |
||||
visibility: hidden; |
||||
} |
||||
.nextgraph-app-auth-iframe.nextgraph-app-auth-iframe--active { |
||||
visibility: visible; |
||||
} |
||||
#banner { |
||||
padding-right: 36px !important; |
||||
width: 100%; position: fixed; left:0; top:0; min-height:36px; background-color: rgb(73, 114, 165); color: white; text-align:center ;z-index:10; padding:3px; font-size: 1.25rem; |
||||
line-height: 1.75rem; overflow-wrap: break-word; |
||||
} |
||||
#close-auth { |
||||
position: fixed; right:0; top:0; width: 36px; height: 36px; background-color: rgb(73, 114, 165);z-index:11; |
||||
cursor:pointer; |
||||
} |
||||
</style> |
||||
</head> |
||||
|
||||
<body> |
||||
<iframe id="nextgraph-app-auth-iframe" class="nextgraph-app-auth-iframe" scrolling="auto" frameborder="0" |
||||
style="position: fixed; left: 0; top: 0; height: 100%; width: 100%; overflow:auto;"> |
||||
</iframe> |
||||
<div id="banner"> |
||||
</div> |
||||
<div id="close-auth"> |
||||
<svg data-slot="icon" fill="none" stroke-width="1.5" stroke="white" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"></path> |
||||
</svg> |
||||
</div> |
||||
<script> |
||||
document.getElementById("close-auth").onclick = (e)=> { |
||||
window.ng_status_callback.write({status:"cancelled"}); |
||||
}; |
||||
</script> |
||||
<div id="splash" class="splashing"> |
||||
<div style="flex-direction: column;justify-content: center;color:#4972a5;width:100%;text-align:center;font-family: Inter, Avenir, Helvetica, Arial, sans-serif;"> |
||||
<svg |
||||
style="width:100px;height:100px;margin: 0 auto 20px ;display:flex;" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
viewBox="0 0 225 225" |
||||
> |
||||
<g> |
||||
<circle |
||||
r="106.98013" |
||||
cy="112.90476" |
||||
cx="109.88096" |
||||
style="fill:#ffffff;stroke:none;stroke-width:0.268375" /> |
||||
<path |
||||
d="M 98.343352,190.26108 C 80.403778,187.53354 65.011938,179.57839 52.608228,166.62327 38.602093,151.99448 31.178059,133.41381 31.178059,112.98841 c 0,-10.21889 1.700058,-19.44396 5.221234,-28.332119 4.28678,-10.820699 10.037295,-19.39063 18.535095,-27.62263 4.72982,-4.58187 6.60687,-6.10643 11.28099,-9.16256 11.89869,-7.779841 24.173884,-11.879991 38.095802,-12.724761 19.80437,-1.2017 39.11165,5.11306 54.60284,17.858751 1.50718,1.24006 2.72951,2.35934 2.71628,2.48729 -0.0132,0.12795 -3.85821,3.63443 -8.54442,7.79217 -4.6862,4.157729 -10.04724,8.96276 -11.91342,10.677819 -1.86617,1.715071 -3.54094,3.11831 -3.7217,3.11831 -0.18075,0 -1.39985,-0.745188 -2.70911,-1.655969 -7.53011,-5.23834 -15.99428,-7.82188 -25.62597,-7.82188 -12.731628,0 -23.249192,4.3379 -32.143882,13.257541 -6.39594,6.413868 -10.70387,14.555268 -12.50018,23.623578 -0.69099,3.48832 -0.68968,13.53072 0.002,17.00893 3.70508,18.62577 18.31886,33.10194 36.642322,36.29729 4.16439,0.72621 11.98099,0.71223 15.98975,-0.0286 14.03187,-2.59311 25.86047,-11.36806 32.26533,-23.93578 0.77379,-1.51834 1.26018,-2.88461 1.08086,-3.03616 -0.17934,-0.15156 -6.87448,-1.1779 -14.87813,-2.28078 -9.7795,-1.34758 -14.92353,-2.21379 -15.68471,-2.64117 -1.52067,-0.85379 -2.83611,-2.88806 -2.83611,-4.3859 0,-1.1732 2.02687,-15.86876 2.49085,-18.05962 0.29676,-1.40127 2.42559,-3.4934 3.84317,-3.77691 0.62227,-0.12445 8.82712,0.85555 18.28065,2.18348 9.43343,1.32511 17.26269,2.29453 17.39833,2.15427 0.13566,-0.14026 1.11808,-6.54833 2.18313,-14.24014 1.10778,-8.000208 2.20407,-14.60184 2.56177,-15.426229 0.34392,-0.792599 1.11019,-1.849131 1.70287,-2.34782 2.06321,-1.736079 3.1433,-1.785011 12.20439,-0.55291 9.63637,1.310309 10.70873,1.56224 12.28077,2.88503 1.64359,1.382979 2.2732,2.810909 2.25906,5.123309 -0.007,1.10173 -0.92172,8.29645 -2.03332,15.98826 -1.11158,7.69182 -1.97159,14.04091 -1.91113,14.1091 0.0605,0.0682 7.16644,1.11143 15.79109,2.31832 11.10566,1.55407 16.00827,2.38757 16.80223,2.85657 1.53015,0.90389 2.48023,2.64785 2.45017,4.49756 -0.0462,2.84349 -2.41252,18.12279 -2.97521,19.21089 -0.66164,1.27949 -2.60244,2.54696 -3.92109,2.56074 -0.51973,0.005 -7.87449,-0.95937 -16.34391,-2.144 -8.46944,-1.18464 -15.47588,-2.077 -15.56986,-1.98301 -0.094,0.094 -1.18792,7.34163 -2.43097,16.10589 -1.44004,10.15311 -2.49792,16.43621 -2.91556,17.31631 -0.72531,1.52848 -2.76261,3.06291 -4.53817,3.41802 -0.95688,0.19138 -10.90014,-0.92798 -13.59859,-1.53084 -0.5471,-0.12223 -1.89146,0.67252 -4.50941,2.66588 -11.2627,8.57562 -24.34195,13.90917 -38.35741,15.64164 -4.40038,0.54395 -15.72658,0.43298 -19.853658,-0.19451 z" |
||||
style="fill:#4972a5;fill-opacity:1;stroke:#4972a5;stroke-width:0.377976;stroke-opacity:1" /> |
||||
</g> |
||||
</svg> |
||||
<div> Loading ...</div> |
||||
</div> |
||||
</div> |
||||
<div id="app" class="noshow"> |
||||
</div> |
||||
<!-- # INSERT SCRIPT HERE --> |
||||
<script type="module" src="/src/main.ts"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,32 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"moduleResolution": "bundler", |
||||
"target": "ESNext", |
||||
"module": "ESNext", |
||||
/** |
||||
* svelte-preprocess cannot figure out whether you have |
||||
* a value or a type, so tell TypeScript to enforce using |
||||
* `import type` instead of `import` for Types. |
||||
*/ |
||||
"verbatimModuleSyntax": true, |
||||
"isolatedModules": true, |
||||
"resolveJsonModule": true, |
||||
/** |
||||
* To have warnings / errors of the Svelte compiler at the |
||||
* correct position, enable source maps by default. |
||||
*/ |
||||
"sourceMap": true, |
||||
"esModuleInterop": true, |
||||
"skipLibCheck": true, |
||||
/** |
||||
* Typecheck JS in `.svelte` and `.js` files by default. |
||||
* Disable this if you'd like to use dynamic types. |
||||
*/ |
||||
"checkJs": true |
||||
}, |
||||
/** |
||||
* Use global.d.ts instead of compilerOptions.types |
||||
* to avoid limiting type declarations. |
||||
*/ |
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] |
||||
} |
@ -0,0 +1,40 @@ |
||||
{ |
||||
"name": "@ng-org/ngnet-auth", |
||||
"private": true, |
||||
"version": "0.1.2", |
||||
"type": "module", |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "vite build --base=./ && shx rm -rf ./dist/assets", |
||||
"builddev": "cross-env NG_DEV=1 vite build --base=./ && shx rm -rf ./dist/assets && shx mkdir -p ../../../app/nextgraph/public_dev && cp ./dist/index.html ../../../app/nextrgaph/public_dev/auth.html", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"flowbite": "^1.6.5", |
||||
"flowbite-svelte": "^0.37.1", |
||||
"svelte-spa-router": "^3.3.0", |
||||
"@tailwindcss/typography": "^0.5.13", |
||||
"svelte-i18n": "^4.0.0", |
||||
"@ng-org/ui-common": "workspace:*", |
||||
"async-proxy": "^0.4.1", |
||||
"remote-web-streams": "^0.2.0" |
||||
}, |
||||
"devDependencies": { |
||||
"shx": "^0.3.4", |
||||
"cross-env": "^7.0.3", |
||||
"node-gzip": "^1.1.2", |
||||
"@sveltejs/vite-plugin-svelte": "^2.0.4", |
||||
"svelte": "^3.58.0", |
||||
"vite": "^4.3.9", |
||||
"postcss": "^8.4.23", |
||||
"postcss-load-config": "^4.0.1", |
||||
"svelte-heros-v2": "^0.10.12", |
||||
"svelte-preprocess": "^5.0.3", |
||||
"tailwindcss": "^3.3.1", |
||||
"autoprefixer": "^10.4.14", |
||||
"vite-plugin-svelte-svg": "^2.2.1", |
||||
"vite-plugin-top-level-await": "1.3.1", |
||||
"vite-plugin-singlefile": "0.13.5", |
||||
"vite-plugin-wasm": "3.2.2" |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@ |
||||
const tailwindcss = require("tailwindcss"); |
||||
const autoprefixer = require("autoprefixer"); |
||||
|
||||
const config = { |
||||
plugins: [ |
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind, |
||||
tailwindcss(), |
||||
//But others, like autoprefixer, need to run after, |
||||
autoprefixer, |
||||
], |
||||
}; |
||||
|
||||
module.exports = config; |
@ -0,0 +1,32 @@ |
||||
var crypto = require('crypto') |
||||
, fs = require('fs') |
||||
const {gzip, } = require('node-gzip'); |
||||
|
||||
var algorithm = 'sha256' |
||||
, shasum = crypto.createHash(algorithm) |
||||
|
||||
const sha_file = './dist/index.sha256'; |
||||
const gzip_file = './dist/index.gzip'; |
||||
var filename = './dist/index.html' |
||||
, s = fs.ReadStream(filename) |
||||
|
||||
var bufs = []; |
||||
s.on('data', function(data) { |
||||
shasum.update(data) |
||||
bufs.push(data); |
||||
}) |
||||
|
||||
s.on('end', function() { |
||||
var hash = shasum.digest('hex') |
||||
console.log(hash + ' ' + filename) |
||||
|
||||
fs.writeFileSync(sha_file, hash, 'utf8'); |
||||
|
||||
var buf = Buffer.concat(bufs); |
||||
gzip(buf).then((compressed) => {fs.writeFileSync(gzip_file, compressed);}); |
||||
|
||||
fs.rm(filename,()=>{}); |
||||
|
||||
}) |
||||
|
||||
|
@ -0,0 +1,180 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
// All rights reserved. |
||||
// Licensed under the Apache License, Version 2.0 |
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||
// at your option. All files in the project carrying such |
||||
// notice may not be copied, modified, or distributed except |
||||
// according to those terms. |
||||
--> |
||||
|
||||
<script lang="ts"> |
||||
import { push, default as Router } from "svelte-spa-router"; |
||||
import { isLoading } from "svelte-i18n"; |
||||
|
||||
import { onMount, tick, onDestroy } from "svelte"; |
||||
import { ng, brokers_info } from "./store"; |
||||
import { |
||||
NotFound, |
||||
Test, |
||||
WalletCreate, |
||||
Invitation, |
||||
WalletLogin, |
||||
WalletInfo, |
||||
User, |
||||
UserRegistered, |
||||
Install, |
||||
ScanQRWeb, |
||||
AccountInfo, |
||||
WalletLoginUsername, |
||||
WalletLoginQr, |
||||
WalletLoginTextCode |
||||
} from "@ng-org/ui-common/routes"; |
||||
import { Bowser } from "../../../../sdk/js/lib-wasm/jsland/bowser.js"; |
||||
|
||||
import Home from "./routes/Home.svelte"; |
||||
|
||||
const routes = new Map(); |
||||
routes.set("/", Home); |
||||
// routes.set("/test", Test); |
||||
// routes.set("/wallet/login", WalletLogin); |
||||
// routes.set("/wallet/username", WalletLoginUsername); |
||||
// routes.set("/wallet/login-qr", WalletLoginQr); |
||||
// routes.set("/wallet/login-text-code", WalletLoginTextCode); |
||||
// routes.set("/wallet/create", WalletCreate); |
||||
// routes.set("/i/:invitation", Invitation); |
||||
// routes.set("/user", User); |
||||
// routes.set("/user/registered", UserRegistered); |
||||
// routes.set("/wallet", WalletInfo); |
||||
// routes.set("/user/accounts", AccountInfo); |
||||
// routes.set("/scanqr", ScanQRWeb); |
||||
// routes.set("/install", Install); |
||||
routes.set("*", NotFound); |
||||
|
||||
// window.refresh_wallets = async () => { |
||||
// let walls = await ng.get_wallets(); |
||||
// wallets.set(walls); |
||||
// }; |
||||
|
||||
let no_local_storage = false; |
||||
let is_safari = false; |
||||
|
||||
function load_bootstraps(bs: string | null) { |
||||
if (bs) { |
||||
let bootstrap_map = JSON.parse(bs); |
||||
brokers_info.set(bootstrap_map); |
||||
} |
||||
} |
||||
|
||||
onMount(async () => { |
||||
|
||||
window.document.getElementById("splash").className="noshow"; |
||||
window.document.getElementById("app").className=""; |
||||
|
||||
let info = Bowser.parse(window.navigator.userAgent); |
||||
//console.log(info); |
||||
is_safari = info.browser.name == "Safari"; |
||||
if (is_safari) return; |
||||
|
||||
window.addEventListener("storage", (event) => { |
||||
//console.log("localStorage event", event); |
||||
if (event.storageArea != localStorage) return; |
||||
if (event.key === "ng_bootstrap") { |
||||
load_bootstraps(event.newValue); |
||||
} |
||||
}); |
||||
|
||||
let ls; |
||||
try { |
||||
ls = localStorage; |
||||
|
||||
try { |
||||
let ret = await document.requestStorageAccess({ localStorage: true }); |
||||
ls = ret.localStorage; |
||||
console.log("REQUEST STORAGE ACCESS GRANTED by chrome"); |
||||
} |
||||
catch(e) { |
||||
console.warn("requestStorageAccess of chrome failed. falling back to previous api", e) |
||||
try { |
||||
await document.requestStorageAccess(); |
||||
localStorage; |
||||
console.log("REQUEST STORAGE ACCESS GRANTED"); |
||||
} catch (e) { |
||||
console.error("REQUEST STORAGE ACCESS DENIED",e); |
||||
no_local_storage = true; |
||||
} |
||||
} |
||||
|
||||
} catch (e) { |
||||
no_local_storage = true; |
||||
console.log("no access to localStorage",e) |
||||
} |
||||
|
||||
if (!no_local_storage) { |
||||
try { |
||||
load_bootstraps(ls.getItem("ng_bootstrap")); |
||||
} catch (e) { |
||||
console.log("load_bootstraps failed") |
||||
} |
||||
} |
||||
|
||||
}); |
||||
|
||||
</script> |
||||
{#if is_safari} |
||||
|
||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<svg |
||||
class="animate-bounce mt-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<p class="mb-5"> |
||||
We are sorry but Safari is not supported yet<br/>for WebApps authentication with your Wallet.<br/>Please use another browser. |
||||
</p> |
||||
</div> |
||||
|
||||
{:else if no_local_storage} |
||||
|
||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<svg |
||||
class="animate-bounce mt-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<p class="mb-5"> |
||||
Please give access to localStorage for the website<br/> |
||||
{location.origin} |
||||
</p> |
||||
</div> |
||||
|
||||
{:else} |
||||
|
||||
{#if $isLoading} |
||||
<p class="text-center">Loading translations...</p> |
||||
{:else} |
||||
<Router {routes} /> |
||||
{/if} |
||||
|
||||
{/if} |
@ -0,0 +1,4 @@ |
||||
/* Write your global styles here, in PostCSS syntax */ |
||||
@tailwind base; |
||||
@tailwind components; |
||||
@tailwind utilities; |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,160 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
// All rights reserved. |
||||
// Licensed under the Apache License, Version 2.0 |
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||
// at your option. All files in the project carrying such |
||||
// notice may not be copied, modified, or distributed except |
||||
// according to those terms. |
||||
--> |
||||
<script lang="ts"> |
||||
import { onMount } from "svelte"; |
||||
import { t, locale } from "svelte-i18n"; |
||||
import { CenteredLayout } from "@ng-org/ui-common/lib"; |
||||
import { LogoSimple } from "@ng-org/ui-common/components"; |
||||
import { |
||||
Sidebar, |
||||
SidebarGroup, |
||||
SidebarItem, |
||||
SidebarWrapper, |
||||
} from "flowbite-svelte"; |
||||
import { |
||||
ComputerDesktop, |
||||
GlobeAlt, |
||||
ServerStack |
||||
} from "svelte-heros-v2"; |
||||
|
||||
import { web_origin, brokers_info, selected_broker } from '../store'; |
||||
import { fromWritablePort, RemoteReadableStream } from 'remote-web-streams'; |
||||
|
||||
let top; |
||||
let nonActiveClass = |
||||
"flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"; |
||||
|
||||
const AUTH_HOME = "#/"; |
||||
|
||||
function select(broker: Object) { |
||||
|
||||
let url; |
||||
if (import.meta.env.NG_DEV && broker.localhost === 14400) { |
||||
// dev mode |
||||
url = "http://localhost:1421/appauth.html"; |
||||
} else if (broker.localhost) { |
||||
url = `http://localhost:${broker.localhost}/auth/`; |
||||
} else if (broker.private) { |
||||
//TODO |
||||
url = `http://unimplemented/auth/`; |
||||
} else if (broker.domain) { |
||||
url = `https://${broker.domain}/auth/`; |
||||
} else if (broker.ngbox) { |
||||
url = `https://nextgraph.app/auth/`; |
||||
} else return; |
||||
|
||||
selected_broker.set(broker); |
||||
let iframe = window.document.getElementById("nextgraph-app-auth-iframe"); |
||||
iframe?.classList.add('nextgraph-app-auth-iframe--active'); |
||||
window.document.getElementById("app").style["display"] = "none"; |
||||
|
||||
(<any>window).ng_iframe_origin = new URL(url).origin; |
||||
|
||||
iframe.addEventListener("load", function() { |
||||
|
||||
(<any>window).ng_broker_selected = this.contentWindow; |
||||
const ready_handler = async function(m) { |
||||
if (m.data.ready && m.origin === (<any>window).ng_iframe_origin) { |
||||
//console.log("got ready message",m); |
||||
//remove this listener |
||||
window.removeEventListener("message",ready_handler); |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
//console.log("sending init message to app-auth"); |
||||
(<any>window).ng_broker_selected.postMessage({ method: "init", manifest:window.ng_manifest, port: writablePort }, (<any>window).ng_iframe_origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
for (var msg; msg = await reader.read(); ) { |
||||
if (msg.done) { |
||||
(<any>window).ng_status_callback.close(); |
||||
break; |
||||
} else { |
||||
//console.log("forwarding upstream",msg.value); |
||||
(<any>window).ng_status_callback.write(msg.value); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
window.addEventListener("message",ready_handler); |
||||
}); |
||||
|
||||
iframe.src = url+"?o="+location.search.substring(3)+AUTH_HOME; |
||||
} |
||||
|
||||
onMount(() => { |
||||
if (Object.keys($brokers_info).length == 1) { |
||||
select(Object.values($brokers_info)[0]); |
||||
} |
||||
}); |
||||
</script> |
||||
|
||||
{#if Object.keys($brokers_info).length > 1} |
||||
<CenteredLayout> |
||||
<div class="container3" bind:this={top}> |
||||
<div class="row"> |
||||
<LogoSimple/> |
||||
</div> |
||||
<div class="row mb-20"> |
||||
<Sidebar {nonActiveClass}> |
||||
<SidebarWrapper |
||||
divClass="bg-gray-60 overflow-y-auto py-4 px-3 rounded dark:bg-gray-800" |
||||
> |
||||
<SidebarGroup ulClass="space-y-2" role="menu"> |
||||
<li> |
||||
<h2 class="text-xl mb-6">{@html $t("auth.select_broker", {values: { origin:$web_origin }})}</h2> |
||||
</li> |
||||
{#each Object.entries($brokers_info) as broker} |
||||
<li |
||||
tabindex="0" |
||||
role="menuitem" |
||||
class="flex items-center p-2 text-base font-normal text-gray-900 clickable rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700" |
||||
on:keypress={()=>select(broker[1])} |
||||
on:click={()=>select(broker[1])} |
||||
> |
||||
{#if broker[1].localhost} |
||||
<ComputerDesktop tabindex="-1" |
||||
class="w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
||||
{:else if broker[1].domain} |
||||
<GlobeAlt tabindex="-1" |
||||
class="w-10 min-w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
||||
{:else if broker[1].private} |
||||
<ServerStack tabindex="-1" |
||||
class="w-10 h-10 mr-4 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"/> |
||||
{:else if broker[1].ngbox} |
||||
<svg |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
version="1.1" |
||||
viewBox="0 0 225 225" |
||||
class="mr-4 block h-10 w-10" |
||||
stroke="currentColor" |
||||
stroke-width="12" |
||||
fill="none" |
||||
> |
||||
<path |
||||
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z" |
||||
/> |
||||
<rect |
||||
ry="37.596001" |
||||
y="10.583322" |
||||
x="14.363095" |
||||
height="204.86308" |
||||
width="195.79167" |
||||
/> |
||||
</svg> |
||||
{/if} |
||||
<span class="text-left text-xl ml-3" style="overflow-wrap: anywhere;">{broker[0]}</span> |
||||
</li> |
||||
{/each} |
||||
</SidebarGroup> |
||||
</SidebarWrapper> |
||||
</Sidebar> |
||||
</div> |
||||
</div> |
||||
</CenteredLayout> |
||||
{/if} |
@ -0,0 +1,120 @@ |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||
// All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
import "./app.postcss"; |
||||
import "../../../../app/ui-common/src/styles.css"; |
||||
import { link, push } from "svelte-spa-router"; |
||||
import App from "./App.svelte"; |
||||
import { fromWritablePort, RemoteReadableStream } from 'remote-web-streams'; |
||||
import { web_origin } from './store'; |
||||
|
||||
import { select_default_lang } from "@ng-org/ui-common/lang"; |
||||
select_default_lang(()=>{return window.navigator.languages;}).then(() => {}); |
||||
|
||||
//let status_callback : WritableStreamDefaultWriter<any> | undefined = undefined;
|
||||
|
||||
const origin = decodeURIComponent(location.search.substring(3)); |
||||
|
||||
document.getElementById("banner").innerText = "Opening Wallet for "+ new URL(origin).host; |
||||
|
||||
async function rpc( method:string, args?: any) : Promise<any> { |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
(<any>window).ng_broker_selected.postMessage({ method, args, port: writablePort }, (<any>window).ng_iframe_origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
let ret = await reader.read(); |
||||
await reader.read(); // the close
|
||||
return ret.value; |
||||
} |
||||
|
||||
async function rpc_stream( method:string, args: any, writer:WritableStreamDefaultWriter<any>) { |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
(<any>window).ng_broker_selected.postMessage({ method, args, port: writablePort }, (<any>window).ng_iframe_origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
for (var msg; msg = await reader.read(); ) { |
||||
if (msg.done) { |
||||
writer.close(); |
||||
break; |
||||
} |
||||
if (msg.value.error) { |
||||
writer.write(msg.value); |
||||
writer.close(); |
||||
break; |
||||
} else if (msg.value.stream) { |
||||
writer.write(msg.value); |
||||
} |
||||
// TODO: deal with end of stream
|
||||
} |
||||
} |
||||
|
||||
const AUTH_HOME = "#/"; |
||||
// const AUTH_USER_PANEL = "#/user";
|
||||
// const AUTH_USER_ACCOUNTS = "#/user/accounts";
|
||||
// const AUTH_WALLET = "#/wallet";
|
||||
|
||||
window.addEventListener("message", async (event)=>{ |
||||
if (event.data.ready) return; |
||||
const { method, port } = event.data; |
||||
const writable = fromWritablePort(port); |
||||
const writer = writable.getWriter(); |
||||
if (event.origin !== origin) { |
||||
console.error("invalid origin",event.origin,origin) |
||||
writer.write({status:'error', error:'invalid origin'}); |
||||
writer.close(); |
||||
} else if ( method === "init" ) { |
||||
|
||||
(<any>window).ng_status_callback = writer; |
||||
web_origin.set(new URL(origin).host); |
||||
|
||||
// make API call with origin, event.data.singleton and event.data.access_requests
|
||||
// in order to get full manifest (including security info)
|
||||
|
||||
(<any>window).ng_manifest = { |
||||
origin: origin, |
||||
singleton: event.data.singleton, |
||||
access_request: event.data.access_requests, |
||||
name: "", |
||||
title: "", |
||||
description: "", // etc...
|
||||
security_info: {} |
||||
}; |
||||
|
||||
} else if ( method === "login" ) { |
||||
|
||||
if (!(<any>window).ng_broker_selected) { |
||||
push(AUTH_HOME); |
||||
writer.write({ok:true, ret: false}); |
||||
writer.close(); |
||||
} else { |
||||
writer.write(await rpc("login")); |
||||
writer.close(); |
||||
} |
||||
} else if ( method === "doc_subscribe" ) { |
||||
|
||||
//console.log("net forward doc_subscribe to app", method, event.data.args)
|
||||
await rpc_stream(method, event.data.args, writer); |
||||
|
||||
} else { |
||||
//console.log("net forward to app", method, event.data.args)
|
||||
// forward to app auth iframe
|
||||
writer.write(await rpc(method, event.data.args)); |
||||
writer.close(); |
||||
|
||||
} |
||||
|
||||
}, false); |
||||
|
||||
/// for test purposes only, when testing with http://localhost:14402/?o=http://localhost:14402
|
||||
// const { readable, writablePort } = new RemoteReadableStream();
|
||||
// window.postMessage({method:"init", port: writablePort }, location.origin, [writablePort]);
|
||||
|
||||
const app = new App({ |
||||
target: document.getElementById("app"), |
||||
}); |
||||
|
||||
export default app; |
@ -0,0 +1,99 @@ |
||||
<!-- |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
// All rights reserved. |
||||
// Licensed under the Apache License, Version 2.0 |
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0> |
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, |
||||
// at your option. All files in the project carrying such |
||||
// notice may not be copied, modified, or distributed except |
||||
// according to those terms. |
||||
--> |
||||
|
||||
<!-- |
||||
Home page to display for logged in users. |
||||
Redirects to no-wallet or login page, if not logged in. |
||||
--> |
||||
|
||||
<script> |
||||
import Home from "../lib/Home.svelte"; |
||||
import { t, locale } from "svelte-i18n"; |
||||
import { NoWallet } from "@ng-org/ui-common/lib"; |
||||
import { push } from "svelte-spa-router"; |
||||
import { onMount, onDestroy } from "svelte"; |
||||
import { brokers_info, web_origin } from "../store"; |
||||
|
||||
</script> |
||||
|
||||
{#if Object.keys($brokers_info).length == 0} |
||||
<!-- <NoWallet /> --> |
||||
{#if import.meta.env.NG_DEV} |
||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<svg |
||||
class="animate-bounce mt-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<p class="mb-5"> |
||||
We could not find a wallet in your browser.<br/> |
||||
This is probably due to the recent changes made to the framework.<br/> |
||||
Please create a new wallet from this browser and try again, |
||||
</p> |
||||
</div> |
||||
{:else} |
||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-blue-600"> |
||||
|
||||
<svg |
||||
class="y-10 h-16 w-16 mx-auto mb-3" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
We could not find a wallet in your browser.<br/> |
||||
For now, creating a new wallet while a Web App<br/> |
||||
is authenticating, is not implemented. Please<br/> |
||||
create or import your wallet in a new tab by<br/><a href="https://nextgraph.eu" target="_blank">clicking here</a> |
||||
</div> |
||||
{/if} |
||||
{:else if $web_origin} |
||||
<Home /> |
||||
{:else} |
||||
<div class="text-center max-w-6xl lg:px-8 mx-auto px-4 text-red-800"> |
||||
<svg |
||||
class="animate-bounce mt-10 h-16 w-16 mx-auto" |
||||
fill="none" |
||||
stroke="currentColor" |
||||
stroke-width="1.5" |
||||
viewBox="0 0 24 24" |
||||
xmlns="http://www.w3.org/2000/svg" |
||||
aria-hidden="true" |
||||
> |
||||
<path |
||||
stroke-linecap="round" |
||||
stroke-linejoin="round" |
||||
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" |
||||
/> |
||||
</svg> |
||||
<p class="mb-5"> |
||||
{@html $t("auth.unexpected_error")} |
||||
</p> |
||||
</div> |
||||
{/if} |
@ -0,0 +1,53 @@ |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||
// All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
import { |
||||
writable, |
||||
readable, |
||||
readonly, |
||||
derived, |
||||
get, |
||||
type Writable, |
||||
} from "svelte/store"; |
||||
|
||||
import { createAsyncProxy } from "async-proxy"; |
||||
import { RemoteReadableStream } from 'remote-web-streams'; |
||||
|
||||
export const selected_broker = writable<undefined | Object>( undefined ); |
||||
|
||||
export const brokers_info = writable( {} ); |
||||
|
||||
export const unlocked_wallet = writable(undefined); |
||||
|
||||
export const web_origin = writable(""); |
||||
|
||||
import worker_ from "./worker.js?worker&inline"; |
||||
const worker = new worker_(); |
||||
|
||||
async function rpc( method:string, args?: any) : Promise<any> { |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
worker.postMessage({ method, args, port: writablePort }, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
let ret = await reader.read(); |
||||
await reader.read(); // the close.
|
||||
return ret.value; |
||||
} |
||||
|
||||
const handler = { |
||||
async apply(_target: object, path: PropertyKey[], _caller: any, args?: any) :Promise<any> { |
||||
|
||||
if (path[0] === "login") { |
||||
|
||||
} else { |
||||
return await rpc(<string>path[0], args); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
export const ng = createAsyncProxy({}, handler); |
@ -0,0 +1,2 @@ |
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,26 @@ |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||
// All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
//import * as sdk from "@ng-org/wasm-tools-auth";
|
||||
import { fromWritablePort } from 'remote-web-streams'; |
||||
|
||||
self.onmessage = (event) => { |
||||
(async function() { |
||||
const { method, args, port } = event.data; |
||||
const writable = fromWritablePort(port); |
||||
const writer = writable.getWriter(); |
||||
console.log("Message received by worker", method, args); |
||||
|
||||
let ret = await Reflect.apply(sdk[method], null, args); |
||||
writer.write(ret); |
||||
writer.close(); |
||||
})(); |
||||
} |
||||
|
||||
console.log("worker loaded"); |
@ -0,0 +1,13 @@ |
||||
import preprocess from "svelte-preprocess"; |
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; |
||||
|
||||
const config = { |
||||
// preprocess: [
|
||||
// vitePreprocess(),
|
||||
// preprocess({
|
||||
// postcss: true,
|
||||
// }),
|
||||
// ],
|
||||
}; |
||||
|
||||
export default config; |
@ -0,0 +1,38 @@ |
||||
/** @type {import('tailwindcss').Config}*/ |
||||
const defaultTheme = require('tailwindcss/defaultTheme') |
||||
const config = { |
||||
content: [ |
||||
"./src/**/*.{html,js,svelte,ts}", |
||||
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}", |
||||
"./node_modules/@nng-org/ui-common/src/**/*.{html,js,svelte,ts}", |
||||
], |
||||
theme: { |
||||
extend: { |
||||
colors: { |
||||
primary: { "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd", "400": "#60a5fa", "500": "#3b82f6", "600": "#1E88E5", "700": "#4972A5", "800": "#1e40af", "900": "#1e3a8a" } |
||||
}, |
||||
}, |
||||
screens: { |
||||
'xxs': '400px', |
||||
'xs': '500px', |
||||
...defaultTheme.screens, |
||||
|
||||
'tall': { 'raw': '(min-height: 450px)' }, |
||||
'tall-xxs': { 'raw': '(min-height: 360px)' }, |
||||
'tall-xs': { 'raw': '(min-height: 480px)' }, |
||||
'tall-sm': { 'raw': '(min-height: 640px)' }, |
||||
'tall-md': { 'raw': '(min-height: 800px)' }, |
||||
'tall-l': { 'raw': '(min-height: 1000px)' }, |
||||
'tall-xl': { 'raw': '(min-height: 1200px)' }, |
||||
'tall-xxl': { 'raw': '(min-height: 1400px)' }, |
||||
}, |
||||
}, |
||||
|
||||
plugins: [ |
||||
require('flowbite/plugin'), |
||||
require('@tailwindcss/typography') |
||||
], |
||||
darkMode: 'selector', |
||||
}; |
||||
|
||||
module.exports = config; |
@ -0,0 +1,70 @@ |
||||
import { defineConfig } from 'vite' |
||||
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte' |
||||
import sveltePreprocess from "svelte-preprocess"; |
||||
import svelteSVG from "vite-plugin-svelte-svg"; |
||||
import wasm from "vite-plugin-wasm"; |
||||
import topLevelAwait from "vite-plugin-top-level-await"; |
||||
import { viteSingleFile } from "vite-plugin-singlefile" |
||||
|
||||
const jsToBottom = () => { |
||||
return { |
||||
name: "script-at-end-of-body", |
||||
transformIndexHtml(html) { |
||||
let scriptTag = html.match(/<script type[^>]*>(.*?)<\/script[^>]*>/)[0] |
||||
//console.log("\n SCRIPT TAG", scriptTag, "\n")
|
||||
html = html.replace(scriptTag, "") |
||||
html = html.replace("<!-- # INSERT SCRIPT HERE -->", scriptTag) |
||||
return html; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({ |
||||
envPrefix: ["VITE_", "NG_"], |
||||
server: { |
||||
port: 14402 |
||||
}, |
||||
worker: { |
||||
format: 'es', |
||||
plugins : [ |
||||
topLevelAwait(), |
||||
wasm(), |
||||
viteSingleFile() |
||||
] |
||||
}, |
||||
plugins: [ |
||||
topLevelAwait(), |
||||
wasm(), |
||||
svelte({ |
||||
preprocess: [ |
||||
vitePreprocess(), |
||||
sveltePreprocess({ |
||||
typescript: false, |
||||
postcss: true, |
||||
}), |
||||
], |
||||
}), |
||||
svelteSVG({ |
||||
svgoConfig: { |
||||
plugins: [ |
||||
{ |
||||
name: 'preset-default', |
||||
params: { |
||||
overrides: { |
||||
// disable plugins
|
||||
removeViewBox: false, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
name: 'prefixIds', |
||||
} |
||||
], |
||||
}, // See https://github.com/svg/svgo#configuration
|
||||
requireSuffix: true, // Set false to accept '.svg' without the '?component'
|
||||
}), |
||||
viteSingleFile(), |
||||
jsToBottom(), |
||||
] |
||||
}) |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
pnpm-debug.log* |
||||
lerna-debug.log* |
||||
|
||||
node_modules |
||||
dist |
||||
dist-ssr |
||||
*.local |
||||
|
||||
# Editor directories and files |
||||
.vscode/* |
||||
!.vscode/extensions.json |
||||
.idea |
||||
.DS_Store |
||||
*.suo |
||||
*.ntvs* |
||||
*.njsproj |
||||
*.sln |
||||
*.sw? |
@ -0,0 +1,16 @@ |
||||
Apache 2.0 License |
||||
|
||||
Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
All rights reserved. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
@ -0,0 +1,22 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers |
||||
All rights reserved. |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,165 @@ |
||||
# @ng-org/web |
||||
|
||||
[![Apache 2.0 Licensed][license-image]][license-link] |
||||
[![MIT Licensed][license-image2]][license-link2] |
||||
[](https://forum.nextgraph.org) |
||||
|
||||
JavaScript/TypeScript package containing the SDK of NextGraph for developing third-party Web Apps |
||||
|
||||
## NextGraph |
||||
|
||||
> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. |
||||
> |
||||
> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. |
||||
> |
||||
> More info here [https://nextgraph.org](https://nextgraph.org) |
||||
|
||||
## Support |
||||
|
||||
Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgraph.org) |
||||
|
||||
And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org) |
||||
|
||||
## For developers |
||||
|
||||
Read our [getting started guide](https://docs.nextgraph.org/en/getting-started/). |
||||
|
||||
You need to create a Wallet for yourself, on one of our Public Broker Service Provider. Alternatively, you can do everything locally, as [described below](#local-development) |
||||
|
||||
``` |
||||
npm i nextgraphweb |
||||
``` |
||||
|
||||
Additionally, you can use [LDO (Linked Data Object) library](https://ldo.js.org/latest/guides/nextgraph/) to help you with RDF handling in the client side. |
||||
|
||||
``` |
||||
npm i @ldo/connected-nextgraph |
||||
``` |
||||
|
||||
More documentation on LDO can be found [here](https://www.npmjs.com/package/@ldo/connected-nextgraph). |
||||
|
||||
The LDO library also offers a React plugin that will be demonstrated in another example. |
||||
|
||||
You will find a full example web app [here](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/ng-sdk-js/example-webapp-vite). |
||||
Specially you will find there instructions for setting up your local dev env. |
||||
|
||||
You have to first call the `init()` and `ng.login()`, then once you receive the status `loggedin` in the callback, you can start using the whole API. |
||||
|
||||
## APIs |
||||
|
||||
All the functions are async. you must use them with `await` (or `.then()`). |
||||
|
||||
They all can throw errors. You must enclose them in `try {} catch(e) {}` |
||||
|
||||
- `ng.doc_create()` |
||||
- `ng.sparql_query(session_id, "[SPARQL query]", base, nuri)` returns or: |
||||
- for SELECT queries: a JSON Sparql Query Result as a Javascript object. [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) |
||||
- for CONSTRUCT queries: a list of quads in the format [RDF-JS data model](http://rdf.js.org/data-model-spec/) that can be used as ingress to RDFjs lib. |
||||
- for ASK queries: a boolean |
||||
- `ng.sparql_update(session_id, "[SPARQL update]")` returns nothing, but can throw an error. |
||||
|
||||
Here is the format of the config object to be supplied in the calls to `init_headless` and `admin_create_user`: |
||||
|
||||
## Local development |
||||
|
||||
you need to have a running local ngd server and a local ng-app frontend too. See those [instructions first](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/DEV.md#first-run). |
||||
|
||||
You will need to create an admin wallet on the local ngd instance, as explained in the above link. |
||||
|
||||
Then you can use that wallet to log in inside your webapp. |
||||
|
||||
Then compile the nextgraphweb package in dev mode: |
||||
|
||||
``` |
||||
pnpm run -C ../../helpers/nextgraphweb builddev |
||||
``` |
||||
|
||||
Then create your app, by example, with the command: |
||||
|
||||
``` |
||||
npm create vite@latest |
||||
``` |
||||
|
||||
change directory to where our app is located. and install dependencies, then run the dev server. |
||||
|
||||
``` |
||||
npm install |
||||
npm link ../../helpers/nextgraphweb |
||||
npm run dev |
||||
``` |
||||
|
||||
Due to the way `npm link` works, you will have to run this command again, after every time you use `npm install`. |
||||
|
||||
See the example code in [src/main.js](./src/main.js) |
||||
|
||||
## Example usage |
||||
|
||||
call : |
||||
|
||||
```javascript |
||||
import { default as ng, init } from "nextgraphweb"; |
||||
|
||||
await init( |
||||
(event) => { |
||||
// callback |
||||
// once you receive event.status == "loggedin" |
||||
// you can use the full API |
||||
}, |
||||
true, // singleton: boolean (will your app create many docs in the system, or should it be launched as a unique instance) |
||||
[] |
||||
); //list of AccessRequests (for now, leave this empty) |
||||
|
||||
await ng.login(); // this will return false at the first attempt. but it will open the wallet login page so the user can login. |
||||
// if you call it again later once the user has logged in already, it will return true, and nothing more will happen |
||||
``` |
||||
|
||||
You can alternatively wrap the callback inside a Promise in order to wait for the "loggedin" event. |
||||
|
||||
```javascript |
||||
import {default as ng, init} from "nextgraphweb"; |
||||
|
||||
let loggedin = new Promise( async (resolve) => { |
||||
await init( (event) => { |
||||
// callback |
||||
// once you receive event.status == "loggedin" |
||||
// you can use the full API |
||||
if (event.status == "loggedin") resolve(event.session); |
||||
else if (event.status == "cancelled" || event.status == "error") resolve(false); |
||||
} |
||||
, true // singleton: boolean (will your app create many docs in the system, or should it be launched as a unique instance) |
||||
, []); //list of AccessRequests (for now, leave this empty) |
||||
}); |
||||
|
||||
await ng.login(); // this will return false at the first attempt. but it will open the wallet login page so the user can login. |
||||
// if you call it again later once the user has logged in already, it will return true, and nothing more will happen |
||||
|
||||
let session = await loggedin; |
||||
if (session) { |
||||
|
||||
await ng.doc_create(session.session_id,...); |
||||
|
||||
await ng.sparql_query(session.session_id,...); |
||||
|
||||
} |
||||
``` |
||||
|
||||
--- |
||||
|
||||
## License |
||||
|
||||
Licensed under either of |
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) |
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) |
||||
at your option. |
||||
|
||||
`SPDX-License-Identifier: Apache-2.0 OR MIT` |
||||
|
||||
--- |
||||
|
||||
NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/assure) and the [NGI Zero Commons Fund](https://nlnet.nl/commonsfund/), both funds established by [NLnet](https://nlnet.nl/) Foundation with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreements No 957073 and No 101092990, respectively. |
||||
|
||||
[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg |
||||
[license-link]: https://git.nextgraph.org/NextGraph/nextgraph-rs/raw/branch/master/LICENSE-APACHE2 |
||||
[license-image2]: https://img.shields.io/badge/license-MIT-blue.svg |
||||
[license-link2]: https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/LICENSE-MIT |
@ -0,0 +1,75 @@ |
||||
{ |
||||
"name": "@ng-org/web", |
||||
"collaborators": [ |
||||
"Niko PLP <niko@nextgraph.org>" |
||||
], |
||||
"description": "JS/TS SDK of NextGraph for third-party web apps", |
||||
"version": "0.1.2", |
||||
"license": "MIT/Apache-2.0", |
||||
"repository": { |
||||
"type": "git", |
||||
"url": "https://git.nextgraph.org/NextGraph/nextgraph-rs" |
||||
}, |
||||
"type": "module", |
||||
"files": ["dist"], |
||||
"main": "./dist/ngweb.umd.cjs", |
||||
"module": "./dist/ngweb.js", |
||||
"types": "./dist/index.d.ts", |
||||
"exports": { |
||||
".": { |
||||
"import":{ |
||||
"types": "./dist/index.d.ts", |
||||
"default": "./dist/ngweb.js" |
||||
}, |
||||
"require": { |
||||
"types": "./dist/index.d.ts", |
||||
"default": "./dist/ngweb.umd.cjs" |
||||
} |
||||
} |
||||
}, |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "tsc && vite build", |
||||
"builddev": "tsc && cross-env NG_DEV=1 vite build", |
||||
"preview": "vite preview" |
||||
}, |
||||
"dependencies": { |
||||
"async-proxy": "^0.4.1", |
||||
"remote-web-streams": "^0.2.0" |
||||
}, |
||||
"devDependencies": { |
||||
"@types/node": "^18.7.10", |
||||
"typescript": "~5.7.2", |
||||
"vite": "^6.2.0", |
||||
"vite-plugin-dts": "^4.5.3", |
||||
"cross-env": "^7.0.3" |
||||
}, |
||||
"keywords": [ |
||||
"crdt", |
||||
"dapp", |
||||
"decentralized", |
||||
"e2ee", |
||||
"local-first", |
||||
"p2p", |
||||
"semantic-web", |
||||
"eventual-consistency", |
||||
"json-ld", |
||||
"markdown", |
||||
"ocap", |
||||
"vc", |
||||
"offline-first", |
||||
"p2p-network", |
||||
"collaboration", |
||||
"privacy-protection", |
||||
"rdf", |
||||
"rich-text-editor", |
||||
"self-hosted", |
||||
"sparql", |
||||
"byzantine-fault-tolerance", |
||||
"web3", |
||||
"graph-database", |
||||
"database", |
||||
"triplestore" |
||||
], |
||||
"homepage": "https://nextgraph.org" |
||||
} |
@ -0,0 +1,194 @@ |
||||
// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers
|
||||
// All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
||||
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
import { createAsyncProxy } from "async-proxy"; |
||||
import { RemoteReadableStream } from 'remote-web-streams'; |
||||
|
||||
let initialized: null | Window = null; |
||||
|
||||
const css = `.nextgraph-auth-modal {
|
||||
visibility: hidden; |
||||
background-color: rgba(0, 0, 0, 0.4); |
||||
|
||||
display: grid; |
||||
place-items: center; |
||||
|
||||
height: 100vh; |
||||
width: 100vw; |
||||
|
||||
position: fixed; |
||||
left: 0; |
||||
top: 0; |
||||
} |
||||
|
||||
.nextgraph-auth-modal.nextgraph-auth-modal--fade { |
||||
opacity: 0; |
||||
transition: 0.2s; |
||||
} |
||||
|
||||
.nextgraph-auth-modal.nextgraph-auth-modal--active { |
||||
visibility: visible; |
||||
z-index: 9999; |
||||
opacity: 1; |
||||
} |
||||
|
||||
.nextgraph-auth-modal__content { |
||||
width: 100%; |
||||
height: 100%; |
||||
background-color: #fff; |
||||
} |
||||
|
||||
.nextgraph-auth-modal.nextgraph-auth-modal--active.nextgraph-auth-modal--fade { |
||||
opacity: 1; |
||||
animation-name: fadeInOpacity; |
||||
animation-iteration-count: 1; |
||||
animation-timing-function: ease-in; |
||||
animation-duration: 0.5s; |
||||
} |
||||
|
||||
@keyframes fadeInOpacity { |
||||
0% { |
||||
opacity: 0; |
||||
} |
||||
100% { |
||||
opacity: 1; |
||||
} |
||||
}`;
|
||||
|
||||
const html = ` |
||||
<div id="nextgraph-auth" class="nextgraph-auth-modal nextgraph-auth-modal--fade"> |
||||
<div class="nextgraph-auth-modal__content"> |
||||
<iframe id="nextgraph-auth-iframe" scrolling="auto" frameborder="0" |
||||
style="position: relative; height: 100%; width: 100%; overflow:auto;"> |
||||
</iframe> |
||||
</div> |
||||
</div>`;
|
||||
|
||||
// const javascript = `
|
||||
// `;
|
||||
|
||||
function addTags() { |
||||
const style = window.document.createElement('style'); |
||||
style.textContent = css; |
||||
window.document.head.append(style); |
||||
|
||||
let body = document.getElementsByTagName("body")[0]; |
||||
body.insertAdjacentHTML("afterbegin", html); |
||||
|
||||
// const js = window.document.createElement('script');
|
||||
// js.type = "text/javascript";
|
||||
// js.textContent = javascript;
|
||||
// body.append(js);
|
||||
} |
||||
|
||||
const iframe_config = import.meta.env.NG_DEV ? {src:"http://localhost:1421/auth.html?o=", origin: "http://localhost:1421"} : {src:"https://nextgraph.net/auth/?o=", origin: "https://nextgraph.net"} ; |
||||
// when developing net-auth
|
||||
//const iframe_config = {src:"http://localhost:14402/?o=", origin: "http://localhost:14402"};
|
||||
// to test ngnet
|
||||
//const iframe_config = {src:"http://127.0.0.1:3033/auth/?o=", origin: "http://127.0.0.1:3033"};
|
||||
|
||||
export const init = async function(callback:Function, singleton:boolean, access_requests:any) { |
||||
if (initialized === null) { |
||||
if (!window) throw new Error("init(callback,..) can only be called from a browser's window"); |
||||
let origin = location.origin; |
||||
let encoded_origin = encodeURIComponent(origin); |
||||
addTags(); |
||||
let iframe: HTMLIFrameElement = <HTMLIFrameElement>window.document.getElementById('nextgraph-auth-iframe'); |
||||
if (iframe) { |
||||
return new Promise(async (resolve) => { |
||||
iframe.addEventListener("load", async function() { |
||||
initialized = this.contentWindow; |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
initialized?.postMessage({ method: "init", singleton, access_requests, port: writablePort }, iframe_config.origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
resolve(true); |
||||
for (var msg; msg = await reader.read(); ) { |
||||
if (msg.done) break; |
||||
if (msg.value.status == "error") { |
||||
console.error(msg.value.error); |
||||
} else if ( msg.value.status == "cancelled") { |
||||
hide_nextgraph_auth(); |
||||
} else if (msg.value.status == "loggedin") { |
||||
hide_nextgraph_auth(); |
||||
} |
||||
await (callback)(msg.value); |
||||
} |
||||
}); |
||||
iframe.src = `${iframe_config.src}${encoded_origin}`; |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function show_nextgraph_auth() { |
||||
window.document.getElementById("nextgraph-auth")?.classList.add('nextgraph-auth-modal--active'); |
||||
} |
||||
|
||||
function hide_nextgraph_auth() { |
||||
window.document.getElementById("nextgraph-auth")?.classList.remove('nextgraph-auth-modal--active'); |
||||
}
|
||||
|
||||
async function rpc( method:string, args?: any) : Promise<any> { |
||||
const { readable, writablePort } = new RemoteReadableStream(); |
||||
//console.log("POSTING",method, args);
|
||||
if (method==="doc_subscribe") { |
||||
let callback = args[2]; |
||||
let new_args = [args[0],args[1]]; |
||||
initialized?.postMessage({ method, args:new_args, port: writablePort }, iframe_config.origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
let unsub = new Promise(async (resolve)=> { |
||||
resolve(()=>{
|
||||
// unsub function that does nothing.
|
||||
//TODO: implement it
|
||||
}); |
||||
for (var msg; msg = await reader.read(); ) { |
||||
if (msg.done) break; |
||||
if (msg.value.error) { |
||||
throw new Error(msg.value.ret); |
||||
} else if (msg.value.stream) { |
||||
(callback)(msg.value.ret); |
||||
} |
||||
// TODO: deal with end of stream
|
||||
} |
||||
}); |
||||
return unsub; |
||||
|
||||
} else { |
||||
initialized?.postMessage({ method, args, port: writablePort }, iframe_config.origin, [writablePort]); |
||||
const reader = readable.getReader(); |
||||
let ret = await reader.read(); |
||||
//console.log(ret)
|
||||
await reader.read(); // the close
|
||||
if (ret.value.ok)
|
||||
return ret.value.ret; |
||||
else |
||||
throw new Error(ret.value.ret); |
||||
} |
||||
|
||||
} |
||||
|
||||
const handler = { |
||||
async apply(_target: object, path: PropertyKey[], _caller: any, args?: any) :Promise<any> { |
||||
if (initialized === null) { |
||||
throw new Error("you must call init() first (and once)"); |
||||
} |
||||
if (path[0] === "login") { |
||||
if (await rpc("login") !== true) { |
||||
show_nextgraph_auth(); |
||||
return false; |
||||
} else { |
||||
return true; |
||||
} |
||||
} else { |
||||
return await rpc(<string>path[0], args); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
export const ng = createAsyncProxy({}, handler); |
@ -0,0 +1 @@ |
||||
/// <reference types="vite/client" />
|
@ -0,0 +1,24 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"target": "ES2020", |
||||
"useDefineForClassFields": true, |
||||
"module": "ESNext", |
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"], |
||||
"skipLibCheck": true, |
||||
|
||||
/* Bundler mode */ |
||||
"moduleResolution": "bundler", |
||||
"allowImportingTsExtensions": true, |
||||
"isolatedModules": true, |
||||
"moduleDetection": "force", |
||||
"noEmit": true, |
||||
|
||||
/* Linting */ |
||||
"strict": true, |
||||
"noUnusedLocals": true, |
||||
"noUnusedParameters": true, |
||||
"noFallthroughCasesInSwitch": true, |
||||
"noUncheckedSideEffectImports": true |
||||
}, |
||||
"include": ["src"] |
||||
} |
@ -0,0 +1,16 @@ |
||||
import { resolve } from 'path'; |
||||
import { defineConfig } from 'vite'; |
||||
import dts from 'vite-plugin-dts'; |
||||
|
||||
// https://vitejs.dev/guide/build.html#library-mode
|
||||
export default defineConfig({ |
||||
build: { |
||||
lib: { |
||||
entry: resolve(__dirname, 'src/index.ts'), |
||||
name: 'ngweb', |
||||
fileName: 'ngweb', |
||||
}, |
||||
}, |
||||
envPrefix: ["VITE_", "NG_"], |
||||
plugins: [dts()], |
||||
}); |
Loading…
Reference in new issue