mirror of
https://github.com/falk-werner/webfuse-example
synced 2024-10-27 20:44:09 +00:00
Merge pull request #2 from falk-werner/integrate_webfuse-js
Integrate webfuse-js
This commit is contained in:
commit
64b9137961
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/www/node_modules
|
||||||
|
/www/package-lock.json
|
||||||
|
/www/dist
|
22
Dockerfile
22
Dockerfile
@ -19,8 +19,6 @@ RUN set -x \
|
|||||||
libconfig-dev \
|
libconfig-dev \
|
||||||
libpam0g-dev
|
libpam0g-dev
|
||||||
|
|
||||||
COPY www /var/www
|
|
||||||
|
|
||||||
ARG PARALLELMFLAGS=-j2
|
ARG PARALLELMFLAGS=-j2
|
||||||
|
|
||||||
ARG DUMB_INIT_VERSION=1.2.2
|
ARG DUMB_INIT_VERSION=1.2.2
|
||||||
@ -124,6 +122,26 @@ RUN set -x \
|
|||||||
|
|
||||||
COPY webfused.conf /etc
|
COPY webfused.conf /etc
|
||||||
|
|
||||||
|
ARG NPM_VERSION=">=6.14.0 <7.0.0"
|
||||||
|
ARG NODEJS_VERSION=12
|
||||||
|
RUN set -x \
|
||||||
|
&& apt update \
|
||||||
|
&& apt upgrade -y \
|
||||||
|
&& apt install --yes --no-install-recommends \
|
||||||
|
nodejs \
|
||||||
|
npm \
|
||||||
|
&& npm install -g npm@"${NPM_VERSION}" \
|
||||||
|
&& npm install -g n \
|
||||||
|
&& n "${NODEJS_VERSION}"
|
||||||
|
|
||||||
|
COPY www /usr/local/src/www
|
||||||
|
RUN set -x \
|
||||||
|
&& cd /usr/local/src/www \
|
||||||
|
&& npm update --no-save \
|
||||||
|
&& npm run build \
|
||||||
|
&& mkdir -p /var/www \
|
||||||
|
&& cp -r ./dist/. /var/www/
|
||||||
|
|
||||||
ARG USERID=1000
|
ARG USERID=1000
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& useradd -u "$USERID" -ms /bin/bash user
|
&& useradd -u "$USERID" -ms /bin/bash user
|
||||||
|
17
README.md
17
README.md
@ -7,7 +7,7 @@ Example of webfuse.
|
|||||||
|
|
||||||
docker build --rm --build-arg "USERID=`id -u`" --tag webfuse .
|
docker build --rm --build-arg "USERID=`id -u`" --tag webfuse .
|
||||||
|
|
||||||
# Run
|
## Run
|
||||||
|
|
||||||
docker run -p 8080:8080 --rm -it \
|
docker run -p 8080:8080 --rm -it \
|
||||||
--device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \
|
--device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined \
|
||||||
@ -21,3 +21,18 @@ Then open another terminal and connect to the container.
|
|||||||
|
|
||||||
docker exec -it <name of container> bash
|
docker exec -it <name of container> bash
|
||||||
cat /tmp/test/hello.txt
|
cat /tmp/test/hello.txt
|
||||||
|
|
||||||
|
## Fellow Repositories
|
||||||
|
|
||||||
|
* [webfuse library](https://github.com/falk-werner/webfuse)
|
||||||
|
Create webfuse adapters and providers in C/C++
|
||||||
|
|
||||||
|
* [webfuse daemon](https://github.com/falk-werner/webfused)
|
||||||
|
Reference implementation of webfuse adapter (server)
|
||||||
|
|
||||||
|
* [webfuse-js](https://github.com/falk-werner/webfuse-js)
|
||||||
|
Create webfuse provider (client) in JavaScript
|
||||||
|
|
||||||
|
## Further Reading
|
||||||
|
|
||||||
|
* [Webfuse Protocol Specification](https://github.com/falk-werner/webfuse/blob/master/doc/protocol.md)
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>WebFuse Example</title>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<link rel="stylesheet" type="text/css" href="style/main.css">
|
|
||||||
<script type="module" src="js/startup.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="page">
|
|
||||||
<div class="window">
|
|
||||||
<div class="title">Connection</div>
|
|
||||||
<div id="connection"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "webfuse-provider",
|
|
||||||
"version": "0.2.0",
|
|
||||||
"description": "Provider for websocket filesystem (webfuse)",
|
|
||||||
"main": "startup.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "Falk Werner",
|
|
||||||
"license": "LGPL-3.0"
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
export class BadState extends Error {
|
|
||||||
static get BAD() { return 1; }
|
|
||||||
|
|
||||||
static get NOT_IMPLEMENTED() { return 2; }
|
|
||||||
static get TIMEOUT() { return 3; }
|
|
||||||
static get FORMAT() { return 4; }
|
|
||||||
|
|
||||||
static get NO_ENTRY() { return 101; }
|
|
||||||
static get NO_ACCESS() { return 102; }
|
|
||||||
|
|
||||||
constructor(code) {
|
|
||||||
super("Bad State");
|
|
||||||
this.code = code;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,223 +0,0 @@
|
|||||||
import { BadState } from "./bad_state.js";
|
|
||||||
|
|
||||||
export class Client {
|
|
||||||
static get _PROTOCOL() { return "webfuse-adapter-server"; }
|
|
||||||
|
|
||||||
constructor(provider) {
|
|
||||||
this._provider = { };
|
|
||||||
this._pendingRequests = {};
|
|
||||||
this._id = 0;
|
|
||||||
this._ws = null;
|
|
||||||
this.onopen = () => { };
|
|
||||||
this.onclose = () => { };
|
|
||||||
this.onerror = () => { };
|
|
||||||
}
|
|
||||||
|
|
||||||
connectTo(url) {
|
|
||||||
this.disconnect();
|
|
||||||
|
|
||||||
this._ws = new WebSocket(url, Client._PROTOCOL);
|
|
||||||
this._ws.onopen = this.onopen;
|
|
||||||
this._ws.onclose = this.onclose;
|
|
||||||
this._ws.onerror = this.onerror;
|
|
||||||
|
|
||||||
this._ws.onmessage = (message) => {
|
|
||||||
this._onmessage(message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_invokeRequest(method, params) {
|
|
||||||
this._id += 1;
|
|
||||||
const id = this._id;
|
|
||||||
const request = {method, params, id};
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this._pendingRequests[id] = {resolve, reject};
|
|
||||||
this._ws.send(JSON.stringify(request));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticate(type, credentials) {
|
|
||||||
return this._invokeRequest("authenticate", [type, credentials]);
|
|
||||||
}
|
|
||||||
|
|
||||||
addProvider(name, provider) {
|
|
||||||
this._provider[name] = provider;
|
|
||||||
const request = {
|
|
||||||
"method": "add_filesystem",
|
|
||||||
"params": [name],
|
|
||||||
"id": 23
|
|
||||||
};
|
|
||||||
|
|
||||||
this._ws.send(JSON.stringify(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
if (this._ws) {
|
|
||||||
this._ws.close();
|
|
||||||
this._ws = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isConnected() {
|
|
||||||
return ((this._ws) && (this._ws.readyState === WebSocket.OPEN));
|
|
||||||
}
|
|
||||||
|
|
||||||
_isRequest(request) {
|
|
||||||
const method = request.method;
|
|
||||||
|
|
||||||
return (("string" === typeof(method)) && ("params" in request));
|
|
||||||
}
|
|
||||||
|
|
||||||
_isResponse(response) {
|
|
||||||
const id = response.id;
|
|
||||||
|
|
||||||
return (("number" === typeof(id)) && (("result" in response) || ("error" in response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
_removePendingRequest(id) {
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
if (id in this._pendingRequests) {
|
|
||||||
result = this._pendingRequests[id];
|
|
||||||
Reflect.deleteProperty(this._pendingRequests, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
_onmessage(message) {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(message.data);
|
|
||||||
|
|
||||||
if (this._isRequest(data)) {
|
|
||||||
const method = data.method;
|
|
||||||
const id = data.id;
|
|
||||||
const params = data.params;
|
|
||||||
|
|
||||||
if ("number" === typeof(id)) {
|
|
||||||
this._invoke(method, params, id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this._notify(method, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (this._isResponse(data)) {
|
|
||||||
const id = data.id;
|
|
||||||
const result = data.result;
|
|
||||||
const error = data.error;
|
|
||||||
|
|
||||||
const request = this._removePendingRequest(id);
|
|
||||||
if (request) {
|
|
||||||
if (result) {
|
|
||||||
request.resolve(result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
request.reject(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
// swallow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_invoke(method, params, id) {
|
|
||||||
this._invokeAsync(method, params).
|
|
||||||
then((result) => {
|
|
||||||
const response = { result, id };
|
|
||||||
this._ws.send(JSON.stringify(response));
|
|
||||||
}).
|
|
||||||
catch((ex) => {
|
|
||||||
const code = ex.code || BadState.BAD;
|
|
||||||
const response = {error: {code}, id};
|
|
||||||
this._ws.send(JSON.stringify(response));
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async _invokeAsync(method, params) {
|
|
||||||
switch(method)
|
|
||||||
{
|
|
||||||
case "lookup":
|
|
||||||
return this._lookup(params);
|
|
||||||
case "getattr":
|
|
||||||
return this._getattr(params);
|
|
||||||
case "readdir":
|
|
||||||
return this._readdir(params);
|
|
||||||
case "open":
|
|
||||||
return this._open(params);
|
|
||||||
case "read":
|
|
||||||
return this._read(params);
|
|
||||||
default:
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_notify(method, params) {
|
|
||||||
switch(method) {
|
|
||||||
case 'close':
|
|
||||||
this._close(params);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Invalid method: "${method}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getProvider(name) {
|
|
||||||
if (name in this._provider) {
|
|
||||||
return this._provider[name];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Unknown provider');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async _lookup([providerName, parent, name]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
|
|
||||||
return provider.lookup(parent, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _getattr([providerName, inode]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
|
|
||||||
return provider.getattr(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _readdir([providerName, inode]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
|
|
||||||
return provider.readdir(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _open([providerName, inode, mode]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
|
|
||||||
return provider.open(inode, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_close([providerName, inode, handle, mode]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
|
|
||||||
provider.close(inode, handle, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _read([providerName, inode, handle, offset, length]) {
|
|
||||||
const provider = this._getProvider(providerName);
|
|
||||||
const data = await provider.read(inode, handle, offset, length);
|
|
||||||
|
|
||||||
if ("string" === typeof(data)) {
|
|
||||||
return {
|
|
||||||
data: btoa(data),
|
|
||||||
format: "base64",
|
|
||||||
count: data.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new BadState(BadState.BAD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
export class FileMode {
|
|
||||||
static get ACCESS_MODE() { return 0x003; }
|
|
||||||
static get READONLY() { return 0x000; }
|
|
||||||
static get WRITEONLY() { return 0x001; }
|
|
||||||
static get READWRITE() { return 0x002; }
|
|
||||||
static get CREATE() { return 0x040; }
|
|
||||||
static get EXCLUSIVE() { return 0x080; }
|
|
||||||
static get TRUNKATE() { return 0x200; }
|
|
||||||
static get APPEND() { return 0x400; }
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
|
||||||
|
|
||||||
import { BadState } from "./bad_state.js";
|
|
||||||
|
|
||||||
export class Provider {
|
|
||||||
|
|
||||||
async lookup(_parent, _name) {
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getattr(_inode) {
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
async readdir(_inode) {
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
async open(_inode, _mode) {
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(_inode, _handle, _mode) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
async read(_inode, _handle, _offset, _length) {
|
|
||||||
throw new BadState(BadState.NOT_IMPLEMENTED);
|
|
||||||
}
|
|
||||||
}
|
|
34
www/package.json
Normal file
34
www/package.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "webfuse-example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Example to demonstate webfuse",
|
||||||
|
"private": true,
|
||||||
|
"main": "./dist/webfuse-example.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/falk-werner/webfuse-example.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"webfuse",
|
||||||
|
"websockets",
|
||||||
|
"libfuse",
|
||||||
|
"fuse",
|
||||||
|
"example"
|
||||||
|
],
|
||||||
|
"author": "Falk Werner",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/falk-werner/webfuse-example#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"copy-webpack-plugin": "^5.1.1",
|
||||||
|
"html-webpack-plugin": "^4.2.0",
|
||||||
|
"webpack": "^4.42.1",
|
||||||
|
"webpack-cli": "^3.3.11"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"webfuse": "^0.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
||||||
|
|
||||||
import { BadState } from "./webfuse/bad_state.js";
|
import { BadState, FileMode, Provider } from "webfuse";
|
||||||
import { FileMode } from "./webfuse/file_mode.js";
|
|
||||||
import { Provider } from "./webfuse/provider.js";
|
|
||||||
|
|
||||||
export class FileSystemProvider extends Provider {
|
export class FileSystemProvider extends Provider {
|
||||||
constructor(root) {
|
constructor(root) {
|
20
www/src/index.html
Normal file
20
www/src/index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel='stylesheet' type='text/css' href='style/main.css'>
|
||||||
|
<%= htmlWebpackPlugin.tags.headTags %>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="page">
|
||||||
|
<div class="window">
|
||||||
|
<div class="title">Connection</div>
|
||||||
|
<div id="connection"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= htmlWebpackPlugin.tags.bodyTage %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,4 +1,4 @@
|
|||||||
import { Client } from "./webfuse/client.js";
|
import { Client } from "webfuse";
|
||||||
import { ConnectionView } from "./connection_view.js";
|
import { ConnectionView } from "./connection_view.js";
|
||||||
import { FileSystemProvider } from "./filesystem_provider.js";
|
import { FileSystemProvider } from "./filesystem_provider.js";
|
||||||
|
|
29
www/webpack.config.js
Normal file
29
www/webpack.config.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'production',
|
||||||
|
entry: './src/index.js',
|
||||||
|
output: {
|
||||||
|
filename: 'webfuse-example.js',
|
||||||
|
library: 'webfuse',
|
||||||
|
libraryTarget: 'umd',
|
||||||
|
path: path.resolve(__dirname, './dist')
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
title: "Webfuse Example",
|
||||||
|
filename: "index.html",
|
||||||
|
template: "./src/index.html"
|
||||||
|
}),
|
||||||
|
new CopyWebpackPlugin([
|
||||||
|
{ from: './src/style', to: 'style' }
|
||||||
|
])
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
webfuse: "webfuse/src/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user