1
0
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:
Falk Werner 2020-04-11 11:21:00 +02:00 committed by GitHub
commit 64b9137961
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 124 additions and 314 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/www/node_modules
/www/package-lock.json
/www/dist

View File

@ -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

View File

@ -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)

View File

@ -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>

View File

@ -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"
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}

View File

@ -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
View 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"
}
}

View File

@ -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
View 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>

View File

@ -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
View 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"
}
}
};