kopia lustrzana https://gitlab.com/rysiekpl/libresilient
Merge branch 'wip-dnslink-ipfs' into 'master'
Plugin: dnslink-ipfs See merge request rysiekpl/libresilient!15merge-requests/16/head
commit
9e5fc8ff72
|
@ -1,13 +1,6 @@
|
||||||
{
|
{
|
||||||
"plugins": [{
|
"plugins": [{
|
||||||
"name": "basic-integrity",
|
"name": "dnslink-ipfs"
|
||||||
"requireIntegrity": false,
|
|
||||||
"integrity": {
|
|
||||||
"http://localhost:8000/__tests__/test.json": "sha256-FCNALvZ0mSxEs0+SjOgx/sDFFVuh0MwkhhYnI0UJWDg="
|
|
||||||
},
|
|
||||||
"uses": [{
|
|
||||||
"name": "fetch"
|
|
||||||
}]
|
|
||||||
}],
|
}],
|
||||||
"loggedComponents": ["service-worker", "fetch", "cache", "basic-integrity"]
|
"loggedComponents": ["service-worker", "dnslink-ipfs"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
const makeServiceWorkerEnv = require('service-worker-mock');
|
||||||
|
|
||||||
|
describe("plugin: dnslink-ipfs", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
Object.assign(global, makeServiceWorkerEnv());
|
||||||
|
jest.resetModules();
|
||||||
|
init = {
|
||||||
|
name: 'dnslink-ipfs',
|
||||||
|
gunPubkey: 'stub'
|
||||||
|
}
|
||||||
|
global.LibResilientPluginConstructors = new Map()
|
||||||
|
LR = {
|
||||||
|
log: jest.fn((component, ...items)=>{
|
||||||
|
console.debug(component + ' :: ', ...items)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Ipfs = {
|
||||||
|
ipfsFixtureAddress: 'QmiPFSiPFSiPFSiPFSiPFSiPFSiPFSiPFSiPFSiPFSiPFS',
|
||||||
|
create: ()=>{
|
||||||
|
return Promise.resolve({
|
||||||
|
cat: (path)=>{
|
||||||
|
return {
|
||||||
|
sourceUsed: false,
|
||||||
|
next: ()=>{
|
||||||
|
if (path.endsWith('nonexistent.path')) {
|
||||||
|
throw new Error('Error: file does not exist')
|
||||||
|
}
|
||||||
|
var prevSourceUsed = self.sourceUsed
|
||||||
|
self.sourceUsed = true
|
||||||
|
var val = undefined
|
||||||
|
if (!prevSourceUsed) {
|
||||||
|
var val = Uint8Array.from(
|
||||||
|
Array
|
||||||
|
.from(JSON.stringify({
|
||||||
|
test: "success",
|
||||||
|
path: path
|
||||||
|
}))
|
||||||
|
.map(
|
||||||
|
letter => letter.charCodeAt(0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
done: prevSourceUsed,
|
||||||
|
value: val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
resolve: (path)=>{
|
||||||
|
var result = path.replace(
|
||||||
|
'/ipns/' + self.location.origin.replace('https://', ''),
|
||||||
|
'/ipfs/' + Ipfs.ipfsFixtureAddress
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
next: ()=> {
|
||||||
|
return Promise.resolve({
|
||||||
|
done: false,
|
||||||
|
value: result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.Ipfs = global.Ipfs
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
test("it should register in LibResilientPlugins", () => {
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
expect(LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).name).toEqual('dnslink-ipfs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("IPFS setup should be initiated", async ()=>{
|
||||||
|
self.importScripts = jest.fn()
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
try {
|
||||||
|
await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch('/test.json')
|
||||||
|
} catch {}
|
||||||
|
expect(self.importScripts).toHaveBeenNthCalledWith(1, './lib/ipfs.js')
|
||||||
|
})
|
||||||
|
|
||||||
|
test("fetching should error out for unpublished content", async ()=>{
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
expect.assertions(1)
|
||||||
|
try {
|
||||||
|
await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch(self.location.origin + '/nonexistent.path')
|
||||||
|
} catch(e) {
|
||||||
|
expect(e).toEqual(new Error('Error: file does not exist'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: probably not necessary in the long run?
|
||||||
|
test("fetching a path ending in <path>/ should instead fetch <path>/index.html", async ()=>{
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
var response = await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch(self.location.origin + '/test/')
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
var blob = await response.blob()
|
||||||
|
expect(JSON.parse(new TextDecoder().decode(blob.parts[0]))).toEqual({test: "success", path: "/ipfs/" + global.Ipfs.ipfsFixtureAddress + '/test/index.html'})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("content types should be guessed correctly when fetching", async ()=>{
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
var dnslinkIpfsPlugin = LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init)
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test/')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : text/html")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.htm')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : text/html")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.css')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : text/css")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.js')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : text/javascript")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.json')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : application/json")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.svg')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : image/svg+xml")
|
||||||
|
LR.log.mockClear()
|
||||||
|
|
||||||
|
try {
|
||||||
|
await dnslinkIpfsPlugin.fetch(self.location.origin + '/test.ico')
|
||||||
|
} catch(e) {}
|
||||||
|
expect(LR.log).toHaveBeenCalledWith('dnslink-ipfs', " +-- guessed contentType : image/x-icon")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("fetching should work", async ()=>{
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
let response = await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch(self.location.origin + '/test.json')
|
||||||
|
expect(response.body.type).toEqual('application/json')
|
||||||
|
var blob = await response.blob()
|
||||||
|
expect(JSON.parse(new TextDecoder().decode(blob.parts[0]))).toEqual({test: "success", path: "/ipfs/" + global.Ipfs.ipfsFixtureAddress + '/test.json'})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("publish() should throw an error", async ()=>{
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
expect.assertions(1)
|
||||||
|
try {
|
||||||
|
LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).publish()
|
||||||
|
} catch(e) {
|
||||||
|
expect(e).toEqual(new Error("Not implemented yet."))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("IPFS load error should be handled", async ()=>{
|
||||||
|
|
||||||
|
global.Ipfs.create = ()=>{
|
||||||
|
throw new Error('Testing IPFS loading failure')
|
||||||
|
}
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
expect.assertions(1)
|
||||||
|
try {
|
||||||
|
await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch('/test.json')
|
||||||
|
} catch(e) {
|
||||||
|
expect(e).toEqual(new Error("Error: Testing IPFS loading failure"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("importScripts being undefined should be handled", async ()=>{
|
||||||
|
self.importScripts = undefined
|
||||||
|
require("../../../plugins/dnslink-ipfs/index.js");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await LibResilientPluginConstructors.get('dnslink-ipfs')(LR, init).fetch('/test.json')
|
||||||
|
} catch(e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(LR.log).toHaveBeenCalledWith("dnslink-ipfs", "Importing IPFS-related libraries...")
|
||||||
|
expect(LR.log).toHaveBeenCalledWith("dnslink-ipfs", "assuming these scripts are already included:")
|
||||||
|
expect(LR.log).toHaveBeenCalledWith("dnslink-ipfs", "+--", "./lib/ipfs.js")
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
});
|
118
lib/ipfs.js
118
lib/ipfs.js
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,43 @@
|
||||||
|
# Plugin: `dnslink-ipfs`
|
||||||
|
|
||||||
|
- **status**: beta
|
||||||
|
- **type**: [transport plugin](../../docs/ARCHITECTURE.md#transport-plugins)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
No specific configuration is available.
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
This plugin relies on the [DNSLink standard](https://dnslink.org/) in order to resolve the IPFS [CID](https://docs.ipfs.tech/concepts/content-addressing/#identifier-formats) of the up-to-date content, and then uses [IPFS](https://docs.ipfs.tech/concepts/what-is-ipfs/#what-is-ipfs) to retrieve the content itself.
|
||||||
|
|
||||||
|
For this plugin to work, the website's domain needs to have the `_dnslink` DNS label with a `TXT` record pointing to the latest IPFS CID of content. [IPNS](https://docs.ipfs.tech/concepts/ipns/) DNSlinks are *not* supported currently, due to a [`js-ipfs` issue with IPNS resolution in the browser](https://github.com/ipfs/js-ipfs/issues/2921).
|
||||||
|
|
||||||
|
Deploying content to IPFS and updating the `_dnslink` `TXT` record is beyond the scope of this plugin, and needs to be set-up by the administrator of the website.
|
||||||
|
|
||||||
|
## Operation
|
||||||
|
|
||||||
|
IPFS is a good way of making static content available in a decentralized way. It uses content-addressing (IPFS address, or "CID", of a particular file depends on its contents), which means that a given CID will always identify a single specific file (or rather, its contents), as long as the file is available anywhere on the IPFS network.
|
||||||
|
|
||||||
|
IPFS CIDs can also exist for directories. In such cases, they reference CIDs of content (directories and files) contained within. Having a single CID of a directory of content hosted on IPFS is enough to be able to retrieve every file anywhere below in that directory structure.
|
||||||
|
|
||||||
|
However, content-addressing means that IPFS addresses are immutable. This is hardly acceptable for a website, so a strategy is needed to distribute the new IPFS CIDs each time the content changes. This is where DNSLink comes in — it specifies a standard way of distributing an up-to-date IPFS CID tied to a specific *domain name*. The IPFS CID for a DNSLink-enabled domain (like say, [`resilient.is`](https://resilient.is/)) can be found in the `TXT` record of the `_dnslink` name in it (so, [`_dnslink.resilient.is`](https://dns-lookup.com/_dnslink.resilient.is):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ kdig +short TXT _dnslink.resilient.is.
|
||||||
|
"dnslink=/ipfs/QmPCXrKyVqeXfyZg4xhJFdQPeKqTPjJgdA65iqFvjVVKp2"
|
||||||
|
```
|
||||||
|
|
||||||
|
IPFS implementations, including the [`js-ipfs`](https://js.ipfs.io/) library used by this plugin, attempt to perform DNS lookups in a way that works around any potentiall local DNS issues. This means that even if a website using the `dnslink-ipfs` plugin is temporarily unavailable locally due to name resolution failure, as long as *some* IPFS nodes can resolve the `_dnslink` label, the plugin will work.
|
||||||
|
|
||||||
|
Once the `_dnslink` resolution is completed, `js-ipfs` is used to retrieve the content of the requested file directly from the IPFS network.
|
||||||
|
|
||||||
|
## Deploying content
|
||||||
|
|
||||||
|
This plugin focuses only on content retrieval. For it to work, the website needs to have a way of pushing content to IPFS and updating the `_dnslink` label with the new IPFS CID.
|
||||||
|
|
||||||
|
The former can be achieved by running your own IPFS node (using [Kubo](https://github.com/ipfs/kubo/), for example), or using third party [IPFS pinning service](https://docs.ipfs.io/concepts/persistence/#pinning-services).
|
||||||
|
|
||||||
|
The latter could involve using your DNS provider's API to automatically update the relevant `TXT` record, using [DNS Dynamic Update](https://www.rfc-editor.org/rfc/rfc2136) if supported by your DNS provider, or running your own minimal DNS server and delegating `_dnslink` zone to it from your main zone — this is the strategy used for `resilient.is` currently.
|
||||||
|
|
||||||
|
One important consideration is the Time-to-live (`TTL`) value on the `_dnslink` `TXT` record: if content updates happen often and need to propagate fast, it needs to be as low as possible, but that will drive more DNS traffic to the nameserver, which might be a consideration. A `TTL` of `900` (15min) is probably a reasonable compromise value to start with.
|
|
@ -0,0 +1,205 @@
|
||||||
|
/**
|
||||||
|
* this is the default DNSLink+IPFS strategy plugin
|
||||||
|
* for LibResilient.
|
||||||
|
*
|
||||||
|
* it uses DNSLink for content address resolution
|
||||||
|
* and IPFS for delivery
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================= *\
|
||||||
|
|* === General stuff and setup === *|
|
||||||
|
\* ========================================================================= */
|
||||||
|
|
||||||
|
// no polluting of the global namespace please
|
||||||
|
(function(LRPC){
|
||||||
|
// this never changes
|
||||||
|
const pluginName = "dnslink-ipfs"
|
||||||
|
LRPC.set(pluginName, (LR, init={})=>{
|
||||||
|
|
||||||
|
var ipfsPromise;
|
||||||
|
|
||||||
|
// sane defaults
|
||||||
|
let defaultConfig = {
|
||||||
|
// the IPFS gateway we're using for verification when publishing; default is usually ok
|
||||||
|
//ipfsGateway: 'https://gateway.ipfs.io'
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge the defaults with settings from init
|
||||||
|
let config = {...defaultConfig, ...init}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* importing stuff works differently between a browser window context
|
||||||
|
* and a ServiceWorker context, because things just can't be easy and sane
|
||||||
|
*/
|
||||||
|
function doImport() {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
if (typeof self.importScripts !== 'undefined') {
|
||||||
|
self.importScripts.apply(self, args)
|
||||||
|
} else {
|
||||||
|
LR.log(pluginName, 'assuming these scripts are already included:')
|
||||||
|
args.forEach(function(src){
|
||||||
|
LR.log(pluginName, '+--', src)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setup_ipfs() {
|
||||||
|
LR.log(pluginName, 'Importing IPFS-related libraries...');
|
||||||
|
doImport(
|
||||||
|
"./lib/ipfs.js");
|
||||||
|
LR.log(pluginName, 'Setting up IPFS...')
|
||||||
|
try {
|
||||||
|
var ipfs = await self.Ipfs.create();
|
||||||
|
LR.log(pluginName, '+-- IPFS loaded :: ipfs is : ' + typeof ipfs)
|
||||||
|
return ipfs
|
||||||
|
} catch(e) {
|
||||||
|
LR.log(pluginName, '+-- Error loading IPFS: ' + e)
|
||||||
|
throw new Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================= *\
|
||||||
|
|* === Main functionality === *|
|
||||||
|
\* ========================================================================= */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the workhorse of this plugin
|
||||||
|
*/
|
||||||
|
async function getContentFromDNSLinkAndIPFS(url, init={}) {
|
||||||
|
|
||||||
|
// make sure IPFS is set-up
|
||||||
|
var ipfs = await ipfsPromise
|
||||||
|
|
||||||
|
// we don't want the scheme
|
||||||
|
var dnslinkAddr = url.replace(/https?:\/\//, '')
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the dnslinkAddr ends in '/', append 'index.html' to it
|
||||||
|
*
|
||||||
|
* TODO: might not be necessary; if removed, update the content-type switch statement below!
|
||||||
|
*/
|
||||||
|
if (dnslinkAddr.charAt(dnslinkAddr.length - 1) === '/') {
|
||||||
|
LR.log(pluginName, "NOTICE: address ends in '/', assuming '/index.html' should be appended.");
|
||||||
|
dnslinkAddr += 'index.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
LR.log(pluginName, "+-- starting DNSLink lookup of: '" + dnslinkAddr + "'");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* naïvely assume content type based on file extension
|
||||||
|
* TODO: this needs a fix
|
||||||
|
*/
|
||||||
|
var contentType = '';
|
||||||
|
switch (dnslinkAddr.split('.').pop().toLowerCase()) {
|
||||||
|
case 'html':
|
||||||
|
case 'htm':
|
||||||
|
contentType = 'text/html';
|
||||||
|
break;
|
||||||
|
case 'css':
|
||||||
|
contentType = 'text/css';
|
||||||
|
break;
|
||||||
|
case 'js':
|
||||||
|
contentType = 'text/javascript';
|
||||||
|
break;
|
||||||
|
case 'svg':
|
||||||
|
contentType = 'image/svg+xml';
|
||||||
|
break;
|
||||||
|
case 'ico':
|
||||||
|
contentType = 'image/x-icon';
|
||||||
|
break;
|
||||||
|
case 'json':
|
||||||
|
contentType = 'application/json';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LR.log(pluginName, " +-- guessed contentType : " + contentType);
|
||||||
|
|
||||||
|
// TODO: error handling!
|
||||||
|
return ipfs.name.resolve('/ipns/' + dnslinkAddr).next().then(ipfsaddr => {
|
||||||
|
|
||||||
|
// TODO: use the iterator, luke
|
||||||
|
LR.log(pluginName, "+-- starting IPFS retrieval of: '" + ipfsaddr.value + "'");
|
||||||
|
return ipfs.cat(ipfsaddr.value);
|
||||||
|
|
||||||
|
}).then(async (source) => {
|
||||||
|
|
||||||
|
LR.log(pluginName, '+-- started receiving file data')
|
||||||
|
// source is an iterator
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
|
||||||
|
var filedata = await source.next();
|
||||||
|
|
||||||
|
// did we get anything?
|
||||||
|
if (filedata.value) {
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
var content = new Uint8Array()
|
||||||
|
|
||||||
|
do {
|
||||||
|
LR.log(pluginName, ' +-- new data:', filedata.done, filedata.value.length)
|
||||||
|
var newContent = new Uint8Array(content.length + filedata.value.length);
|
||||||
|
newContent.set(content)
|
||||||
|
newContent.set(filedata.value, content.length)
|
||||||
|
content = newContent
|
||||||
|
filedata = await source.next()
|
||||||
|
} while (! filedata.done)
|
||||||
|
|
||||||
|
LR.log(pluginName, '+-- got a DNSLink-resolved IPFS-stored file; content is: ' + typeof content);
|
||||||
|
|
||||||
|
// creating and populating the blob
|
||||||
|
var blob = new Blob(
|
||||||
|
[content],
|
||||||
|
{'type': contentType}
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
blob,
|
||||||
|
{
|
||||||
|
'status': 200,
|
||||||
|
'statusText': 'OK',
|
||||||
|
'headers': {
|
||||||
|
'Content-Type': contentType,
|
||||||
|
'ETag': 'WOLOLO',
|
||||||
|
'X-LibResilient-Method': pluginName,
|
||||||
|
'X-LibResilient-ETag': 'WOLOLO'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
LR.log(pluginName, '+-- IPFS retrieval failed: no content.')
|
||||||
|
throw new Error('IPFS retrieval failed: no content.')
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ========================================================================= *\
|
||||||
|
|* === Publishing stuff === *|
|
||||||
|
\* ========================================================================= */
|
||||||
|
/*
|
||||||
|
* TODO: to be implemented
|
||||||
|
*/
|
||||||
|
let publishContent = (resource, user, password) => {
|
||||||
|
throw new Error("Not implemented yet.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================================================================= *\
|
||||||
|
|* === Initialization === *|
|
||||||
|
\* ========================================================================= */
|
||||||
|
|
||||||
|
// we probably need to handle this better
|
||||||
|
ipfsPromise = setup_ipfs();
|
||||||
|
|
||||||
|
|
||||||
|
// and add ourselves to it
|
||||||
|
// with some additional metadata
|
||||||
|
return {
|
||||||
|
name: pluginName,
|
||||||
|
description: 'Decentralized resource fetching using DNSLink for content address resolution and IPFS for content delivery.',
|
||||||
|
version: 'COMMIT_UNKNOWN',
|
||||||
|
fetch: getContentFromDNSLinkAndIPFS,
|
||||||
|
publish: publishContent
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
// done with not polluting the global namespace
|
||||||
|
})(LibResilientPluginConstructors)
|
Ładowanie…
Reference in New Issue