mirror of
https://github.com/falk-werner/webfuse-example
synced 2024-10-27 20:44:09 +00:00
feature: imported sources from webfuse
This commit is contained in:
parent
288b38c7f5
commit
460fedae22
8
.travis.yml
Normal file
8
.travis.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
dist: bionic
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
script:
|
||||||
|
- docker build --rm --buildarg "USERID=`id -u`" -tag webfuse .
|
||||||
|
|
125
Dockerfile
Normal file
125
Dockerfile
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
ARG REGISTRY_PREFIX=''
|
||||||
|
ARG CODENAME=bionic
|
||||||
|
|
||||||
|
FROM ${REGISTRY_PREFIX}ubuntu:${CODENAME} as builder
|
||||||
|
|
||||||
|
RUN set -x \
|
||||||
|
&& apt update \
|
||||||
|
&& apt upgrade -y \
|
||||||
|
&& apt install --yes --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
cmake \
|
||||||
|
ninja-build \
|
||||||
|
pkg-config \
|
||||||
|
ca-certificates \
|
||||||
|
openssl \
|
||||||
|
libssl-dev \
|
||||||
|
uuid-dev \
|
||||||
|
wget
|
||||||
|
|
||||||
|
COPY www /var/www
|
||||||
|
|
||||||
|
ARG PARALLELMFLAGS=-j2
|
||||||
|
|
||||||
|
ARG DUMB_INIT_VERSION=1.2.2
|
||||||
|
RUN set -x \
|
||||||
|
&& builddeps="xxd" \
|
||||||
|
&& apt install --yes --no-install-recommends $builddeps \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/Yelp/dumb-init/archive/v${DUMB_INIT_VERSION}.tar.gz" -O dumb_init.tar.gz \
|
||||||
|
&& tar -xf dumb_init.tar.gz \
|
||||||
|
&& cd "dumb-init-$DUMB_INIT_VERSION" \
|
||||||
|
&& make "$PARALLELMFLAGS" \
|
||||||
|
&& chmod +x dumb-init \
|
||||||
|
&& mv dumb-init /usr/local/bin/dumb-init \
|
||||||
|
&& dumb-init --version \
|
||||||
|
&& rm -rf "$builddir" \
|
||||||
|
&& apt purge -y $builddeps
|
||||||
|
|
||||||
|
ARG FUSE_VERSION=3.9.0
|
||||||
|
RUN set -x \
|
||||||
|
&& builddeps="udev gettext python3 python3-pip python3-setuptools python3-wheel" \
|
||||||
|
&& apt install --yes --no-install-recommends $builddeps \
|
||||||
|
&& pip3 install --system meson \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/libfuse/libfuse/archive/fuse-${FUSE_VERSION}.tar.gz" -O libfuse.tar.gz \
|
||||||
|
&& tar -xf libfuse.tar.gz \
|
||||||
|
&& cd "libfuse-fuse-$FUSE_VERSION" \
|
||||||
|
&& mkdir .build \
|
||||||
|
&& cd .build \
|
||||||
|
&& meson .. \
|
||||||
|
&& ninja \
|
||||||
|
&& ninja install \
|
||||||
|
&& pip3 uninstall -y meson \
|
||||||
|
&& rm -rf "$builddir" \
|
||||||
|
&& apt purge -y $builddeps
|
||||||
|
|
||||||
|
ARG WEBSOCKETS_VERSION=3.2.0
|
||||||
|
RUN set -x \
|
||||||
|
&& apt install --yes --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
openssl \
|
||||||
|
libssl-dev \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/warmcat/libwebsockets/archive/v${WEBSOCKETS_VERSION}.tar.gz" -O libwebsockets.tar.gz \
|
||||||
|
&& tar -xf libwebsockets.tar.gz \
|
||||||
|
&& cd "libwebsockets-$WEBSOCKETS_VERSION" \
|
||||||
|
&& mkdir .build \
|
||||||
|
&& cd .build \
|
||||||
|
&& cmake .. \
|
||||||
|
&& make "$PARALLELMFLAGS" install \
|
||||||
|
&& rm -rf "$builddir"
|
||||||
|
|
||||||
|
ARG JANSSON_VERSION=2.12
|
||||||
|
RUN set -x \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/akheron/jansson/archive/v${JANSSON_VERSION}.tar.gz" -O jansson.tar.gz \
|
||||||
|
&& tar -xf jansson.tar.gz \
|
||||||
|
&& cd "jansson-$JANSSON_VERSION" \
|
||||||
|
&& mkdir .build \
|
||||||
|
&& cd .build \
|
||||||
|
&& cmake -DJANSSON_BUILD_DOCS=OFF ".." \
|
||||||
|
&& make "$PARALLELMFLAGS" install \
|
||||||
|
&& rm -rf "$builddir"
|
||||||
|
|
||||||
|
ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
|
||||||
|
|
||||||
|
ARG WEBFUSE_VERSION=master
|
||||||
|
RUN set -x \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/falk-werner/webfuse/archive/${WEBFUSE_VERSION}.tar.gz" -O webfuse.tar.gz \
|
||||||
|
&& tar -xf webfuse.tar.gz \
|
||||||
|
&& cd "webfuse-$WEBFUSE_VERSION" \
|
||||||
|
&& mkdir .build \
|
||||||
|
&& cd .build \
|
||||||
|
&& cmake -DWITHOUT_TESTS=ON -DWITHOUT_EXAMPLE=ON ".." \
|
||||||
|
&& make "$PARALLELMFLAGS" install \
|
||||||
|
&& rm -rf "$builddir"
|
||||||
|
|
||||||
|
ARG WEBFUSED_VERSION=master
|
||||||
|
RUN set -x \
|
||||||
|
&& builddir="/tmp/out" \
|
||||||
|
&& mkdir -p "$builddir" \
|
||||||
|
&& cd "$builddir" \
|
||||||
|
&& wget "https://github.com/falk-werner/webfused/archive/${WEBFUSED_VERSION}.tar.gz" -O webfused.tar.gz \
|
||||||
|
&& tar -xf webfused.tar.gz \
|
||||||
|
&& cd "webfused-$WEBFUSED_VERSION" \
|
||||||
|
&& mkdir .build \
|
||||||
|
&& cd .build \
|
||||||
|
&& cmake ".." \
|
||||||
|
&& make "$PARALLELMFLAGS" install \
|
||||||
|
&& rm -rf "$builddir"
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["dumb-init", "--"]
|
13
README.md
13
README.md
@ -1,2 +1,13 @@
|
|||||||
# webfuse-example
|
# webfuse-example
|
||||||
Example of webfuse
|
Example of webfuse.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
docker build --rm --buildarg "USERID=`id -u`" -tag webfuse .
|
||||||
|
|
||||||
|
# Run
|
||||||
|
|
||||||
|
docker run -p 8080:8080 --rm -it --user "`id -u`" webfuse bash
|
||||||
|
webfused -m /tmp -d /var/www -p 8080
|
||||||
|
|
||||||
|
Open a webbrowser and visit http://localhost:8080.
|
18
www/index.html
Normal file
18
www/index.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<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>
|
287
www/js/.eslintrc.js
Normal file
287
www/js/.eslintrc.js
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
module.exports = {
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"globals": {
|
||||||
|
"Atomics": "readonly",
|
||||||
|
"SharedArrayBuffer": "readonly"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"accessor-pairs": "error",
|
||||||
|
"array-bracket-newline": "error",
|
||||||
|
"array-bracket-spacing": "error",
|
||||||
|
"array-callback-return": "error",
|
||||||
|
"array-element-newline": ["error", "consistent"],
|
||||||
|
"arrow-body-style": "error",
|
||||||
|
"arrow-parens": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"arrow-spacing": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"after": true,
|
||||||
|
"before": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"block-scoped-var": "error",
|
||||||
|
"block-spacing": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"brace-style": "off",
|
||||||
|
"callback-return": "off",
|
||||||
|
"camelcase": "error",
|
||||||
|
"capitalized-comments": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"class-methods-use-this": "off",
|
||||||
|
"comma-dangle": "error",
|
||||||
|
"comma-spacing": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"after": true,
|
||||||
|
"before": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comma-style": [
|
||||||
|
"error",
|
||||||
|
"last"
|
||||||
|
],
|
||||||
|
"complexity": "error",
|
||||||
|
"computed-property-spacing": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"consistent-return": "error",
|
||||||
|
"consistent-this": "error",
|
||||||
|
"curly": "error",
|
||||||
|
"default-case": "error",
|
||||||
|
"dot-location": "error",
|
||||||
|
"dot-notation": "error",
|
||||||
|
"eol-last": "off",
|
||||||
|
"eqeqeq": "off",
|
||||||
|
"func-call-spacing": "error",
|
||||||
|
"func-name-matching": "error",
|
||||||
|
"func-names": "error",
|
||||||
|
"func-style": [
|
||||||
|
"error",
|
||||||
|
"declaration"
|
||||||
|
],
|
||||||
|
"function-paren-newline": "error",
|
||||||
|
"generator-star-spacing": "error",
|
||||||
|
"global-require": "error",
|
||||||
|
"guard-for-in": "error",
|
||||||
|
"handle-callback-err": "error",
|
||||||
|
"id-blacklist": "error",
|
||||||
|
"id-length": "error",
|
||||||
|
"id-match": "error",
|
||||||
|
"implicit-arrow-linebreak": [
|
||||||
|
"error",
|
||||||
|
"beside"
|
||||||
|
],
|
||||||
|
"indent": "off",
|
||||||
|
"indent-legacy": "off",
|
||||||
|
"init-declarations": "off",
|
||||||
|
"jsx-quotes": "error",
|
||||||
|
"key-spacing": "off",
|
||||||
|
"keyword-spacing": "off",
|
||||||
|
"line-comment-position": "error",
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"lines-around-comment": "error",
|
||||||
|
"lines-around-directive": "error",
|
||||||
|
"lines-between-class-members": "off",
|
||||||
|
"max-classes-per-file": "error",
|
||||||
|
"max-depth": "error",
|
||||||
|
"max-len": "off",
|
||||||
|
"max-lines": "error",
|
||||||
|
"max-lines-per-function": "off",
|
||||||
|
"max-nested-callbacks": "error",
|
||||||
|
"max-params": "off",
|
||||||
|
"max-statements": "off",
|
||||||
|
"max-statements-per-line": "off",
|
||||||
|
"multiline-comment-style": "error",
|
||||||
|
"new-cap": "error",
|
||||||
|
"new-parens": "error",
|
||||||
|
"newline-after-var": "off",
|
||||||
|
"newline-before-return": "error",
|
||||||
|
"newline-per-chained-call": "error",
|
||||||
|
"no-alert": "error",
|
||||||
|
"no-array-constructor": "error",
|
||||||
|
"no-async-promise-executor": "error",
|
||||||
|
"no-await-in-loop": "error",
|
||||||
|
"no-bitwise": "off",
|
||||||
|
"no-buffer-constructor": "error",
|
||||||
|
"no-caller": "error",
|
||||||
|
"no-catch-shadow": "error",
|
||||||
|
"no-confusing-arrow": "error",
|
||||||
|
"no-continue": "error",
|
||||||
|
"no-div-regex": "error",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"no-else-return": "off",
|
||||||
|
"no-empty-function": "off",
|
||||||
|
"no-eq-null": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-extend-native": "error",
|
||||||
|
"no-extra-bind": "error",
|
||||||
|
"no-extra-label": "error",
|
||||||
|
"no-extra-parens": "off",
|
||||||
|
"no-floating-decimal": "error",
|
||||||
|
"no-implicit-coercion": "error",
|
||||||
|
"no-implicit-globals": "off",
|
||||||
|
"no-implied-eval": "error",
|
||||||
|
"no-inline-comments": "error",
|
||||||
|
"no-invalid-this": "error",
|
||||||
|
"no-iterator": "error",
|
||||||
|
"no-label-var": "error",
|
||||||
|
"no-labels": "error",
|
||||||
|
"no-lone-blocks": "error",
|
||||||
|
"no-lonely-if": "error",
|
||||||
|
"no-loop-func": "error",
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"no-misleading-character-class": "error",
|
||||||
|
"no-mixed-operators": "error",
|
||||||
|
"no-mixed-requires": "error",
|
||||||
|
"no-multi-assign": "error",
|
||||||
|
"no-multi-spaces": "off",
|
||||||
|
"no-multi-str": "error",
|
||||||
|
"no-multiple-empty-lines": "error",
|
||||||
|
"no-native-reassign": "error",
|
||||||
|
"no-negated-condition": "off",
|
||||||
|
"no-negated-in-lhs": "error",
|
||||||
|
"no-nested-ternary": "error",
|
||||||
|
"no-new": "error",
|
||||||
|
"no-new-func": "error",
|
||||||
|
"no-new-object": "error",
|
||||||
|
"no-new-require": "error",
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
"no-octal-escape": "error",
|
||||||
|
"no-param-reassign": "error",
|
||||||
|
"no-path-concat": "error",
|
||||||
|
"no-plusplus": "error",
|
||||||
|
"no-process-env": "error",
|
||||||
|
"no-process-exit": "error",
|
||||||
|
"no-proto": "error",
|
||||||
|
"no-prototype-builtins": "error",
|
||||||
|
"no-restricted-globals": "error",
|
||||||
|
"no-restricted-imports": "error",
|
||||||
|
"no-restricted-modules": "error",
|
||||||
|
"no-restricted-properties": "error",
|
||||||
|
"no-restricted-syntax": "error",
|
||||||
|
"no-return-assign": "error",
|
||||||
|
"no-return-await": "error",
|
||||||
|
"no-script-url": "error",
|
||||||
|
"no-self-compare": "error",
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-shadow": "off",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-spaced-func": "error",
|
||||||
|
"no-sync": "error",
|
||||||
|
"no-tabs": "off",
|
||||||
|
"no-template-curly-in-string": "error",
|
||||||
|
"no-ternary": "off",
|
||||||
|
"no-throw-literal": "error",
|
||||||
|
"no-trailing-spaces": "off",
|
||||||
|
"no-undef-init": "error",
|
||||||
|
"no-undefined": "error",
|
||||||
|
"no-unmodified-loop-condition": "error",
|
||||||
|
"no-unneeded-ternary": "error",
|
||||||
|
"no-unused-expressions": "error",
|
||||||
|
"no-use-before-define": "error",
|
||||||
|
"no-useless-call": "error",
|
||||||
|
// "no-useless-catch": "error",
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
"no-useless-concat": "error",
|
||||||
|
"no-useless-constructor": "error",
|
||||||
|
"no-useless-rename": "error",
|
||||||
|
"no-useless-return": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"no-void": "error",
|
||||||
|
"no-warning-comments": "error",
|
||||||
|
"no-whitespace-before-property": "error",
|
||||||
|
"no-with": "error",
|
||||||
|
"nonblock-statement-body-position": "error",
|
||||||
|
"object-curly-newline": "error",
|
||||||
|
"object-curly-spacing": "off",
|
||||||
|
"object-shorthand": "off",
|
||||||
|
"one-var": "off",
|
||||||
|
"one-var-declaration-per-line": "error",
|
||||||
|
"operator-assignment": "error",
|
||||||
|
"operator-linebreak": "error",
|
||||||
|
"padded-blocks": "off",
|
||||||
|
"padding-line-between-statements": "error",
|
||||||
|
"prefer-arrow-callback": "error",
|
||||||
|
"prefer-const": "off",
|
||||||
|
"prefer-destructuring": "off",
|
||||||
|
"prefer-numeric-literals": "off",
|
||||||
|
"prefer-object-spread": "error",
|
||||||
|
"prefer-promise-reject-errors": "error",
|
||||||
|
"prefer-reflect": "error",
|
||||||
|
"prefer-rest-params": "error",
|
||||||
|
"prefer-spread": "error",
|
||||||
|
"prefer-template": "error",
|
||||||
|
"quote-props": "off",
|
||||||
|
"quotes": "off",
|
||||||
|
"radix": "error",
|
||||||
|
"require-atomic-updates": "error",
|
||||||
|
"require-await": "off",
|
||||||
|
"require-jsdoc": "off",
|
||||||
|
"require-unicode-regexp": "off",
|
||||||
|
"rest-spread-spacing": "error",
|
||||||
|
"semi": "error",
|
||||||
|
"semi-spacing": "error",
|
||||||
|
"semi-style": [
|
||||||
|
"error",
|
||||||
|
"last"
|
||||||
|
],
|
||||||
|
"sort-imports": "error",
|
||||||
|
"sort-keys": "off",
|
||||||
|
"sort-vars": "error",
|
||||||
|
"space-before-blocks": "error",
|
||||||
|
"space-before-function-paren": "off",
|
||||||
|
"space-in-parens": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"space-infix-ops": "error",
|
||||||
|
"space-unary-ops": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"nonwords": false,
|
||||||
|
"words": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spaced-comment": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"strict": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"switch-colon-spacing": "error",
|
||||||
|
"symbol-description": "error",
|
||||||
|
"template-curly-spacing": "error",
|
||||||
|
"template-tag-spacing": "error",
|
||||||
|
"unicode-bom": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"valid-jsdoc": "error",
|
||||||
|
"vars-on-top": "error",
|
||||||
|
"wrap-iife": "error",
|
||||||
|
"wrap-regex": "error",
|
||||||
|
"yield-star-spacing": "error",
|
||||||
|
"yoda": "off"
|
||||||
|
}
|
||||||
|
};
|
93
www/js/connection_view.js
Normal file
93
www/js/connection_view.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
export class ConnectionView {
|
||||||
|
constructor(client, provider) {
|
||||||
|
this._provider = provider;
|
||||||
|
this._client = client;
|
||||||
|
this._client.onopen = () => { this._onConnectionOpened(); };
|
||||||
|
this._client.onclose = () => { this._onConnectionClosed(); };
|
||||||
|
|
||||||
|
this.element = document.createElement("div");
|
||||||
|
|
||||||
|
const connectBox = document.createElement("div");
|
||||||
|
this.element.appendChild(connectBox);
|
||||||
|
|
||||||
|
const urlLabel = document.createElement("span");
|
||||||
|
urlLabel.textContent = "URL:";
|
||||||
|
connectBox.appendChild(urlLabel);
|
||||||
|
|
||||||
|
this.urlTextbox = document.createElement("input");
|
||||||
|
this.urlTextbox.type = "text";
|
||||||
|
this.urlTextbox.value = window.location.href.replace(/^http/, "ws");
|
||||||
|
connectBox.appendChild(this.urlTextbox);
|
||||||
|
|
||||||
|
this.connectButton = document.createElement("input");
|
||||||
|
this.connectButton.type = "button";
|
||||||
|
this.connectButton.value = "connect";
|
||||||
|
this.connectButton.addEventListener("click", () => { this._onConnectButtonClicked(); });
|
||||||
|
connectBox.appendChild(this.connectButton);
|
||||||
|
|
||||||
|
|
||||||
|
const authenticateBox = document.createElement("div");
|
||||||
|
this.element.appendChild(authenticateBox);
|
||||||
|
|
||||||
|
const authLabel = document.createElement("span");
|
||||||
|
authLabel.textContent = "use authentication:";
|
||||||
|
authenticateBox.appendChild(authLabel);
|
||||||
|
|
||||||
|
this.authenticateCheckbox = document.createElement("input");
|
||||||
|
this.authenticateCheckbox.type = "checkbox";
|
||||||
|
authenticateBox.appendChild(this.authenticateCheckbox);
|
||||||
|
|
||||||
|
const usernameLabel = document.createElement("span");
|
||||||
|
usernameLabel.textContent = "user:";
|
||||||
|
authenticateBox.appendChild(usernameLabel);
|
||||||
|
|
||||||
|
this.usernameTextbox = document.createElement("input");
|
||||||
|
this.usernameTextbox.type = "text";
|
||||||
|
this.usernameTextbox.value = "bob";
|
||||||
|
authenticateBox.appendChild(this.usernameTextbox);
|
||||||
|
|
||||||
|
const passwordLabel = document.createElement("span");
|
||||||
|
passwordLabel.textContent = "user:";
|
||||||
|
authenticateBox.appendChild(passwordLabel);
|
||||||
|
|
||||||
|
this.passwordTextbox = document.createElement("input");
|
||||||
|
this.passwordTextbox.type = "password";
|
||||||
|
this.passwordTextbox.value = "secret";
|
||||||
|
authenticateBox.appendChild(this.passwordTextbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onConnectButtonClicked() {
|
||||||
|
if (!this._client.isConnected()) {
|
||||||
|
let url = this.urlTextbox.value;
|
||||||
|
this._client.connectTo(url);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._client.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAuthenticateButtonClicked() {
|
||||||
|
if (this._client.isConnected()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onConnectionOpened() {
|
||||||
|
if (this.authenticateCheckbox.checked) {
|
||||||
|
const username = this.usernameTextbox.value;
|
||||||
|
const password = this.passwordTextbox.value;
|
||||||
|
|
||||||
|
const promise = this._client.authenticate("username", { username, password });
|
||||||
|
promise.then(() => { this._client.addProvider("test", this._provider); });
|
||||||
|
} else {
|
||||||
|
this._client.addProvider("test", this._provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectButton.value = "disconnect";
|
||||||
|
}
|
||||||
|
|
||||||
|
_onConnectionClosed() {
|
||||||
|
this.connectButton.value = "connect";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
122
www/js/filesystem_provider.js
Normal file
122
www/js/filesystem_provider.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/* 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";
|
||||||
|
|
||||||
|
export class FileSystemProvider extends Provider {
|
||||||
|
constructor(root) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.root = root;
|
||||||
|
this._inodes = { };
|
||||||
|
|
||||||
|
this._walk(this.root, (entry) => { this._inodes[entry.inode] = entry; });
|
||||||
|
}
|
||||||
|
|
||||||
|
_walk(node, callback) {
|
||||||
|
callback(node);
|
||||||
|
|
||||||
|
const entries = node.entries;
|
||||||
|
if (entries) {
|
||||||
|
for(let entry of Object.entries(entries)) {
|
||||||
|
this._walk(entry[1], callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async lookup(parent, name) {
|
||||||
|
const parentEntry = this._inodes[parent];
|
||||||
|
const entry = (parentEntry && parentEntry.entries && parentEntry.entries[name]) || null;
|
||||||
|
if (entry) {
|
||||||
|
return {
|
||||||
|
inode: entry.inode,
|
||||||
|
mode: entry.mode || parseInt("755", 8),
|
||||||
|
type: entry.type || "file",
|
||||||
|
size: entry.size || (entry.contents && entry.contents.length) || 0,
|
||||||
|
atime: entry.atime || 0,
|
||||||
|
mtime: entry.mtime || 0,
|
||||||
|
ctime: entry.ctime || 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getattr(inode) {
|
||||||
|
let entry = this._inodes[inode];
|
||||||
|
if (entry) {
|
||||||
|
return {
|
||||||
|
mode: entry.mode || parseInt("755", 8),
|
||||||
|
type: entry.type || "file",
|
||||||
|
size: entry.size || (entry.contents && entry.contents.length) || 0,
|
||||||
|
atime: entry.atime || 0,
|
||||||
|
mtime: entry.mtime || 0,
|
||||||
|
ctime: entry.ctime || 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async readdir(inode) {
|
||||||
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
|
if ((entry) && ("dir" === entry.type)) {
|
||||||
|
let result = [
|
||||||
|
{name: ".", inode: entry.inode},
|
||||||
|
{name: "..", inode: entry.inode}
|
||||||
|
];
|
||||||
|
for(let subdir of Object.entries(entry.entries)) {
|
||||||
|
const name = subdir[0];
|
||||||
|
const inode = subdir[1].inode;
|
||||||
|
result.push({name, inode});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(inode, mode) {
|
||||||
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
|
if (entry.type === "file") {
|
||||||
|
if ((mode & FileMode.ACCESS_MODE) === FileMode.READONLY) {
|
||||||
|
return {handle: 1337};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ACCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(_inode, _handle, _mode) {
|
||||||
|
// do nothing
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async read(inode, handle, offset, length) {
|
||||||
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
|
if (entry.type === "file") {
|
||||||
|
const end = Math.min(offset + length, entry.contents.length);
|
||||||
|
const data = (offset < entry.contents.length) ? entry.contents.substring(offset, end) : "";
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
www/js/package.json
Normal file
11
www/js/package.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
25
www/js/startup.js
Normal file
25
www/js/startup.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Client } from "./webfuse/client.js";
|
||||||
|
import { ConnectionView } from "./connection_view.js";
|
||||||
|
import { FileSystemProvider } from "./filesystem_provider.js";
|
||||||
|
|
||||||
|
|
||||||
|
function mode(value) {
|
||||||
|
return parseInt(value, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup() {
|
||||||
|
const provider = new FileSystemProvider({
|
||||||
|
inode: 1,
|
||||||
|
mode: mode("0755"),
|
||||||
|
type: "dir",
|
||||||
|
entries: {
|
||||||
|
"hello.txt" : { inode: 2, mode: mode("0444"), type: "file", contents: "Hello, World!"},
|
||||||
|
"say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const client = new Client();
|
||||||
|
const connectionView = new ConnectionView(client, provider);
|
||||||
|
document.getElementById('connection').appendChild(connectionView.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = startup;
|
15
www/js/webfuse/bad_state.js
Normal file
15
www/js/webfuse/bad_state.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
223
www/js/webfuse/client.js
Normal file
223
www/js/webfuse/client.js
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import { BadState } from "./bad_state.js";
|
||||||
|
|
||||||
|
export class Client {
|
||||||
|
static get _PROTOCOL() { return "fs"; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
www/js/webfuse/file_mode.js
Normal file
10
www/js/webfuse/file_mode.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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; }
|
||||||
|
}
|
30
www/js/webfuse/provider.js
Normal file
30
www/js/webfuse/provider.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
47
www/style/main.css
Normal file
47
www/style/main.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
html, body {
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
margin-left: 50px;
|
||||||
|
margin-right: 50px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window {
|
||||||
|
border: 1px solid black;
|
||||||
|
background-color: black;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window .title {
|
||||||
|
text-align: center;
|
||||||
|
color: #dba329;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #dba329;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commands {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
column-count: 2;
|
||||||
|
column-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content > div {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#connection {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user