diff --git a/.github/workflows/client_build.yml b/.github/workflows/client_build.yml index b94aefd3..945671b1 100644 --- a/.github/workflows/client_build.yml +++ b/.github/workflows/client_build.yml @@ -1,21 +1,17 @@ -name: Build Client +name: Build and Publish Client Artifacts on: workflow_call: - inputs: - with-artifact: - required: false - type: boolean - default: true - description: | - If true, the build artifacts will be uploaded as a GitHub Actions artifact. - This is useful for debugging and testing purposes. If false, the artifacts - will not be uploaded. This is useful for test builds where you don't need - the artifacts. + #pull_request: # Change to push when ready to deploy + # branches: + # - master + # paths: + # - client/** + # - .github/workflows/client_build.yml jobs: - build-client: - name: Build Client + client_build: + name: Build and Publish Client Artifacts runs-on: ubuntu-latest steps: @@ -39,7 +35,6 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v4 - if: ${{ inputs.with-artifact }} with: name: client path: client/dist diff --git a/.github/workflows/client_test.yml b/.github/workflows/client_test.yml index efebf3a6..7dbe6655 100644 --- a/.github/workflows/client_test.yml +++ b/.github/workflows/client_test.yml @@ -1,4 +1,4 @@ -name: Test Client +name: Test Client Build on: pull_request: @@ -6,14 +6,28 @@ on: - master paths: - client/** - - .github/workflows/client_build.yml - .github/workflows/client_test.yml jobs: - test-client: - name: Test Client - uses: ./.github/workflows/client_build.yml - with: - # Do not upload artifacts for test builds - with-artifact: false - secrets: inherit + client_test: + name: Test Client Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + cache-dependency-path: client/package-lock.json + + - name: Install dependencies + working-directory: ./client + run: npm ci + + - name: Build client + working-directory: ./client + run: npm run build diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml index 1d3964fb..9339eaf4 100644 --- a/.github/workflows/dockerhub.yml +++ b/.github/workflows/dockerhub.yml @@ -1,11 +1,8 @@ -name: Build and Push to Docker Hub +name: "build and push amd64 images to Docker Hub" on: push: - branches: - - master - paths-ignore: - - 'webpage/**' + branches: [ master ] # # Run this action periodically to keep browsers up-to-date # even if there is no activity in this repo. @@ -13,119 +10,68 @@ on: schedule: - cron: "43 2 * * 1" -# allow only one workflow to run at a time -# and cancel in-progress jobs if a new one is triggered -concurrency: - group: "dockerhub" - cancel-in-progress: true - env: DOCKER_IMAGE: m1k1o/neko jobs: build-base: - name: Base Image runs-on: ubuntu-latest # # do not run on forks # if: github.repository_owner == 'm1k1o' steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Extract metadata (tags, labels) for Docker - uses: docker/metadata-action@v5 - id: meta - with: - images: ${{ env.DOCKER_IMAGE }} - tags: | - type=raw,value=base + - name: Check Out Repo + uses: actions/checkout@v2 - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ github.actor }} - password: ${{ secrets.DOCKER_TOKEN }} + run: | + docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}" + env: + DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - - name: Generate base Dockerfile - run: go run utils/docker/main.go -i Dockerfile.tmpl -o Dockerfile + - name: Build base + run: | + ./build -b ${DOCKER_IMAGE}:base + docker push ${DOCKER_IMAGE}:base - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: ./ - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - build-app: - name: App Image + build: runs-on: ubuntu-latest # # do not run on forks # if: github.repository_owner == 'm1k1o' - needs: build-base + needs: [ build-base ] strategy: # Will build all images even if some fail. fail-fast: false matrix: - tag: - - firefox - # Temporarily disabled due to Cloudflare blocked download link - #- waterfox - - chromium - - google-chrome - - ungoogled-chromium - - microsoft-edge - - brave - - vivaldi - - opera - - tor-browser - - remmina - - vlc - - xfce - - kde + tags: [ firefox, waterfox, chromium, google-chrome, ungoogled-chromium, microsoft-edge, brave, vivaldi, opera, tor-browser, remmina, vlc, xfce, kde ] + env: + DOCKER_TAG: ${{ matrix.tags }} steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Extract metadata (tags, labels) for Docker - uses: docker/metadata-action@v5 - id: meta - with: - images: ${{ env.DOCKER_IMAGE }} - tags: | - type=raw,value=latest,enable=${{ matrix.tag == 'firefox' }} - type=raw,value=${{ matrix.tag }} + - name: Check Out Repo + uses: actions/checkout@v2 - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ github.actor }} - password: ${{ secrets.DOCKER_TOKEN }} + run: | + docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}" + env: + DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: apps/${{ matrix.tag }} - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - BASE_IMAGE=${{ env.DOCKER_IMAGE }}:base - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Build container + run: | + ./build -b ${DOCKER_IMAGE}:base -i ${DOCKER_IMAGE} + docker tag ${DOCKER_IMAGE}/${DOCKER_TAG} ${DOCKER_IMAGE}:${DOCKER_TAG} + docker push ${DOCKER_IMAGE}:${DOCKER_TAG} + + - name: Push latest tag + if: ${{ matrix.tags == 'firefox' }} + run: | + docker pull ${DOCKER_IMAGE}:${DOCKER_TAG} + docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest + docker push ${DOCKER_IMAGE}:latest diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml deleted file mode 100644 index 2ab82986..00000000 --- a/.github/workflows/ghcr.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Build and Push to GHCR - -on: - push: - tags: - - 'v*' - -jobs: - build-base: - name: Base Image - uses: ./.github/workflows/image_base.yml - with: - platforms: linux/amd64,linux/arm64,linux/arm/v7 - secrets: inherit - - build-app: - name: App Image - uses: ./.github/workflows/image_app.yml - needs: build-base - strategy: - # Will build all images even if some fail. - fail-fast: false - matrix: - include: - - name: firefox - platforms: linux/amd64,linux/arm64,linux/arm/v7 - # Temporarily disabled due to Cloudflare blocked download link - #- name: waterfox - # platforms: linux/amd64 - - name: chromium - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - name: google-chrome - platforms: linux/amd64 - - name: ungoogled-chromium - platforms: linux/amd64 - - name: microsoft-edge - platforms: linux/amd64 - - name: brave - platforms: linux/amd64,linux/arm64 - - name: vivaldi - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - name: opera - platforms: linux/amd64 - - name: tor-browser - platforms: linux/amd64 - - name: remmina - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - name: vlc - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - name: xfce - platforms: linux/amd64,linux/arm64,linux/arm/v7 - - name: kde - platforms: linux/amd64,linux/arm64,linux/arm/v7 - with: - name: ${{ matrix.name }} - platforms: ${{ matrix.platforms }} - secrets: inherit diff --git a/.github/workflows/ghcr_amd64.yml b/.github/workflows/ghcr_amd64.yml new file mode 100644 index 00000000..e238338d --- /dev/null +++ b/.github/workflows/ghcr_amd64.yml @@ -0,0 +1,44 @@ +name: "amd64 images" + +on: + push: + tags: + - 'v*' + +jobs: + build-base: + uses: ./.github/workflows/image_base.yml + with: + platforms: linux/amd64 + dockerfile: Dockerfile + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} + + build-apps: + uses: ./.github/workflows/image_app.yml + needs: [ build-base ] + strategy: + # Will build all images even if some fail. + fail-fast: false + matrix: + include: + - name: firefox + - name: waterfox + - name: chromium + - name: google-chrome + - name: ungoogled-chromium + - name: microsoft-edge + - name: brave + - name: vivaldi + - name: opera + - name: tor-browser + - name: remmina + - name: vlc + - name: xfce + - name: kde + with: + name: ${{ matrix.name }} + dockerfile: ${{ matrix.dockerfile }} + platforms: linux/amd64 + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} diff --git a/.github/workflows/ghcr_arm.yml b/.github/workflows/ghcr_arm.yml new file mode 100644 index 00000000..6b7cb210 --- /dev/null +++ b/.github/workflows/ghcr_arm.yml @@ -0,0 +1,36 @@ +name: "arm64v8 and arm32v7 images" + +on: + push: + tags: + - 'v*' + +jobs: + build-base: + uses: ./.github/workflows/image_base.yml + with: + flavor: arm + platforms: linux/arm64,linux/arm/v7 + dockerfile: Dockerfile + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} + + build-apps: + uses: ./.github/workflows/image_app.yml + needs: [ build-base ] + strategy: + # Will build all images even if some fail. + fail-fast: false + matrix: + include: + - name: firefox + - name: chromium + - name: vlc + - name: xfce + with: + name: ${{ matrix.name }} + dockerfile: ${{ matrix.dockerfile }} + flavor: arm + platforms: linux/arm64,linux/arm/v7 + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} diff --git a/.github/workflows/ghcr_intel.yml b/.github/workflows/ghcr_intel.yml index a6ee46b0..9032db9b 100644 --- a/.github/workflows/ghcr_intel.yml +++ b/.github/workflows/ghcr_intel.yml @@ -1,4 +1,4 @@ -name: Build and Push to GHCR for Intel +name: "intel gpu supported images" on: push: @@ -7,26 +7,24 @@ on: jobs: build-base: - name: Base Image uses: ./.github/workflows/image_base.yml with: flavor: intel platforms: linux/amd64 dockerfile: Dockerfile.intel - secrets: inherit + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} - build-app: - name: App Image + build-apps: uses: ./.github/workflows/image_app.yml - needs: build-base + needs: [ build-base ] strategy: # Will build all images even if some fail. fail-fast: false matrix: include: - name: firefox - # Temporarily disabled due to Cloudflare blocked download link - #- name: waterfox + - name: waterfox - name: chromium - name: google-chrome - name: ungoogled-chromium @@ -41,7 +39,8 @@ jobs: - name: kde with: name: ${{ matrix.name }} - flavor: intel - platforms: ${{ matrix.platforms }} dockerfile: ${{ matrix.dockerfile }} - secrets: inherit + flavor: intel + platforms: linux/amd64 + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} diff --git a/.github/workflows/ghcr_nvidia.yml b/.github/workflows/ghcr_nvidia.yml index 594241d3..2be33df4 100644 --- a/.github/workflows/ghcr_nvidia.yml +++ b/.github/workflows/ghcr_nvidia.yml @@ -1,4 +1,4 @@ -name: Build and Push to GHCR for Nvidia +name: "nvidia gpu supported images" on: push: @@ -7,18 +7,17 @@ on: jobs: build-base: - name: Base Image uses: ./.github/workflows/image_base.yml with: flavor: nvidia platforms: linux/amd64 dockerfile: Dockerfile.nvidia - secrets: inherit + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} - build-app: - name: App Image + build-apps: uses: ./.github/workflows/image_app.yml - needs: build-base + needs: [ build-base ] strategy: # Will build all images even if some fail. fail-fast: false @@ -36,7 +35,8 @@ jobs: dockerfile: Dockerfile.nvidia with: name: ${{ matrix.name }} - flavor: nvidia - platforms: ${{ matrix.platforms }} dockerfile: ${{ matrix.dockerfile }} - secrets: inherit + flavor: nvidia + platforms: linux/amd64 + secrets: + GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }} diff --git a/.github/workflows/image_app.yml b/.github/workflows/image_app.yml index db667538..1e01dc12 100644 --- a/.github/workflows/image_app.yml +++ b/.github/workflows/image_app.yml @@ -1,4 +1,4 @@ -name: Build App Image +name: Build and Publish Application Image on: workflow_call: @@ -6,7 +6,12 @@ on: name: required: true type: string - description: "The name of the app to build." + description: "The name of the application to build." + dockerfile: + required: false + type: string + default: "Dockerfile" + description: "The Dockerfile to use for building the image." flavor: required: false type: string @@ -17,18 +22,17 @@ on: type: string default: "linux/amd64" description: "The platforms to build for." - dockerfile: - required: false - type: string - default: "Dockerfile" - description: "The Dockerfile to use for building the image." + secrets: + GHCR_ACCESS_TOKEN: + required: true + description: "GitHub Container Registry access token." env: FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }} jobs: build-app: - name: Build App Image + name: Build and Publish Application Image runs-on: ubuntu-latest steps: - name: Checkout @@ -48,7 +52,6 @@ jobs: with: images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}${{ inputs.name }} tags: | - type=edge,branch=master type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} @@ -72,5 +75,3 @@ jobs: build-args: | BASE_IMAGE=ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base:sha-${{ github.sha }} platforms: ${{ inputs.platforms || 'linux/amd64' }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/image_base.yml b/.github/workflows/image_base.yml index 3ed7432a..f17931f8 100644 --- a/.github/workflows/image_base.yml +++ b/.github/workflows/image_base.yml @@ -1,8 +1,13 @@ -name: Build Base Image +name: Build and Publish Base Image on: workflow_call: inputs: + dockerfile: + required: false + type: string + default: "Dockerfile" + description: "The Dockerfile to use for building the image." flavor: required: false type: string @@ -13,24 +18,22 @@ on: type: string default: "linux/amd64" description: "The platforms to build for." - dockerfile: - required: false - type: string - default: "Dockerfile" - description: "The Dockerfile to use for building the image." + secrets: + GHCR_ACCESS_TOKEN: + required: true + description: "GitHub Container Registry access token." env: FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }} jobs: build-client: - name: Build Client Artifacts uses: ./.github/workflows/client_build.yml build-base: - name: Build Base Image + name: Build and Publish Base Image runs-on: ubuntu-latest - needs: build-client + needs: [ build-client ] steps: - name: Checkout uses: actions/checkout@v4 @@ -55,7 +58,6 @@ jobs: with: images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base tags: | - type=edge,branch=master type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} @@ -71,7 +73,7 @@ jobs: - name: Generate base Dockerfile env: RUNTIME_DOCKERFILE: ${{ inputs.dockerfile || 'Dockerfile' }} - run: go run utils/docker/main.go -i Dockerfile.tmpl -o Dockerfile -client client/dist + run: go run docker/main.go -i Dockerfile.tmpl -o Dockerfile -client client/dist - name: Build and push uses: docker/build-push-action@v6 @@ -81,5 +83,3 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: ${{ inputs.platforms || 'linux/amd64' }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/server_build.yml b/.github/workflows/server_build.yml new file mode 100644 index 00000000..b378453c --- /dev/null +++ b/.github/workflows/server_build.yml @@ -0,0 +1,65 @@ +name: Build and Publish Server Artifacts + +on: + workflow_call: + #pull_request: # Change to push when ready to deploy + # branches: + # - master + # paths: + # - server/** + # - .github/workflows/server_build.yml + +jobs: + server_build: + name: Build and Publish Server Artifacts + runs-on: ubuntu-latest + strategy: + matrix: + variant: + - name: server-amd64 + platform: linux/amd64 + dockerfile: Dockerfile + - name: server-arm64 + platform: linux/arm64 + dockerfile: Dockerfile.bookworm + fail-fast: false + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + # Temporary hotfix for the setup-qemu-action + # Ref: https://github.com/tonistiigi/binfmt/issues/240 + with: + platforms: linux/amd64,linux/arm64 + image: tonistiigi/binfmt:qemu-v7.0.0-28 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + id: build + uses: docker/build-push-action@v6 + with: + context: ./server/ + file: ./server/${{ matrix.variant.dockerfile }} + tags: ${{ matrix.variant.name }} + platforms: ${{ matrix.variant.platform }} + load: true + + - name: Copy artifacts + run: | + container_id=$(docker create ${{ matrix.variant.name }}) + mkdir -p artifacts/${{ matrix.variant.name }} + docker cp $container_id:/src/bin/. artifacts/${{ matrix.variant.name }}/ + docker rm $container_id + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.variant.name }} + path: artifacts/${{ matrix.variant.name }} diff --git a/.github/workflows/server_test.yml b/.github/workflows/server_test.yml index cb7cff6e..7c07c480 100644 --- a/.github/workflows/server_test.yml +++ b/.github/workflows/server_test.yml @@ -1,4 +1,4 @@ -name: Test Server +name: Test Server Build on: pull_request: @@ -9,8 +9,8 @@ on: - .github/workflows/server_test.yml jobs: - build-amd64: - name: Build amd64 + server_test: + name: Test Server Build runs-on: ubuntu-latest permissions: contents: read @@ -24,21 +24,3 @@ jobs: uses: docker/build-push-action@v6 with: context: ./server - platforms: linux/amd64 - - build-arm64: - name: Build arm64 - runs-on: ubuntu-24.04-arm - permissions: - contents: read - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - context: ./server - platforms: linux/arm64 diff --git a/.github/workflows/webpage_build.yml b/.github/workflows/webpage_build.yml deleted file mode 100644 index 0962ec77..00000000 --- a/.github/workflows/webpage_build.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build Webpage - -on: - workflow_call: - inputs: - with-artifact: - required: false - type: boolean - default: true - description: | - If true, the build artifacts will be uploaded as a GitHub Actions artifact. - This is useful for debugging and testing purposes. If false, the artifacts - will not be uploaded. This is useful for test builds where you don't need - the artifacts. - -jobs: - build-webpage: - name: Build Webpage - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: 18 - cache: npm - cache-dependency-path: webpage/package-lock.json - - - name: Install dependencies - working-directory: ./webpage - run: npm ci - - - name: Build webpage - working-directory: ./webpage - run: npm run build - - - if: ${{ inputs.with-artifact }} - name: Upload artifacts - uses: actions/upload-pages-artifact@v3 - with: - artifact-name: github-pages - path: ./webpage/build diff --git a/.github/workflows/webpage_deploy.yml b/.github/workflows/webpage_deploy.yml index f99925da..064b61d2 100644 --- a/.github/workflows/webpage_deploy.yml +++ b/.github/workflows/webpage_deploy.yml @@ -1,39 +1,50 @@ name: Build and Deploy Webpage to GitHub Pages on: - # Runs on pushes targeting the default branch push: branches: - master paths: - webpage/** - - .github/workflows/webpage_build.yml - .github/workflows/webpage_deploy.yml - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - jobs: - build-webpage: - name: Build Webpage - uses: ./.github/workflows/webpage_build.yml - secrets: inherit + webpage_build: + name: Build and Deploy Webpage to GitHub Pages + runs-on: ubuntu-latest - deploy-webpage: + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + cache-dependency-path: webpage/package-lock.json + + - name: Install dependencies + working-directory: ./webpage + run: npm ci + + - name: Build webpage + working-directory: ./webpage + run: npm run build + + - name: Upload Build Artifact + uses: actions/upload-pages-artifact@v3 + with: + artifact-name: github-pages + path: ./webpage/build + + webpage_deploy: name: Deploy to GitHub Pages - needs: build-webpage + needs: webpage_build + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source # Deploy to the github-pages environment environment: diff --git a/.github/workflows/webpage_test.yml b/.github/workflows/webpage_test.yml index 5d09fb4b..1fac2638 100644 --- a/.github/workflows/webpage_test.yml +++ b/.github/workflows/webpage_test.yml @@ -1,4 +1,4 @@ -name: Test Webpage +name: Test Webpage Build on: pull_request: @@ -6,14 +6,26 @@ on: - master paths: - webpage/** - - .github/workflows/webpage_build.yml - .github/workflows/webpage_test.yml jobs: - test-webpage: - name: Test Webpage - uses: ./.github/workflows/webpage_build.yml - with: - # Do not upload artifacts for test builds - with-artifact: false - secrets: inherit + webpage_test: + name: Test Webpage Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + cache-dependency-path: webpage/package-lock.json + + - name: Install dependencies + working-directory: ./webpage + run: npm ci + - name: Build webpage + working-directory: ./webpage + run: npm run build diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 100cd3bf..18d37f53 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -1,17 +1,14 @@ -# This Dockerfile is pre-processed by the ./utils/docker script, it is not meant to be used directly. +# This Dockerfile is pre-processed by the ./docker script, it is not meant to be used directly. +FROM ./runtime/xorg-deps/ AS xorg-deps FROM ./server/ AS server FROM ./client/ AS client -FROM ./utils/xorg-deps/ AS xorg-deps FROM ./runtime/$RUNTIME_DOCKERFILE AS runtime -# tells neko-rooms which version of the API to use -LABEL net.m1k1o.neko.api-version=3 - +COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so +COPY --from=xorg-deps /usr/local/lib/xorg/modules/input/neko_drv.so /usr/lib/xorg/modules/input/neko_drv.so COPY --from=server /src/bin/plugins/ /etc/neko/plugins/ COPY --from=server /src/bin/neko /usr/bin/neko COPY --from=client /src/dist/ /var/www -COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so -COPY --from=xorg-deps /usr/local/lib/xorg/modules/input/neko_drv.so /usr/lib/xorg/modules/input/neko_drv.so COPY config.yml /etc/neko/neko.yaml diff --git a/README.md b/README.md index 33aace77..2012f0c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- +

@@ -21,14 +21,11 @@ Chat on discord - - Featured|HelloGitHub - - build + build

- +
# n.eko @@ -86,60 +83,47 @@ Compared to clientless remote desktop gateway (e.g. [Apache Guacamole](https://g ### Supported browsers
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ... see [all available images](https://neko.m1k1o.net/docs/v3/installation/docker-images) + + + + + + + +
-### Other applications +### Other programs
- - - - - - - - - - - - + + + + ... others in m1k1o/neko-apps
-### Why neko? +### Features + + * Text Chat (With basic markdown support, discord flavor) + * Admin users (Kick, Ban & Force Give/Release Controls, Lock room) + * Clipboard synchronization (on [supported browsers](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText)) + * Emote overlay + * Ignore user (chat and emotes) + * Persistent settings + * Automatic Login with custom url args. (add `?usr=&pwd=` to the url.) + * Broadcasting room content using RTMP (to e.g. twitch or youtube...) + * Bidirectional file transfer (if enabled) + +
+ +With `NEKO_FILE_TRANSFER_ENABLED=true`: + + +
+ +### Why n.eko? I like cats 🐱 (`Neko` is the Japanese word for cat), I'm a weeb/nerd. @@ -147,26 +131,28 @@ I like cats 🐱 (`Neko` is the Japanese word for cat), I'm a weeb/nerd. ## Multiple rooms -For neko room management software, visit [neko-rooms](https://github.com/m1k1o/neko-rooms). +For n.eko room management software, visit [neko-rooms](https://github.com/m1k1o/neko-rooms). -It also offers [Zero-knowledge installation (with HTTPS)](https://github.com/m1k1o/neko-rooms/?tab=readme-ov-file#zero-knowledge-installation-with-https). +It also offers zero-knowledge [installation script (with HTTPS and Traefik)](https://github.com/m1k1o/neko-rooms/#zero-knowledge-installation-with-https-and-traefik). ## Documentation -Full documentation is available at [neko.m1k1o.net](https://neko.m1k1o.net/). Key sections include: +* [Getting Started](https://neko.m1k1o.net/#/getting-started/) + * [Quick Start](https://neko.m1k1o.net/#/getting-started/quick-start) + * [Examples](https://neko.m1k1o.net/#/getting-started/examples) + * [Reverse Proxy](https://neko.m1k1o.net/#/getting-started/reverse-proxy) + * [Configuration](https://neko.m1k1o.net/#/getting-started/configuration) + * [Troubleshooting](https://neko.m1k1o.net/#/getting-started/troubleshooting) +* [Mobile Support](https://neko.m1k1o.net/#/mobile-support) +* [Contributing](https://neko.m1k1o.net/#/contributing) + * [Non Goals](https://neko.m1k1o.net/#/non-goals) + * [Technologies](https://neko.m1k1o.net/#/technologies) +* [Changelog](https://neko.m1k1o.net/#/changelog) -- [Migration from V2](https://neko.m1k1o.net/docs/v3/migration-from-v2) -- [Getting Started](https://neko.m1k1o.net/docs/v3/quick-start) -- [Installation](https://neko.m1k1o.net/docs/v3/installation) -- [Examples](https://neko.m1k1o.net/docs/v3/installation/examples) -- [Configuration](https://neko.m1k1o.net/docs/v3/configuration) -- [Frequently Asked Questions](https://neko.m1k1o.net/docs/v3/faq) -- [Troubleshooting](https://neko.m1k1o.net/docs/v3/troubleshooting) +## How to contribute? How to build? -## How to Contribute - -Contributions are welcome! Check the [Contributing Guide](https://neko.m1k1o.net/contributing) for details. +Navigate to [.docker](.docker) folder for further information. ## Support -If you find Neko useful, consider supporting the project via [GitHub Sponsors](https://github.com/sponsors/m1k1o). +If you want to support this project, you can do it [here](https://github.com/sponsors/m1k1o). diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 7db0c453..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,19 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -If there are any vulnerabilities in **m1k1o/neko**, don't hesitate to _report them_. - -1. Send an email to `security@m1k1o.net`. - -2. Describe the vulnerability. - - If you have a fix, that is most welcome -- please attach or summarize it in your message! - -3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. - - Please **do not disclose the vulnerability publicly** until a fix is released! - -4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. - -We appreciate your help in keeping Neko secure. diff --git a/apps/brave/Dockerfile b/apps/brave/Dockerfile index 53c4745b..a0e2890d 100644 --- a/apps/brave/Dockerfile +++ b/apps/brave/Dockerfile @@ -1,13 +1,12 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE RUN set -eux; apt-get update; \ apt-get install -y --no-install-recommends apt-transport-https curl openbox; \ # # install brave browser - ARCH=$(dpkg --print-architecture); \ curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg; \ - echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=${ARCH}] https://brave-browser-apt-release.s3.brave.com/ stable main" \ + echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=amd64] https://brave-browser-apt-release.s3.brave.com/ stable main" \ | tee /etc/apt/sources.list.d/brave-browser-release.list; \ apt-get update; \ apt-get install -y --no-install-recommends brave-browser; \ diff --git a/apps/brave/Dockerfile.nvidia b/apps/brave/Dockerfile.nvidia index acd6da94..0ac80280 100644 --- a/apps/brave/Dockerfile.nvidia +++ b/apps/brave/Dockerfile.nvidia @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest +ARG BASE_IMAGE=m1k1o/neko:nvidia-base FROM $BASE_IMAGE RUN set -eux; apt-get update; \ diff --git a/apps/brave/supervisord.nvidia.conf b/apps/brave/supervisord.nvidia.conf index ef25d415..995886a9 100644 --- a/apps/brave/supervisord.nvidia.conf +++ b/apps/brave/supervisord.nvidia.conf @@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/brave-browser --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --ignore-gpu-blocklist --disable-seccomp-filter-sandbox - --use-angle=vulkan + --use-gl=egl --disable-software-rasterizer --disable-dev-shm-usage - --disable-vulkan-surface - --enable-unsafe-webgpu stopsignal=INT autorestart=true priority=800 diff --git a/apps/chromium/Dockerfile b/apps/chromium/Dockerfile index 4a3171f5..70f4085d 100644 --- a/apps/chromium/Dockerfile +++ b/apps/chromium/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/apps/chromium/Dockerfile.nvidia b/apps/chromium/Dockerfile.nvidia index 8af30e5e..752a7d98 100644 --- a/apps/chromium/Dockerfile.nvidia +++ b/apps/chromium/Dockerfile.nvidia @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/apps/chromium/supervisord.nvidia.conf b/apps/chromium/supervisord.nvidia.conf index 2f83368d..7eb6785d 100644 --- a/apps/chromium/supervisord.nvidia.conf +++ b/apps/chromium/supervisord.nvidia.conf @@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/chromium --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --ignore-gpu-blocklist --disable-seccomp-filter-sandbox - --use-angle=vulkan + --use-gl=egl --disable-software-rasterizer --disable-dev-shm-usage - --disable-vulkan-surface - --enable-unsafe-webgpu stopsignal=INT autorestart=true priority=800 diff --git a/apps/firefox/Dockerfile b/apps/firefox/Dockerfile index b07b02b9..6a510434 100644 --- a/apps/firefox/Dockerfile +++ b/apps/firefox/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # @@ -10,7 +10,7 @@ RUN set -eux; apt-get update; \ # # install firefox-esr for armhf apt-get install -y --no-install-recommends firefox-esr; \ - ln -s /usr/lib/firefox-esr /usr/lib/firefox; \ + ln -s /usr/bin/firefox-esr /usr/bin/firefox; \ # # install extensions mkdir -p /usr/lib/firefox-esr/distribution/extensions; \ diff --git a/apps/firefox/Dockerfile.nvidia b/apps/firefox/Dockerfile.nvidia index 62df72f0..a43c5279 100644 --- a/apps/firefox/Dockerfile.nvidia +++ b/apps/firefox/Dockerfile.nvidia @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US" diff --git a/apps/firefox/neko.js b/apps/firefox/neko.js index 0552774a..a55e1e51 100644 --- a/apps/firefox/neko.js +++ b/apps/firefox/neko.js @@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true); lockPref("profile.allow_automigration", false); lockPref("signon.prefillForms", false); lockPref("signon.rememberSignons", false); -//lockPref("xpinstall.enabled", false); -//lockPref("xpinstall.whitelist.required", true); +lockPref("xpinstall.enabled", false); +lockPref("xpinstall.whitelist.required", true); lockPref("browser.download.manager.retention", 0); lockPref("browser.download.folderList", 2); lockPref("browser.download.forbid_open_with", true); diff --git a/apps/google-chrome/Dockerfile b/apps/google-chrome/Dockerfile index 04347401..669c5ee1 100644 --- a/apps/google-chrome/Dockerfile +++ b/apps/google-chrome/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" diff --git a/apps/google-chrome/Dockerfile.nvidia b/apps/google-chrome/Dockerfile.nvidia index 9dc778ee..f96645b5 100644 --- a/apps/google-chrome/Dockerfile.nvidia +++ b/apps/google-chrome/Dockerfile.nvidia @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest +ARG BASE_IMAGE=m1k1o/neko:nvidia-base FROM $BASE_IMAGE #Β latest working version with EGL: 111.0.5563.146, revert when resolved diff --git a/apps/google-chrome/supervisord.nvidia.conf b/apps/google-chrome/supervisord.nvidia.conf index 1e6ca2ed..f3821fd0 100644 --- a/apps/google-chrome/supervisord.nvidia.conf +++ b/apps/google-chrome/supervisord.nvidia.conf @@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/google-chrome --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --ignore-gpu-blocklist --disable-seccomp-filter-sandbox - --use-angle=vulkan + --use-gl=egl --disable-software-rasterizer --disable-dev-shm-usage - --disable-vulkan-surface - --enable-unsafe-webgpu stopsignal=INT autorestart=true priority=800 diff --git a/apps/kde/Dockerfile b/apps/kde/Dockerfile index 3ca3ba38..a96820b6 100644 --- a/apps/kde/Dockerfile +++ b/apps/kde/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/apps/microsoft-edge/Dockerfile b/apps/microsoft-edge/Dockerfile index 50724792..a291fad2 100644 --- a/apps/microsoft-edge/Dockerfile +++ b/apps/microsoft-edge/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/" diff --git a/apps/microsoft-edge/Dockerfile.nvidia b/apps/microsoft-edge/Dockerfile.nvidia index 177e8490..1ba67c18 100644 --- a/apps/microsoft-edge/Dockerfile.nvidia +++ b/apps/microsoft-edge/Dockerfile.nvidia @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/" diff --git a/apps/microsoft-edge/supervisord.nvidia.conf b/apps/microsoft-edge/supervisord.nvidia.conf index beb2df4d..971bb3b7 100644 --- a/apps/microsoft-edge/supervisord.nvidia.conf +++ b/apps/microsoft-edge/supervisord.nvidia.conf @@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/microsoft-edge --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --ignore-gpu-blocklist --disable-seccomp-filter-sandbox - --use-angle=vulkan + --use-gl=egl --disable-software-rasterizer --disable-dev-shm-usage - --disable-vulkan-surface - --enable-unsafe-webgpu stopsignal=INT autorestart=true priority=800 diff --git a/apps/opera/Dockerfile b/apps/opera/Dockerfile index dc333e08..e02697d6 100644 --- a/apps/opera/Dockerfile +++ b/apps/opera/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG API_URL="https://download5.operacdn.com/pub/opera/desktop/" diff --git a/apps/remmina/Dockerfile b/apps/remmina/Dockerfile index fae54608..e527c218 100644 --- a/apps/remmina/Dockerfile +++ b/apps/remmina/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # install remmina diff --git a/apps/remmina/run-remmina.sh b/apps/remmina/run-remmina.sh index f2d22979..a9817805 100755 --- a/apps/remmina/run-remmina.sh +++ b/apps/remmina/run-remmina.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -u err() { echo "ERROR: $*" >&2 @@ -15,28 +16,23 @@ if [[ -n "$REMMINA_PROFILE" ]]; then exec remmina -c "$profile" fi -if [[ ! -z "$REMMINA_URL" ]]; then - readarray -t arr < <( echo -n "$REMMINA_URL" | perl -pe 's|^(\w+)\:\/\/(?:([^:]+)(?::([^@]+))?@)?(.*)$|\1\n\2\n\3\n\4|' ) - proto="${arr[0]}" - user="${arr[1]}" - pw="${arr[2]}" - host="${arr[3]}" - echo "Parsed url in 'REMMINA_URL': proto:$proto username:$user host:$host" +[[ -z "$REMMINA_URL" ]] && err "Neither 'REMMINA_PROFILE' nor 'REMMINA_URL' found in env vars" - [[ "$proto" != "vnc" && "$proto" != "rdp" && "$proto" != "spice" ]] && err "Unsupported protocol $proto in connection url 'REMMINA_URL'" +readarray -t arr < <( echo -n "$REMMINA_URL" | perl -pe 's|^(\w+)\:\/\/(?:([^:]+)(?::([^@]+))?@)?(.*)$|\1\n\2\n\3\n\4|' ) +proto="${arr[0]}" +user="${arr[1]}" +pw="${arr[2]}" +host="${arr[3]}" +echo "Parsed url in 'REMMINA_URL': proto:$proto username:$user host:$host" - profile="$profile_dir"/"$proto".remmina - remmina --set-option username="$user" --update-profile "$profile" - remmina --set-option password="$pw" --update-profile "$profile" - remmina --set-option server="$host" --update-profile "$profile" +[[ "$proto" != "vnc" && "$proto" != "rdp" && "$proto" != "spice" ]] && err "Unsupported protocol $proto in connection url 'REMMINA_URL'" - # remmina --set-option window_maximize=1 --update-profile "$profile" - # remmina --set-option scale=1 --update-profile "$profile" +profile="$profile_dir"/"$proto".remmina +remmina --set-option username="$user" --update-profile "$profile" +remmina --set-option password="$pw" --update-profile "$profile" +remmina --set-option server="$host" --update-profile "$profile" - echo "Running remmina with URL $REMMINA_URL" - exec remmina -c "$profile" -fi +# remmina --set-option window_maximize=1 --update-profile "$profile" +# remmina --set-option scale=1 --update-profile "$profile" - -echo "Running remmina without connection profile" -exec remmina +exec remmina -c "$profile" diff --git a/apps/tor-browser/Dockerfile b/apps/tor-browser/Dockerfile index 8ded6895..dd8c5bfc 100644 --- a/apps/tor-browser/Dockerfile +++ b/apps/tor-browser/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/apps/ungoogled-chromium/Dockerfile b/apps/ungoogled-chromium/Dockerfile index d7ab45a3..97a6cac1 100644 --- a/apps/ungoogled-chromium/Dockerfile +++ b/apps/ungoogled-chromium/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG API_URL="https://api.github.com/repos/macchrome/linchrome/releases/latest" diff --git a/apps/vivaldi/Dockerfile b/apps/vivaldi/Dockerfile index 6ce74a6b..b5fa22d6 100644 --- a/apps/vivaldi/Dockerfile +++ b/apps/vivaldi/Dockerfile @@ -1,14 +1,17 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE +ARG VIVALDI_VERSION="5.3.2679.34-1" +# TODO: Get chromium version from vivaldi +ARG CHROMIUM_VERSION="102.0.5005.72" + # # install vivaldi SHELL ["/bin/bash", "-c"] RUN set -eux; apt-get update; \ - ARCH=$(dpkg --print-architecture); \ - wget -O /tmp/vivaldi.deb "https://downloads.vivaldi.com/stable/vivaldi-stable_${ARCH}.deb"; \ - apt-get install -y --no-install-recommends wget unzip xz-utils jq openbox; \ - apt install -y --no-install-recommends /tmp/vivaldi.deb; \ + wget -O /tmp/vivaldi.deb "https://downloads.vivaldi.com/stable/vivaldi-stable_${VIVALDI_VERSION}_amd64.deb"; \ + apt-get install -y --no-install-recommends wget unzip xz-utils jq openbox /tmp/vivaldi.deb; \ + /opt/vivaldi/update-ffmpeg; \ # # install latest version of uBlock Origin and SponsorBlock for YouTube EXTENSIONS_DIR="/usr/share/chromium/extensions"; \ @@ -19,7 +22,7 @@ RUN set -eux; apt-get update; \ mkdir -p "${EXTENSIONS_DIR}"; \ for EXT_ID in "${EXTENSIONS[@]}"; \ do \ - EXT_URL="https://clients2.google.com/service/update2/crx?response=redirect&prodversion=100&acceptformat=crx2,crx3&x=id%3D${EXT_ID}%26installsource%3Dondemand%26uc"; \ + EXT_URL="https://clients2.google.com/service/update2/crx?response=redirect&nacl_arch=x86-64&prodversion=${CHROMIUM_VERSION}&acceptformat=crx2,crx3&x=id%3D${EXT_ID}%26installsource%3Dondemand%26uc"; \ EXT_PATH="${EXTENSIONS_DIR}/${EXT_ID}.crx"; \ wget -O "${EXT_PATH}" "${EXT_URL}"; \ EXT_VERSION="$(unzip -p "${EXT_PATH}" manifest.json 2>/dev/null | jq -r ".version")"; \ diff --git a/apps/vlc/Dockerfile b/apps/vlc/Dockerfile index 02572d2f..aea2e1d6 100644 --- a/apps/vlc/Dockerfile +++ b/apps/vlc/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/apps/waterfox/Dockerfile b/apps/waterfox/Dockerfile index 7dbf9d80..49f4c66b 100644 --- a/apps/waterfox/Dockerfile +++ b/apps/waterfox/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE ARG SRC_URL="https://cdn1.waterfox.net/waterfox/releases/latest/linux" @@ -10,7 +10,7 @@ RUN set -eux; apt-get update; \ xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \ # # fetch latest release - wget --user-agent="Mozilla/5.0" -O /tmp/waterfox-setup.tar.bz2 "${SRC_URL}"; \ + wget -O /tmp/waterfox-setup.tar.bz2 "${SRC_URL}"; \ mkdir /usr/lib/waterfox; \ tar -xjf /tmp/waterfox-setup.tar.bz2 -C /usr/lib; \ rm -f /tmp/waterfox-setup.tar.bz2; \ diff --git a/apps/waterfox/neko.js b/apps/waterfox/neko.js index 0552774a..a55e1e51 100644 --- a/apps/waterfox/neko.js +++ b/apps/waterfox/neko.js @@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true); lockPref("profile.allow_automigration", false); lockPref("signon.prefillForms", false); lockPref("signon.rememberSignons", false); -//lockPref("xpinstall.enabled", false); -//lockPref("xpinstall.whitelist.required", true); +lockPref("xpinstall.enabled", false); +lockPref("xpinstall.whitelist.required", true); lockPref("browser.download.manager.retention", 0); lockPref("browser.download.folderList", 2); lockPref("browser.download.forbid_open_with", true); diff --git a/apps/xfce/Dockerfile b/apps/xfce/Dockerfile index b9f45b0c..b1aa6a7d 100644 --- a/apps/xfce/Dockerfile +++ b/apps/xfce/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=m1k1o/neko:base FROM $BASE_IMAGE # diff --git a/build b/build index 9c311608..0e061a5e 100755 --- a/build +++ b/build @@ -2,57 +2,39 @@ set -e cd "$(dirname "$0")" -# -# This script builds the neko base image and all the applications -# - # disable buildx because of https://github.com/docker/buildx/issues/847 -# if you want to use buildx, set USE_BUILDX=1 if [ -z "$USE_BUILDX" ]; then USE_BUILDX=0 fi # check if docker buildx is available, its not docker-buildx command but rather subcommand of docker -#if [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then -# if docker buildx version >/dev/null 2>&1; then -# USE_BUILDX=1 -# fi -#fi +if [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then + if docker buildx version >/dev/null 2>&1; then + USE_BUILDX=1 + fi +fi + +# +# This script builds the neko base image and all the applications +# function log() { echo "$(date +'%Y-%m-%d %H:%M:%S') - [NEKO] - $1" > /dev/stderr } function help() { - echo "Usage: $0 [options] [image]" - echo - echo "Options:" - echo " -p, --platform : The platform (default: system architecture)" - echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)" - echo " -t, --tag : The image tag, can be specified multiple times, if not specified" - echo " uses 'latest' and if available, current git semver tag (v*.*.*)" - echo " -f, --flavor : The flavor, if not specified, builds without flavor" - echo " -b, --base_image : The base image name (default: [-]base:)" - echo " -a, --application : The app to build, if not specified, builds the base image" - echo " -y, --yes : Skip confirmation prompts" - echo " --no-cache : Build without docker cache" - echo " --push : Push the image to the registry after building" - echo " -h, --help : Show this help message" - echo - echo "Positional arguments:" - echo " : The image name, if not specified, uses the full image name" - echo " in the format /-:" - echo " Example: ghcr.io/m1k1o/neko/nvidia-firefox:latest" - echo " You can override any of the above options by specifying them" - echo " after the image name." - echo - echo "Environment variables:" - echo " USE_BUILDX : Set to 1 to use docker buildx instead of docker build" - echo " (default: 0)" - echo " CLIENT_DIST : The client dist file to use, if not specified, builds them" - echo " from the source code." - echo " (options) : Options can be specified as environment variables, for example:" - echo " PLATFORM=linux/arm64 $0 --repository ghcr.io/m1k1o/neko" + echo "Usage: $0" + echo " -p, --platform : The platform (default: linux/amd64)" + echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)" + echo " -t, --tag : The image tag, can be specified multiple times, if not specified" + echo " uses 'latest' and if available, current git semver tag (v*.*.*)" + echo " -f, --flavor : The flavor, if not specified, builds without flavor" + echo " -b, --base : The base image name (default: [-]base:)" + echo " -a, --app : The app to build, if not specified, builds the base image" + echo " -y, --yes : Skip confirmation prompts" + echo " --no-cache : Build without docker cache" + echo " --push : Push the image to the registry after building" + echo " -h, --help : Show this help message" } FULL_IMAGE="" @@ -62,8 +44,8 @@ while [[ "$#" -gt 0 ]]; do --repository|-r) REPOSITORY="$2"; shift ;; --tag|-t) TAGS+=("$2"); TAG="$2"; shift ;; --flavor|-f) FLAVOR="$2"; shift ;; - --base_image|-b) BASE_IMAGE="$2"; shift ;; - --application|-a) APPLICATION="$2"; shift ;; + --base|-b) BASE_IMAGE="$2"; shift ;; + --app|-a) APPLICATION="$2"; shift ;; --yes|-y) YES=1 ;; --no-cache) NO_CACHE="--no-cache" log "Building without cache" ;; --push) PUSH=1 ;; @@ -149,12 +131,17 @@ function build_image() { local APPLICATION_IMAGE="$1" shift - # get list of tags in full format: : - local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}" local FULL_TAGS=() - for T in "${TAGS[@]}"; do - FULL_TAGS+=("$IMAGE_NO_TAG:$T") - done + # if the image name starts with local/, just use the tag as is + if [[ "$APPLICATION_IMAGE" == *"local/"* ]]; then + FULL_TAGS=("$APPLICATION_IMAGE") + else + # get list of tags in full format: : + local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}" + for T in "${TAGS[@]}"; do + FULL_TAGS+=("$IMAGE_NO_TAG:$T") + done + fi if [ -z "$USE_BUILDX" ] || [ "$USE_BUILDX" != "1" ]; then # if buildx is not available, use docker build @@ -210,18 +197,7 @@ else fi if [ -z "$PLATFORM" ]; then - # use system architecture if not specified - UNAME="$(uname -m)" - if [ "$UNAME" == "x86_64" ]; then - PLATFORM="linux/amd64" - elif [ "$UNAME" == "aarch64" ] || [ "$UNAME" == "arm64" ]; then - PLATFORM="linux/arm64" - elif [ "$UNAME" == "armv7l" ]; then - PLATFORM="linux/arm/v7" - else - log "Unknown architecture: $UNAME" - exit 1 - fi + PLATFORM="linux/amd64" fi log "Using platform: $PLATFORM" @@ -314,10 +290,10 @@ fi log "Building base image: $BASE_IMAGE" docker run --rm -i \ - -v "$(pwd)":/src \ + -v ./:/src \ -e "RUNTIME_DOCKERFILE=$RUNTIME_DOCKERFILE" \ --workdir /src \ --entrypoint go \ golang:1.24-bullseye \ - run utils/docker/main.go \ - -i Dockerfile.tmpl -client "$CLIENT_DIST" | build_image $BASE_IMAGE -f - . + run ./docker/main.go \ + -i Dockerfile.tmpl -client $CLIENT_DIST | build_image $BASE_IMAGE -f - . diff --git a/client/src/app.vue b/client/src/app.vue index e908ebd1..668e7a42 100644 --- a/client/src/app.vue +++ b/client/src/app.vue @@ -119,43 +119,23 @@ } } - @media only screen and (max-width: 1024px) { - html, - body { - overflow-y: auto !important; - width: auto !important; - height: auto !important; - } - - body > p { - display: none; - } - - #neko { - position: relative; - flex-direction: column; - max-height: initial !important; - + @media only screen and (max-width: 600px) { + #neko.expanded { .neko-main { - height: 100vh; + transform: translateX(calc(-100% + 65px)); + + video { + display: none; + } } .neko-menu { - height: 100vh; - width: 100% !important; - } - } - } - - @media only screen and (max-width: 1024px) and (orientation: portrait) { - #neko { - &.expanded .neko-main { - height: 40vh; - } - - &.expanded .neko-menu { - height: 60vh; - width: 100% !important; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 65px; + width: calc(100% - 65px); } } } @@ -237,20 +217,6 @@ } } - @Watch('side') - onSide(side: boolean) { - if (side) { - console.log('side enabled') - // scroll to the side - this.$nextTick(() => { - const side = document.querySelector('aside') - if (side) { - side.scrollIntoView({ behavior: 'smooth', block: 'start' }) - } - }) - } - } - controlAttempt() { if (this.shakeKbd || this.$accessor.remote.hosted) return diff --git a/client/src/components/connect.vue b/client/src/components/connect.vue index fbe35645..92d04476 100644 --- a/client/src/components/connect.vue +++ b/client/src/components/connect.vue @@ -24,7 +24,7 @@ diff --git a/client/src/neko/events.ts b/client/src/neko/events.ts index fc76e83c..45c264db 100644 --- a/client/src/neko/events.ts +++ b/client/src/neko/events.ts @@ -15,7 +15,7 @@ export const EVENT = { ERROR: 'system/error', }, CLIENT: { - HEARTBEAT: 'client/heartbeat', + HEARTBEAT: 'client/heartbeat' }, SIGNAL: { OFFER: 'signal/offer', diff --git a/client/src/plugins/i18n.ts b/client/src/plugins/i18n.ts index 7d563a15..6b199e86 100644 --- a/client/src/plugins/i18n.ts +++ b/client/src/plugins/i18n.ts @@ -1,31 +1,11 @@ import Vue from 'vue' import VueI18n from 'vue-i18n' import { messages } from '~/locale' -import { get } from '~/utils/localstorage' Vue.use(VueI18n) -const fallbackLocale = 'en' - -function detectBrowserLanguage(): string { - const browserLang = navigator.language.toLowerCase() - - const supportedLangs = Object.keys(messages) - if (supportedLangs.includes(browserLang)) { - return browserLang - } - - const baseLang = browserLang.split('-')[0] - const matchingLang = supportedLangs.find((lang) => lang.startsWith(baseLang)) - if (matchingLang) { - return matchingLang - } - - return fallbackLocale -} - export const i18n = new VueI18n({ - locale: get('lang', detectBrowserLanguage()), - fallbackLocale, + locale: 'en', + fallbackLocale: 'en', messages, }) diff --git a/client/src/store/remote.ts b/client/src/store/remote.ts index 5cd40cd0..c8e9d282 100644 --- a/client/src/store/remote.ts +++ b/client/src/store/remote.ts @@ -18,9 +18,6 @@ export const state = () => ({ }) export const getters = getterTree(state, { - controlling: (state, getters, root) => { - return root.user.id === state.id - }, hosting: (state, getters, root) => { return root.user.id === state.id || state.implicitHosting }, @@ -92,7 +89,7 @@ export const actions = actionTree( }, request({ getters }) { - if (!accessor.connected || getters.controlling) { + if (!accessor.connected || getters.hosting) { return } diff --git a/config.yml b/config.yml index 8cb20f2c..42be43b0 100644 --- a/config.yml +++ b/config.yml @@ -15,3 +15,6 @@ session: cookie: # needed for legacy API enabled: false + +webrtc: + icelite: true diff --git a/docker-compose.yaml b/docker-compose.yaml index c8f81530..8738b2b8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,14 +1,15 @@ +version: "3.4" services: neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" + image: "m1k1o/neko:firefox" restart: "unless-stopped" shm_size: "2gb" ports: - "8080:8080" - "52000-52100:52000-52100/udp" environment: - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 + NEKO_SCREEN: 1920x1080@30 + NEKO_PASSWORD: neko + NEKO_PASSWORD_ADMIN: admin + NEKO_EPR: 52000-52100 + NEKO_ICELITE: 1 diff --git a/docker/go.mod b/docker/go.mod new file mode 100644 index 00000000..ee6c7af1 --- /dev/null +++ b/docker/go.mod @@ -0,0 +1,3 @@ +module github.com/m1k1o/neko/docker + +go 1.24.1 diff --git a/utils/docker/main.go b/docker/main.go similarity index 100% rename from utils/docker/main.go rename to docker/main.go diff --git a/runtime/Dockerfile b/runtime/Dockerfile index b4c165ef..880781be 100644 --- a/runtime/Dockerfile +++ b/runtime/Dockerfile @@ -103,9 +103,7 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/ # # add healthcheck HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ - CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ - wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \ - exit 1 + CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1 # # run neko diff --git a/runtime/Dockerfile.bookworm b/runtime/Dockerfile.bookworm index b53e1ad0..07e8e6b9 100644 --- a/runtime/Dockerfile.bookworm +++ b/runtime/Dockerfile.bookworm @@ -95,9 +95,8 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/ # # add healthcheck HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ - CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ - wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \ - exit 1 + CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1 + # # run neko CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"] diff --git a/runtime/Dockerfile.intel b/runtime/Dockerfile.intel index bb44c189..657cd8af 100644 --- a/runtime/Dockerfile.intel +++ b/runtime/Dockerfile.intel @@ -115,9 +115,8 @@ ENV RENDER_GID= # # add healthcheck HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ - CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ - wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \ - exit 1 + CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1 + # # run neko CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"] diff --git a/runtime/Dockerfile.nvidia b/runtime/Dockerfile.nvidia index 1cf652c5..88f1c1e0 100644 --- a/runtime/Dockerfile.nvidia +++ b/runtime/Dockerfile.nvidia @@ -1,6 +1,6 @@ ARG UBUNTU_RELEASE=20.04 ARG CUDA_VERSION=11.4.3 -ARG VIRTUALGL_VERSION=3.1.3-20250409 +ARG VIRTUALGL_VERSION=3.1 ARG GSTREAMER_VERSION=1.20 # @@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE ARG VIRTUALGL_VERSION # Make all NVIDIA GPUs visible by default -ENV NVIDIA_VISIBLE_DEVICES=all +ENV NVIDIA_VISIBLE_DEVICES all # All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work -ENV NVIDIA_DRIVER_CAPABILITIES=all +ENV NVIDIA_DRIVER_CAPABILITIES all # # set vgl-display to headless 3d gpu card/// correct values are egl[n] or /dev/dri/card0:if this is passed into container -ENV VGL_DISPLAY=egl +ENV VGL_DISPLAY egl # # set custom user @@ -205,22 +205,23 @@ RUN VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | gr }" > /etc/vulkan/icd.d/nvidia_icd.json # -# install an up-to-date version of VirtualGL -RUN apt-get update; \ - apt-get install -y --no-install-recommends wget gpg ca-certificates; \ - # Add VirtualGL GPG key - wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | \ - gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg; \ - # Download the official VirtualGL.list file - wget -q -O /etc/apt/sources.list.d/VirtualGL.list \ - https://raw.githubusercontent.com/VirtualGL/repo/main/VirtualGL.list; \ - # Install packages +# install VirtualGL and make libraries available for preload +RUN set -eux; \ apt-get update; \ - apt-get install -y --no-install-recommends virtualgl=${VIRTUALGL_VERSION}; \ + wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \ + wget "https://sourceforge.net/projects/virtualgl/files/virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \ + apt-get install -y --no-install-recommends ./virtualgl_${VIRTUALGL_VERSION}_amd64.deb ./virtualgl32_${VIRTUALGL_VERSION}_amd64.deb; \ + rm -f "virtualgl_${VIRTUALGL_VERSION}_amd64.deb" "virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \ + chmod u+s /usr/lib/libvglfaker.so; \ + chmod u+s /usr/lib/libdlfaker.so; \ + chmod u+s /usr/lib32/libvglfaker.so; \ + chmod u+s /usr/lib32/libdlfaker.so; \ + chmod u+s /usr/lib/i386-linux-gnu/libvglfaker.so; \ + chmod u+s /usr/lib/i386-linux-gnu/libdlfaker.so; \ # # clean up apt-get clean -y; \ - rm -rf /var/lib/apt/lists/* /var/cache/apt/* + rm -rf /var/lib/apt/lists/* /var/cache/apt/*; # # copy runtime configs @@ -261,9 +262,7 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer # # add healthcheck HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ - CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ - wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \ - exit 1 + CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1 # # run neko diff --git a/runtime/Dockerfile.nvidia.bookworm b/runtime/Dockerfile.nvidia.bookworm index 0e3c3c1d..7798c6ff 100644 --- a/runtime/Dockerfile.nvidia.bookworm +++ b/runtime/Dockerfile.nvidia.bookworm @@ -1,6 +1,6 @@ ARG UBUNTU_RELEASE=22.04 ARG CUDA_VERSION=12.2.0 -ARG VIRTUALGL_VERSION=3.1.3-20250409 +ARG VIRTUALGL_VERSION=3.1 ARG GSTREAMER_VERSION=1.22 # @@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE ARG VIRTUALGL_VERSION # Make all NVIDIA GPUs visible by default -ENV NVIDIA_VISIBLE_DEVICES=all +ENV NVIDIA_VISIBLE_DEVICES all # All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work -ENV NVIDIA_DRIVER_CAPABILITIES=all +ENV NVIDIA_DRIVER_CAPABILITIES all # # set vgl-display to headless 3d gpu card/// correct values are egl[n] or /dev/dri/card0:if this is passed into container -ENV VGL_DISPLAY=egl +ENV VGL_DISPLAY egl # # set custom user @@ -199,22 +199,23 @@ RUN VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | gr }" > /etc/vulkan/icd.d/nvidia_icd.json # -# install an up-to-date version of VirtualGL -RUN apt-get update; \ - apt-get install -y --no-install-recommends wget gpg ca-certificates; \ - # Add VirtualGL GPG key - wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | \ - gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg; \ - # Download the official VirtualGL.list file - wget -q -O /etc/apt/sources.list.d/VirtualGL.list \ - https://raw.githubusercontent.com/VirtualGL/repo/main/VirtualGL.list; \ - # Install packages +# install VirtualGL and make libraries available for preload +RUN set -eux; \ apt-get update; \ - apt-get install -y --no-install-recommends virtualgl=${VIRTUALGL_VERSION}; \ + wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \ + wget "https://sourceforge.net/projects/virtualgl/files/virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \ + apt-get install -y --no-install-recommends ./virtualgl_${VIRTUALGL_VERSION}_amd64.deb ./virtualgl32_${VIRTUALGL_VERSION}_amd64.deb; \ + rm -f "virtualgl_${VIRTUALGL_VERSION}_amd64.deb" "virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \ + chmod u+s /usr/lib/libvglfaker.so; \ + chmod u+s /usr/lib/libdlfaker.so; \ + chmod u+s /usr/lib32/libvglfaker.so; \ + chmod u+s /usr/lib32/libdlfaker.so; \ + chmod u+s /usr/lib/i386-linux-gnu/libvglfaker.so; \ + chmod u+s /usr/lib/i386-linux-gnu/libdlfaker.so; \ # # clean up apt-get clean -y; \ - rm -rf /var/lib/apt/lists/* /var/cache/apt/* + rm -rf /var/lib/apt/lists/* /var/cache/apt/*; # # copy runtime configs @@ -253,9 +254,7 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer # # add healthcheck HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ - CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ - wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \ - exit 1 + CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1 # # run neko diff --git a/runtime/supervisord.conf b/runtime/supervisord.conf index 16431d13..950ddbb5 100644 --- a/runtime/supervisord.conf +++ b/runtime/supervisord.conf @@ -33,7 +33,7 @@ redirect_stderr=true [program:neko] environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s" -command=/usr/bin/neko serve --server.static "/var/www" +command=/usr/bin/neko serve --static "/var/www" stopsignal=INT stopwaitsecs=3 autorestart=true diff --git a/utils/xorg-deps/Dockerfile b/runtime/xorg-deps/Dockerfile similarity index 100% rename from utils/xorg-deps/Dockerfile rename to runtime/xorg-deps/Dockerfile diff --git a/utils/xorg-deps/xf86-input-neko/.gitignore b/runtime/xorg-deps/xf86-input-neko/.gitignore similarity index 100% rename from utils/xorg-deps/xf86-input-neko/.gitignore rename to runtime/xorg-deps/xf86-input-neko/.gitignore diff --git a/utils/xorg-deps/xf86-input-neko/80-neko.conf b/runtime/xorg-deps/xf86-input-neko/80-neko.conf similarity index 100% rename from utils/xorg-deps/xf86-input-neko/80-neko.conf rename to runtime/xorg-deps/xf86-input-neko/80-neko.conf diff --git a/utils/xorg-deps/xf86-input-neko/COPYING b/runtime/xorg-deps/xf86-input-neko/COPYING similarity index 100% rename from utils/xorg-deps/xf86-input-neko/COPYING rename to runtime/xorg-deps/xf86-input-neko/COPYING diff --git a/utils/xorg-deps/xf86-input-neko/Dockerfile b/runtime/xorg-deps/xf86-input-neko/Dockerfile similarity index 100% rename from utils/xorg-deps/xf86-input-neko/Dockerfile rename to runtime/xorg-deps/xf86-input-neko/Dockerfile diff --git a/utils/xorg-deps/xf86-input-neko/Makefile.am b/runtime/xorg-deps/xf86-input-neko/Makefile.am similarity index 100% rename from utils/xorg-deps/xf86-input-neko/Makefile.am rename to runtime/xorg-deps/xf86-input-neko/Makefile.am diff --git a/utils/xorg-deps/xf86-input-neko/README.md b/runtime/xorg-deps/xf86-input-neko/README.md similarity index 100% rename from utils/xorg-deps/xf86-input-neko/README.md rename to runtime/xorg-deps/xf86-input-neko/README.md diff --git a/utils/xorg-deps/xf86-input-neko/autogen-clean.sh b/runtime/xorg-deps/xf86-input-neko/autogen-clean.sh similarity index 100% rename from utils/xorg-deps/xf86-input-neko/autogen-clean.sh rename to runtime/xorg-deps/xf86-input-neko/autogen-clean.sh diff --git a/utils/xorg-deps/xf86-input-neko/autogen.sh b/runtime/xorg-deps/xf86-input-neko/autogen.sh similarity index 100% rename from utils/xorg-deps/xf86-input-neko/autogen.sh rename to runtime/xorg-deps/xf86-input-neko/autogen.sh diff --git a/utils/xorg-deps/xf86-input-neko/configure.ac b/runtime/xorg-deps/xf86-input-neko/configure.ac similarity index 100% rename from utils/xorg-deps/xf86-input-neko/configure.ac rename to runtime/xorg-deps/xf86-input-neko/configure.ac diff --git a/utils/xorg-deps/xf86-input-neko/m4/.gitkeep b/runtime/xorg-deps/xf86-input-neko/m4/.gitkeep similarity index 100% rename from utils/xorg-deps/xf86-input-neko/m4/.gitkeep rename to runtime/xorg-deps/xf86-input-neko/m4/.gitkeep diff --git a/utils/xorg-deps/xf86-input-neko/release.sh b/runtime/xorg-deps/xf86-input-neko/release.sh similarity index 100% rename from utils/xorg-deps/xf86-input-neko/release.sh rename to runtime/xorg-deps/xf86-input-neko/release.sh diff --git a/utils/xorg-deps/xf86-input-neko/src/Makefile.am b/runtime/xorg-deps/xf86-input-neko/src/Makefile.am similarity index 100% rename from utils/xorg-deps/xf86-input-neko/src/Makefile.am rename to runtime/xorg-deps/xf86-input-neko/src/Makefile.am diff --git a/utils/xorg-deps/xf86-input-neko/src/neko.c b/runtime/xorg-deps/xf86-input-neko/src/neko.c similarity index 100% rename from utils/xorg-deps/xf86-input-neko/src/neko.c rename to runtime/xorg-deps/xf86-input-neko/src/neko.c diff --git a/utils/xorg-deps/xf86-input-neko/xorg-neko.pc.in b/runtime/xorg-deps/xf86-input-neko/xorg-neko.pc.in similarity index 100% rename from utils/xorg-deps/xf86-input-neko/xorg-neko.pc.in rename to runtime/xorg-deps/xf86-input-neko/xorg-neko.pc.in diff --git a/utils/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch b/runtime/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch rename to runtime/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch diff --git a/utils/xorg-deps/xf86-video-dummy/README.md b/runtime/xorg-deps/xf86-video-dummy/README.md similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/README.md rename to runtime/xorg-deps/xf86-video-dummy/README.md diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/COPYING b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/COPYING similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/COPYING rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/COPYING diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/README b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/README similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/README rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/README diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4 b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4 similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4 rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4 diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/compile b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/compile similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/compile rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/compile diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.guess b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.guess similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/config.guess rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.guess diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.sub b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.sub similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/config.sub rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.sub diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/configure b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/configure rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/depcomp b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/depcomp similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/depcomp rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/depcomp diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/install-sh b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/install-sh similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/install-sh rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/install-sh diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/missing b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/missing similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/missing rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/missing diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c diff --git a/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c b/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c similarity index 100% rename from utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c rename to runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c diff --git a/server/build b/server/build index 9d00766b..6bf20bef 100755 --- a/server/build +++ b/server/build @@ -22,7 +22,7 @@ fi # # load server dependencies -go get -v -t . +go get -v -t -d . # # build server diff --git a/server/dev/build b/server/dev/build index 0522631c..c67c5acb 100755 --- a/server/dev/build +++ b/server/dev/build @@ -9,10 +9,9 @@ GIT_COMMIT=`git rev-parse --short HEAD` GIT_BRANCH=`git rev-parse --symbolic-full-name --abbrev-ref HEAD` echo "Building server image" -docker build -t neko_server:src -f ../Dockerfile .. +docker build -t neko_server --build-arg "GIT_COMMIT=$GIT_COMMIT" --build-arg "GIT_BRANCH=$GIT_BRANCH" -f ../Dockerfile .. -echo "Building base image" -../../build -y -b neko_server:base -f "$1" +BUILD_IMAGE=neko_server FLAVOUR=$1 ../../build echo "Building app image" docker build -t neko_server:app --build-arg "BASE_IMAGE=neko_server:base" -f ./runtime/Dockerfile ./runtime diff --git a/server/dev/fmt b/server/dev/fmt index a294473e..f8b3130e 100755 --- a/server/dev/fmt +++ b/server/dev/fmt @@ -1,12 +1,12 @@ #!/bin/bash cd "$(dirname "$0")" -if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then - echo "Image 'neko_server:src' not found. Run ./build first." +if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then + echo "Image 'neko_server' not found. Run ./build first." exit 1 fi docker run -it --rm \ --entrypoint="go" \ -v "${PWD}/../:/src" \ - neko_server:src fmt ./... + neko_server fmt ./... diff --git a/server/dev/go b/server/dev/go index 229a9365..29a62e91 100755 --- a/server/dev/go +++ b/server/dev/go @@ -1,8 +1,8 @@ #!/bin/bash cd "$(dirname "$0")" -if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then - echo "Image 'neko_server:src' not found. Run ./build first." +if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then + echo "Image 'neko_server' not found. Run ./build first." exit 1 fi @@ -10,7 +10,7 @@ docker run -it \ --name "neko_server_go" \ --entrypoint="go" \ -v "${PWD}/../:/src" \ - neko_server:src "$@"; + neko_server "$@"; # # copy package files docker cp neko_server_go:/src/go.mod "../go.mod" diff --git a/server/dev/lint b/server/dev/lint index 0c9e9bed..c0f0b5bb 100755 --- a/server/dev/lint +++ b/server/dev/lint @@ -1,8 +1,8 @@ #!/bin/bash cd "$(dirname "$0")" -if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then - echo "Image 'neko_server:src' not found. Run ./build first." +if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then + echo "Image 'neko_server' not found. Run ./build first." exit 1 fi @@ -11,4 +11,4 @@ fi docker run --rm -it \ -v "${PWD}/../:/src" \ --entrypoint="/bin/bash" \ - neko_server:src -c '[ -f ./bin/golangci-lint ] || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.31.0;./bin/golangci-lint run'; + neko_server -c '[ -f ./bin/golangci-lint ] || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.31.0;./bin/golangci-lint run'; diff --git a/server/dev/rebuild b/server/dev/rebuild index 696af7aa..d2132fe7 100755 --- a/server/dev/rebuild +++ b/server/dev/rebuild @@ -10,7 +10,7 @@ set -e docker run --rm -it \ -v "${PWD}/../:/src" \ --entrypoint="/bin/bash" \ - neko_server:src "./build" "$@"; + neko_server "./build" "$@"; # # remove old plugins diff --git a/server/dev/rebuild.input b/server/dev/rebuild.input index bd6a97c6..006ba9c8 100755 --- a/server/dev/rebuild.input +++ b/server/dev/rebuild.input @@ -1,6 +1,6 @@ #!/bin/bash cd "$(dirname "$0")" -cd ../../utils/xorg-deps/xf86-input-neko +cd ../../runtime/xorg-deps/xf86-input-neko # # aborting if any command returns a non-zero value diff --git a/server/dev/runtime/Dockerfile b/server/dev/runtime/Dockerfile index 438acb2f..0be6746f 100644 --- a/server/dev/runtime/Dockerfile +++ b/server/dev/runtime/Dockerfile @@ -1,4 +1,4 @@ -ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest +ARG BASE_IMAGE=neko_server:base FROM $BASE_IMAGE ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US" @@ -8,13 +8,13 @@ ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lan RUN set -eux; apt-get update; \ apt-get install -y --no-install-recommends \ dbus-x11 xfce4 xfce4-terminal sudo \ - xz-utils libgtk-3-0 libdbus-glib-1-2; \ + xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \ # # fetch latest firefox release - wget -O /tmp/firefox-setup.tar.xz "${SRC_URL}"; \ + wget -O /tmp/firefox-setup.tar.bz2 "${SRC_URL}"; \ mkdir /usr/lib/firefox; \ - tar -xvf /tmp/firefox-setup.tar.xz -C /usr/lib; \ - rm -f /tmp/firefox-setup.tar.xz; \ + tar -xjf /tmp/firefox-setup.tar.bz2 -C /usr/lib; \ + rm -f /tmp/firefox-setup.tar.bz2; \ ln -s /usr/lib/firefox/firefox /usr/bin/firefox; \ # # add user to sudoers @@ -22,7 +22,7 @@ RUN set -eux; apt-get update; \ echo "neko:neko" | chpasswd; \ echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers; \ # clean up - apt-get --purge autoremove -y xz-utils; \ + apt-get --purge autoremove -y xz-utils bzip2; \ apt-get clean -y; \ rm -rf /var/lib/apt/lists/* /var/cache/apt/* diff --git a/server/internal/config/capture.go b/server/internal/config/capture.go index 298e69cd..3d8025e0 100644 --- a/server/internal/config/capture.go +++ b/server/internal/config/capture.go @@ -90,12 +90,12 @@ func (Capture) Init(cmd *cobra.Command) error { return err } - cmd.PersistentFlags().String("capture.video.pipelines", "{}", "pipelines config used for video streaming") + cmd.PersistentFlags().String("capture.video.pipelines", "[]", "pipelines config in JSON used for video streaming") if err := viper.BindPFlag("capture.video.pipelines", cmd.PersistentFlags().Lookup("capture.video.pipelines")); err != nil { return err } - cmd.PersistentFlags().String("capture.video.pipeline", "", "shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set") + cmd.PersistentFlags().String("capture.video.pipeline", "", "gstreamer pipeline used for video streaming; shortcut for having only a single video pipeline instead of multiple, ignored if capture.video.pipelines is set") if err := viper.BindPFlag("capture.video.pipeline", cmd.PersistentFlags().Lookup("capture.video.pipeline")); err != nil { return err } @@ -451,7 +451,6 @@ func (s *Capture) SetV2() { enableLegacy = true } - modifiedVideoCodec := false if videoCodec := viper.GetString("video_codec"); videoCodec != "" { s.VideoCodec, ok = codec.ParseStr(videoCodec) if !ok || s.VideoCodec.Type != webrtc.RTPCodecTypeVideo { @@ -460,29 +459,24 @@ func (s *Capture) SetV2() { } log.Warn().Msg("you are using v2 configuration 'NEKO_VIDEO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_VIDEO_CODEC' instead") enableLegacy = true - modifiedVideoCodec = true } if viper.GetBool("vp8") { s.VideoCodec = codec.VP8() log.Warn().Msg("you are using deprecated config setting 'NEKO_VP8=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp8' instead") enableLegacy = true - modifiedVideoCodec = true } else if viper.GetBool("vp9") { s.VideoCodec = codec.VP9() log.Warn().Msg("you are using deprecated config setting 'NEKO_VP9=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp9' instead") enableLegacy = true - modifiedVideoCodec = true } else if viper.GetBool("h264") { s.VideoCodec = codec.H264() log.Warn().Msg("you are using deprecated config setting 'NEKO_H264=true', use 'NEKO_CAPTURE_VIDEO_CODEC=h264' instead") enableLegacy = true - modifiedVideoCodec = true } else if viper.GetBool("av1") { s.VideoCodec = codec.AV1() log.Warn().Msg("you are using deprecated config setting 'NEKO_AV1=true', use 'NEKO_CAPTURE_VIDEO_CODEC=av1' instead") enableLegacy = true - modifiedVideoCodec = true } videoHWEnc := HwEncUnset @@ -504,7 +498,7 @@ func (s *Capture) SetV2() { videoPipeline := viper.GetString("video") // video pipeline - if modifiedVideoCodec || videoHWEnc != HwEncUnset || videoBitrate != 0 || videoMaxFPS != 0 || videoPipeline != "" { + if videoHWEnc != HwEncUnset || videoBitrate != 0 || videoMaxFPS != 0 || videoPipeline != "" { pipeline, err := NewVideoPipeline(s.VideoCodec, s.Display, videoPipeline, videoMaxFPS, videoBitrate, videoHWEnc) if err != nil { log.Warn().Err(err).Msg("unable to create video pipeline, using default") @@ -540,7 +534,6 @@ func (s *Capture) SetV2() { enableLegacy = true } - modifiedAudioCodec := false if audioCodec := viper.GetString("audio_codec"); audioCodec != "" { s.AudioCodec, ok = codec.ParseStr(audioCodec) if !ok || s.AudioCodec.Type != webrtc.RTPCodecTypeAudio { @@ -549,36 +542,31 @@ func (s *Capture) SetV2() { } log.Warn().Msg("you are using v2 configuration 'NEKO_AUDIO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_AUDIO_CODEC' instead") enableLegacy = true - modifiedAudioCodec = true } if viper.GetBool("opus") { s.AudioCodec = codec.Opus() log.Warn().Msg("you are using deprecated config setting 'NEKO_OPUS=true', use 'NEKO_CAPTURE_AUDIO_CODEC=opus' instead") enableLegacy = true - modifiedAudioCodec = true } else if viper.GetBool("g722") { s.AudioCodec = codec.G722() log.Warn().Msg("you are using deprecated config setting 'NEKO_G722=true', use 'NEKO_CAPTURE_AUDIO_CODEC=g722' instead") enableLegacy = true - modifiedAudioCodec = true } else if viper.GetBool("pcmu") { s.AudioCodec = codec.PCMU() log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMU=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcmu' instead") enableLegacy = true - modifiedAudioCodec = true } else if viper.GetBool("pcma") { s.AudioCodec = codec.PCMA() log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMA=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcma' instead") enableLegacy = true - modifiedAudioCodec = true } audioBitrate := viper.GetUint("audio_bitrate") audioPipeline := viper.GetString("audio") // audio pipeline - if modifiedAudioCodec || audioBitrate != 0 || audioPipeline != "" { + if audioBitrate != 0 || audioPipeline != "" { pipeline, err := NewAudioPipeline(s.AudioCodec, s.AudioDevice, audioPipeline, audioBitrate) if err != nil { log.Warn().Err(err).Msg("unable to create audio pipeline, using default") @@ -616,7 +604,7 @@ func (s *Capture) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/desktop.go b/server/internal/config/desktop.go index 8331035e..2814b697 100644 --- a/server/internal/config/desktop.go +++ b/server/internal/config/desktop.go @@ -133,7 +133,7 @@ func (s *Desktop) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/member.go b/server/internal/config/member.go index db9c5daa..c3a7d242 100644 --- a/server/internal/config/member.go +++ b/server/internal/config/member.go @@ -22,45 +22,45 @@ type Member struct { } func (Member) Init(cmd *cobra.Command) error { - cmd.PersistentFlags().String("member.provider", "multiuser", "selected member provider") + cmd.PersistentFlags().String("member.provider", "multiuser", "choose member provider") if err := viper.BindPFlag("member.provider", cmd.PersistentFlags().Lookup("member.provider")); err != nil { return err } // file provider - cmd.PersistentFlags().String("member.file.path", "", "member file provider: path to the file containing the users and their passwords") + cmd.PersistentFlags().String("member.file.path", "", "member file provider: storage path") if err := viper.BindPFlag("member.file.path", cmd.PersistentFlags().Lookup("member.file.path")); err != nil { return err } - cmd.PersistentFlags().Bool("member.file.hash", true, "member file provider: whether the passwords are hashed using sha256 or not (recommended)") + cmd.PersistentFlags().Bool("member.file.hash", true, "member file provider: whether to hash passwords using sha256 (recommended)") if err := viper.BindPFlag("member.file.hash", cmd.PersistentFlags().Lookup("member.file.hash")); err != nil { return err } // object provider - cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: list of users with their passwords and profiles") + cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: users in JSON format") if err := viper.BindPFlag("member.object.users", cmd.PersistentFlags().Lookup("member.object.users")); err != nil { return err } // multiuser provider - cmd.PersistentFlags().String("member.multiuser.user_password", "neko", "member multiuser provider: password for regular users") + cmd.PersistentFlags().String("member.multiuser.user_password", "neko", "member multiuser provider: user password") if err := viper.BindPFlag("member.multiuser.user_password", cmd.PersistentFlags().Lookup("member.multiuser.user_password")); err != nil { return err } - cmd.PersistentFlags().String("member.multiuser.admin_password", "admin", "member multiuser provider: password for admin users") + cmd.PersistentFlags().String("member.multiuser.admin_password", "admin", "member multiuser provider: admin password") if err := viper.BindPFlag("member.multiuser.admin_password", cmd.PersistentFlags().Lookup("member.multiuser.admin_password")); err != nil { return err } - cmd.PersistentFlags().String("member.multiuser.user_profile", "{}", "member multiuser provider: profile template for regular users") + cmd.PersistentFlags().String("member.multiuser.user_profile", "{}", "member multiuser provider: user profile in JSON format") if err := viper.BindPFlag("member.multiuser.user_profile", cmd.PersistentFlags().Lookup("member.multiuser.user_profile")); err != nil { return err } - cmd.PersistentFlags().String("member.multiuser.admin_profile", "{}", "member multiuser provider: profile template for admin users") + cmd.PersistentFlags().String("member.multiuser.admin_profile", "{}", "member multiuser provider: admin profile in JSON format") if err := viper.BindPFlag("member.multiuser.admin_profile", cmd.PersistentFlags().Lookup("member.multiuser.admin_profile")); err != nil { return err } @@ -162,7 +162,7 @@ func (s *Member) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/root.go b/server/internal/config/root.go index c8902087..ab6ddc8b 100644 --- a/server/internal/config/root.go +++ b/server/internal/config/root.go @@ -139,7 +139,7 @@ func (s *Root) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/server.go b/server/internal/config/server.go index 99be15f4..8491d43c 100644 --- a/server/internal/config/server.go +++ b/server/internal/config/server.go @@ -172,7 +172,7 @@ func (s *Server) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/session.go b/server/internal/config/session.go index a38d05aa..4349ee9c 100644 --- a/server/internal/config/session.go +++ b/server/internal/config/session.go @@ -206,7 +206,7 @@ func (s *Session) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/config/webrtc.go b/server/internal/config/webrtc.go index bc202d70..c28593df 100644 --- a/server/internal/config/webrtc.go +++ b/server/internal/config/webrtc.go @@ -67,17 +67,17 @@ func (WebRTC) Init(cmd *cobra.Command) error { } // Looks like this is conflicting with the frontend and backend ICE servers since latest versions - //cmd.PersistentFlags().String("webrtc.iceservers", "[]", "STUN and TURN servers used by the ICE agent") + //cmd.PersistentFlags().String("webrtc.iceservers", "[]", "Global STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys") //if err := viper.BindPFlag("webrtc.iceservers", cmd.PersistentFlags().Lookup("webrtc.iceservers")); err != nil { // return err //} - cmd.PersistentFlags().String("webrtc.iceservers.frontend", "[]", "STUN and TURN servers used by the frontend") + cmd.PersistentFlags().String("webrtc.iceservers.frontend", "[]", "Frontend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys") if err := viper.BindPFlag("webrtc.iceservers.frontend", cmd.PersistentFlags().Lookup("webrtc.iceservers.frontend")); err != nil { return err } - cmd.PersistentFlags().String("webrtc.iceservers.backend", "[]", "STUN and TURN servers used by the backend") + cmd.PersistentFlags().String("webrtc.iceservers.backend", "[]", "Backend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys") if err := viper.BindPFlag("webrtc.iceservers.backend", cmd.PersistentFlags().Lookup("webrtc.iceservers.backend")); err != nil { return err } @@ -217,14 +217,14 @@ func (s *WebRTC) Set() { // parse frontend ice servers if err := viper.UnmarshalKey("webrtc.iceservers.frontend", &s.ICEServersFrontend, viper.DecodeHook( - utils.JsonStringAutoDecode(s.ICEServersFrontend), + utils.JsonStringAutoDecode([]types.ICEServer{}), )); err != nil { log.Warn().Err(err).Msgf("unable to parse frontend ICE servers") } // parse backend ice servers if err := viper.UnmarshalKey("webrtc.iceservers.backend", &s.ICEServersBackend, viper.DecodeHook( - utils.JsonStringAutoDecode(s.ICEServersBackend), + utils.JsonStringAutoDecode([]types.ICEServer{}), )); err != nil { log.Warn().Err(err).Msgf("unable to parse backend ICE servers") } @@ -238,7 +238,7 @@ func (s *WebRTC) Set() { // parse global ice servers var iceServers []types.ICEServer if err := viper.UnmarshalKey("webrtc.iceservers", &iceServers, viper.DecodeHook( - utils.JsonStringAutoDecode(iceServers), + utils.JsonStringAutoDecode([]types.ICEServer{}), )); err != nil { log.Warn().Err(err).Msgf("unable to parse global ICE servers") } @@ -409,7 +409,7 @@ func (s *WebRTC) SetV2() { // set legacy flag if any V2 configuration was used if !viper.IsSet("legacy") && enableLegacy { - log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details") + log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message") viper.Set("legacy", true) } } diff --git a/server/internal/desktop/clipboard.go b/server/internal/desktop/clipboard.go index fff20d8b..49c2f00f 100644 --- a/server/internal/desktop/clipboard.go +++ b/server/internal/desktop/clipboard.go @@ -10,19 +10,14 @@ import ( "github.com/m1k1o/neko/server/pkg/xevent" ) -const ( - ClipboardTextPlainTarget = "UTF8_STRING" - ClipboardTextHtmlTarget = "text/html" -) - func (manager *DesktopManagerCtx) ClipboardGetText() (*types.ClipboardText, error) { - text, err := manager.ClipboardGetBinary(ClipboardTextPlainTarget) + text, err := manager.ClipboardGetBinary("STRING") if err != nil { return nil, err } // Rich text must not always be available, can fail silently. - html, _ := manager.ClipboardGetBinary(ClipboardTextHtmlTarget) + html, _ := manager.ClipboardGetBinary("text/html") return &types.ClipboardText{ Text: string(text), @@ -36,10 +31,10 @@ func (manager *DesktopManagerCtx) ClipboardSetText(data types.ClipboardText) err // is set, if available. Otherwise plain text. if data.HTML != "" { - return manager.ClipboardSetBinary(ClipboardTextHtmlTarget, []byte(data.HTML)) + return manager.ClipboardSetBinary("text/html", []byte(data.HTML)) } - return manager.ClipboardSetBinary(ClipboardTextPlainTarget, []byte(data.Text)) + return manager.ClipboardSetBinary("STRING", []byte(data.Text)) } func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error) { @@ -58,23 +53,6 @@ func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error return stdout.Bytes(), nil } -func (manager *DesktopManagerCtx) replaceClipboardCommand(newCmd *exec.Cmd) { - // Swap the current clipboard command with the new one. - oldCmd := manager.clipboardCommand.Swap(newCmd) - - // If the command is already running, we need to shutdown it properly. - if oldCmd == nil || oldCmd.ProcessState != nil { - return - } - - // If there is a previous command running and it was not stopped yet, we need to kill it. - if err := oldCmd.Process.Kill(); err != nil { - manager.logger.Err(err).Msg("failed to kill previous clipboard command") - } else { - manager.logger.Debug().Msg("killed previous clipboard command") - } -} - func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) error { cmd := exec.Command("xclip", "-selection", "clipboard", "-in", "-target", mime) @@ -86,9 +64,7 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e return err } - // Shutdown previous command if it exists and replace it with the new one. - manager.replaceClipboardCommand(cmd) - + // TODO: Refactor. // We need to wait until the data came to the clipboard. wait := make(chan struct{}) xevent.Emmiter.Once("clipboard-updated", func(payload ...any) { @@ -108,23 +84,9 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e stdin.Close() - select { - case <-manager.shutdown: - return fmt.Errorf("clipboard manager is shutting down") - case <-wait: - } - - manager.wg.Add(1) - go func() { - defer manager.wg.Done() - - if err := cmd.Wait(); err != nil { - msg := strings.TrimSpace(stderr.String()) - manager.logger.Err(err).Msgf("clipboard command finished with error: %s", msg) - } else { - manager.logger.Debug().Msg("clipboard command finished successfully") - } - }() + // TODO: Refactor. + // cmd.Wait() + <-wait return nil } diff --git a/server/internal/desktop/manager.go b/server/internal/desktop/manager.go index aca5fe1a..0ffadd73 100644 --- a/server/internal/desktop/manager.go +++ b/server/internal/desktop/manager.go @@ -1,9 +1,7 @@ package desktop import ( - "os/exec" "sync" - "sync/atomic" "time" "github.com/kataras/go-events" @@ -27,11 +25,6 @@ type DesktopManagerCtx struct { config *config.Desktop screenSize types.ScreenSize // cached screen size input xinput.Driver - - // Clipboard process holding the most recent clipboard data. - // It must remain running to allow pasting clipboard data. - // The last command is kept running until it is replaced or shutdown. - clipboardCommand atomic.Pointer[exec.Cmd] } func New(config *config.Desktop) *DesktopManagerCtx { @@ -138,8 +131,6 @@ func (manager *DesktopManagerCtx) Shutdown() error { manager.logger.Info().Msgf("shutdown") close(manager.shutdown) - - manager.replaceClipboardCommand(nil) manager.wg.Wait() xorg.DisplayClose() diff --git a/server/internal/http/legacy/handler.go b/server/internal/http/legacy/handler.go index bfd29f3a..65a0349b 100644 --- a/server/internal/http/legacy/handler.go +++ b/server/internal/http/legacy/handler.go @@ -9,7 +9,6 @@ import ( "net/http" "net/url" "strings" - "time" "github.com/m1k1o/neko/server/internal/api" oldEvent "github.com/m1k1o/neko/server/internal/http/legacy/event" @@ -36,6 +35,9 @@ var ( return true }, } + + // DefaultDialer is a dialer with all fields set to the default zero values. + DefaultDialer = websocket.DefaultDialer ) type LegacyHandler struct { @@ -43,21 +45,16 @@ type LegacyHandler struct { serverAddr string bannedIPs map[string]struct{} sessionIPs map[string]string - wsDialer *websocket.Dialer } -func New(serverAddr string) *LegacyHandler { +func New() *LegacyHandler { // Init return &LegacyHandler{ logger: log.With().Str("module", "legacy").Logger(), - serverAddr: serverAddr, + serverAddr: "127.0.0.1:8080", bannedIPs: make(map[string]struct{}), sessionIPs: make(map[string]string), - wsDialer: &websocket.Dialer{ - Proxy: nil, // disable proxy for local requests - HandshakeTimeout: 45 * time.Second, - }, } } @@ -102,7 +99,7 @@ func (h *LegacyHandler) Route(r types.Router) { defer s.destroy() // dial to the remote backend - connBackend, _, err := h.wsDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+url.QueryEscape(s.token), nil) + connBackend, _, err := DefaultDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+url.QueryEscape(s.token), nil) if err != nil { h.logger.Error().Err(err).Msg("couldn't dial to the remote backend") @@ -145,12 +142,10 @@ func (h *LegacyHandler) Route(r types.Router) { m = websocket.FormatCloseMessage(e.Code, e.Text) } } - errc <- fmt.Errorf("src read message error: %w", err) + errc <- err dst.WriteMessage(websocket.CloseMessage, m) break } - - // handle text messages if msgType == websocket.TextMessage { err = rewriteTextMessage(msg) @@ -167,26 +162,12 @@ func (h *LegacyHandler) Route(r types.Router) { Message: strings.ReplaceAll(err.Error(), ErrBackendRespone.Error()+": ", ""), }) continue - } - - if errors.Is(err, ErrWebsocketSend) { - errc <- fmt.Errorf("dst write message error: %w", err) - break - } - - h.logger.Error().Err(err).Msg("couldn't rewrite text message") - continue - } - - // forward ping pong messages - if msgType == websocket.PingMessage || - msgType == websocket.PongMessage { - err = dst.WriteMessage(msgType, msg) - if err != nil { + } else if errors.Is(err, ErrWebsocketSend) { errc <- err break + } else { + h.logger.Error().Err(err).Msg("couldn't rewrite text message") } - continue } } } @@ -200,9 +181,9 @@ func (h *LegacyHandler) Route(r types.Router) { var message string select { case err = <-errClient: - message = "websocketproxy: Error when copying from backend to client" + message = "websocketproxy: Error when copying from backend to client: %v" case err = <-errBackend: - message = "websocketproxy: Error when copying from client to backend" + message = "websocketproxy: Error when copying from client to backend: %v" } if e, ok := err.(*websocket.CloseError); !ok || e.Code == websocket.CloseAbnormalClosure { diff --git a/server/internal/http/legacy/session.go b/server/internal/http/legacy/session.go index 02f45ec0..bd28aa03 100644 --- a/server/internal/http/legacy/session.go +++ b/server/internal/http/legacy/session.go @@ -55,18 +55,13 @@ type session struct { } func (h *LegacyHandler) newSession(r *http.Request) *session { - transport := http.DefaultTransport.(*http.Transport).Clone() - transport.Proxy = nil // disable proxy for local requests - return &session{ r: r, h: h, logger: h.logger, serverAddr: h.serverAddr, - client: &http.Client{ - Transport: transport, - }, - sessions: make(map[string]*memberStruct), + client: http.DefaultClient, + sessions: make(map[string]*memberStruct), } } diff --git a/server/internal/http/manager.go b/server/internal/http/manager.go index acea79db..8aadd086 100644 --- a/server/internal/http/manager.go +++ b/server/internal/http/manager.go @@ -2,7 +2,6 @@ package http import ( "context" - "net" "net/http" "os" @@ -57,6 +56,11 @@ func New(WebSocketManager types.WebSocketManager, ApiManager types.ApiManager, c return config.AllowOrigin(r.Header.Get("Origin")) })) + // Legacy handler + if viper.GetBool("legacy") { + legacy.New().Route(router) + } + batch := batchHandler{ Router: router, PathPrefix: "/api", @@ -118,24 +122,6 @@ func (manager *HttpManagerCtx) Start() { } }() manager.logger.Info().Msgf("https listening on %s", manager.http.Addr) - - // if we have legacy mode, we need to start local http server too - if viper.GetBool("legacy") { - // create a listener for the API server with a random port - listener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - manager.logger.Panic().Err(err).Msg("unable to start legacy http proxy") - } - - go func() { - if err := http.Serve(listener, manager.router); err != http.ErrServerClosed { - manager.logger.Panic().Err(err).Msg("unable to start http server") - } - }() - manager.logger.Info().Msgf("legacy proxy listening on %s", listener.Addr().String()) - - legacy.New(listener.Addr().String()).Route(manager.router) - } } else { go func() { if err := manager.http.ListenAndServe(); err != http.ErrServerClosed { @@ -143,11 +129,6 @@ func (manager *HttpManagerCtx) Start() { } }() manager.logger.Info().Msgf("http listening on %s", manager.http.Addr) - - // start legacy proxy if enabled - if viper.GetBool("legacy") { - legacy.New(manager.http.Addr).Route(manager.router) - } } } diff --git a/server/internal/webrtc/legacyhandler.go b/server/internal/webrtc/legacyhandler.go index 1185e6e0..e7d05c98 100644 --- a/server/internal/webrtc/legacyhandler.go +++ b/server/internal/webrtc/legacyhandler.go @@ -78,7 +78,7 @@ func (manager *WebRTCManagerCtx) handleLegacy( } logger. - Trace(). + Debug(). Str("x", strconv.Itoa(int(payload.X))). Str("y", strconv.Itoa(int(payload.Y))). Msg("scroll") @@ -97,7 +97,7 @@ func (manager *WebRTCManagerCtx) handleLegacy( return nil } - logger.Trace().Msgf("button down %d", payload.Key) + logger.Debug().Msgf("button down %d", payload.Key) } else { err := manager.desktop.KeyDown(uint32(payload.Key)) if err != nil { @@ -105,7 +105,7 @@ func (manager *WebRTCManagerCtx) handleLegacy( return nil } - logger.Trace().Msgf("key down %d", payload.Key) + logger.Debug().Msgf("key down %d", payload.Key) } case OP_KEY_UP: payload := &PayloadKey{} @@ -121,7 +121,7 @@ func (manager *WebRTCManagerCtx) handleLegacy( return nil } - logger.Trace().Msgf("button up %d", payload.Key) + logger.Debug().Msgf("button up %d", payload.Key) } else { err := manager.desktop.KeyUp(uint32(payload.Key)) if err != nil { @@ -129,7 +129,7 @@ func (manager *WebRTCManagerCtx) handleLegacy( return nil } - logger.Trace().Msgf("key up %d", payload.Key) + logger.Debug().Msgf("key up %d", payload.Key) } case OP_KEY_CLK: // unused diff --git a/server/internal/websocket/manager.go b/server/internal/websocket/manager.go index 9fc21f60..0f7baafd 100644 --- a/server/internal/websocket/manager.go +++ b/server/internal/websocket/manager.go @@ -276,9 +276,7 @@ func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http. e, ok := err.(*websocket.CloseError) if !ok { - if e := errors.Unwrap(err); e != nil { - err = e // unwrap if possible - } + err = errors.Unwrap(err) // unwrap if possible logger.Warn().Err(err).Msg("read message error") // client is expected to reconnect soon delayedDisconnect = true diff --git a/server/internal/websocket/peer.go b/server/internal/websocket/peer.go index 0c2fd553..3861e3a0 100644 --- a/server/internal/websocket/peer.go +++ b/server/internal/websocket/peer.go @@ -43,9 +43,7 @@ func (peer *WebSocketPeerCtx) Send(event string, payload any) { }) if err != nil { - if e := errors.Unwrap(err); e != nil { - err = e // unwrap if possible - } + err = errors.Unwrap(err) // unwrap if possible peer.logger.Warn().Err(err).Str("event", event).Msg("send message error") return } diff --git a/utils/docker/go.mod b/utils/docker/go.mod deleted file mode 100644 index 62bde8c0..00000000 --- a/utils/docker/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/m1k1o/neko/utils/docker - -go 1.24.1 diff --git a/webpage/docs/app-customization/chromium-browsers.md b/webpage/docs/app-customization/chromium-browsers.md new file mode 100644 index 00000000..e89e1928 --- /dev/null +++ b/webpage/docs/app-customization/chromium-browsers.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 1 +--- + +# Chromium-based Browsers + +Custom settings, bookmarks, and persistence across sessions. diff --git a/webpage/docs/app-customization/customizing-ui.md b/webpage/docs/app-customization/customizing-ui.md new file mode 100644 index 00000000..cccd3aa7 --- /dev/null +++ b/webpage/docs/app-customization/customizing-ui.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 4 +--- + +# Customizing the UI + +Modifying Neko’s interface, branding, and user experience. diff --git a/webpage/docs/app-customization/firefox-customization.md b/webpage/docs/app-customization/firefox-customization.md new file mode 100644 index 00000000..3b61efc6 --- /dev/null +++ b/webpage/docs/app-customization/firefox-customization.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 2 +--- + +# Firefox Customization + +Specific tweaks for running and configuring Firefox in Neko. diff --git a/webpage/docs/app-customization/persistent-data-storage.md b/webpage/docs/app-customization/persistent-data-storage.md new file mode 100644 index 00000000..3bd40ea4 --- /dev/null +++ b/webpage/docs/app-customization/persistent-data-storage.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 3 +--- + +# Persistent Data Storage + +How to persist browser data (history, bookmarks, extensions) across sessions. diff --git a/webpage/docs/configuration/README.md b/webpage/docs/configuration/README.md index f5127104..a813bf0a 100644 --- a/webpage/docs/configuration/README.md +++ b/webpage/docs/configuration/README.md @@ -1,6 +1,4 @@ import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # Configuration @@ -293,23 +291,24 @@ import TabItem from '@theme/TabItem'; This is the initial configuration of the room that can be modified by an admin in real-time. - +```yaml title="config.yaml" +session: + private_mode: false + locked_logins: false + locked_controls: false + control_protection: false + implicit_hosting: true + inactive_cursors: false + merciful_reconnect: true + heartbeat_interval: 120 +``` - whether private mode is enabled, users do not receive the room video or audio. - whether logins are locked for users, admins can still login. - whether controls are locked for users, admins can still control. - users can gain control only if at least one admin is in the room. -- automatically grants control to a user when they click on the screen, unless an admin has locked the controls. -- whether to show inactive cursors server-wide (only for users that have it enabled in their profile). +- allows switching control implicitly without the need for explicit control request before +- whether to show inactive cursors server-wide (only for users that have it enabled in their profile) - whether to allow reconnecting to the websocket even if the previous connection was not closed. This means that a new login can kick out the previous one. - interval in seconds for sending a heartbeat message to the server. This is used to keep the connection alive and to detect when the connection is lost. @@ -317,17 +316,18 @@ This is the initial configuration of the room that can be modified by an admin i This is the configuration of the neko server. - +```yaml title="config.yaml" +server: + bind: "127.0.0.1:8080" + cert: "/path/to/cert.pem" + key: "/path/to/key.pem" + cors: [ "*" ] + metrics: true + path_prefix: "/neko" + pprof: true + proxy: true + static: "/var/www/neko" +``` - address/port/socket to serve neko. For docker you might want to bind to `0.0.0.0` to allow connections from outside the container. - and paths to the SSL cert and key used to secure the neko server. If both are empty, the server will run in plain HTTP. @@ -345,13 +345,14 @@ This is the configuration of the neko server. This is the configuration of the logging system. - +```yaml title="config.yaml" +log: + dir: + json: true + level: "info" + nocolor: true + time: "unix" +``` - directory to store logs. If empty, logs are written to stdout. This is useful when running neko in a container. - when true, logs are written in JSON format. @@ -367,7 +368,10 @@ Shortcut environment variable to enable DEBUG mode: `NEKO_DEBUG=true` Here is a full configuration with default values as shown in the help command. Please refer to the sub-sections for more details. - +import Configuration from '@site/src/components/Configuration'; +import configOptions from './help.json'; + + ## Next Steps {#next} diff --git a/webpage/docs/configuration/authentication.md b/webpage/docs/configuration/authentication.md index 0b06d7f7..3fd1f758 100644 --- a/webpage/docs/configuration/authentication.md +++ b/webpage/docs/configuration/authentication.md @@ -3,8 +3,6 @@ description: Configuration related to the Authentication and Sessions in Neko. --- import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # Authentication @@ -80,10 +78,6 @@ import TabItem from '@theme/TabItem'; Member providers are responsible for deciding whether given credentials are valid or not. This validation can either be done against a local database or an external system. -:::info -Currently, Neko supports configuring only one authentication provider at a time. This means you must choose a single provider that best fits your deployment needs. -::: - ### Multi-User Provider {#member.multiuser} This is the **default provider** that works exactly like the authentication used to work in v2 of neko. @@ -92,25 +86,21 @@ This provider allows you to define two types of users: **regular** users and **a Profiles for regular users and admins are optional, if not provided, the default profiles are used (see below in the example configuration). - +```yaml title="config.yaml" +member: + provider: multiuser + multiuser: + # Password for admins, in plain text. + admin_password: "adminPassword" + # Profile fields as described above + admin_profile: + ... + # Password for regular users, in plain text. + user_password: "userPassword" + # Profile fields as described above + user_profile: + ... +```
See example configuration @@ -162,8 +152,8 @@ For easier configuration, you can specify only passwords using environment varia ```yaml title="docker-compose.yaml" environment: - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "admin" NEKO_MEMBER_MULTIUSER_USER_PASSWORD: "neko" + NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "admin" ``` ::: @@ -171,17 +161,15 @@ environment: This provider reads the user's credentials from a file. It is useful for small deployments where you don't want to set up a database or LDAP server and still want to have persistent users. - +```yaml title="config.yaml" +member: + provider: file + file: + # Absolute path to the file containing the users and their passwords. + path: /opt/neko/members.json + # Whether the passwords are hashed using sha256 or not. + hash: true +``` It allows you to store the user's credentials in a JSON file. The JSON structure maps user logins to their passwords and profiles. @@ -201,7 +189,7 @@ You can leave the file empty and add users later using the HTTP API. We have two users, `admin` and `user` with their passwords and profiles. `admin` is a regular user, while `user` is an admin. - Please note that the passwords are stored in plain text. To store them securely, set the `hash` field to `true` in the configuration. After that, the passwords are expected to be hashed using sha256 and base64-encoded. The file will look like this: + Please note that the passwords are stored in plain text. To store them securely, set the `hash` field to `true` in the configuration. After that, the passwords are expected to be hashed using the bcrypt algorithm. ```json title="members.json" { @@ -240,10 +228,10 @@ You can leave the file empty and add users later using the HTTP API. } ``` - If you want to hash the passwords, you can use the following command to generate a sha256 base64-encoded hash of the password: + If you want to hash the passwords, you can use the following command to generate a sha256 hash: ```bash - echo -n "password" | openssl sha256 -binary | base64 - + echo -n "password" | sha256sum ```
@@ -251,13 +239,18 @@ You can leave the file empty and add users later using the HTTP API. This provider is the same as the file provider, but it saves the users only in memory. That means that the users are lost when the server is restarted. However, the default users can be set in the configuration file. The difference from the multi-user provider is that the users are not generated on demand and we define exactly which users with their passwords and profiles are allowed to log in. They cannot be logged in twice with the same username. - +```yaml title="config.yaml" +member: + provider: object + object: + # List of users with their passwords and profiles + - username: "admin" + # Password in plain text + password: "admin" + # Profile fields as described above + profile: + ... +```
See example configuration @@ -303,9 +296,10 @@ This provider is the same as the file provider, but it saves the users only in m This provider allows any user to log in without any authentication. It is useful for testing and development purposes. - +```yaml title="config.yaml" +member: + provider: noauth +``` :::danger Do not use this provider in production environments unless you know exactly what you are doing. It allows anyone to log in and control neko as an admin. @@ -317,9 +311,10 @@ Currently, there are only two providers available for sessions: **memory** and * Simply by specifying the `session.file` to a file path, the session provider will store the sessions in a file. Otherwise, the sessions are stored in memory and are lost when the server is restarted. - +```yaml title="config.yaml" +session: + file: /opt/neko/sessions.json +``` :::info In the future, we plan to add more session providers, such as Redis, PostgreSQL, etc. So the Configuration Options may change. @@ -329,9 +324,10 @@ In the future, we plan to add more session providers, such as Redis, PostgreSQL, The API User is a special user that is used to authenticate the HTTP API requests. It cannot connect to the room, but it can perform administrative tasks. The API User does not have a password but only a token that is used to authenticate the requests. If the token is not set, the API User is disabled. -', -}} comments={false} /> +```yaml title="config.yaml" +session: + api_token: "apiToken" +``` :::tip This user is useful in some situations when the rooms are generated by the server and the token is guaranteed to be random every time a short-lived room is run. It is not a good idea to define this token for long-lived rooms, as it can be stolen and used to perform administrative tasks. @@ -351,15 +347,17 @@ The authentication between the client and the server can be done using cookies o If you disable the cookies, the token will be sent to the client in the login response and saved in local storage. This is less secure than using cookies, as the token **can be stolen using XSS attacks**. Therefore, it is recommended to use cookies. ::: - +```yaml title="config.yaml" +session: + cookie: + enabled: true + name: "NEKO_SESSION" + expiration: "24h" + secure: true + http_only: true + domain: "" + path: "" +``` - - Whether the cookies are enabled or not. - - Name of the cookie used to store the session. diff --git a/webpage/docs/configuration/capture.md b/webpage/docs/configuration/capture.md index be998e71..2778c0d5 100644 --- a/webpage/docs/configuration/capture.md +++ b/webpage/docs/configuration/capture.md @@ -3,8 +3,6 @@ description: Configuration related to Gstreamer capture in Neko. --- import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # Audio & Video Capture @@ -29,19 +27,22 @@ All video pipelines must use the same video codec (defined in the +```yaml title="config.yaml" +capture: + video: + display: "" + codec: "vp8" # default video codec + ids: [ , , ... ] + pipelines: + : + : + ... +``` - is the name of the [X display](https://www.x.org/wiki/) that you want to capture. If not specified, the environment variable `DISPLAY` will be used. - available codecs are `vp8`, `vp9`, `av1`, `h264`. [Supported video codecs](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/WebRTC_codecs#supported_video_codecs) are dependent on the WebRTC implementation used by the client, `vp8` and `h264` are supported by all WebRTC implementations. - is a list of pipeline ids that are defined in the section. The first pipeline in the list will be the default pipeline. -- is a shorthand for defining [Gstreamer pipeline description](#video.gst_pipeline) for a single pipeline. This is option is ignored if is defined. -- is a dictionary of pipeline configurations. Each pipeline configuration is defined by a unique pipeline id. They can be defined in two ways: either by building the pipeline dynamically using [Expression-Driven Configuration](#video.expression) or by defining the pipeline using a [Gstreamer Pipeline Description](#video.gst_pipeline). +- is a dictionary of pipeline configurations. Each pipeline configuration is defined by a unique pipeline id. They can be defined in two ways: either by building the pipeline dynamically using [Expression-Driven Configuration](#video.expression) or by defining the pipeline using a [Gstreamer Pipeline Description](#video.pipeline). ### Expression-Driven Configuration {#video.expression} @@ -150,7 +151,7 @@ import TabItem from '@theme/TabItem';
-### Gstreamer Pipeline Description {#video.gst_pipeline} +### Gstreamer Pipeline Description {#video.pipeline} If you want to define the pipeline using a [Gstreamer pipeline description](https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description), you can do so by setting the parameter. @@ -168,7 +169,7 @@ Since now you have to define the whole pipeline, you need to specify the src ele Your typical pipeline string would look like this: ``` -ximagesrc display-name={display} show-pointer=true use-damage=false ! ! appsink name=appsink +ximagesrc display-name={display} show-pointer=true use-damage=false ! ! appsink name=appsink" ``` See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentation/ximagesrc/index.html) and [appsink](https://gstreamer.freedesktop.org/documentation/app/appsink.html) for more information. @@ -188,9 +189,8 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio hq: gst_pipeline: | ximagesrc display-name={display} show-pointer=true use-damage=false - ! videoconvert ! queue + ! videoconvert ! vp8enc - name=encoder target-bitrate=3072000 cpu-used=4 end-usage=cbr @@ -207,9 +207,8 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio lq: gst_pipeline: | ximagesrc display-name={display} show-pointer=true use-damage=false - ! videoconvert ! queue + ! videoconvert ! vp8enc - name=encoder target-bitrate=1024000 cpu-used=4 end-usage=cbr @@ -237,9 +236,8 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio main: gst_pipeline: | ximagesrc display-name={display} show-pointer=true use-damage=false - ! videoconvert ! queue + ! videoconvert ! x264enc - name=encoder threads=4 bitrate=4096 key-int-max=15 @@ -250,62 +248,23 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio ! appsink name=appsink ``` - - - ```yaml title="config.yaml" - capture: - video: - codec: h264 - ids: [ main ] - pipelines: - main: - gst_pipeline: | - ximagesrc display-name={display} show-pointer=true use-damage=false - ! videoconvert ! queue - ! video/x-raw,format=NV12 - ! nvh264enc - name=encoder - preset=2 - gop-size=25 - spatial-aq=true - temporal-aq=true - bitrate=4096 - vbv-buffer-size=4096 - rc-mode=6 - ! h264parse config-interval=-1 - ! video/x-h264,stream-format=byte-stream - ! appsink name=appsink - ``` - - This configuration requires [Nvidia GPU](https://developer.nvidia.com/cuda-gpus) with [NVENC](https://developer.nvidia.com/nvidia-video-codec-sdk) support. - - -Overview of available encoders for each codec is shown in the table below. The encoder name is used in the parameter. The parameters for each encoder are different and you can find the documentation for each encoder in the links below. - -| codec | encoder | vaapi encoder | nvenc encoder | -| ----- | ------- | ------------- | ------------- | -| VP8 | [vp8enc](https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html?gi-language=c) | [vaapivp8enc](https://github.com/GStreamer/gstreamer-vaapi/blob/master/gst/vaapi/gstvaapiencode_vp8.c) | ? | -| VP9 | [vp9enc](https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html?gi-language=c) | [vaapivp9enc](https://github.com/GStreamer/gstreamer-vaapi/blob/master/gst/vaapi/gstvaapiencode_vp9.c) | ? | -| AV1 | [av1enc](https://gstreamer.freedesktop.org/documentation/aom/av1enc.html?gi-language=c) | ? | [nvav1enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvav1enc.html?gi-language=c) | -| H264 | [x264enc](https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c) | [vaapih264enc](https://gstreamer.freedesktop.org/documentation/vaapi/vaapih264enc.html?gi-language=c) | [nvh264enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvh264enc.html?gi-language=c) | -| H265 | [x265enc](https://gstreamer.freedesktop.org/documentation/x265/index.html?gi-language=c) | [vaapih265enc](https://gstreamer.freedesktop.org/documentation/vaapi/vaapih265enc.html?gi-language=c) | [nvh265enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvh265enc.html?gi-language=c) | - - ## WebRTC Audio {#audio} Only one audio pipeline can be defined in neko. The audio pipeline is used to capture and encode audio, similar to the video pipeline. The encoded audio is then sent to the client using WebRTC. The Gstreamer pipeline is started when the first client requests the video stream and is stopped after the last client disconnects. - +```yaml title="config.yaml" +capture: + audio: + device: "audio_output.monitor" # default audio device + codec: "opus" # default audio codec + pipeline: "" +``` - is the name of the [pulseaudio device](https://wiki.archlinux.org/title/PulseAudio/Examples) that you want to capture. If not specified, the default audio device will be used. - available codecs are `opus`, `g722`, `pcmu`, `pcma`. [Supported audio codecs](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/WebRTC_codecs#supported_audio_codecs) are dependent on the WebRTC implementation used by the client, `opus` is supported by all WebRTC implementations. @@ -334,21 +293,23 @@ Neko allows you to broadcast out-of-the-box the display and audio capture to a t The Gstreamer pipeline is started when the broadcast is started and is stopped when the broadcast is stopped regardless of the clients connected. - +```yaml title="config.yaml" +capture: + broadcast: + audio_bitrate: 128 # in KB/s + video_bitrate: 4096 # in KB/s + preset: "veryfast" + pipeline: "" + url: "rtmp:////" + autostart: true +``` The default encoder uses `h264` for video and `aac` for audio, muxed in the `flv` container and sent over the `rtmp` protocol. You can change the encoder settings by setting a custom Gstreamer pipeline description in the parameter. - and are the bitrate settings for the default audio and video encoders expressed in kilobits per second. - is the encoding speed preset for the default video encoder. See available presets [here](https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c#GstX264EncPreset). - when set, encoder settings above are ignored and the custom Gstreamer pipeline description is used. In the pipeline, you can use `{display}`, `{device}` and `{url}` as placeholders for the X display name, pulseaudio audio device name, and broadcast URL respectively. -- is the URL of the RTMP server where the broadcast will be sent e.g. `rtmp:////`. This can be set later using the API if the URL is not known at the time of configuration or is expected to change. +- is the URL of the RTMP server where the broadcast will be sent. This can be set later using the API if the URL is not known at the time of configuration or is expected to change. - is a boolean value that determines whether the broadcast should start automatically when neko starts, works only if the URL is set.
@@ -419,12 +380,14 @@ This is a fallback mechanism and should not be used as a primary video stream be The Gstreamer pipeline is started in the background when the first client requests the screencast and is stopped after a period of inactivity. - +```yaml title="config.yaml" +capture: + screencast: + enabled: true + rate: "10/1" + quality: 60 + pipeline: "" +``` - is a boolean value that determines whether the screencast is enabled or not. - is the framerate of the screencast. It is expressed as a fraction of frames per second, for example, `10/1` means 10 frames per second. @@ -459,12 +422,14 @@ Neko allows you to capture the webcam on the client machine and send it to the s The Gstreamer pipeline is started when the client shares their webcam and is stopped when the client stops sharing the webcam. Maximum one webcam pipeline can be active at a time. - +```yaml title="config.yaml" +capture: + webcam: + enabled: true + device: "/dev/video0" # default webcam device + width: 640 + height: 480 +``` - is a boolean value that determines whether the webcam capture is enabled or not. - is the name of the [video4linux device](https://www.kernel.org/doc/html/v4.12/media/v4l-drivers/index.html) that will be used as a virtual webcam. @@ -498,10 +463,12 @@ Neko allows you to capture the microphone on the client machine and send it to t The Gstreamer pipeline is started when the client shares their microphone and is stopped when the client stops sharing the microphone. Maximum one microphone pipeline can be active at a time. - +```yaml title="config.yaml" +capture: + microphone: + enabled: true + device: "audio_input" +``` - is a boolean value that determines whether the microphone capture is enabled or not. - is the name of the [pulseaudio device](https://wiki.archlinux.org/title/PulseAudio/Examples) that will be used as a virtual microphone. diff --git a/webpage/docs/configuration/desktop.md b/webpage/docs/configuration/desktop.md index b492e9ce..d1a3f47f 100644 --- a/webpage/docs/configuration/desktop.md +++ b/webpage/docs/configuration/desktop.md @@ -3,8 +3,6 @@ description: Configuration related to the Desktop Environment in Neko. --- import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # Desktop Environment @@ -12,15 +10,18 @@ This section describes how to configure the desktop environment inside neko. Neko uses the [X Server](https://www.x.org/archive/X11R7.6/doc/man/man1/Xserver.1.xhtml) as the display server with [Openbox](http://openbox.org/wiki/Main_Page) as the default window manager. For audio, [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) is used. - +```yaml title="config.yaml" +desktop: + display: "" + screen: "1280x720@30" # default +``` - refers to the X server that is running on the system. If it is not specified, the environment variable `DISPLAY` is used. The same display is referred to in the [Capture](capture#video.display) configuration to capture the screen. In most cases, we want to use the same display for both. - refers to the screen resolution and refresh rate. The format is `x@`. If not specified, the default is `1280x720@30`. :::tip +You can specify the screen resolution using the environment variable `NEKO_DESKTOP_SCREEN`. + Admin can change the resolution in the GUI. ::: @@ -32,10 +33,12 @@ Neko uses the [XTEST Extension Library](https://www.x.org/releases/X11R7.7/doc/l Currently, only touchscreens are supported through the custom driver. ::: - +```yaml title="config.yaml" +desktop: + input: + enabled: true # default + socket: "/tmp/xf86-input-neko.sock" # default +``` - enables the input device support. If not specified, the default is `false`. - refers to the socket file that the custom driver creates. If not specified, the default is `/tmp/xf86-input-neko.sock`. @@ -48,9 +51,10 @@ When using Docker, the custom driver is already included in the image and the so Most of the time, only a single application is used in the minimal desktop environment without any taskbar or desktop icons. It could happen that the user accidentally minimizes the application and then it is not possible to restore it. To prevent this, we can use the `unminimize` feature that simply listens for the minimize event and restores the window back to the original state. - +```yaml title="config.yaml" +desktop: + unminimize: true # default +``` ## Upload Drop {#upload_drop} @@ -58,9 +62,10 @@ The upload drop is a feature that allows the user to upload files to the applica The current approach is to catch the drag and drop events on the client side, upload them to the server along with the coordinates of the drop event, and then open an invisible overlay window on the server that has set the file path to the uploaded file and allows it to be dragged and dropped into the application. Then the mouse events are simulated to drag the file from the overlay window to the application window. - +```yaml title="config.yaml" +desktop: + upload_drop: true # default +``` ## File Chooser Dialog {#file_chooser_dialog} @@ -72,6 +77,7 @@ The file chooser dialog is a feature that allows handling the file chooser dialo The current approach is to put the file chooser dialog in the background as soon as it is displayed, prompt the user to upload the file, and then select this file in the file chooser dialog by simulating the keyboard events to navigate to the file and press the open button. **This is very error-prone and may not work as expected.** - +```yaml title="config.yaml" +desktop: + file_chooser_dialog: false # default +``` diff --git a/webpage/docs/configuration/help.json b/webpage/docs/configuration/help.json index 8320f7e9..e449cf8e 100644 --- a/webpage/docs/configuration/help.json +++ b/webpage/docs/configuration/help.json @@ -173,24 +173,15 @@ "type": "strings", "description": "ordered list of video ids" }, - { - "key": [ - "capture", - "video", - "pipeline" - ], - "type": "string", - "description": "shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set" - }, { "key": [ "capture", "video", "pipelines" ], - "type": "object", - "defaultValue": {}, - "description": "pipelines config used for video streaming" + "type": "string", + "defaultValue": "[]", + "description": "pipelines config in JSON used for video streaming" }, { "key": [ @@ -304,7 +295,7 @@ ], "type": "boolean", "defaultValue": "true", - "description": "member file provider: whether the passwords are hashed using sha256 or not (recommended)" + "description": "member file provider: whether to hash passwords using sha256 (recommended)" }, { "key": [ @@ -313,7 +304,7 @@ "path" ], "type": "string", - "description": "member file provider: path to the file containing the users and their passwords" + "description": "member file provider: storage path" }, { "key": [ @@ -323,7 +314,7 @@ ], "type": "string", "defaultValue": "admin", - "description": "member multiuser provider: password for admin users" + "description": "member multiuser provider: admin password" }, { "key": [ @@ -331,9 +322,9 @@ "multiuser", "admin_profile" ], - "type": "object", - "defaultValue": {}, - "description": "member multiuser provider: profile template for admin users" + "type": "string", + "defaultValue": "{}", + "description": "member multiuser provider: admin profile in JSON format" }, { "key": [ @@ -343,7 +334,7 @@ ], "type": "string", "defaultValue": "neko", - "description": "member multiuser provider: password for regular users" + "description": "member multiuser provider: user password" }, { "key": [ @@ -351,9 +342,9 @@ "multiuser", "user_profile" ], - "type": "object", - "defaultValue": {}, - "description": "member multiuser provider: profile template for regular users" + "type": "string", + "defaultValue": "{}", + "description": "member multiuser provider: user profile in JSON format" }, { "key": [ @@ -361,9 +352,9 @@ "object", "users" ], - "type": "array", - "defaultValue": [], - "description": "member object provider: list of users with their passwords and profiles" + "type": "string", + "defaultValue": "[]", + "description": "member object provider: users in JSON format" }, { "key": [ @@ -372,7 +363,7 @@ ], "type": "string", "defaultValue": "multiuser", - "description": "selected member provider" + "description": "choose member provider" }, { "key": [ @@ -767,9 +758,9 @@ "iceservers", "backend" ], - "type": "array", - "defaultValue": [], - "description": "STUN and TURN servers used by the backend" + "type": "urls", + "defaultValue": "[]", + "description": "Backend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys" }, { "key": [ @@ -777,9 +768,9 @@ "iceservers", "frontend" ], - "type": "array", - "defaultValue": [], - "description": "STUN and TURN servers used by the frontend" + "type": "urls", + "defaultValue": "[]", + "description": "Frontend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys" }, { "key": [ diff --git a/webpage/docs/configuration/help.txt b/webpage/docs/configuration/help.txt index 89ef9120..9ca36858 100644 --- a/webpage/docs/configuration/help.txt +++ b/webpage/docs/configuration/help.txt @@ -16,8 +16,7 @@ --capture.video.codec string video codec to be used (default "vp8") --capture.video.display string X display to capture --capture.video.ids strings ordered list of video ids - --capture.video.pipeline string shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set - --capture.video.pipelines string pipelines config used for video streaming (default "{}") + --capture.video.pipelines string pipelines config in JSON used for video streaming (default "[]") --capture.webcam.device string v4l2sink device used for webcam (default "/dev/video0") --capture.webcam.enabled enable webcam stream --capture.webcam.height int webcam stream height (default 720) @@ -29,14 +28,14 @@ --desktop.screen string default screen size and framerate (default "1280x720@30") --desktop.unminimize automatically unminimize window when it is minimized (default true) --desktop.upload_drop whether drop upload is enabled (default true) - --member.file.hash member file provider: whether the passwords are hashed using sha256 or not (recommended) (default true) - --member.file.path string member file provider: path to the file containing the users and their passwords - --member.multiuser.admin_password string member multiuser provider: password for admin users (default "admin") - --member.multiuser.admin_profile string member multiuser provider: profile template for admin users (default "{}") - --member.multiuser.user_password string member multiuser provider: password for regular users (default "neko") - --member.multiuser.user_profile string member multiuser provider: profile template for regular users (default "{}") - --member.object.users string member object provider: list of users with their passwords and profiles (default "[]") - --member.provider string selected member provider (default "multiuser") + --member.file.hash member file provider: whether to hash passwords using sha256 (recommended) (default true) + --member.file.path string member file provider: storage path + --member.multiuser.admin_password string member multiuser provider: admin password (default "admin") + --member.multiuser.admin_profile string member multiuser provider: admin profile in JSON format (default "{}") + --member.multiuser.user_password string member multiuser provider: user password (default "neko") + --member.multiuser.user_profile string member multiuser provider: user profile in JSON format (default "{}") + --member.object.users string member object provider: users in JSON format (default "[]") + --member.provider string choose member provider (default "multiuser") --plugins.dir string path to neko plugins to load (default "./bin/plugins") --plugins.enabled load plugins in runtime --plugins.required if true, neko will exit if there is an error when loading a plugin @@ -79,8 +78,8 @@ --webrtc.estimator.unstable_duration duration how long to wait for stalled connection (neutral trend with low bandwidth) before downgrading (default 6s) --webrtc.estimator.upgrade_backoff duration how long to wait before upgrading again after previous upgrade (default 5s) --webrtc.icelite configures whether or not the ICE agent should be a lite agent - --webrtc.iceservers.backend string STUN and TURN servers used by the backend (default "[]") - --webrtc.iceservers.frontend string STUN and TURN servers used by the frontend (default "[]") + --webrtc.iceservers.backend urls Backend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys (default "[]") + --webrtc.iceservers.frontend urls Frontend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys (default "[]") --webrtc.icetrickle configures whether cadidates should be sent asynchronously using Trickle ICE (default true) --webrtc.ip_retrieval_url string URL address used for retrieval of the external IP address (default "https://checkip.amazonaws.com") --webrtc.nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used diff --git a/webpage/docs/configuration/plugins.md b/webpage/docs/configuration/plugins.md index 5f6d5e38..9d6fc85a 100644 --- a/webpage/docs/configuration/plugins.md +++ b/webpage/docs/configuration/plugins.md @@ -3,18 +3,17 @@ description: Configuration related to the Neko plugins. --- import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # Plugins Configuration Neko allows you to extend its functionality by using [plugins](https://pkg.go.dev/plugin). Go plugins come with a lot of benefits as well as some limitations. The main advantage is that you can extend the functionality of the application without recompiling the main application. But the main limitation is that you need to use the same Go version and all dependencies with the same version as the main application. - +```yaml title="config.yaml" +plugins: + enabled: true + required: true + dir: "./bin/plugins" +``` - enables the plugin support. If set to `false`, the plugins are not loaded. - makes the plugin loading mandatory, meaning that if a plugin fails to load, the application will not start. @@ -30,9 +29,10 @@ There exist a few pre-loaded internal plugins that are shipped with Neko: The chat plugin is a simple pre-loaded internal plugin that allows you to chat with other users in the same session. The chat messages are sent to the server and then broadcasted to all users in the same session. - +```yaml title="config.yaml" +chat: + enabled: true +``` - enables the chat support. If set to `false`, the chat is disabled. @@ -51,15 +51,12 @@ plugins: The file transfer plugin is a simple pre-loaded internal plugin that allows you to transfer files between the client and the server. The files are uploaded to the server and then downloaded by the client. - - +```yaml title="config.yaml" +filetransfer: + enabled: true + dir: "./uploads" + refresh_interval: 30s +``` - enables the file transfer support. If set to `false`, the file transfer is disabled. - refers to the directory where the files are stored. @@ -73,3 +70,4 @@ plugins: ``` - `filetransfer.enabled` in the room settings context controls whether the file transfer is enabled for any user in the room, and in the user's profile context controls whether the user can transfer files. + diff --git a/webpage/docs/configuration/webrtc.md b/webpage/docs/configuration/webrtc.md index 6606480f..47c5face 100644 --- a/webpage/docs/configuration/webrtc.md +++ b/webpage/docs/configuration/webrtc.md @@ -3,8 +3,6 @@ description: Configuration related to the WebRTC and Networking in Neko. --- import { Def, Opt } from '@site/src/components/Anchor'; -import { ConfigurationTab } from '@site/src/components/Configuration'; -import configOptions from './help.json'; # WebRTC Configuration @@ -20,17 +18,19 @@ ICE, which stands for Interactive Connectivity Establishment, is a protocol used ICE Trickle is a feature that allows ICE candidates to be sent as they are discovered, rather than waiting for all candidates to be discovered before sending them. It means that the ICE connection can be established faster as the server can start connecting to the client as soon as it has a few ICE candidates and doesn't have to wait for all of them to be discovered. - +```yaml title="config.yaml" +webrtc: + icetrickle: false +``` ### ICE Lite {#icelite} ICE Lite is a minimal implementation of the ICE protocol intended for servers running on a public IP address. It is not enabled by default to allow more complex ICE configurations out of the box. - +```yaml title="config.yaml" +webrtc: + icelite: false +``` :::info When using ICE Servers, ICE Lite must be disabled. @@ -100,10 +100,16 @@ import TabItem from '@theme/TabItem'; The ICE servers are divided into two groups: - +```yaml title="config.yaml" +webrtc: + iceservers: + frontend: + # List of ICE Server configurations as described above + - urls: "stun:stun.l.google.com:19302" + backend: + # List of ICE Server configurations as described above + - urls: "stun:stun.l.google.com:19302" +``` - - ICE servers that are sent to the client and used to establish a connection between the client and the server. - - ICE servers that are used by the server to gather ICE candidates. They might contain private IP addresses or other sensitive information that should not be sent to the client. @@ -156,14 +162,15 @@ There exist two types of connections: The ephemeral UDP port range can be configured using the following configuration: - +```yaml title="config.yaml" +webrtc: + epr: "59000-59100" +``` The range `59000-59100` contains 101 ports, which should be open on the server's firewall. The server uses these ports to establish a connection with the client. You can specify a different range of ports if needed, with fewer or more ports, depending on the number of simultaneous connections you expect. -:::tip Make sure -When specifying the ephemeral UDP port range in `docker-compose.yaml`, make sure to use the same range for ports **as UDP**. +:::tip +You can specify the ephemeral UDP port range as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_EPR` environment variable. When using docker, make sure to expose the ports in the `docker-compose.yaml`. ```yaml title="docker-compose.yaml" environment: @@ -179,18 +186,19 @@ It is important to expose the same ports to the host machine, without any remapp The UDP/TCP multiplexing port can be configured using the following configuration: - +```yaml title="config.yaml" +webrtc: + udpmux: 59000 + tcpmux: 59000 +``` - - The port used for UDP connections. - - The port used for TCP connections. The server uses only port `59000` for both UDP and TCP connections. This port should be open on the server's firewall. You can specify a different port if needed, or specify only one of the two protocols. UDP is generally better for latency, but some networks block UDP so it is good to have TCP available as a fallback. -:::tip Make sure -When specifying the UDP/TCP multiplexing port in `docker-compose.yaml`, make sure to correctly specify the protocol in the ports section. +:::tip +You can specify the UDP/TCP multiplexing port as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_TCPMUX` and `NEKO_WEBRTC_UDPMUX` environment variables. When using docker, make sure to expose the ports in the `docker-compose.yaml`. ```yaml title="docker-compose.yaml" environment: @@ -209,20 +217,42 @@ It is important to expose the same ports to the host machine, without any remapp The server IP address is sent to the client in ICE candidates so that the client can establish a connection with the server. By default, the server IP address is automatically resolved by the server to the public IP address of the server. If the server is behind a NAT, you want to specify a different IP address or use neko only in a local network, you can specify the server IP address manually. #### NAT 1-to-1 {#nat1to1} - -Currently, only one address can be specified. Therefore if you want to access your instance from both local and public networks, your router must support [NAT loopback (hairpinning)](https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning). +```yaml title="config.yaml" +webrtc: + nat1to1: + # IPv4 address of the server + - 10.10.0.5 + # IPv6 address of the server + - 2001:db8:85a3::8a2e:370:7334 +``` + +Currently, only one IPv4 and one IPv6 address can be specified. Therefore if you want to access your instance from both local and public networks, your router must support [NAT loopback (hairpinning)](https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning). + +:::tip +You can specify the server IP address as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_NAT1TO1` environment variable. + +```yaml title="docker-compose.yaml" +environment: + NEKO_WEBRTC_NAT1TO1: "10.10.0.5" +``` + +If you want to specify also an IPv6 address, use whitespace to separate the addresses. + +```yaml title="docker-compose.yaml" +environment: + NEKO_WEBRTC_NAT1TO1: "10.10.0.5 2001:db8:85a3::8a2e:370:7334" +``` +::: #### IP Retrieval URL {#ip_retrieval_url} If you do not specify the server IP address, the server will try to resolve the public IP address of the server automatically. - - +```yaml title="config.yaml" +webrtc: + ip_retrieval_url: "https://checkip.amazonaws.com" +``` The server will send an HTTP GET request to the specified URL to retrieve the public IP address of the server. ## Bandwidth Estimator {#estimator} @@ -233,6 +263,29 @@ The bandwidth estimator is an experimental feature and might not work as expecte The bandwidth estimator is a feature that allows the server to estimate the available bandwidth between the client and the server. It is used to switch between different video qualities based on the available bandwidth. The bandwidth estimator is disabled by default. - +```yaml title="config.yaml" +webrtc: + estimator: + # Whether to enable the bandwidth estimator + enabled: false + # Whether the bandwidth estimator is passive - only used for logging and not for actual decisions + passive: false + # Enable debug logging for the bandwidth estimator (will print the current state and decisions) + debug: false + # Initial bitrate for the bandwidth estimator to start with (in bps) + initial_bitrate: 1000000 + # How often to read and process bandwidth estimation reports + read_interval: "2s" + # How long to wait for a stable connection (upward or neutral trend) before upgrading + stable_duration: "12s" + # How long to wait for a stalled connection (neutral trend with low bandwidth) before downgrading + unstable_duration: "6s" + # How long to wait for stalled bandwidth estimation before downgrading + stalled_duration: "24s" + # How long to wait before downgrading again after the previous downgrade + downgrade_backoff: "10s" + # How long to wait before upgrading again after the previous upgrade + upgrade_backoff: "5s" + # How much bigger the difference between estimated and stream bitrate must be to trigger a change + diff_threshold: 0.15 +``` diff --git a/webpage/docs/customization/README.md b/webpage/docs/customization/README.md deleted file mode 100644 index 1adf6cb0..00000000 --- a/webpage/docs/customization/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Customization - -When you want to customize the Neko virtual environment beyond the server configuration, you can do so by mounting your own files to the container that replace or extend the default files. This allows you to customize the desktop environment, browser settings, and more. - -## Supervisord Configuration {#supervisord} - -The Neko container uses [supervisord](https://supervisord.org/) to manage the processes inside the container. The search path for the configuration files is `/etc/neko/supervisord/.conf`. You can mount your own `app.conf` file to the directory `/etc/neko/supervisord/` to add a new process to the container. - -```config title="supervisord.conf" -[program:app] -environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s" -command=/opt/path/to/my-app -stopsignal=INT -autorestart=true -priority=800 -user=%(ENV_USER)s -stdout_logfile=/var/log/neko/app.log -stdout_logfile_maxbytes=100MB -stdout_logfile_backups=10 -redirect_stderr=true -``` - -For example, with firefox, you can mount your own `firefox.conf` file to the directory `/etc/neko/supervisord/` to overwrite the default configuration file and modify the command that starts Firefox. Make sure to copy the default configuration file from the container to your local machine first: - -```bash -# Create a container without starting it -docker create --name neko ghcr.io/m1k1o/neko/firefox:latest -# Copy the default configuration file to your local machine -docker cp neko:/etc/neko/supervisord/firefox.conf ./firefox.conf -# Remove the container -docker rm -f neko -``` - -Then, you can modify the configuration file to your liking and mount your new version to the container: - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - # highlight-start - volumes: - - "./firefox.conf:/etc/neko/supervisord/firefox.conf" - # highlight-end - environment: - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 -``` - -## Next Steps - -import DocCardList from '@theme/DocCardList'; -import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; - - diff --git a/webpage/docs/customization/browsers.json b/webpage/docs/customization/browsers.json deleted file mode 100644 index c0393d4a..00000000 --- a/webpage/docs/customization/browsers.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "tag": "firefox", - "flavor": "firefox-based", - "profileDir": "/home/neko/.mozilla/firefox/profile.default", - "policiesFile": "/usr/lib/firefox/distribution/policies.json" - }, - { - "tag": "tor-browser", - "flavor": "firefox-based", - "profileDir": "/opt/tor-browser_en-US/Browser/TorBrowser/Data/Browser/profile.default", - "policiesFile": "/opt/tor-browser_en-US/Browser/distribution/policies.json" - }, - { - "tag": "waterfox", - "flavor": "firefox-based", - "profileDir": "/home/neko/.waterfox/profile.default", - "policiesFile": "/usr/lib/waterfox/distribution/policies.json" - }, - { - "tag": "chromium", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/chromium", - "policiesFile": "/etc/chromium/policies/managed/policies.json" - }, - { - "tag": "google-chrome", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/google-chrome", - "policiesFile": "/etc/opt/chrome/policies/managed/policies.json" - }, - { - "tag": "ungoogled-chromium", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/chromium", - "policiesFile": "/etc/chromium/policies/managed/policies.json" - }, - { - "tag": "microsoft-edge", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/microsoft-edge", - "policiesFile": "/etc/opt/edge/policies/managed/policies.json" - }, - { - "tag": "brave", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/brave", - "policiesFile": "/etc/brave/policies/managed/policies.json" - }, - { - "tag": "vivaldi", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/vivaldi", - "policiesFile": "/etc/opt/vivaldi/policies/managed/policies.json" - }, - { - "tag": "opera", - "flavor": "chromium-based", - "profileDir": "/home/neko/.config/opera" - } -] \ No newline at end of file diff --git a/webpage/docs/customization/browsers.md b/webpage/docs/customization/browsers.md deleted file mode 100644 index 6b3c5c0c..00000000 --- a/webpage/docs/customization/browsers.md +++ /dev/null @@ -1,232 +0,0 @@ ---- -sidebar_label: "Browsers" -description: "Customize your browser settings and configurations in Neko." ---- - -import { AppIcon } from '@site/src/components/AppIcon'; -import { ProfileDirectoryPaths, PolicyFilePaths } from './browsers' - -# Browsers Customization - -Browsers use policies to manage settings and configurations programmatically. This is useful for Neko containers that can be set up with a specific configuration every time a fresh container is created. It also prevents users from changing certain settings, which is useful for security and privacy. For example, not allowing users to install extensions can prevent them from installing malicious extensions that could compromise their privacy and security. - -However, as a user of Neko, you may want to customize your browser settings (install your own extensions) or wish to have your bookmarks and settings persist across sessions. - -## Persistent Browser Profile {#persistent-profile} - -When you run a browser in a container, the browser runs in a fresh environment every time you create (or upgrade) the container. This means that any changes you make to the browser settings or your browsing history will be lost when you stop the container. This is because the container is ephemeral and does not persist data across sessions. - -If you want to persist your browser settings, bookmarks, extensions, and browsing history across sessions, you need to mount a volume to the browser's profile directory. This allows you to store your browser data outside of the container so it can be accessed even after the container is stopped or removed. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - # highlight-start - volumes: - - "./profile:/home/neko/.mozilla/firefox/profile.default" - # highlight-end - environment: - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 -``` - -Replace `./profile` with the path to the directory where you want to store your browser data. This directory will be created if it does not exist. - -Make sure to set the correct permissions for the directory so that the container can access it. The Neko user inside the container has a UID of `1000`, so you need to set the ownership of the directory to `1000:1000`. You can do this by running the following command: - -```bash -sudo chown -R 1000:1000 ./profile -``` - -The path inside the container will be `/home/neko/.mozilla/firefox/profile.default`, which is the default profile directory for Firefox. You can find the profile directory for other browsers in the table below: - - - -## Browser Policy Files {#policy-files} - -Browser policy files are JSON files that contain settings and configurations for the browser. These files are used to manage the browser settings programmatically and can be used to enforce certain policies, such as disabling extensions, setting the homepage, and more. - -:::note -In the example below, we are using the Firefox browser. Make sure you replace `/usr/lib/firefox/distribution/policies.json` with the correct path for the browser you are using according to the tables below. -::: - -If you want to customize the policy file, you can mount your own policy file to the container. This allows you to customize the browser settings and configurations to your liking. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - # highlight-start - volumes: - - "./policy.json:/usr/lib/firefox/distribution/policies.json" - # highlight-end - environment: - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 -``` - -If you just want to modify the default policy file, you can copy the current policy file from the container to your local machine: - -```bash -# Create a container without starting it -docker create --name neko ghcr.io/m1k1o/neko/firefox:latest -# Copy the policy file from the container to your local machine -docker cp neko:/usr/lib/firefox/distribution/policies.json ./policy.json -# Remove the container -docker rm neko -``` - -Or you can download the default policy file from the repository directly: - -```bash -# Replace firefox with the browser you are using -curl -o ./policy.json https://raw.githubusercontent.com/m1k1o/neko/refs/heads/main/apps/firefox/policies.json -``` - -If you wish to disable the policies altogether, you can just create an empty JSON file. This will disable all policies and allow you to customize the browser settings as you wish. - -```json title="policy.json" -{} -``` - -### Firefox-based Browsers {#firefox-based} - -The full configuration options for the Firefox-based policy JSON file can be found in the [Mozilla Policy Templates](https://mozilla.github.io/policy-templates/) documentation. - -The policy files are located in the following paths: - - - -**Allow persistent data in policies** - -By default, the browsers in Neko are set up to forget all cookies and browsing history when they are closed. If you want to allow persistent data, you can set the following policies in the JSON file: - -```json title="policy.json" -{ - "policies": { - "SanitizeOnShutdown": false, - "Homepage": { - "StartPage": "previous-session" - } - } -} -``` - -**Manage extensions** - -By default, the browsers in Neko do not allow installing extensions except for the ones that are pre-installed. - -- `installation_mode`: The installation mode for the extension. It can be one of the following: - - `allowed`: The extension can be installed by the user. - - `blocked`: The extension cannot be installed by the user. - - `force_installed`: The extension is installed automatically and cannot be removed by the user. - -```json title="policy.json" -{ - "policies": { - "ExtensionSettings": { - "*": { - "installation_mode": "blocked" - }, - "sponsorBlocker@ajay.app": { - "install_url": "https://addons.mozilla.org/firefox/downloads/latest/sponsorblock/latest.xpi", - "installation_mode": "force_installed" - }, - "uBlock0@raymondhill.net": { - "install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi", - "installation_mode": "force_installed" - } - } - } -} -``` - -
- How to find the extension ID? - - Extension IDs for Firefox are not available in the URL like in Chrome. You can find the extension ID by navigating to the `about:debugging#/runtime/this-firefox` page and clicking on the extension you want to install. The extension ID will be displayed in the URL. - - Another way is to find the extension on the [Official Add-ons Webpage](https://addons.mozilla.org/en-US/firefox/), then open DevTools (F12) and go to the `Console` tab. Enter the following command: - - ```javascript - Object.keys(JSON.parse(document.getElementById('redux-store-state').textContent).addons.byGUID)[0] - ``` - - This will return the ID of the first extension on the page. -
- -### Chromium-based Browsers {#chromium-based} - -The full configuration options for the Chromium-based policy JSON file can be found in the [Chrome Enterprise](https://chromeenterprise.google/policies) documentation. - -The policy files are located in the following paths: - - - -**Allow file uploading & downloading** - -By default, the browsers in Neko do not allow local file access. If you want to allow file uploading and downloading, you can set the following policies in the JSON file: - -```json title="policy.json" -{ - "DownloadRestrictions": 0, - "AllowFileSelectionDialogs": true, - "URLAllowlist": [ - "file:///home/neko/Downloads" - ] -} -``` - -**Allow persistent data in policies** - -By default, the browsers in Neko are set up to forget all cookies and browsing history when they are closed. If you want to allow persistent data, you can set the following policies in the JSON file: - -```json title="policy.json" -{ - "DefaultCookiesSetting": 1, - "RestoreOnStartup": 1 -} -``` - -**Manage extensions** - -By default, the browsers in Neko do not allow installing extensions except for the ones that are pre-installed. - -- `ExtensionInstallForcelist`: These extensions will be installed automatically and cannot be removed by the user. -- `ExtensionInstallAllowlist`: These extensions can be installed by the user when needed; they are not pre-installed. -- `ExtensionInstallBlocklist`: These extensions cannot be installed by the user, which is `*` (all extensions) by default. - -The ID of the extension can be found in the URL of the extension in the Chrome Web Store. For example, the ID of the [uBlock Origin](https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm) extension is `cjpalhdlnbpafiamejdnhcphjbkeiagm`. - -```json title="policy.json" -{ - "ExtensionInstallForcelist": [ - "cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx", - "mnjggcdmjocbbbhaepdhchncahnbgone;https://clients2.google.com/service/update2/crx" - ], - "ExtensionInstallAllowlist": [ - "cjpalhdlnbpafiamejdnhcphjbkeiagm", - "mnjggcdmjocbbbhaepdhchncahnbgone" - ], - "ExtensionInstallBlocklist": [ - "*" - ] -} -``` diff --git a/webpage/docs/customization/browsers.tsx b/webpage/docs/customization/browsers.tsx deleted file mode 100644 index 091f00ca..00000000 --- a/webpage/docs/customization/browsers.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import browsers from './browsers.json'; -import Link from '@docusaurus/Link'; - -export function ProfileDirectoryPaths({flavors, ...props}: { flavors: string[] }) { - if (!flavors) { - flavors = []; - } - return ( - - - - - - - - - {browsers.filter(({ flavor }) => flavors.length == 0 || flavors.includes(flavor)).map(({ tag, profileDir }) => ( - - - - - ))} - -
BrowserProfile Directory Path
{tag} - {profileDir ? {profileDir} : Does not support profiles.} -
- ); -} - -export function PolicyFilePaths({flavors, ...props}: { flavors: string[] }) { - if (!flavors) { - flavors = []; - } - return ( - - - - - - - - - {browsers.filter(({ flavor }) => flavors.length == 0 || flavors.includes(flavor)).map(({ tag, policiesFile }) => ( - - - - - ))} - -
BrowserPolicy File Path
{tag} - {policiesFile ? {policiesFile} : Does not support policies.} -
- ); -} diff --git a/webpage/docs/customization/networking.md b/webpage/docs/customization/networking.md deleted file mode 100644 index 36b076e2..00000000 --- a/webpage/docs/customization/networking.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -sidebar_label: "Networking" -description: "Example networking configurations for Neko." ---- - -# Networking Customization - -## Accessing Neko over the Internet {#internet} - -If you want to access Neko over the internet, you need to expose the necessary ports on your router or firewall. - -This is the default configuration for Neko so no additional configuration is needed. - -## Accessing Neko over a VPN {#vpn} - -If you want to access Neko over a VPN, you need to set NAT1TO1 to your server's IP address in the VPN network. This allows Neko to communicate with the server over the VPN. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - environment: - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 - NEKO_WEBRTC_NAT1TO1: -``` - -## Accessing Neko over SSH {#ssh} - -If you do not want to expose Neko to the internet and want to access it securely over SSH, you can set up port forwarding using SSH. This allows you to access Neko from your local machine without exposing it to the internet. - -Start neko with TCP multiplexing enabled and NAT1to1 set to loopback address. That way everytime you access Neko, it will use the loopback address to connect to the server. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/nvidia-firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000:52000" - environment: - NEKO_WEBRTC_TCPMUX: 52000 - NEKO_WEBRTC_ICELITE: 1 - NEKO_WEBRTC_NAT1TO1: 127.0.0.1 -``` - -Set up your SSH configuration file (`~/.ssh/config`) to include the following port forwarding settings. This will forward the ports from the remote server to your local machine. - -```shell title="~/.ssh/config" -Host PC-Work - HostName work.example.com - User xxx - Port xyy - RemoteForward 8080 localhost:8080 - RemoteForward 52000 localhost:52000 -``` diff --git a/webpage/docs/customization/ui.md b/webpage/docs/customization/ui.md deleted file mode 100644 index 8e372ee0..00000000 --- a/webpage/docs/customization/ui.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -sidebar_label: "User Interface" -description: "Customize the Neko user interface with your own UI files." ---- - -# Customizing the UI - -Currently there is no configuration for customizing the UI of Neko. You need to modify the source code to change the UI. - -```bash -# Clone the repository -git clone https://github.com/m1k1o/neko -# Change to the client directory -cd neko/client -# Install the dependencies -npm install -# Build the project -npm run build -``` - -You can mount your newly created UI files to the container to `/var/www` to overwrite the default files. The Neko web server will automatically reload the new files when they are changed. You can use the following command to mount your new UI files to the container: - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - # highlight-start - volumes: - - "./client/dist:/var/www" - # highlight-end - environment: - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 -``` - -## Query parameters {#query-parameters} - -You can use query parameters to customize the Neko web interface. These parameters can be added to the URL when accessing the Neko web interface. The following table lists the available query parameters: - -| Query Parameter | Description | -|--------------------|------------------------------------------------------------| -| `?usr=` | Prefills the username field. | -| `?pwd=` | Prefills the password field. | -| `?cast=1` | Hides all controls and shows only the video. | -| `?embed=1` | Hides most additional components and shows only the video. | -| `?volume=<0-1>` | Sets the volume to the given value (between 0 and 1). | -| `?lang=` | Sets the language to the given value. | -| `?show_side=1` | Shows the sidebar on startup. | -| `?mute_chat=1` | Mutes the chat on startup. | - -You can combine multiple query parameters in the URL. For example, to set the username to `guest`, the password to `neko`, and enable casting mode, you can use the following URL: - -Example: `http(s):///?pwd=neko&usr=guest&cast=1` \ No newline at end of file diff --git a/webpage/docs/developer-guide/api-reference.md b/webpage/docs/developer-guide/api-reference.md new file mode 100644 index 00000000..ea634205 --- /dev/null +++ b/webpage/docs/developer-guide/api-reference.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 4 +--- + +# API Reference + +Documentation of available APIs for integrating with Neko (WebSocket, REST, etc.). diff --git a/webpage/docs/developer-guide/build.md b/webpage/docs/developer-guide/build.md deleted file mode 100644 index 90a1df52..00000000 --- a/webpage/docs/developer-guide/build.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -description: Building Neko from source ---- - -# Building From Source - -This guide walks you through the process of setting up Neko on your local machine or server. - -Start by cloning the Neko Git repository to your machine: - -```bash -git clone https://github.com/m1k1o/neko.git -cd neko -``` - -## Building the Frontend {#frontend} - -Prerequisites for building the frontend: -- [node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) - -Navigate to the `client` directory and install the dependencies: - -```bash -cd client; -npm install; -npm run build; -``` - -The `npm run build` command will create a production build of the frontend in the `client/build` directory. - -## Building the Server {#server} - -Prerequisites for building the server: -- [go](https://golang.org/) (version 1.18 or higher) -- Dependencies for building the server: - ```bash - sudo apt-get install -y --no-install-recommends libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev libxcvt-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev - ``` -Navigate to the `server` directory and build the server: - -```bash -cd server; -./build; -``` - -This will create a binary file named `neko` in the `bin` directory along with `plugins` that were built with the server. diff --git a/webpage/docs/developer-guide/README.md b/webpage/docs/developer-guide/building-from-source.md similarity index 52% rename from webpage/docs/developer-guide/README.md rename to webpage/docs/developer-guide/building-from-source.md index 8296199c..2e709039 100644 --- a/webpage/docs/developer-guide/README.md +++ b/webpage/docs/developer-guide/building-from-source.md @@ -1,10 +1,14 @@ -# Developer Guide +--- +sidebar_position: 2 +--- -:::info -This guide is Work in Progress. It is not complete and will be updated over time. -::: +# Building from Source -## Dependencies +This guide walks you through the process of setting up Neko on your local machine or server. + +## Prerequisites {#prerequisites} + +Before proceeding, ensure that you have the following installed on your system: - [node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) (for building the frontend). - [go](https://golang.org/) (for building the server). @@ -28,9 +32,38 @@ This guide is Work in Progress. It is not complete and will be updated over time sudo apt-get install xdotool xclip libgtk-3-0 libgtk-3-dev libopus0 libvpx6; ``` -## Next Steps +## Step 1: Clone the Repository {#step-1} -import DocCardList from '@theme/DocCardList'; -import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; +Start by cloning the Neko Git repository to your machine: - +```bash +git clone https://github.com/m1k1o/neko.git +cd neko +``` + +## Step 2: Build the Frontend {#step-2} + +Navigate to the `client` directory and install the dependencies: + +```shell +cd client; +npm install; +npm run build; +``` + +## Step 3: Build the Server {#step-3} + +Navigate to the `server` directory and build the server: + +```shell +cd server; +go build; +``` + +## Step 4: Run the Server {#step-4} + +Finally, run the server: + +```shell +./server/server; +``` diff --git a/webpage/docs/developer-guide/code-structure.md b/webpage/docs/developer-guide/code-structure.md new file mode 100644 index 00000000..57ac0b26 --- /dev/null +++ b/webpage/docs/developer-guide/code-structure.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 1 +--- + +# Code Structure + +Explanation of the project’s architecture and key components. diff --git a/webpage/docs/developer-guide/contributing-guide.md b/webpage/docs/developer-guide/contributing-guide.md new file mode 100644 index 00000000..8fa0e43c --- /dev/null +++ b/webpage/docs/developer-guide/contributing-guide.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 6 +--- + +# Contributing Guide + +How to contribute to the project, submit patches, and report issues. diff --git a/webpage/docs/developer-guide/development-mode.md b/webpage/docs/developer-guide/development-mode.md new file mode 100644 index 00000000..ffe525a7 --- /dev/null +++ b/webpage/docs/developer-guide/development-mode.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 3 +--- + +# Running in Development Mode + +Setting up a development environment for contributing. diff --git a/webpage/docs/developer-guide/repository-structure.md b/webpage/docs/developer-guide/repository-structure.md deleted file mode 100644 index b8b6db78..00000000 --- a/webpage/docs/developer-guide/repository-structure.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -description: "Project structure of the neko repository." ---- - -# Repository Structure - -This project uses a monorepo structure with the following directories: - -- `server/`: Backend code, written in [Go](https://go.dev/). - - `server/cmd/`: Subcommands for the neko server, such as `serve` and `plugins`. - - `server/dev/`: Development scripts to run/build the neko server locally in Docker. - - `server/internal/`: Internal packages used by the neko server. - - `server/pkg/`: Packages used by the neko server that can be used by other projects. - - `server/plugins/`: Directory for plugins that will be built and used by the neko server. - -- `client/`: Frontend code, written in [TypeScript](https://www.typescriptlang.org/) and [Vue.js](https://vuejs.org/). - - `client/dev/`: Development scripts to run/build the neko client locally in Docker. - - `client/public/`: Public files for the neko client, such as icons and images. - - `client/src/`: Source code for the neko client, including components and pages. - - `client/tools/`: Tools used for generating code for the neko client, such as emojis. - -- `runtime/`: Runtime environment, with [Xorg](https://www.x.org/wiki/) and [Pulseaudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) servers and the necessary dependencies for the applications to run. - - `runtime/fontconfig/`: Font configuration files copied to `/etc/fonts/conf.d/` in the neko runtime. - - `runtime/fonts/`: Empty directory placeholder to include custom fonts in the neko runtime. - - `runtime/icon-theme/`: Empty directory placeholder to include a custom icon theme in the neko runtime. - - `runtime/intel/` or `runtime/nvidia/`: Directories with files for flavor-specific releases of the neko runtime. - -- `apps/`: Contains the applications that run in the neko, such as Firefox, Chrome, etc. - - `apps//Dockerfile`: Dockerfile extending `BASE_IMAGE` for the application used by the build script. - - `apps//Dockerfile.`: Dockerfile for the application with flavor-specific optimizations. - - Supervisord configuration is expected at `/etc/neko/supervisord/.conf` in the application image. - -- `utils/`: Utility scripts, tools, and dependencies. - - `utils/docker/`: Dockerfile generator that builds the base image by concatenating multiple Dockerfiles. - - `utils/xorg-deps/`: Custom-made or patched Xorg dependencies, such as drivers. - -- `webpage/`: Webpage code for neko.m1k1o.net, written in [TypeScript](https://www.typescriptlang.org/) and [Docusaurus](https://docusaurus.io/), deployed on [GitHub Pages](https://pages.github.com/). - - `webpage/docs/`: Documentation for the neko project, including this README file. - - `webpage/scripts/`: Helper scripts for generating configuration and OpenAPI docs. - - `webpage/src/`: Source code, components, and styles for the webpage. - - `webpage/static/`: Static files for the webpage, such as images and icons. - - `webpage/versioned_*`: Versioned documentation files for the neko project, generated by [Docusaurus](https://docusaurus.io/). \ No newline at end of file diff --git a/webpage/docs/developer-guide/style-guide.md b/webpage/docs/developer-guide/style-guide.md new file mode 100644 index 00000000..1198e0be --- /dev/null +++ b/webpage/docs/developer-guide/style-guide.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 7 +--- + +# Style Guide + +Code formatting, linting, and other development conventions. diff --git a/webpage/docs/developer-guide/testing-debugging.md b/webpage/docs/developer-guide/testing-debugging.md new file mode 100644 index 00000000..71778ae1 --- /dev/null +++ b/webpage/docs/developer-guide/testing-debugging.md @@ -0,0 +1,7 @@ +--- +sidebar_position: 5 +--- + +# Testing & Debugging + +Debugging tools, test cases, and best practices for troubleshooting. diff --git a/webpage/docs/installation/docker-images.md b/webpage/docs/installation/docker-images.md index 44b4d903..baf03587 100644 --- a/webpage/docs/installation/docker-images.md +++ b/webpage/docs/installation/docker-images.md @@ -2,86 +2,49 @@ description: List of available Neko Docker images and their flavors. --- -import { AppIcon } from '@site/src/components/AppIcon'; - # Docker Images Neko as a standalone streaming server is available as a Docker image. But that is rarely interesting for general use. The real power of Neko is in its ability to accommodate custom applications in the virtual desktop environment. This is where the various flavors of Neko Docker images come in. -The base image is available as multi-arch image at [`ghcr.io/m1k1o/neko/base`](https://ghcr.io/m1k1o/neko/base). See [Supported Architectures](#arch) for more information. +The base image is available at [`ghcr.io/m1k1o/neko/base`](https://ghcr.io/m1k1o/neko/base). ## Naming Convention {#naming} -Neko images are available on two public registries. The [GitHub Container Registry (GHCR)](#ghcr.io) hosts stable releases with all flavors and architectures. The latest development version of the Neko image for the AMD64 architecture is available on [Docker Hub](#docker.io). - -:::info -You should always prefer the GHCR registry, as it supports flavors and specific versions, unless you want to test the latest development version. -::: - -### GitHub Container Registry (GHCR) {#ghcr.io} - -Neko Docker images are available on the [GitHub Container Registry (GHCR)](https://github.com/m1k1o?tab=packages&repo_name=neko). The naming convention for Neko Docker images is as follows: +Neko Docker images are available on [GitHub Container Registry (GHCR)](https://github.com/m1k1o?tab=packages&repo_name=neko). The naming convention for Neko Docker images is as follows: ``` ghcr.io/m1k1o/neko/[-]: ``` -- `` is the optional flavor of the image. See [Available Flavors](#flavors) for more information. -- `` is the application name or base image. See [Available Applications](#apps) for more information. -- `` is the version of the image. See [Versioning](#ghcr.io-versioning) for more information. +- `` is the optional flavor of the image, see [Available Flavors](#flavors) for more information. +- `` is the application name or base image, see [Available Applications](#apps) for more information. +- `` is the [semantic version](https://semver.org/) of the image from the [GitHub tags](https://github.com/m1k1o/neko/tags). There is always a `latest` tag available. -#### Versioning scheme {#ghcr.io-versioning} - -The versioning scheme follows the [Semantic Versioning 2.0.0](https://semver.org/) specification. The following tags are available for each image: - -- `latest` - Points to the most recent stable release. -- `MAJOR` - Tracks the latest release within the specified major version. -- `MAJOR.MINOR` - Tracks the latest release within the specified major and minor version. -- `MAJOR.MINOR.PATCH` - Refers to a specific release. - -For example: -- `ghcr.io/m1k1o/neko/firefox:latest` - Latest stable version. -- `ghcr.io/m1k1o/neko/firefox:3` - Latest release in the 3.x.x series. -- `ghcr.io/m1k1o/neko/firefox:3.0` - Latest release in the 3.0.x series. -- `ghcr.io/m1k1o/neko/firefox:3.0.0` - Specific version 3.0.0. - -A full list of published versions can be found in the [GitHub tags](https://github.com/m1k1o/neko/tags). - -### Docker Hub {#docker.io} - -An alternative registry is available on [Docker Hub](https://hub.docker.com/r/m1k1o/neko). This registry hosts images built from the latest code in the [master branch](https://github.com/m1k1o/neko/tree/master). However, it only includes images without flavors and supports the AMD64 architecture. The naming convention for these images is as follows: +An alternative registry is also available on [Docker Hub](https://hub.docker.com/r/m1k1o/neko), however, only images without flavor and with the latest version are available there. ``` m1k1o/neko: ``` -- `` is the application name or base image. See [Available Applications](#apps) for more information. - :::info -`m1k1o/neko:latest` is an alias for `m1k1o/neko:firefox` due to historical reasons. It is recommended to use the `ghcr.io/m1k1o/neko/firefox:latest` image instead. +You should always prefer the GHCR registry with the ability to use flavors and specific versions. ::: ## Available Applications {#apps} The following applications are available as Neko Docker images: -### Firefox-based browsers {#firefox-based-browsers} +### Firefox-based browsers {#firefox} In comparison to Chromium-based browsers, Firefox-based browsers do not require additional capabilities or a bigger shared memory size to not crash. | Icon | Name | Docker Image | | ---- | ---- | ------------ | -| | [Firefox](https://www.mozilla.org/firefox/)
The open-source browser from Mozilla. | [`ghcr.io/m1k1o/neko/firefox`](https://ghcr.io/m1k1o/neko/firefox) | -| | [Tor Browser](https://www.torproject.org/)
A browser designed to access the Tor network for enhanced privacy. | [`ghcr.io/m1k1o/neko/tor-browser`](https://ghcr.io/m1k1o/neko/tor-browser) | -| | [Waterfox](https://www.waterfox.net/)
A privacy-focused browser based on Firefox. | [`ghcr.io/m1k1o/neko/waterfox`](https://ghcr.io/m1k1o/neko/waterfox) | +| | [Firefox](https://www.mozilla.org/firefox/)
The open-source browser from Mozilla. | [`ghcr.io/m1k1o/neko/firefox`](https://ghcr.io/m1k1o/neko/firefox) | +| | [Tor Browser](https://www.torproject.org/)
A browser designed to access the Tor network for enhanced privacy. | [`ghcr.io/m1k1o/neko/tor-browser`](https://ghcr.io/m1k1o/neko/tor-browser) | +| | [Waterfox](https://www.waterfox.net/)
A privacy-focused browser based on Firefox. | [`ghcr.io/m1k1o/neko/waterfox`](https://ghcr.io/m1k1o/neko/waterfox) | -:::warning -**Waterfox** is currently not built automatically, because Cloudflare blocks the download and therefore github actions are failing. You can build it manually to get the latest version. -::: - -Check the [Firefox-based browsers customization guide](/docs/v3/customization/browsers#firefox-based) for more information on how to customize Firefox-based browsers (configuring profile, installing extensions, etc.). - -### Chromium-based browsers {#chromium-based-browsers} +### Chromium-based browsers {#chromium} There are multiple flavors of Chromium-based browsers available as Neko Docker images. @@ -115,26 +78,24 @@ import TabItem from '@theme/TabItem'; | Icon | Name | Docker Image | | ---- | ---- | ------------ | -| | [Chromium](https://www.chromium.org/chromium-projects/)
The open-source project behind Google Chrome. | [`ghcr.io/m1k1o/neko/chromium`](https://ghcr.io/m1k1o/neko/chromium) | -| | [Google Chrome](https://www.google.com/chrome/)
The most popular browser in the world. | [`ghcr.io/m1k1o/neko/google-chrome`](https://ghcr.io/m1k1o/neko/google-chrome) | -| | [Ungoogled Chromium](https://ungoogled-software.github.io/)
A fork of Chromium without Google integration. | [`ghcr.io/m1k1o/neko/ungoogled-chromium`](https://ghcr.io/m1k1o/neko/ungoogled-chromium) | -| | [Microsoft Edge](https://www.microsoft.com/edge)
The new Microsoft Edge is based on Chromium. | [`ghcr.io/m1k1o/neko/microsoft-edge`](https://ghcr.io/m1k1o/neko/microsoft-edge) | -| | [Brave](https://brave.com/)
A privacy-focused browser. | [`ghcr.io/m1k1o/neko/brave`](https://ghcr.io/m1k1o/neko/brave) | -| | [Vivaldi](https://vivaldi.com/)
A highly customizable browser. | [`ghcr.io/m1k1o/neko/vivaldi`](https://ghcr.io/m1k1o/neko/vivaldi) | -| | [Opera](https://www.opera.com/)*
A fast and secure browser. | [`ghcr.io/m1k1o/neko/opera`](https://ghcr.io/m1k1o/neko/opera) | +| | [Chromium](https://www.chromium.org/chromium-projects/)
The open-source project behind Google Chrome. | [`ghcr.io/m1k1o/neko/chromium`](https://ghcr.io/m1k1o/neko/chromium) | +| | [Google Chrome](https://www.google.com/chrome/)
The most popular browser in the world. | [`ghcr.io/m1k1o/neko/google-chrome`](https://ghcr.io/m1k1o/neko/google-chrome) | +| | [Ungoogled Chromium](https://ungoogled-software.github.io/)
A fork of Chromium without Google integration. | [`ghcr.io/m1k1o/neko/ungoogled-chromium`](https://ghcr.io/m1k1o/neko/ungoogled-chromium) | +| | [Microsoft Edge](https://www.microsoft.com/edge)
The new Microsoft Edge is based on Chromium. | [`ghcr.io/m1k1o/neko/microsoft-edge`](https://ghcr.io/m1k1o/neko/microsoft-edge) | +| | [Brave](https://brave.com/)
A privacy-focused browser. | [`ghcr.io/m1k1o/neko/brave`](https://ghcr.io/m1k1o/neko/brave) | +| | [Vivaldi](https://vivaldi.com/)
A highly customizable browser. | [`ghcr.io/m1k1o/neko/vivaldi`](https://ghcr.io/m1k1o/neko/vivaldi) | +| | [Opera](https://www.opera.com/)*
A fast and secure browser. | [`ghcr.io/m1k1o/neko/opera`](https://ghcr.io/m1k1o/neko/opera) | \* requires extra steps to enable DRM, see instructions [here](https://www.reddit.com/r/operabrowser/wiki/opera/linux_widevine_config/). `libffmpeg` is already configured. -Check the [Chromium-based browsers customization guide](/docs/v3/customization/browsers#chromium-based) for more information on how to customize Chromium-based browsers (configuring profile, installing extensions, etc.). - ### Desktop Environments {#desktop} These images feature a full desktop environment where you can install and run multiple applications, use window management, and more. This is useful for people who want to run multiple applications in a single container. | Icon | Name | Docker Image | | ---- | ---- | ------------ | -| | [Xfce](https://xfce.org/)
A lightweight desktop environment. | [`ghcr.io/m1k1o/neko/xfce`](https://ghcr.io/m1k1o/neko/xfce) | -| | [KDE Plasma](https://kde.org/plasma-desktop)
A feature-rich desktop environment. | [`ghcr.io/m1k1o/neko/kde`](https://ghcr.io/m1k1o/neko/kde) | +| | [Xfce](https://xfce.org/)
A lightweight desktop environment. | [`ghcr.io/m1k1o/neko/xfce`](https://ghcr.io/m1k1o/neko/xfce) | +| | [KDE Plasma](https://kde.org/plasma-desktop)
A feature-rich desktop environment. | [`ghcr.io/m1k1o/neko/kde`](https://ghcr.io/m1k1o/neko/kde) | ### Other Applications {#other} @@ -142,8 +103,8 @@ As it would be impossible to include all possible applications in the repository | Icon | Name | Docker Image | | ---- | ---- | ------------ | -| | [Remmina](https://remmina.org/)
A remote desktop client. | [`ghcr.io/m1k1o/neko/remmina`](https://ghcr.io/m1k1o/neko/remmina) | -| | [VLC](https://www.videolan.org/vlc/)
A media player. | [`ghcr.io/m1k1o/neko/vlc`](https://ghcr.io/m1k1o/neko/vlc) | +| | [Remmina](https://remmina.org/)
A remote desktop client. | [`ghcr.io/m1k1o/neko/remmina`](https://ghcr.io/m1k1o/neko/remmina) | +| | [VLC](https://www.videolan.org/vlc/)
A media player. | [`ghcr.io/m1k1o/neko/vlc`](https://ghcr.io/m1k1o/neko/vlc) | #### Remmina Configuration {#remmina} @@ -198,22 +159,37 @@ See [neko-apps](https://github.com/m1k1o/neko-apps) repository for more applicat ## Available Flavors {#flavors} :::danger Keep in Mind -Currently the focus is on CPU images (wihout any flavor). So the GPU support might not work as expected. +Currently the focus is on AMD64 & CPU image (wihout any flavor). So the flavor images might not work as expected. ::: + The following flavors are available for Neko Docker images: +- `arm` - ARM64 and ARMv7 architecture support. - `nvidia` - NVIDIA GPU support. - `intel` - Intel GPU support. -### Intel (VAAPI GPU hardware acceleration) {#intel} +:::note +Not all flavors are available for all applications. Since not all applications support ARM architecture or GPU acceleration, the flavors are only available where they make sense. +::: -Only for architecture `linux/amd64`. +### ARM {#arm} + +For ARM-based images (like Raspberry Pi - with GPU hardware acceleration, [Oracle Cloud ARM free tier](https://www.oracle.com/cloud/free/)). Currently, not all images are available for ARM, because not all applications are available for ARM. Please use the images below: + +- [`ghcr.io/m1k1o/neko/arm-firefox`](https://ghcr.io/m1k1o/neko/arm-firefox) +- [`ghcr.io/m1k1o/neko/arm-chromium`](https://ghcr.io/m1k1o/neko/arm-chromium) +- [`ghcr.io/m1k1o/neko/arm-ungoogled-chromium`](https://ghcr.io/m1k1o/neko/arm-ungoogled-chromium) +- [`ghcr.io/m1k1o/neko/arm-vlc`](https://ghcr.io/m1k1o/neko/arm-vlc) +- [`ghcr.io/m1k1o/neko/arm-xfce`](https://ghcr.io/m1k1o/neko/arm-xfce) + +The base image is available at [`ghcr.io/m1k1o/neko/arm-base`](https://ghcr.io/m1k1o/neko/arm-base). + +### Intel {#intel} For images with VAAPI GPU hardware acceleration using Intel drivers use: - [`ghcr.io/m1k1o/neko/intel-firefox`](https://ghcr.io/m1k1o/neko/intel-firefox) -- [`ghcr.io/m1k1o/neko/intel-waterfox`](https://ghcr.io/m1k1o/neko/intel-waterfox) - [`ghcr.io/m1k1o/neko/intel-chromium`](https://ghcr.io/m1k1o/neko/intel-chromium) - [`ghcr.io/m1k1o/neko/intel-google-chrome`](https://ghcr.io/m1k1o/neko/intel-google-chrome) - [`ghcr.io/m1k1o/neko/intel-ungoogled-chromium`](https://ghcr.io/m1k1o/neko/intel-ungoogled-chromium) @@ -229,9 +205,7 @@ For images with VAAPI GPU hardware acceleration using Intel drivers use: The base image is available at [`ghcr.io/m1k1o/neko/intel-base`](https://ghcr.io/m1k1o/neko/intel-base). -### Nvidia (CUDA GPU hardware acceleration) {#nvidia} - -Only for architecture `linux/amd64`. +### Nvidia {#nvidia} For images with Nvidia GPU hardware acceleration using EGL use: @@ -243,37 +217,7 @@ For images with Nvidia GPU hardware acceleration using EGL use: The base image is available at [`ghcr.io/m1k1o/neko/nvidia-base`](https://ghcr.io/m1k1o/neko/nvidia-base). -## Supported Architectures {#arch} - -Neko Docker images are built with docker buildx and are available for multiple architectures. The following architectures are supported by the base image: - -- `linux/amd64` - 64-bit Intel/AMD architecture (most common). -- `linux/arm64` - 64-bit ARM architecture (e.g., Raspberry Pi 4, Apple M1/M2). -- `linux/arm/v7` - 32-bit ARM architecture (e.g., Raspberry Pi 3, Raspberry Pi Zero). - -### Availability Matrix {#availability} - -The availability of applications for ARM architecture is limited due to the lack of support for some applications. The following table shows the availability of each application for each architecture. The `βœ…` symbol indicates that the application is available for that architecture, while the `❌` symbol indicates that it is not available. - -| Application | AMD64 | ARM64 | ARMv7 | Reference | -| ----------------------------------------- | ----- | ----- | ----- | --------- | -| [Firefox](#firefox) | βœ… | βœ… \* | βœ… \* | - | -| [Tor Browser](#tor-browser) | βœ… | ❌ | ❌ | [Forum Post](https://forum.torproject.org/t/tor-browser-for-arm-linux/5240) | -| [Waterfox](#waterfox) | βœ… | ❌ | ❌ | [Github Issue](https://github.com/BrowserWorks/Waterfox/issues/1506), [Reddit](https://www.reddit.com/r/waterfox/comments/jpqsds/are_there_any_builds_for_arm64/) | -| [Chromium](#chromium) | βœ… | βœ… \* | βœ… \* | - | -| [Google Chrome](#google-chrome) | βœ… | ❌ | ❌ | [Community Post](https://askubuntu.com/a/1383791) | -| [Ungoogled Chromium](#ungoogled-chromium) | βœ… | ❌ | ❌ | [Downloads Page](https://ungoogled-software.github.io/ungoogled-chromium-binaries/) | -| [Microsoft Edge](#microsoft-edge) | βœ… | ❌ | ❌ | [Community Post](https://techcommunity.microsoft.com/discussions/edgeinsiderdiscussions/edge-for-linuxarm64/1532272) | -| [Brave](#brave) | βœ… | βœ… \* | ❌ | [Requirements Page](https://support.brave.com/hc/en-us/articles/360021357112-What-are-the-system-requirements-to-install-Brave) | -| [Vivaldi](#vivaldi) | βœ… | βœ… \* | βœ… \* | - | -| [Opera](#opera) | βœ… | ❌ | ❌ | [Forum Post](https://forums.opera.com/topic/52811/opera-do-not-support-arm64-on-linux) | -| [Xfce](#xfce) | βœ… | βœ… | βœ… | - | -| [KDE](#kde) | βœ… | βœ… | βœ… | - | -| [Remmina](#remmina) | βœ… | βœ… | βœ… | - | -| [VLC](#vlc) | βœ… | βœ… | βœ… | - | - -\* No DRM support. - -:::tip -[Oracle Cloud ARM free tier](https://www.oracle.com/cloud/free/) is a great way to test Neko on ARM architecture for free. You can use the `ghcr.io/m1k1o/neko/xfce` image to run a full desktop environment with Xfce and test the applications. +:::danger +There is a known issue with EGL and Chromium-based browsers, see [m1k1o/neko #279](https://github.com/m1k1o/neko/issues/279). ::: + diff --git a/webpage/docs/installation/examples.md b/webpage/docs/installation/examples.md index ee229cae..a74615f7 100644 --- a/webpage/docs/installation/examples.md +++ b/webpage/docs/installation/examples.md @@ -76,12 +76,31 @@ services: NEKO_WEBRTC_NAT1TO1: ``` -## Raspberry Pi GPU Acceleration {#raspberry-pi} +## Raspberry Pi {#raspberry-pi} + +```yaml title="config.yaml" +capture: + video: + codec: h264 + ids: [ main ] + pipelines: + main: + gst_pipeline: | + ximagesrc display-name=%s use-damage=0 show-pointer=true use-damage=false + ! video/x-raw,framerate=30/1 + ! videoconvert + ! queue + ! video/x-raw,framerate=30/1,format=NV12 + ! v4l2h264enc extra-controls="controls,h264_profile=1,video_bitrate=1250000;" + ! h264parse config-interval=3 + ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline +``` ```yaml title="docker-compose.yaml" services: neko: - image: "ghcr.io/m1k1o/neko/chromium:latest" + # see docs for more variants + image: "ghcr.io/m1k1o/neko/arm-chromium:latest" restart: "unless-stopped" # increase on rpi's with more then 1gb ram. shm_size: "520mb" @@ -91,121 +110,12 @@ services: # note: this is important since we need a GPU for hardware acceleration alternatively # mount the devices into the docker. privileged: true + volumes: + - "./config.yaml:/etc/neko/neko.yaml" environment: - NEKO_CAPTURE_VIDEO_PIPELINE: | - ximagesrc display-name={display} show-pointer=true use-damage=false - ! video/x-raw,framerate=25/1 - ! videoconvert ! queue - ! video/x-raw,format=NV12 - ! v4l2h264enc - name=encoder - extra-controls="controls,h264_profile=1,video_bitrate=1250000;" - ! h264parse config-interval=-1 - ! video/x-h264,stream-format=byte-stream - ! appsink name=appsink - NEKO_CAPTURE_VIDEO_CODEC: "h264" NEKO_DESKTOP_SCREEN: '1280x720@30' - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin + NEKO_MEMBER_MULTIUSER_USER_PASSWORD: 'neko' + NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: 'admin' NEKO_WEBRTC_EPR: 52000-52100 NEKO_WEBRTC_ICELITE: 1 ``` - -## Nvidia GPU Acceleration {#nvidia} - -Neko supports hardware acceleration using Nvidia GPUs. To use this feature, you need to have the Nvidia Container Toolkit installed on your system. You can find the installation instructions [here](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html). - -This example shows how to accelerate video encoding and as well the browser rendering using the GPU. You can test if the GPU is used by running `nvidia-smi`, which should show the GPU usage of both the browser and neko. In the browser, you can run the [WebGL Aquarium Demo](https://webglsamples.org/aquarium/aquarium.html) to test the GPU usage. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/nvidia-firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - environment: - NEKO_CAPTURE_VIDEO_PIPELINE: | - ximagesrc display-name={display} show-pointer=true use-damage=false - ! video/x-raw,framerate=25/1 - ! videoconvert ! queue - ! video/x-raw,format=NV12 - ! nvh264enc - name=encoder - preset=2 - gop-size=25 - spatial-aq=true - temporal-aq=true - bitrate=4096 - vbv-buffer-size=4096 - rc-mode=6 - ! h264parse config-interval=-1 - ! video/x-h264,stream-format=byte-stream - ! appsink name=appsink - NEKO_CAPTURE_VIDEO_CODEC: "h264" - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] -``` - -See available [Nvidia Docker Images](/docs/v3/installation/docker-images#nvidia). - -If you only want to accelerate the encoding, **not the browser rendering**, you can use the default image with additional environment variables: - -```yaml title="docker-compose.yaml" -services: - neko: - # highlight-next-line - image: "ghcr.io/m1k1o/neko/firefox:latest" - restart: "unless-stopped" - shm_size: "2gb" - ports: - - "8080:8080" - - "52000-52100:52000-52100/udp" - environment: - # highlight-start - NVIDIA_VISIBLE_DEVICES: all - NVIDIA_DRIVER_CAPABILITIES: all - # highlight-end - NEKO_CAPTURE_VIDEO_PIPELINE: | - ximagesrc display-name={display} show-pointer=true use-damage=false - ! video/x-raw,framerate=25/1 - ! videoconvert ! queue - ! video/x-raw,format=NV12 - ! nvh264enc - name=encoder - preset=2 - gop-size=25 - spatial-aq=true - temporal-aq=true - bitrate=4096 - vbv-buffer-size=4096 - rc-mode=6 - ! h264parse config-interval=-1 - ! video/x-h264,stream-format=byte-stream - ! appsink name=appsink - NEKO_CAPTURE_VIDEO_CODEC: "h264" - NEKO_DESKTOP_SCREEN: 1920x1080@30 - NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko - NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin - NEKO_WEBRTC_EPR: 52000-52100 - NEKO_WEBRTC_ICELITE: 1 - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] -``` diff --git a/webpage/docs/migration-from-v2/README.md b/webpage/docs/migration-from-v2/README.md index b8f9bded..61ccbade 100644 --- a/webpage/docs/migration-from-v2/README.md +++ b/webpage/docs/migration-from-v2/README.md @@ -2,21 +2,15 @@ Currently, Neko is in compatibility mode, meaning that as soon as a single V2 configuration option is set, the legacy mode is enabled. This approach allows for a smooth transition from V2 to V3, where it does not expose the V2 API for new users but still allows existing users who use the old configuration to continue using it as before. -The legacy mode includes a compatibility layer that allows V2 clients to connect to V3. Currently, the **client is not compatible with V3**, so the legacy mode is enabled by default. It can be explicitly enabled or disabled by setting the `NEKO_LEGACY` environment variable to `true` or `false`. +The legacy mode can be explicitly enabled or disabled by setting the `NEKO_LEGACY` environment variable to `true` or `false`. -:::warning -The legacy mode is **still used by the client**. It is recommended to migrate to the new configuration options, but do not disable the legacy mode unless you are using a new client that is compatible with V3 (e.g., [demodesk/neko-client](https://github.com/demodesk/neko-client)). Once the new client is released, the legacy mode will be automatically removed from the server. +:::tip +You can migrate to a new configuration even if you are using a V2 client. Just make sure to set the `NEKO_LEGACY` environment variable to `true`. ::: -If you set both V3 and V2 configuration options, the V2 configuration options will take precedence over the V3 configuration options. This is to ensure that the legacy mode works as expected and does not break existing configurations. - -## Docker Images {#docker} - -Previously, neko was available primarily on Dockerhub as `m1k1o/neko`. While it stays as an option there, now the primary location is `ghcr.io/m1k1o/neko`. - -ARM images were previously available as an flavor e.g. `m1k1o/neko:arm-firefox` or `ghcr.io/m1k1o/neko/arm-firefox`. Now, the ARM images are available as multi-arch images under the same tags as the amd64 images., e.g. `ghcr.io/m1k1o/neko/firefox`. - -All applications available in the V2 images are also available in the V3 images. See the [Docker Images](/docs/v3/installation/docker-images) documentation for more details. +:::info Built-in Client +When using Neko in a container with a built-in client, the client will always be compatible with the server regardless of what configuration is used. +::: ## Configuration {#config} @@ -40,10 +34,8 @@ In order to migrate from V2 to V3, you need to update the configuration to the n | `NEKO_IMPLICIT_CONTROL` | `NEKO_SESSION_IMPLICIT_HOSTING` | | `NEKO_CONTROL_PROTECTION` | `NEKO_SESSION_CONTROL_PROTECTION` | | `NEKO_HEARTBEAT_INTERVAL` | `NEKO_SESSION_HEARTBEAT_INTERVAL` | -| `NEKO_FILE_TRANSFER_ENABLED` | `NEKO_FILETRANSFER_ENABLED` | -| `NEKO_FILE_TRANSFER_PATH` | `NEKO_FILETRANSFER_DIR` | -See the V3 [configuration options](/docs/v3/configuration). For file transfer, see the [File Transfer Plugin](/docs/v3/configuration/plugins#filetransfer). +See the V3 [configuration options](/docs/v3/configuration). ### WebRTC Video {#config.video} @@ -58,9 +50,9 @@ See the V3 configuration options for the [WebRTC Video](/docs/v3/configuration/c | `NEKO_VP8=true` *deprecated* | `NEKO_CAPTURE_VIDEO_CODEC=vp8` | | `NEKO_VP9=true` *deprecated* | `NEKO_CAPTURE_VIDEO_CODEC=vp9` | | `NEKO_VIDEO` | `NEKO_CAPTURE_VIDEO_PIPELINE`, V3 allows multiple video pipelines | -| `NEKO_VIDEO_BITRATE` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead | -| `NEKO_HWENC` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead | -| `NEKO_MAX_FPS` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead | +| `NEKO_VIDEO_BITRATE` | **removed**, use custom pipeline instead | +| `NEKO_HWENC` | **removed**, use custom pipeline instead | +| `NEKO_MAX_FPS` | **removed**, use custom pipeline instead | :::warning Limitation @@ -133,10 +125,10 @@ See the V3 configuration options for the [WebRTC](/docs/v3/configuration/webrtc) Here is a full list of all the configuration options available in Neko V2 that are still available in Neko V3 with legacy support enabled. -import { ConfigurationTab } from '@site/src/components/Configuration'; +import Configuration from '@site/src/components/Configuration'; import configOptions from './help.json'; - + See the full [V3 configuration reference](/docs/v3/configuration/#full) for more details. @@ -160,8 +152,6 @@ Only the [`multiuser`](/docs/v3/configuration/authentication#member.multiuser) p Since WebSocket messages are not user-facing API, there exists no migration guide for them. When the legacy API is enabled, the user connects to the `/ws` endpoint and is handled by the compatibility layer V2 API. The V3 API is available at the `/api/ws` endpoint. -V2 used to send WebSocket ping messages every 60 seconds, whereas V3 sends them every 10 seconds and additionally uses a heartbeat mechanism to verify if the connection is still active. - ### WebRTC API {#api.webrtc} Since the WebRTC API is not user-facing API, there exists no migration guide for it. It has been changed to Big Endian format (previously Little Endian) to allow easier manipulation on the client side. diff --git a/webpage/docs/migration-from-v2/help.json b/webpage/docs/migration-from-v2/help.json index 0110d40f..a0c4aa84 100644 --- a/webpage/docs/migration-from-v2/help.json +++ b/webpage/docs/migration-from-v2/help.json @@ -15,102 +15,6 @@ "defaultValue": "false", "description": "save logs to file" }, - { - "key": [ - "cert" - ], - "type": "string", - "description": "path to the SSL cert used to secure the neko server" - }, - { - "key": [ - "key" - ], - "type": "string", - "description": "path to the SSL key used to secure the neko server" - }, - { - "key": [ - "bind" - ], - "type": "string", - "description": "address/port/socket to serve neko" - }, - { - "key": [ - "proxy" - ], - "type": "boolean", - "defaultValue": "false", - "description": "enable reverse proxy mode" - }, - { - "key": [ - "static" - ], - "type": "string", - "description": "path to neko client files to serve" - }, - { - "key": [ - "path_prefix" - ], - "type": "string", - "description": "path prefix for HTTP requests" - }, - { - "key": [ - "cors" - ], - "type": "strings", - "description": "list of allowed origins for CORS" - }, - { - "key": [ - "locks" - ], - "type": "strings", - "description": "resources, that will be locked when starting (control, login)" - }, - { - "key": [ - "implicit_control" - ], - "type": "boolean", - "defaultValue": "false", - "description": "if enabled members can gain control implicitly" - }, - { - "key": [ - "control_protection" - ], - "type": "boolean", - "defaultValue": "false", - "description": "control protection means, users can gain control only if at least one admin is in the room" - }, - { - "key": [ - "heartbeat_interval" - ], - "type": "int", - "defaultValue": "120", - "description": "heartbeat interval in seconds" - }, - { - "key": [ - "file_transfer_enabled" - ], - "type": "boolean", - "defaultValue": "false", - "description": "enable file transfer feature" - }, - { - "key": [ - "file_transfer_path" - ], - "type": "string", - "description": "path to use for file transfer" - }, { "key": [ "display" @@ -288,12 +192,93 @@ "type": "string", "description": "admin password for connecting to stream" }, + { + "key": [ + "cert" + ], + "type": "string", + "description": "path to the SSL cert used to secure the neko server" + }, + { + "key": [ + "key" + ], + "type": "string", + "description": "path to the SSL key used to secure the neko server" + }, + { + "key": [ + "bind" + ], + "type": "string", + "description": "address/port/socket to serve neko" + }, + { + "key": [ + "proxy" + ], + "type": "boolean", + "defaultValue": "false", + "description": "enable reverse proxy mode" + }, + { + "key": [ + "static" + ], + "type": "string", + "description": "path to neko client files to serve" + }, + { + "key": [ + "path_prefix" + ], + "type": "string", + "description": "path prefix for HTTP requests" + }, + { + "key": [ + "cors" + ], + "type": "strings", + "description": "list of allowed origins for CORS" + }, + { + "key": [ + "locks" + ], + "type": "strings", + "description": "resources, that will be locked when starting (control, login)" + }, + { + "key": [ + "implicit_control" + ], + "type": "boolean", + "defaultValue": "false", + "description": "if enabled members can gain control implicitly" + }, + { + "key": [ + "control_protection" + ], + "type": "boolean", + "defaultValue": "false", + "description": "control protection means, users can gain control only if at least one admin is in the room" + }, + { + "key": [ + "heartbeat_interval" + ], + "type": "int", + "defaultValue": "120", + "description": "heartbeat interval in seconds" + }, { "key": [ "nat1to1" ], "type": "strings", - "description": "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used" + "description": "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP " }, { "key": [ @@ -322,14 +307,14 @@ "iceserver" ], "type": "strings", - "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer" + "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection " }, { "key": [ "iceservers" ], "type": "string", - "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer" + "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection " }, { "key": [ diff --git a/webpage/docs/migration-from-v2/help.txt b/webpage/docs/migration-from-v2/help.txt index 097f114f..84d0db36 100644 --- a/webpage/docs/migration-from-v2/help.txt +++ b/webpage/docs/migration-from-v2/help.txt @@ -1,22 +1,7 @@ --legacy enable legacy mode (default true) - --logs save logs to file - --cert string path to the SSL cert used to secure the neko server - --key string path to the SSL key used to secure the neko server - --bind string address/port/socket to serve neko - --proxy enable reverse proxy mode - --static string path to neko client files to serve - --path_prefix string path prefix for HTTP requests - --cors strings list of allowed origins for CORS - --locks strings resources, that will be locked when starting (control, login) - --implicit_control if enabled members can gain control implicitly - --control_protection control protection means, users can gain control only if at least one admin is in the room - --heartbeat_interval int heartbeat interval in seconds (default 120) - --file_transfer_enabled enable file transfer feature - --file_transfer_path string path to use for file transfer - --display string XDisplay to capture --video_codec string video codec to be used --av1 DEPRECATED: use video_codec @@ -27,7 +12,6 @@ --video_bitrate int video bitrate in kbit/s --hwenc string use hardware accelerated encoding --max_fps int maximum fps delivered via WebRTC, 0 is for no maximum - --device string audio device to capture --audio_codec string audio codec to be used --g722 DEPRECATED: use audio_codec @@ -36,7 +20,6 @@ --pcmu DEPRECATED: use audio_codec --audio string audio codec parameters to use for streaming --audio_bitrate int audio bitrate in kbit/s - --broadcast_pipeline string custom gst pipeline used for broadcasting, strings {url} {device} {display} will be replaced --broadcast_url string a default default URL for broadcast streams, can be disabled/changed later by admins in the GUI --broadcast_autostart automatically start broadcasting when neko starts and broadcast_url is set @@ -46,11 +29,24 @@ --password string password for connecting to stream --password_admin string admin password for connecting to stream - --nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used + --cert string path to the SSL cert used to secure the neko server + --key string path to the SSL key used to secure the neko server + --bind string address/port/socket to serve neko + --proxy enable reverse proxy mode + --static string path to neko client files to serve + --path_prefix string path prefix for HTTP requests + --cors strings list of allowed origins for CORS + + --locks strings resources, that will be locked when starting (control, login) + --implicit_control if enabled members can gain control implicitly + --control_protection control protection means, users can gain control only if at least one admin is in the room + --heartbeat_interval int heartbeat interval in seconds (default 120) + + --nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP --tcpmux int single TCP mux port for all peers --udpmux int single UDP mux port for all peers --icelite configures whether or not the ice agent should be a lite agent - --iceserver strings describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer - --iceservers string describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer + --iceserver strings describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection + --iceservers string describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection --ipfetch string automatically fetch IP address from given URL when nat1to1 is not present --epr string limits the pool of ephemeral ports that ICE UDP connections can allocate from diff --git a/webpage/docs/quick-start.md b/webpage/docs/quick-start.md index 3216c0f3..2b4a15f7 100644 --- a/webpage/docs/quick-start.md +++ b/webpage/docs/quick-start.md @@ -46,7 +46,7 @@ Neko is easy to use and requires no technical expertise to get started. All you ``` :::note - If you want to run Neko on your local network, you have to add `NEKO_NAT1TO1: ` to the `docker-compose.yaml` file. + If you want to run Neko on your local network, you have to add `NEKO_NAT1TO1=` to the `docker-compose.yaml` file. ::: 6. Visit the server's IP address in your browser and log in, the default password is `neko`. diff --git a/webpage/docs/release-notes.md b/webpage/docs/release-notes.md index 9b3f29f9..02587a20 100644 --- a/webpage/docs/release-notes.md +++ b/webpage/docs/release-notes.md @@ -1,73 +1,5 @@ # Release Notes -## master {#master} - -### New Features {#master-feats} -- Scroll to chat on mobile ([#496](https://github.com/m1k1o/neko/pull/496)) -- Added mobile keyboard icon to open the keyboard on mobile devices ([#497](https://github.com/m1k1o/neko/pull/497)) - -### Fixes {#master-fixes} -- Fixed various bugs related to the legacy client and migration. -- Fixed long standing issue [#279](https://github.com/m1k1o/neko/pull/279) where Google Chrome GPU acceleration did not work with Nvidia GPUs, thanks to [@TobyColeman](https://github.com/TobyColeman), [@alexbakerdev](https://github.com/alexbakerdev) and [@samstefan](https://github.com/samstefan) from [@wearewildcards](https://github.com/wearewildcards). - -### Misc {#master-misc} -- Added an https condition to the healthcheck ([#503](https://github.com/m1k1o/neko/pull/503), by @Garrulousbrevity). - -## [n.eko v3.0.0](https://github.com/m1k1o/neko/releases/tag/v3.0.0) {#v3.0.0} - -### Repository Changes {#v3.0.0-repo} -- The default registry is now `ghcr.io/m1k1o/neko` instead of `docker.io/m1k1o/neko`. -- Multiarch builds for `linux/amd64`, `linux/arm64`, and `linux/arm/v7` are now available instead of `arm-`based images. -- App folders from `.docker/` have been moved to `apps/`. -- Dev scripts from `.docker/` are now available in `client/dev/` and `server/dev/`. -- The `docs/` folder is now available at `webpage/docs/` with a completely new structure. -- The base `Dockerfile` has been split into `client/Dockerfile`, `server/Dockerfile`, and `runtime/Dockerfile`. -- The build process has been moved from `.docker/build` to `build/`, supporting more options. See `--help` for more information. -- Brave, Vivaldi, Remmina, and KDE are now also available as ARM-based images. -- Waterfox is now available as a new browser. - -### Server Changes {#v3.0.0-server} -- A REST API with OpenAPI 3.0 documentation is now available. -- Prometheus metrics are now available. -- The server name is now `github.com/m1k1o/neko/server` and can be used as a library. -- Reusable components and types are available in the `server/pkg/` folder, such as `gst`, `xevent`, and `xorg`. -- A new authentication system with support for multiple authentication methods has been added. -- A new user management system with support for granular feature access control has been implemented. -- The structure for configuration options has been updated, with options now separated into modules. -- Compatibility with V2 configuration options is still available but deprecated. -- **Capture**: Added a screencast feature as a fallback for WebRTC. -- **Capture**: Added experimental webcam and microphone passthrough support. -- **Capture**: Added video simulcast support and a stream selector. -- **Capture**: When joining a pipeline, a keyframe is requested on demand and sent to the client. -- **Desktop**: Clipboard now uses `xclip` instead of `libclipboard`, allowing multiple targets. -- **Desktop**: Added drag-and-drop file upload support. -- **Desktop**: Added a file chooser dialog to prompt users for file uploads (experimental). -- **Desktop**: Added an unminimize feature to ensure that the window is not minimized when the user is trying to control it. -- **Desktop**: Created a custom input X11 driver to support touchscreen devices. -- **Desktop**: Added support for `xrandr` to set the virtual monitor resolution to any resolution, not just predefined ones. -- **Desktop**: Added a function to send events when the cursor changes, along with the cursor image. -- **HTTP**: Added batch mode to allow multiple requests in a single connection. -- **HTTP**: Added `pprof` support to enable server profiling. -- **HTTP**: Created a legacy driver to support the current neko client. -- **HTTP**: Refactored HTTP logging. -- **Plugins**: Added support for Go plugins to enable custom features to be added to the server. -- **Plugins**: Chat has been implemented as a plugin that can be disabled globally or per user (mute feature). -- **Plugins**: File upload has been implemented as a plugin that can be disabled globally or per user. -- **Session**: Added support to save session tokens as cookies to allow persistent login. -- **Session**: Added the ability to serialize and deserialize sessions to a file to survive restarts. -- **Session**: Added support for dynamic permissions with granular feature access control. -- **WebRTC**: Forwarded desktop cursor changes to the client. -- **WebRTC**: Forwarded cursor position to other clients that have enabled the inactive cursors option. -- **WebRTC**: Switched from LittleEndian to BigEndian for the video stream to improve browser compatibility. -- **WebRTC**: Created a legacy driver to support the current neko client. -- **WebRTC**: Added WebRTC ping to check if the connection is still alive and to determine latency. -- **WebRTC**: Added the ability to switch video pipelines on the fly. -- **WebRTC**: Implemented bandwidth estimation and adaptive quality (experimental). -- **WebSocket**: Added support for controlling the desktop using WebSockets as a fallback for WebRTC. -- **WebSocket**: Added support for sending unicast and broadcast messages to all clients. - -Please note that in this version, only the server has been updated. The client is still in the old version; therefore, new features may not yet be available in the client. - ## [n.eko v2.9.0](https://github.com/m1k1o/neko/releases/tag/v2.9.0) {#v2.9.0} ### New Features {#v2.9.0-feats} diff --git a/webpage/docs/reverse-proxy-setup.md b/webpage/docs/reverse-proxy-setup.md index 95bb13fb..e2a5bf95 100644 --- a/webpage/docs/reverse-proxy-setup.md +++ b/webpage/docs/reverse-proxy-setup.md @@ -6,8 +6,6 @@ If you want to run Neko behind a reverse proxy, you can use the following exampl Do not forget to enable [`server.proxy=true`](/docs/v3/configuration#server.proxy) in your `config.yml` file to allow the server to trust the proxy headers. ::: -Neko pings websocket client every 10 seconds, and client is scheduled to send [heartbeat](/docs/v3/configuration#session.heartbeat_interval) to the server every 120 seconds. Make sure, that your timeout settings in the reverse proxy are set accordingly. - ## Traefik v2 {#traefik-v2} See the example below for a `docker-compose.yml` file. diff --git a/webpage/docs/troubleshooting.md b/webpage/docs/troubleshooting.md index 14b978a6..f381b79f 100644 --- a/webpage/docs/troubleshooting.md +++ b/webpage/docs/troubleshooting.md @@ -187,85 +187,6 @@ services: NEKO_WEBRTC_EPR: 52000-52100 ``` -### No internet in the remote browser {#no-internet} - -Try visiting `https://1.1.1.1` in the browser. If it works, then your internet is functioning, but DNS is not resolving. - -You can specify a custom DNS server in the Docker file using the `--dns` flag or in your `docker-compose.yaml` file. - -```yaml title="docker-compose.yaml" -services: - neko: - image: "ghcr.io/m1k1o/neko/chromium:latest" - # highlight-start - dns: - - 1.1.1.1 - - 8.8.8.8 - # highlight-end - cap_add: - - SYS_ADMIN - restart: "unless-stopped" - shm_size: "2gb" - # ... -``` - -If it still doesn't work, the issue is likely in the Docker/networking configuration. Check if your Docker network is not conflicting with your host network. - -List all Docker networks: - -```bash -$ for n in `docker network ls --format '{{ .ID }}'`; do docker network inspect --format '{{ .IPAM.Config }} {{ .Name }}' $n; done -[{172.16.0.0/24 172.16.0.1 map[]}] bridge -[] host -[{172.17.0.0/24 172.17.0.1 map[]}] neko1-net -[] none -[{172.18.0.0/24 172.18.0.1 map[]}] neko2-net -``` - -You can check your host network using: - -```bash -$ ip route | grep default -default via 172.18.0.1 dev eth0 proto dhcp src 172.18.0.2 metric 100 -``` - -In this case, the host subnet is the same as `neko2-net`, meaning that the internet stops working as soon as a second Docker network is created. - -To fix this, you can either remove the conflicting Docker network or change the subnet of the Docker network by modifying the `daemon.json` file: - -```json title="/etc/docker/daemon.json" -{ - "default-address-pools": [ - { - "base" : "10.10.0.0/16", - "size" : 24 - } - ] -} -``` - -### Browser is not starting with persistent profile {#browser-profile-not-starting} - -If you are using a persistent profile like `google-chrome` shown below, and the browser is not starting (you see a black screen), it may be because the profile is corrupted or not mounted correctly. - -```yaml title="docker-compose.yaml" -volumes: -# For google-chrome -- /data:/home/neko/.config/google-chrome -``` - -Possible reasons are: -- The profile is corrupted, which can happen if the container is not stopped properly. Browsers should be able to recover from this, but it may not work in some cases. -- The profile is not mounted to the correct path. Make sure that you are mounting the profile to the correct path in your `docker-compose.yaml` file. -- The profile is not owned by the correct user. Make sure that the profile is owned by the `neko` user in the container. You can check this by running the following command: - -```bash -# Check the owner of the profile -docker exec -it ls -la /home/neko/.config/google-chrome -# To change the owner -docker exec -it chown -R neko:neko /home/neko/.config/google-chrome -``` - ### Common server errors {#common-server-errors} ``` diff --git a/webpage/scripts/gen-api-docs.sh b/webpage/gen-api-docs.sh similarity index 95% rename from webpage/scripts/gen-api-docs.sh rename to webpage/gen-api-docs.sh index 55fb7d23..443d9737 100755 --- a/webpage/scripts/gen-api-docs.sh +++ b/webpage/gen-api-docs.sh @@ -1,5 +1,4 @@ #!/bin/bash -cd "$(dirname "$0")/.." # Clean the API docs docusaurus clean-api-docs all diff --git a/webpage/package.json b/webpage/package.json index 1eddf2a6..2fd52f5b 100644 --- a/webpage/package.json +++ b/webpage/package.json @@ -10,8 +10,8 @@ "deploy": "docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve", - "gen-config": "node ./src/components/Configuration/generate.js ./docs/configuration/help.txt ./docs/configuration/help.json", - "gen-config:v2": "node ./src/components/Configuration/generate.js ./docs/migration-from-v2/help.txt ./docs/migration-from-v2/help.json", + "gen-help": "node ./src/components/Configuration/generate.js ./docs/configuration/help.txt ./docs/configuration/help.json", + "gen-help:v2": "node ./src/components/Configuration/generate.js ./docs/v2-migration/help.txt ./docs/v2-migration/help.json", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "gen-api-docs": "./gen-api-docs.sh", diff --git a/webpage/scripts/gen-config.sh b/webpage/scripts/gen-config.sh deleted file mode 100755 index 3212d208..00000000 --- a/webpage/scripts/gen-config.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -cd "$(dirname "$0")/.." - -HELP_FILE="$(realpath -m docs/configuration/help.txt)" - -pushd ../server -go run cmd/neko/main.go serve --help > $HELP_FILE -popd - -# remove all lines with " V2: " -sed -i '/ V2: /d' $HELP_FILE -# remove all lines with " V2 DEPRECATED: " -sed -i '/ V2 DEPRECATED: /d' $HELP_FILE -#Β remove --legacy -sed -i '/--legacy/d' $HELP_FILE - -# remove evething until first "Flags:" -sed -i '1,/Flags:/d' $HELP_FILE -#Β remove --help -sed -i '/--help/d' $HELP_FILE - -npm run gen-config diff --git a/webpage/sidebars.ts b/webpage/sidebars.ts index ae8a319f..2ced8912 100644 --- a/webpage/sidebars.ts +++ b/webpage/sidebars.ts @@ -54,35 +54,13 @@ const sidebars: SidebarsConfig = { 'configuration/plugins', ] }, - { - type: 'category', - label: 'Customization', - collapsed: false, - link: { type: "doc", id: "customization/README" }, - items: [ - 'customization/networking', - 'customization/browsers', - 'customization/ui', - ] - }, - { - type: 'category', - label: 'Developer Guide', - collapsed: true, - link: { type: "doc", id: "developer-guide/README" }, - items: [ - 'developer-guide/repository-structure', - 'developer-guide/build', - { - type: 'link', - label: 'API Reference', - description: 'OpenAPI documentation for the Neko API', - href: '/docs/v3/api', - }, - ] - }, 'migration-from-v2/README', - 'reverse-proxy-setup' + 'reverse-proxy-setup', + { + type: 'link', + label: 'API Reference', + href: '/docs/v3/api', + } ] }, //{ @@ -90,6 +68,16 @@ const sidebars: SidebarsConfig = { // label: 'Hardware Acceleration', // items: [{ type: "autogenerated", dirName: "hardware-acceleration" }] //}, + //{ + // type: 'category', + // label: 'Application Customization', + // items: [{ type: "autogenerated", dirName: "app-customization" }] + //}, + //{ + // type: 'category', + // label: 'Developer Guide', + // items: [{ type: "autogenerated", dirName: "developer-guide" }] + //}, { type: 'category', label: 'Help & Support', diff --git a/webpage/src/components/Anchor.tsx b/webpage/src/components/Anchor.tsx index 838770fc..d9305ee1 100644 --- a/webpage/src/components/Anchor.tsx +++ b/webpage/src/components/Anchor.tsx @@ -3,7 +3,7 @@ import React from 'react'; export function Anchor(props: {id: string}) { useBrokenLinks().collectAnchor(props.id); - return ; + return ; } // -> http_only @@ -14,7 +14,7 @@ export function Def({id, code, ...props}: {id: string, code?: string}) { } useBrokenLinks().collectAnchor(id); // get current heading id - return {code}; + return {code}; } // -> http_only @@ -24,5 +24,5 @@ export function Opt({id, code, ...props}: {id: string, code?: string}) { code = id.split('.').pop(); } // get current heading id - return {code}; + return {code}; } diff --git a/webpage/src/components/AppIcon.tsx b/webpage/src/components/AppIcon.tsx deleted file mode 100644 index e616ef7b..00000000 --- a/webpage/src/components/AppIcon.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import useBrokenLinks from '@docusaurus/useBrokenLinks'; - -export function AppIcon({id, ...props}: {id: string}) { - useBrokenLinks().collectAnchor(id); - return - - -} diff --git a/webpage/src/components/Configuration/generate.js b/webpage/src/components/Configuration/generate.js index ecc02341..1695c2e7 100644 --- a/webpage/src/components/Configuration/generate.js +++ b/webpage/src/components/Configuration/generate.js @@ -17,16 +17,6 @@ const parseConfigOptions = (text) => { defaultValue = 'true'; } } - // this is an opaque object - if (type === 'string' && defaultValue === '{}') { - type = 'object'; - defaultValue = {}; - } - // this is an opaque array - if (type === 'string' && defaultValue === '[]') { - type = 'array'; - defaultValue = []; - } return { key: key.split('.'), type, defaultValue: defaultValue || undefined, description }; } return null; diff --git a/webpage/src/components/Configuration/index.tsx b/webpage/src/components/Configuration/index.tsx index 79f2422c..1497cd87 100644 --- a/webpage/src/components/Configuration/index.tsx +++ b/webpage/src/components/Configuration/index.tsx @@ -3,244 +3,130 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; -interface ConfigOptionValue { - type?: string; - description?: string; - defaultValue?: string; -} - -interface ConfigOption extends ConfigOptionValue { +interface ConfigOption { key: string[]; + description: string; + defaultValue?: string; + type?: string; } -function configKey(key: string | string[], value: ConfigOptionValue | any): ConfigOption { - if (typeof key === 'string') { - key = key.split('.'); - } - if (typeof value === 'object') { - return { - key, - type: value.type || getType(value.defaultValue), - description: value.description, - defaultValue: value.defaultValue, - } - } else { - return { - key, - type: getType(value), - defaultValue: value, - } - } +interface ConfigurationTabProps { + configOptions: ConfigOption[]; } -function configKeys(optValues: Record): ConfigOption[] { - let options: ConfigOption[] = []; - Object.entries(optValues).forEach(([key, value]) => { - options.push(configKey(key, value)); - }); - return options; -} - -function filterKeys(options: ConfigOption[], filter: string): ConfigOption[] { - return options.filter(option => { - const key = option.key.join('.'); - return key.startsWith(filter) - }); -} - -function defaultValue(value: ConfigOptionValue): string { - switch (value.type) { - case 'boolean': - return `${value.defaultValue || false}`; - case 'int': - case 'float': - case 'number': - return `${value.defaultValue || 0}`; - case 'duration': - case 'string': - return `${value.defaultValue ? `"${value.defaultValue}"` : ''}`; - case 'strings': - return ''; - case 'object': - return ''; - case 'array': - return ''; - default: - return value.type ? `<${value.type}>` : ''; - } -} - -function getType(value: any): string { - if (Array.isArray(value)) { - return 'array'; - } - return typeof value; -} - -export function EnvironmentVariables({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) { - if (typeof comments === 'undefined') { - comments = true; - } - - let code = ''; - options.forEach(option => { - const description = option.description ? option.description : ''; - const type = option.type ? ` (${option.type})` : ''; - if (comments && description) { - code += `# ${description}${type}\n`; - } - code += `NEKO_${option.key.join('_').toUpperCase()}=${defaultValue(option)}\n`; - }); - - return ( - - {code} - - ); -} - -export function CommandLineArguments({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) { - if (typeof comments === 'undefined') { - comments = true; - } - - let code = ''; - options.forEach(option => { - const description = option.description ? option.description : ''; - const type = option.type ? ` (${option.type})` : ''; - if (comments && description) { - code += `# ${description}${type}\n`; - } - code += `--${option.key.join('.')} ${defaultValue(option)}\n`; - }); - - return ( - - {code} - - ); -} - -export function YamlFileContent({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) { - if (typeof comments === 'undefined') { - comments = true; - } - - const final = Symbol('final'); - - const buildYaml = (obj: Record, prefix = '') => { +const ConfigurationTab: React.FC = ({ configOptions }) => { + const environmentVariables = () => { let code = ''; - Object.entries(obj).forEach(([key, option]) => { - if (typeof option === 'object' && !Array.isArray(option) && !option[final]) { - code += prefix+`${key}:\n`; - code += buildYaml(option, prefix + ' '); - } else { - const description = option.description ? option.description : ''; - const type = option.type ? ` (${option.type})` : ''; - if (comments && description) { - code += `${prefix}# ${description}${type}\n`; - } - let value: string; - if (option.type === 'strings') { - value = option.defaultValue ? `[ "${option.defaultValue}" ]` : '[ ]'; - } else if (option.type === 'object') { - value = "{}" - } else if (option.type === 'array') { - value = "[]" - } else { - value = defaultValue(option); - } - code += `${prefix}${key}: ${value}\n`; + configOptions.forEach(option => { + let value = "" + if (option.defaultValue) { + value = `"${option.defaultValue}"` + } else if (option.type) { + value = `<${option.type}>` } + code += `# ${option.description}\n`; + code += `NEKO_${option.key.join('_').toUpperCase()}: ${value}\n`; }); - return code; - }; + return ( + + {code} + + ); + } - const yamlCode = buildYaml(options.reduce((acc, option) => { - const keys = option.key; - let current = acc; - keys.forEach((key, index) => { - if (!current[key]) { - current[key] = index === keys.length - 1 ? option : {}; + const cmdArguments = () => { + let code = ''; + configOptions.forEach(option => { + code += `# ${option.description}\n`; + code += `--${option.key.join('.')}`; + if (option.type) { + code += ` <${option.type}>`; } - current = current[key]; + code += '\n'; }); - current[final] = true; - return acc; - }, {})); + return ( + + {code} + + ); + } + + const yamlFile = () => { + const final = Symbol('final'); + + const buildYaml = (obj, prefix = '') => { + let code = ''; + Object.keys(obj).forEach(key => { + const value = obj[key]; + if (typeof value === 'object' && !Array.isArray(value) && !value[final]) { + code += prefix+`${key}:\n`; + code += buildYaml(value, prefix + ' '); + } else { + let val = ''; + switch (value.type) { + case 'boolean': + val = `${value.defaultValue || false}`; + break; + case 'int': + case 'float': + val = `${value.defaultValue || 0}`; + break; + case 'strings': + val = `[ ${value.defaultValue ? value.defaultValue.map(v => `"${v}"`).join(', ') : ''} ]`; + break; + case 'duration': + case 'string': + val = `${value.defaultValue ? `"${value.defaultValue}"` : ''}`; + break; + default: + val = `<${value.type}>`; + break; + } + code += prefix+`# ${value.description || ''}\n`; + code += prefix+`${key}: ${val}\n`; + } + }); + return code; + }; + + const yamlCode = buildYaml(configOptions.reduce((acc, option) => { + const keys = option.key; + let current = acc; + keys.forEach((key, index) => { + if (!current[key]) { + current[key] = index === keys.length - 1 ? option : {}; + } + current = current[key]; + }); + current[final] = true; + return acc; + }, {})); + + return ( + + {yamlCode} + + ); + } return ( - - {yamlCode} - +
+ + +

You can set the following environment variables in your docker-compose.yaml file or in your shell environment.

+ {environmentVariables()} +
+ +

You can list the following command line arguments using neko serve --help.

+ {cmdArguments()} +
+ +

You can create a /etc/neko/neko.yaml file with the following configuration options.

+ {yamlFile()} +
+
+
); -} - -type ConfigurationTabProps = { - options?: ConfigOption[] | Record; - heading?: boolean; - comments?: boolean; - filter?: string | string[] | Record; }; -export function ConfigurationTab({ options, heading, comments, filter, ...props }: ConfigurationTabProps) { - var configOptions: ConfigOption[] = []; - if (Array.isArray(options)) { - configOptions = options; - } else { - configOptions = configKeys(options) - } - if (typeof comments === 'undefined') { - comments = true; - } - if (typeof heading === 'undefined') { - heading = false; - } - - if (Array.isArray(filter)) { - let filteredOptions: ConfigOption[] = []; - for (const f of filter) { - filteredOptions = [ ...filteredOptions, ...filterKeys(configOptions, f) ]; - } - configOptions = filteredOptions; - } else if (typeof filter === 'string') { - configOptions = filterKeys(configOptions, filter); - } else if (typeof filter === 'object') { - let filteredOptions: ConfigOption[] = []; - for (const k in filter) { - let filtered = configOptions.find(option => { - return option.key.join('.') === k; - }); - let replaced = configKey(k, filter[k]); - filteredOptions = [ ...filteredOptions, { ...filtered, ...replaced } ]; - } - configOptions = filteredOptions; - } - - return ( - - - {heading && ( -

You can set the following environment variables in your docker-compose.yaml file or in your shell environment.

- )} - {EnvironmentVariables({ options: configOptions, comments })} -
- - {heading && ( -

You can list the following command line arguments using neko serve --help.

- )} - {CommandLineArguments({ options: configOptions, comments })} -
- - {heading && ( -

You can create a /etc/neko/neko.yaml file with the following configuration options.

- )} - {YamlFileContent({ options: configOptions, comments })} -
-
- ); -} +export default ConfigurationTab; diff --git a/webpage/src/css/custom.css b/webpage/src/css/custom.css index 8ce333f1..38a6b564 100644 --- a/webpage/src/css/custom.css +++ b/webpage/src/css/custom.css @@ -29,10 +29,6 @@ --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); } -.anchorOffset { - scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem) -} - /* * Custom CSS for OpenAPI documentation */ diff --git a/webpage/src/pages/contact.md b/webpage/src/pages/contact.md index 42ea917f..fec9db6a 100644 --- a/webpage/src/pages/contact.md +++ b/webpage/src/pages/contact.md @@ -9,11 +9,3 @@ We are here to assist you with any issues you may face while setting up or using - For installation or usage questions, [join our Discord](https://discord.gg/3U6hWpC) and post in the [#community-help](https://discord.com/channels/665851821906067466/696222582114091088) channel. - To report bugs or request features, [open a new issue on GitHub](https://github.com/m1k1o/neko/issues). - If you find an issue with this documentation, click the `Edit this page` button at the bottom of the page and then the `edit` button on the GitHub page that opens to make edits directly from your browser. (See a [step-by-step guide here](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files)). - -**Enterprise Support** - -For enterprises or organizations requiring **dedicated, paid support**, custom solutions, or priority assistance, please contact us at neko@m1k1o.net. - -**Security** - -If you discover a security vulnerability in Neko, please report it to us directly at security@m1k1o.net. We take security seriously and will work with you to address the issue promptly. Please do not disclose the vulnerability publicly until we have had a chance to address it. We appreciate your help in keeping Neko secure. diff --git a/webpage/src/pages/contributing.md b/webpage/src/pages/contributing.md index 0ceed0a2..15f55d16 100644 --- a/webpage/src/pages/contributing.md +++ b/webpage/src/pages/contributing.md @@ -2,9 +2,9 @@ Neko is an open-source project, and we welcome contributions from the community. Whether you're a developer, designer, or user, you can help improve Neko by reporting bugs, suggesting new features, or submitting code changes. -## Reporting Issues {#issues} +## Reporting issues -If you encounter a bug or have a feature request, please open a new issue on the [GitHub repository](https://github.com/m1k1o/neko/issues). Before opening an issue, please check if a similar issue has already been reported and ensure you read the [Troubleshooting Guide](/docs/v3/troubleshooting) and the [FAQ](/docs/v3/faq) to see if your issue has already been addressed. +If you encounter a bug or have a feature request, please open a new issue on the [GitHub repository](https://github.com/m1k1o/neko/issues). Before opening an issue, please check if a similar issue has already been reported. When reporting an issue, please provide as much information as possible, including: @@ -15,14 +15,34 @@ When reporting an issue, please provide as much information as possible, includi - Screenshots or error messages (if applicable) - Your operating system and browser version -## Contributing Code {#pull-requests} +## Contributing code If you're a developer and want to contribute code to Neko, follow these steps: 1. **Fork the [project](https://github.com/m1k1o/neko)**: Create a personal copy of the repository by [forking it](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) to your GitHub account. -2. **Edit files in your branch**: Make your changes in a new branch created from the `master` branch. Ensure your changes are well-documented and tested. +2. **Navigate to [.docker/README.md](https://github.com/m1k1o/neko/tree/master/.docker)**: Follow the instructions in the `.docker/README.md` file for setting up the Docker environment required for development. -3. **Submit a [pull request](https://github.com/m1k1o/neko/pulls)**: Once your changes are ready, submit a pull request with a detailed explanation of the improvements and any relevant information for the reviewers. +3. **Edit files in your branch**: Make your changes in a new branch created from the `master` branch. Ensure your changes are as well documented and tested. -Make sure you read the [Developer Guide](/docs/v3/developer-guide/) for more information on the code structure and development process. +4. **Submit a [pull request](https://github.com/m1k1o/neko/pulls)**: Once your changes are ready, submit a pull request with a detailed explanation of the improvements and any relevant information for the reviewers. + +## Server build dependencies + +To compile the Golang code locally, you need to install the following dependencies: + +```shell +apt-get install -y --no-install-recommends libx11-dev libxrandr-dev libxtst-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +``` + +### Retrieving Libclipboard files + +Libclipboard files can be retrieved from the `neko_dev_server` Docker container. Run the following commands to copy the necessary files: + +```shell +mkdir -p /usr/local/lib/pkgconfig/ /usr/local/include/ +docker cp neko_dev_server:/usr/local/lib/libclipboard.a /usr/local/lib/ +docker cp neko_dev_server:/usr/local/lib/pkgconfig/libclipboard.pc /usr/local/lib/pkgconfig/ +docker cp neko_dev_server:/usr/local/include/libclipboard-config.h /usr/local/include/ +docker cp neko_dev_server:/usr/local/include/libclipboard.h /usr/local/include/ +``` diff --git a/webpage/static/.well-known/security.txt b/webpage/static/.well-known/security.txt deleted file mode 100644 index 15c7ba55..00000000 --- a/webpage/static/.well-known/security.txt +++ /dev/null @@ -1,3 +0,0 @@ -Contact: mailto:security@m1k1o.net -Policy: https://github.com/m1k1o/neko/security -Expires: 2030-04-01T00:00:00z