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 \
|
||||
libpam0g-dev
|
||||
|
||||
COPY www /var/www
|
||||
|
||||
ARG PARALLELMFLAGS=-j2
|
||||
|
||||
ARG DUMB_INIT_VERSION=1.2.2
|
||||
@ -124,6 +122,26 @@ RUN set -x \
|
||||
|
||||
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
|
||||
RUN set -x \
|
||||
&& 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 .
|
||||
|
||||
# Run
|
||||
## Run
|
||||
|
||||
docker run -p 8080:8080 --rm -it \
|
||||
--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
|
||||
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": "^_" }] */
|
||||
|
||||
import { BadState } from "./webfuse/bad_state.js";
|
||||
import { FileMode } from "./webfuse/file_mode.js";
|
||||
import { Provider } from "./webfuse/provider.js";
|
||||
import { BadState, FileMode, Provider } from "webfuse";
|
||||
|
||||
export class FileSystemProvider extends Provider {
|
||||
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 { 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