Compare commits

..

No commits in common. "master" and "v3.0.0-beta.12" have entirely different histories.

174 changed files with 1398 additions and 2710 deletions

View file

@ -1,21 +1,17 @@
name: Build Client name: Build and Publish Client Artifacts
on: on:
workflow_call: workflow_call:
inputs: #pull_request: # Change to push when ready to deploy
with-artifact: # branches:
required: false # - master
type: boolean # paths:
default: true # - client/**
description: | # - .github/workflows/client_build.yml
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: jobs:
build-client: client_build:
name: Build Client name: Build and Publish Client Artifacts
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -39,7 +35,6 @@ jobs:
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: ${{ inputs.with-artifact }}
with: with:
name: client name: client
path: client/dist path: client/dist

View file

@ -1,4 +1,4 @@
name: Test Client name: Test Client Build
on: on:
pull_request: pull_request:
@ -6,14 +6,28 @@ on:
- master - master
paths: paths:
- client/** - client/**
- .github/workflows/client_build.yml
- .github/workflows/client_test.yml - .github/workflows/client_test.yml
jobs: jobs:
test-client: client_test:
name: Test Client name: Test Client Build
uses: ./.github/workflows/client_build.yml runs-on: ubuntu-latest
with:
# Do not upload artifacts for test builds steps:
with-artifact: false - uses: actions/checkout@v4
secrets: inherit 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

View file

@ -1,11 +1,8 @@
name: Build and Push to Docker Hub name: "build and push amd64 images to Docker Hub"
on: on:
push: push:
branches: branches: [ master ]
- master
paths-ignore:
- 'webpage/**'
# #
# Run this action periodically to keep browsers up-to-date # Run this action periodically to keep browsers up-to-date
# even if there is no activity in this repo. # even if there is no activity in this repo.
@ -13,119 +10,68 @@ on:
schedule: schedule:
- cron: "43 2 * * 1" - 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: env:
DOCKER_IMAGE: m1k1o/neko DOCKER_IMAGE: m1k1o/neko
jobs: jobs:
build-base: build-base:
name: Base Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
# #
# do not run on forks # do not run on forks
# #
if: github.repository_owner == 'm1k1o' if: github.repository_owner == 'm1k1o'
steps: steps:
- name: Checkout - name: Check Out Repo
uses: actions/checkout@v4 uses: actions/checkout@v2
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: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 run: |
with: docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}"
username: ${{ github.actor }} env:
password: ${{ secrets.DOCKER_TOKEN }} DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
- name: Generate base Dockerfile - name: Build base
run: go run utils/docker/main.go -i Dockerfile.tmpl -o Dockerfile run: |
./build -b ${DOCKER_IMAGE}:base
docker push ${DOCKER_IMAGE}:base
- name: Build and push build:
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
runs-on: ubuntu-latest runs-on: ubuntu-latest
# #
# do not run on forks # do not run on forks
# #
if: github.repository_owner == 'm1k1o' if: github.repository_owner == 'm1k1o'
needs: build-base needs: [ build-base ]
strategy: strategy:
# Will build all images even if some fail. # Will build all images even if some fail.
fail-fast: false fail-fast: false
matrix: matrix:
tag: tags: [ firefox, waterfox, chromium, google-chrome, ungoogled-chromium, microsoft-edge, brave, vivaldi, opera, tor-browser, remmina, vlc, xfce, kde ]
- firefox env:
# Temporarily disabled due to Cloudflare blocked download link DOCKER_TAG: ${{ matrix.tags }}
#- waterfox
- chromium
- google-chrome
- ungoogled-chromium
- microsoft-edge
- brave
- vivaldi
- opera
- tor-browser
- remmina
- vlc
- xfce
- kde
steps: steps:
- name: Checkout - name: Check Out Repo
uses: actions/checkout@v4 uses: actions/checkout@v2
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: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 run: |
with: docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}"
username: ${{ github.actor }} env:
password: ${{ secrets.DOCKER_TOKEN }} DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push - name: Build container
uses: docker/build-push-action@v6 run: |
with: ./build -b ${DOCKER_IMAGE}:base -i ${DOCKER_IMAGE}
context: apps/${{ matrix.tag }} docker tag ${DOCKER_IMAGE}/${DOCKER_TAG} ${DOCKER_IMAGE}:${DOCKER_TAG}
push: true docker push ${DOCKER_IMAGE}:${DOCKER_TAG}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} - name: Push latest tag
build-args: | if: ${{ matrix.tags == 'firefox' }}
BASE_IMAGE=${{ env.DOCKER_IMAGE }}:base run: |
cache-from: type=gha docker pull ${DOCKER_IMAGE}:${DOCKER_TAG}
cache-to: type=gha,mode=max docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest
docker push ${DOCKER_IMAGE}:latest

View file

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

44
.github/workflows/ghcr_amd64.yml vendored Normal file
View file

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

36
.github/workflows/ghcr_arm.yml vendored Normal file
View file

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

View file

@ -1,4 +1,4 @@
name: Build and Push to GHCR for Intel name: "intel gpu supported images"
on: on:
push: push:
@ -7,26 +7,24 @@ on:
jobs: jobs:
build-base: build-base:
name: Base Image
uses: ./.github/workflows/image_base.yml uses: ./.github/workflows/image_base.yml
with: with:
flavor: intel flavor: intel
platforms: linux/amd64 platforms: linux/amd64
dockerfile: Dockerfile.intel dockerfile: Dockerfile.intel
secrets: inherit secrets:
GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }}
build-app: build-apps:
name: App Image
uses: ./.github/workflows/image_app.yml uses: ./.github/workflows/image_app.yml
needs: build-base needs: [ build-base ]
strategy: strategy:
# Will build all images even if some fail. # Will build all images even if some fail.
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- name: firefox - name: firefox
# Temporarily disabled due to Cloudflare blocked download link - name: waterfox
#- name: waterfox
- name: chromium - name: chromium
- name: google-chrome - name: google-chrome
- name: ungoogled-chromium - name: ungoogled-chromium
@ -41,7 +39,8 @@ jobs:
- name: kde - name: kde
with: with:
name: ${{ matrix.name }} name: ${{ matrix.name }}
flavor: intel
platforms: ${{ matrix.platforms }}
dockerfile: ${{ matrix.dockerfile }} dockerfile: ${{ matrix.dockerfile }}
secrets: inherit flavor: intel
platforms: linux/amd64
secrets:
GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }}

View file

@ -1,4 +1,4 @@
name: Build and Push to GHCR for Nvidia name: "nvidia gpu supported images"
on: on:
push: push:
@ -7,18 +7,17 @@ on:
jobs: jobs:
build-base: build-base:
name: Base Image
uses: ./.github/workflows/image_base.yml uses: ./.github/workflows/image_base.yml
with: with:
flavor: nvidia flavor: nvidia
platforms: linux/amd64 platforms: linux/amd64
dockerfile: Dockerfile.nvidia dockerfile: Dockerfile.nvidia
secrets: inherit secrets:
GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }}
build-app: build-apps:
name: App Image
uses: ./.github/workflows/image_app.yml uses: ./.github/workflows/image_app.yml
needs: build-base needs: [ build-base ]
strategy: strategy:
# Will build all images even if some fail. # Will build all images even if some fail.
fail-fast: false fail-fast: false
@ -36,7 +35,8 @@ jobs:
dockerfile: Dockerfile.nvidia dockerfile: Dockerfile.nvidia
with: with:
name: ${{ matrix.name }} name: ${{ matrix.name }}
flavor: nvidia
platforms: ${{ matrix.platforms }}
dockerfile: ${{ matrix.dockerfile }} dockerfile: ${{ matrix.dockerfile }}
secrets: inherit flavor: nvidia
platforms: linux/amd64
secrets:
GHCR_ACCESS_TOKEN: ${{ secrets.GHCR_ACCESS_TOKEN }}

View file

@ -1,4 +1,4 @@
name: Build App Image name: Build and Publish Application Image
on: on:
workflow_call: workflow_call:
@ -6,7 +6,12 @@ on:
name: name:
required: true required: true
type: string 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: flavor:
required: false required: false
type: string type: string
@ -17,18 +22,17 @@ on:
type: string type: string
default: "linux/amd64" default: "linux/amd64"
description: "The platforms to build for." description: "The platforms to build for."
dockerfile: secrets:
required: false GHCR_ACCESS_TOKEN:
type: string required: true
default: "Dockerfile" description: "GitHub Container Registry access token."
description: "The Dockerfile to use for building the image."
env: env:
FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }} FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }}
jobs: jobs:
build-app: build-app:
name: Build App Image name: Build and Publish Application Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@ -48,7 +52,6 @@ jobs:
with: with:
images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}${{ inputs.name }} images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}${{ inputs.name }}
tags: | tags: |
type=edge,branch=master
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
@ -72,5 +75,3 @@ jobs:
build-args: | build-args: |
BASE_IMAGE=ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base:sha-${{ github.sha }} BASE_IMAGE=ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base:sha-${{ github.sha }}
platforms: ${{ inputs.platforms || 'linux/amd64' }} platforms: ${{ inputs.platforms || 'linux/amd64' }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -1,8 +1,13 @@
name: Build Base Image name: Build and Publish Base Image
on: on:
workflow_call: workflow_call:
inputs: inputs:
dockerfile:
required: false
type: string
default: "Dockerfile"
description: "The Dockerfile to use for building the image."
flavor: flavor:
required: false required: false
type: string type: string
@ -13,24 +18,22 @@ on:
type: string type: string
default: "linux/amd64" default: "linux/amd64"
description: "The platforms to build for." description: "The platforms to build for."
dockerfile: secrets:
required: false GHCR_ACCESS_TOKEN:
type: string required: true
default: "Dockerfile" description: "GitHub Container Registry access token."
description: "The Dockerfile to use for building the image."
env: env:
FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }} FLAVOR_PREFIX: ${{ inputs.flavor && format('{0}-', inputs.flavor) || '' }}
jobs: jobs:
build-client: build-client:
name: Build Client Artifacts
uses: ./.github/workflows/client_build.yml uses: ./.github/workflows/client_build.yml
build-base: build-base:
name: Build Base Image name: Build and Publish Base Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build-client needs: [ build-client ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -55,7 +58,6 @@ jobs:
with: with:
images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base images: ghcr.io/${{ github.repository }}/${{ env.FLAVOR_PREFIX }}base
tags: | tags: |
type=edge,branch=master
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
@ -71,7 +73,7 @@ jobs:
- name: Generate base Dockerfile - name: Generate base Dockerfile
env: env:
RUNTIME_DOCKERFILE: ${{ inputs.dockerfile || 'Dockerfile' }} 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 - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
@ -81,5 +83,3 @@ jobs:
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
platforms: ${{ inputs.platforms || 'linux/amd64' }} platforms: ${{ inputs.platforms || 'linux/amd64' }}
cache-from: type=gha
cache-to: type=gha,mode=max

65
.github/workflows/server_build.yml vendored Normal file
View file

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

View file

@ -1,4 +1,4 @@
name: Test Server name: Test Server Build
on: on:
pull_request: pull_request:
@ -9,8 +9,8 @@ on:
- .github/workflows/server_test.yml - .github/workflows/server_test.yml
jobs: jobs:
build-amd64: server_test:
name: Build amd64 name: Test Server Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read contents: read
@ -24,21 +24,3 @@ jobs:
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: ./server 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

View file

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

View file

@ -1,39 +1,50 @@
name: Build and Deploy Webpage to GitHub Pages name: Build and Deploy Webpage to GitHub Pages
on: on:
# Runs on pushes targeting the default branch
push: push:
branches: branches:
- master - master
paths: paths:
- webpage/** - webpage/**
- .github/workflows/webpage_build.yml
- .github/workflows/webpage_deploy.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: jobs:
build-webpage: webpage_build:
name: Build Webpage name: Build and Deploy Webpage to GitHub Pages
uses: ./.github/workflows/webpage_build.yml runs-on: ubuntu-latest
secrets: inherit
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 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 # Deploy to the github-pages environment
environment: environment:

View file

@ -1,4 +1,4 @@
name: Test Webpage name: Test Webpage Build
on: on:
pull_request: pull_request:
@ -6,14 +6,26 @@ on:
- master - master
paths: paths:
- webpage/** - webpage/**
- .github/workflows/webpage_build.yml
- .github/workflows/webpage_test.yml - .github/workflows/webpage_test.yml
jobs: jobs:
test-webpage: webpage_test:
name: Test Webpage name: Test Webpage Build
uses: ./.github/workflows/webpage_build.yml runs-on: ubuntu-latest
with:
# Do not upload artifacts for test builds steps:
with-artifact: false - uses: actions/checkout@v4
secrets: inherit 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

View file

@ -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 ./server/ AS server
FROM ./client/ AS client FROM ./client/ AS client
FROM ./utils/xorg-deps/ AS xorg-deps
FROM ./runtime/$RUNTIME_DOCKERFILE AS runtime FROM ./runtime/$RUNTIME_DOCKERFILE AS runtime
# tells neko-rooms which version of the API to use COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so
LABEL net.m1k1o.neko.api-version=3 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/plugins/ /etc/neko/plugins/
COPY --from=server /src/bin/neko /usr/bin/neko COPY --from=server /src/bin/neko /usr/bin/neko
COPY --from=client /src/dist/ /var/www 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 COPY config.yml /etc/neko/neko.yaml

118
README.md
View file

@ -1,6 +1,6 @@
<div align="center"> <div align="center">
<a href="https://github.com/m1k1o/neko" title="Neko's Github repository."> <a href="https://github.com/m1k1o/neko" title="Neko's Github repository.">
<img src="https://neko.m1k1o.net/img/logo.png" width="400" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/logo.png" width="400" height="auto"/>
</a> </a>
<p align="center"> <p align="center">
<a href="https://github.com/m1k1o/neko/releases"> <a href="https://github.com/m1k1o/neko/releases">
@ -21,14 +21,11 @@
<a href="https://discord.gg/3U6hWpC"> <a href="https://discord.gg/3U6hWpC">
<img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord"> <img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord">
</a> </a>
<a href="https://hellogithub.com/repository/4536d4546af24196af3f08a023dfa007" target="_blank">
<img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=4536d4546af24196af3f08a023dfa007&claim_uid=0x19e4dJwD83aW2&theme=small" alt="FeaturedHelloGitHub" />
</a>
<a href="https://github.com/m1k1o/neko/actions"> <a href="https://github.com/m1k1o/neko/actions">
<img src="https://github.com/m1k1o/neko/actions/workflows/ghcr.yml/badge.svg" alt="build"> <img src="https://github.com/m1k1o/neko/actions/workflows/ghcr-amd.yml/badge.svg" alt="build">
</a> </a>
</p> </p>
<img src="https://neko.m1k1o.net/img/intro.gif" width="650" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/intro.gif" width="650" height="auto"/>
</div> </div>
# n.eko # n.eko
@ -86,60 +83,47 @@ Compared to clientless remote desktop gateway (e.g. [Apache Guacamole](https://g
### Supported browsers ### Supported browsers
<div align="center"> <div align="center">
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#firefox"> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/firefox.svg" title="m1k1o/neko:firefox" width="60" height="auto"/>
<img src="https://neko.m1k1o.net/img/icons/firefox.svg" title="ghcr.io/m1k1o/neko/firefox" width="60" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/google-chrome.svg" title="m1k1o/neko:google-chrome" width="60" height="auto"/>
</a> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/chromium.svg" title="m1k1o/neko:chromium" width="60" height="auto"/>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#tor-browser"> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/microsoft-edge.svg" title="m1k1o/neko:microsoft-edge" width="60" height="auto"/>
<img src="https://neko.m1k1o.net/img/icons/tor-browser.svg" title="ghcr.io/m1k1o/neko/tor-browser" width="60" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/brave.svg" title="m1k1o/neko:brave" width="60" height="auto"/>
</a> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/vivaldi.svg" title="m1k1o/neko:vivaldi" width="60" height="auto"/>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#waterfox"> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/opera.svg" title="m1k1o/neko:opera" width="60" height="auto"/>
<img src="https://neko.m1k1o.net/img/icons/waterfox.svg" title="ghcr.io/m1k1o/neko/waterfox" width="60" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/tor-browser.svg" title="m1k1o/neko:tor-browser" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#chromium">
<img src="https://neko.m1k1o.net/img/icons/chromium.svg" title="ghcr.io/m1k1o/neko/chromium" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#google-chrome">
<img src="https://neko.m1k1o.net/img/icons/google-chrome.svg" title="ghcr.io/m1k1o/neko/google-chrome" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#ungoogled-chromium">
<img src="https://neko.m1k1o.net/img/icons/ungoogled-chromium.svg" title="ghcr.io/m1k1o/neko/google-chrome" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#microsoft-edge">
<img src="https://neko.m1k1o.net/img/icons/microsoft-edge.svg" title="ghcr.io/m1k1o/neko/microsoft-edge" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#brave">
<img src="https://neko.m1k1o.net/img/icons/brave.svg" title="ghcr.io/m1k1o/neko/brave" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#vivaldi">
<img src="https://neko.m1k1o.net/img/icons/vivaldi.svg" title="ghcr.io/m1k1o/neko/vivaldi" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#opera">
<img src="https://neko.m1k1o.net/img/icons/opera.svg" title="ghcr.io/m1k1o/neko/opera" width="60" height="auto"/>
</a>
... see [all available images](https://neko.m1k1o.net/docs/v3/installation/docker-images)
</div> </div>
### Other applications ### Other programs
<div align="center"> <div align="center">
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#xfce"> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/remmina.png" title="m1k1o/neko:remmina" width="60" height="auto"/>
<img src="https://neko.m1k1o.net/img/icons/xfce.svg" title="ghcr.io/m1k1o/neko/xfce" width="60" height="auto"/> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/vlc.svg" title="m1k1o/neko:vlc" width="60" height="auto"/>
</a> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/xfce.svg" title="m1k1o/neko:xfce" width="60" height="auto"/>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#kde"> <img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/icons/kde.svg" title="m1k1o/neko:kde" width="60" height="auto"/>
<img src="https://neko.m1k1o.net/img/icons/kde.svg" title="ghcr.io/m1k1o/neko/kde" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#remmina">
<img src="https://neko.m1k1o.net/img/icons/remmina.svg" title="ghcr.io/m1k1o/neko/remmina" width="60" height="auto"/>
</a>
<a href="https://neko.m1k1o.net/docs/v3/installation/docker-images#vlc">
<img src="https://neko.m1k1o.net/img/icons/vlc.svg" title="ghcr.io/m1k1o/neko/vlc" width="60" height="auto"/>
</a>
... others in <a href="https://github.com/m1k1o/neko-apps">m1k1o/neko-apps</a> ... others in <a href="https://github.com/m1k1o/neko-apps">m1k1o/neko-apps</a>
</div> </div>
### 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=<your-user-name>&pwd=<room-pass>` to the url.)
* Broadcasting room content using RTMP (to e.g. twitch or youtube...)
* Bidirectional file transfer (if enabled)
<div align="center">
With `NEKO_FILE_TRANSFER_ENABLED=true`:
<img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/file-transfer.gif" width="650" height="auto"/>
</div>
### Why n.eko?
I like cats 🐱 (`Neko` is the Japanese word for cat), I'm a weeb/nerd. 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 ## 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 ## 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) ## How to contribute? How to build?
- [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 Navigate to [.docker](.docker) folder for further information.
Contributions are welcome! Check the [Contributing Guide](https://neko.m1k1o.net/contributing) for details.
## Support ## 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).

View file

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

View file

@ -1,13 +1,12 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
RUN set -eux; apt-get update; \ RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends apt-transport-https curl openbox; \ apt-get install -y --no-install-recommends apt-transport-https curl openbox; \
# #
# install brave browser # 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; \ 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; \ | tee /etc/apt/sources.list.d/brave-browser-release.list; \
apt-get update; \ apt-get update; \
apt-get install -y --no-install-recommends brave-browser; \ apt-get install -y --no-install-recommends brave-browser; \

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest ARG BASE_IMAGE=m1k1o/neko:nvidia-base
FROM $BASE_IMAGE FROM $BASE_IMAGE
RUN set -eux; apt-get update; \ RUN set -eux; apt-get update; \

View file

@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/brave-browser
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist --ignore-gpu-blocklist
--disable-seccomp-filter-sandbox --disable-seccomp-filter-sandbox
--use-angle=vulkan --use-gl=egl
--disable-software-rasterizer --disable-software-rasterizer
--disable-dev-shm-usage --disable-dev-shm-usage
--disable-vulkan-surface
--enable-unsafe-webgpu
stopsignal=INT stopsignal=INT
autorestart=true autorestart=true
priority=800 priority=800

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

View file

@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/chromium
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist --ignore-gpu-blocklist
--disable-seccomp-filter-sandbox --disable-seccomp-filter-sandbox
--use-angle=vulkan --use-gl=egl
--disable-software-rasterizer --disable-software-rasterizer
--disable-dev-shm-usage --disable-dev-shm-usage
--disable-vulkan-surface
--enable-unsafe-webgpu
stopsignal=INT stopsignal=INT
autorestart=true autorestart=true
priority=800 priority=800

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #
@ -10,7 +10,7 @@ RUN set -eux; apt-get update; \
# #
# install firefox-esr for armhf # install firefox-esr for armhf
apt-get install -y --no-install-recommends firefox-esr; \ 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 # install extensions
mkdir -p /usr/lib/firefox-esr/distribution/extensions; \ mkdir -p /usr/lib/firefox-esr/distribution/extensions; \

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US" ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US"

View file

@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true);
lockPref("profile.allow_automigration", false); lockPref("profile.allow_automigration", false);
lockPref("signon.prefillForms", false); lockPref("signon.prefillForms", false);
lockPref("signon.rememberSignons", false); lockPref("signon.rememberSignons", false);
//lockPref("xpinstall.enabled", false); lockPref("xpinstall.enabled", false);
//lockPref("xpinstall.whitelist.required", true); lockPref("xpinstall.whitelist.required", true);
lockPref("browser.download.manager.retention", 0); lockPref("browser.download.manager.retention", 0);
lockPref("browser.download.folderList", 2); lockPref("browser.download.folderList", 2);
lockPref("browser.download.forbid_open_with", true); lockPref("browser.download.forbid_open_with", true);

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest ARG BASE_IMAGE=m1k1o/neko:nvidia-base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# latest working version with EGL: 111.0.5563.146, revert when resolved # latest working version with EGL: 111.0.5563.146, revert when resolved

View file

@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/google-chrome
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist --ignore-gpu-blocklist
--disable-seccomp-filter-sandbox --disable-seccomp-filter-sandbox
--use-angle=vulkan --use-gl=egl
--disable-software-rasterizer --disable-software-rasterizer
--disable-dev-shm-usage --disable-dev-shm-usage
--disable-vulkan-surface
--enable-unsafe-webgpu
stopsignal=INT stopsignal=INT
autorestart=true autorestart=true
priority=800 priority=800

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/" ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/"

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/" ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/"

View file

@ -12,11 +12,9 @@ command=/bin/entrypoint.sh /usr/bin/microsoft-edge
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization --enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist --ignore-gpu-blocklist
--disable-seccomp-filter-sandbox --disable-seccomp-filter-sandbox
--use-angle=vulkan --use-gl=egl
--disable-software-rasterizer --disable-software-rasterizer
--disable-dev-shm-usage --disable-dev-shm-usage
--disable-vulkan-surface
--enable-unsafe-webgpu
stopsignal=INT stopsignal=INT
autorestart=true autorestart=true
priority=800 priority=800

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG API_URL="https://download5.operacdn.com/pub/opera/desktop/" ARG API_URL="https://download5.operacdn.com/pub/opera/desktop/"

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# install remmina # install remmina

View file

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -u
err() { err() {
echo "ERROR: $*" >&2 echo "ERROR: $*" >&2
@ -15,28 +16,23 @@ if [[ -n "$REMMINA_PROFILE" ]]; then
exec remmina -c "$profile" exec remmina -c "$profile"
fi fi
if [[ ! -z "$REMMINA_URL" ]]; then [[ -z "$REMMINA_URL" ]] && err "Neither 'REMMINA_PROFILE' nor 'REMMINA_URL' found in env vars"
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"
[[ "$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 [[ "$proto" != "vnc" && "$proto" != "rdp" && "$proto" != "spice" ]] && err "Unsupported protocol $proto in connection url 'REMMINA_URL'"
remmina --set-option username="$user" --update-profile "$profile"
remmina --set-option password="$pw" --update-profile "$profile"
remmina --set-option server="$host" --update-profile "$profile"
# remmina --set-option window_maximize=1 --update-profile "$profile" profile="$profile_dir"/"$proto".remmina
# remmina --set-option scale=1 --update-profile "$profile" 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" # remmina --set-option window_maximize=1 --update-profile "$profile"
exec remmina -c "$profile" # remmina --set-option scale=1 --update-profile "$profile"
fi
exec remmina -c "$profile"
echo "Running remmina without connection profile"
exec remmina

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG API_URL="https://api.github.com/repos/macchrome/linchrome/releases/latest" ARG API_URL="https://api.github.com/repos/macchrome/linchrome/releases/latest"

View file

@ -1,14 +1,17 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE 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 # install vivaldi
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
RUN set -eux; apt-get update; \ RUN set -eux; apt-get update; \
ARCH=$(dpkg --print-architecture); \ wget -O /tmp/vivaldi.deb "https://downloads.vivaldi.com/stable/vivaldi-stable_${VIVALDI_VERSION}_amd64.deb"; \
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 /tmp/vivaldi.deb; \
apt-get install -y --no-install-recommends wget unzip xz-utils jq openbox; \ /opt/vivaldi/update-ffmpeg; \
apt install -y --no-install-recommends /tmp/vivaldi.deb; \
# #
# install latest version of uBlock Origin and SponsorBlock for YouTube # install latest version of uBlock Origin and SponsorBlock for YouTube
EXTENSIONS_DIR="/usr/share/chromium/extensions"; \ EXTENSIONS_DIR="/usr/share/chromium/extensions"; \
@ -19,7 +22,7 @@ RUN set -eux; apt-get update; \
mkdir -p "${EXTENSIONS_DIR}"; \ mkdir -p "${EXTENSIONS_DIR}"; \
for EXT_ID in "${EXTENSIONS[@]}"; \ for EXT_ID in "${EXTENSIONS[@]}"; \
do \ 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"; \ EXT_PATH="${EXTENSIONS_DIR}/${EXT_ID}.crx"; \
wget -O "${EXT_PATH}" "${EXT_URL}"; \ wget -O "${EXT_PATH}" "${EXT_URL}"; \
EXT_VERSION="$(unzip -p "${EXT_PATH}" manifest.json 2>/dev/null | jq -r ".version")"; \ EXT_VERSION="$(unzip -p "${EXT_PATH}" manifest.json 2>/dev/null | jq -r ".version")"; \

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
ARG SRC_URL="https://cdn1.waterfox.net/waterfox/releases/latest/linux" 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; \ xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \
# #
# fetch latest release # 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; \ mkdir /usr/lib/waterfox; \
tar -xjf /tmp/waterfox-setup.tar.bz2 -C /usr/lib; \ tar -xjf /tmp/waterfox-setup.tar.bz2 -C /usr/lib; \
rm -f /tmp/waterfox-setup.tar.bz2; \ rm -f /tmp/waterfox-setup.tar.bz2; \

View file

@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true);
lockPref("profile.allow_automigration", false); lockPref("profile.allow_automigration", false);
lockPref("signon.prefillForms", false); lockPref("signon.prefillForms", false);
lockPref("signon.rememberSignons", false); lockPref("signon.rememberSignons", false);
//lockPref("xpinstall.enabled", false); lockPref("xpinstall.enabled", false);
//lockPref("xpinstall.whitelist.required", true); lockPref("xpinstall.whitelist.required", true);
lockPref("browser.download.manager.retention", 0); lockPref("browser.download.manager.retention", 0);
lockPref("browser.download.folderList", 2); lockPref("browser.download.folderList", 2);
lockPref("browser.download.forbid_open_with", true); lockPref("browser.download.forbid_open_with", true);

View file

@ -1,4 +1,4 @@
ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest ARG BASE_IMAGE=m1k1o/neko:base
FROM $BASE_IMAGE FROM $BASE_IMAGE
# #

98
build
View file

@ -2,57 +2,39 @@
set -e set -e
cd "$(dirname "$0")" 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 # 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 if [ -z "$USE_BUILDX" ]; then
USE_BUILDX=0 USE_BUILDX=0
fi fi
# check if docker buildx is available, its not docker-buildx command but rather subcommand of docker # 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 [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then
# if docker buildx version >/dev/null 2>&1; then if docker buildx version >/dev/null 2>&1; then
# USE_BUILDX=1 USE_BUILDX=1
# fi fi
#fi fi
#
# This script builds the neko base image and all the applications
#
function log() { function log() {
echo "$(date +'%Y-%m-%d %H:%M:%S') - [NEKO] - $1" > /dev/stderr echo "$(date +'%Y-%m-%d %H:%M:%S') - [NEKO] - $1" > /dev/stderr
} }
function help() { function help() {
echo "Usage: $0 [options] [image]" echo "Usage: $0"
echo echo " -p, --platform : The platform (default: linux/amd64)"
echo "Options:" echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)"
echo " -p, --platform : The platform (default: system architecture)" echo " -t, --tag : The image tag, can be specified multiple times, if not specified"
echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)" echo " uses 'latest' and if available, current git semver tag (v*.*.*)"
echo " -t, --tag : The image tag, can be specified multiple times, if not specified" echo " -f, --flavor : The flavor, if not specified, builds without flavor"
echo " uses 'latest' and if available, current git semver tag (v*.*.*)" echo " -b, --base : The base image name (default: <repository>[<flavor>-]base:<tag>)"
echo " -f, --flavor : The flavor, if not specified, builds without flavor" echo " -a, --app : The app to build, if not specified, builds the base image"
echo " -b, --base_image : The base image name (default: <repository>[<flavor>-]base:<tag>)" echo " -y, --yes : Skip confirmation prompts"
echo " -a, --application : The app to build, if not specified, builds the base image" echo " --no-cache : Build without docker cache"
echo " -y, --yes : Skip confirmation prompts" echo " --push : Push the image to the registry after building"
echo " --no-cache : Build without docker cache" echo " -h, --help : Show this help message"
echo " --push : Push the image to the registry after building"
echo " -h, --help : Show this help message"
echo
echo "Positional arguments:"
echo " <image> : The image name, if not specified, uses the full image name"
echo " in the format <repository>/<flavor>-<application>:<tag>"
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"
} }
FULL_IMAGE="" FULL_IMAGE=""
@ -62,8 +44,8 @@ while [[ "$#" -gt 0 ]]; do
--repository|-r) REPOSITORY="$2"; shift ;; --repository|-r) REPOSITORY="$2"; shift ;;
--tag|-t) TAGS+=("$2"); TAG="$2"; shift ;; --tag|-t) TAGS+=("$2"); TAG="$2"; shift ;;
--flavor|-f) FLAVOR="$2"; shift ;; --flavor|-f) FLAVOR="$2"; shift ;;
--base_image|-b) BASE_IMAGE="$2"; shift ;; --base|-b) BASE_IMAGE="$2"; shift ;;
--application|-a) APPLICATION="$2"; shift ;; --app|-a) APPLICATION="$2"; shift ;;
--yes|-y) YES=1 ;; --yes|-y) YES=1 ;;
--no-cache) NO_CACHE="--no-cache" log "Building without cache" ;; --no-cache) NO_CACHE="--no-cache" log "Building without cache" ;;
--push) PUSH=1 ;; --push) PUSH=1 ;;
@ -149,12 +131,17 @@ function build_image() {
local APPLICATION_IMAGE="$1" local APPLICATION_IMAGE="$1"
shift shift
# get list of tags in full format: <image>:<tag>
local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}"
local FULL_TAGS=() local FULL_TAGS=()
for T in "${TAGS[@]}"; do # if the image name starts with local/, just use the tag as is
FULL_TAGS+=("$IMAGE_NO_TAG:$T") if [[ "$APPLICATION_IMAGE" == *"local/"* ]]; then
done FULL_TAGS=("$APPLICATION_IMAGE")
else
# get list of tags in full format: <image>:<tag>
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 [ -z "$USE_BUILDX" ] || [ "$USE_BUILDX" != "1" ]; then
# if buildx is not available, use docker build # if buildx is not available, use docker build
@ -210,18 +197,7 @@ else
fi fi
if [ -z "$PLATFORM" ]; then if [ -z "$PLATFORM" ]; then
# use system architecture if not specified PLATFORM="linux/amd64"
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
fi fi
log "Using platform: $PLATFORM" log "Using platform: $PLATFORM"
@ -314,10 +290,10 @@ fi
log "Building base image: $BASE_IMAGE" log "Building base image: $BASE_IMAGE"
docker run --rm -i \ docker run --rm -i \
-v "$(pwd)":/src \ -v ./:/src \
-e "RUNTIME_DOCKERFILE=$RUNTIME_DOCKERFILE" \ -e "RUNTIME_DOCKERFILE=$RUNTIME_DOCKERFILE" \
--workdir /src \ --workdir /src \
--entrypoint go \ --entrypoint go \
golang:1.24-bullseye \ golang:1.24-bullseye \
run utils/docker/main.go \ run ./docker/main.go \
-i Dockerfile.tmpl -client "$CLIENT_DIST" | build_image $BASE_IMAGE -f - . -i Dockerfile.tmpl -client $CLIENT_DIST | build_image $BASE_IMAGE -f - .

View file

@ -119,43 +119,23 @@
} }
} }
@media only screen and (max-width: 1024px) { @media only screen and (max-width: 600px) {
html, #neko.expanded {
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;
.neko-main { .neko-main {
height: 100vh; transform: translateX(calc(-100% + 65px));
video {
display: none;
}
} }
.neko-menu { .neko-menu {
height: 100vh; position: absolute;
width: 100% !important; top: 0;
} right: 0;
} bottom: 0;
} left: 65px;
width: calc(100% - 65px);
@media only screen and (max-width: 1024px) and (orientation: portrait) {
#neko {
&.expanded .neko-main {
height: 40vh;
}
&.expanded .neko-menu {
height: 60vh;
width: 100% !important;
} }
} }
} }
@ -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() { controlAttempt() {
if (this.shakeKbd || this.$accessor.remote.hosted) return if (this.shakeKbd || this.$accessor.remote.hosted) return

View file

@ -24,7 +24,7 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.connect { .connect {
position: fixed; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;

View file

@ -60,9 +60,8 @@
</style> </style>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator' import { Component, Vue } from 'vue-property-decorator'
import { messages } from '~/locale' import { messages } from '~/locale'
import { set } from '~/utils/localstorage'
@Component({ name: 'neko-menu' }) @Component({ name: 'neko-menu' })
export default class extends Vue { export default class extends Vue {
@ -78,11 +77,6 @@
this.$accessor.client.toggleAbout() this.$accessor.client.toggleAbout()
} }
@Watch('$i18n.locale')
onLanguageChange(newLang: string) {
set('lang', newLang)
}
mounted() { mounted() {
const default_lang = new URL(location.href).searchParams.get('lang') const default_lang = new URL(location.href).searchParams.get('lang')
if (default_lang && this.langs.includes(default_lang)) { if (default_lang && this.langs.includes(default_lang)) {

View file

@ -40,12 +40,7 @@
<li v-if="admin"><i @click.stop.prevent="openResolution" class="fas fa-desktop"></i></li> <li v-if="admin"><i @click.stop.prevent="openResolution" class="fas fa-desktop"></i></li>
<li v-if="!controlLocked && !implicitHosting" :class="extraControls || 'extra-control'"> <li v-if="!controlLocked && !implicitHosting" :class="extraControls || 'extra-control'">
<i <i
:class="[ :class="[hosted && !hosting ? 'disabled' : '', !hosted && !hosting ? 'faded' : '', 'fas', 'fa-keyboard']"
hosted && !hosting ? 'disabled' : '',
!hosted && !hosting ? 'faded' : '',
'fas',
'fa-computer-mouse',
]"
@click.stop.prevent="toggleControl" @click.stop.prevent="toggleControl"
/> />
</li> </li>
@ -62,13 +57,6 @@
class="fas fa-external-link-alt" class="fas fa-external-link-alt"
/> />
</li> </li>
<li
v-if="hosting && is_touch_device"
:class="extraControls || 'extra-control'"
@click.stop.prevent="openMobileKeyboard"
>
<i class="fas fa-keyboard" />
</li>
</ul> </ul>
<neko-resolution ref="resolution" v-if="admin" /> <neko-resolution ref="resolution" v-if="admin" />
<neko-clipboard ref="clipboard" v-if="hosting && (!clipboard_read_available || !clipboard_write_available)" /> <neko-clipboard ref="clipboard" v-if="hosting && (!clipboard_read_available || !clipboard_write_available)" />
@ -129,7 +117,7 @@
} }
@media (max-width: 768px) { @media (max-width: 768px) {
&.extra-control { &.extra-control {
display: block; display: inline-block;
} }
} }
@ -263,10 +251,6 @@
return this.$accessor.connecting return this.$accessor.connecting
} }
get controlling() {
return this.$accessor.remote.controlling
}
get hosting() { get hosting() {
return this.$accessor.remote.hosting return this.$accessor.remote.hosting
} }
@ -369,16 +353,6 @@
return this.$accessor.video.horizontal return this.$accessor.video.horizontal
} }
get is_touch_device() {
return (
// detect if the device has touch support
('ontouchstart' in window || navigator.maxTouchPoints > 0) &&
// the primary input mechanism includes a pointing device of
// limited accuracy, such as a finger on a touchscreen.
window.matchMedia('(pointer: coarse)').matches
)
}
@Watch('width') @Watch('width')
onWidthChanged() { onWidthChanged() {
this.onResize() this.onResize()
@ -756,17 +730,12 @@
first.target.dispatchEvent(simulatedEvent) first.target.dispatchEvent(simulatedEvent)
} }
isMouseDown = false
onMouseDown(e: MouseEvent) { onMouseDown(e: MouseEvent) {
this.isMouseDown = true if (!this.hosting) {
this.$emit('control-attempt', e)
if (this.locked) {
return
} }
if (!this.controlling) { if (!this.hosting || this.locked) {
this.implicitHostingRequest(e)
return return
} }
@ -775,16 +744,7 @@
} }
onMouseUp(e: MouseEvent) { onMouseUp(e: MouseEvent) {
// only if we are the one who started the mouse down if (!this.hosting || this.locked) {
if (!this.isMouseDown) return
this.isMouseDown = false
if (this.locked) {
return
}
if (!this.controlling) {
this.implicitHostingRequest(e)
return return
} }
@ -792,40 +752,6 @@
this.$client.sendData('mouseup', { key: e.button + 1 }) this.$client.sendData('mouseup', { key: e.button + 1 })
} }
private reqMouseDown: MouseEvent | null = null
private reqMouseUp: MouseEvent | null = null
@Watch('controlling')
onControlChange(controlling: boolean) {
if (controlling && this.reqMouseDown) {
this.onMouseDown(this.reqMouseDown)
}
if (controlling && this.reqMouseUp) {
this.onMouseUp(this.reqMouseUp)
}
this.reqMouseDown = null
this.reqMouseUp = null
}
implicitHostingRequest(e: MouseEvent) {
if (this.implicitHosting) {
if (e.type === 'mousedown') {
this.reqMouseDown = e
this.reqMouseUp = null
this.$accessor.remote.request()
} else if (e.type === 'mouseup') {
this.reqMouseUp = e
}
return
}
if (e.type === 'mousedown') {
this.$emit('control-attempt', e)
}
}
onMouseMove(e: MouseEvent) { onMouseMove(e: MouseEvent) {
if (!this.hosting || this.locked) { if (!this.hosting || this.locked) {
return return
@ -873,20 +799,10 @@
@Watch('hosting') @Watch('hosting')
@Watch('locked') @Watch('locked')
onFocus() { onFocus() {
// focus opens the keyboard on mobile
if (this.is_touch_device) {
return
}
// in order to capture key events, overlay must be focused // in order to capture key events, overlay must be focused
if (this.focused && this.hosting && !this.locked) { if (this.focused && this.hosting && !this.locked) {
this._overlay.focus() this._overlay.focus()
} }
} }
openMobileKeyboard() {
// focus opens the keyboard on mobile
this._overlay.focus()
}
} }
</script> </script>

View file

@ -15,7 +15,7 @@ export const EVENT = {
ERROR: 'system/error', ERROR: 'system/error',
}, },
CLIENT: { CLIENT: {
HEARTBEAT: 'client/heartbeat', HEARTBEAT: 'client/heartbeat'
}, },
SIGNAL: { SIGNAL: {
OFFER: 'signal/offer', OFFER: 'signal/offer',

View file

@ -1,31 +1,11 @@
import Vue from 'vue' import Vue from 'vue'
import VueI18n from 'vue-i18n' import VueI18n from 'vue-i18n'
import { messages } from '~/locale' import { messages } from '~/locale'
import { get } from '~/utils/localstorage'
Vue.use(VueI18n) 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({ export const i18n = new VueI18n({
locale: get<string>('lang', detectBrowserLanguage()), locale: 'en',
fallbackLocale, fallbackLocale: 'en',
messages, messages,
}) })

View file

@ -18,9 +18,6 @@ export const state = () => ({
}) })
export const getters = getterTree(state, { export const getters = getterTree(state, {
controlling: (state, getters, root) => {
return root.user.id === state.id
},
hosting: (state, getters, root) => { hosting: (state, getters, root) => {
return root.user.id === state.id || state.implicitHosting return root.user.id === state.id || state.implicitHosting
}, },
@ -92,7 +89,7 @@ export const actions = actionTree(
}, },
request({ getters }) { request({ getters }) {
if (!accessor.connected || getters.controlling) { if (!accessor.connected || getters.hosting) {
return return
} }

View file

@ -15,3 +15,6 @@ session:
cookie: cookie:
# needed for legacy API # needed for legacy API
enabled: false enabled: false
webrtc:
icelite: true

View file

@ -1,14 +1,15 @@
version: "3.4"
services: services:
neko: neko:
image: "ghcr.io/m1k1o/neko/firefox:latest" image: "m1k1o/neko:firefox"
restart: "unless-stopped" restart: "unless-stopped"
shm_size: "2gb" shm_size: "2gb"
ports: ports:
- "8080:8080" - "8080:8080"
- "52000-52100:52000-52100/udp" - "52000-52100:52000-52100/udp"
environment: environment:
NEKO_DESKTOP_SCREEN: 1920x1080@30 NEKO_SCREEN: 1920x1080@30
NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko NEKO_PASSWORD: neko
NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin NEKO_PASSWORD_ADMIN: admin
NEKO_WEBRTC_EPR: 52000-52100 NEKO_EPR: 52000-52100
NEKO_WEBRTC_ICELITE: 1 NEKO_ICELITE: 1

3
docker/go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/m1k1o/neko/docker
go 1.24.1

View file

@ -103,9 +103,7 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/
# #
# add healthcheck # add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
exit 1
# #
# run neko # run neko

View file

@ -95,9 +95,8 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/
# #
# add healthcheck # add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
exit 1
# #
# run neko # run neko
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"] CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]

View file

@ -115,9 +115,8 @@ ENV RENDER_GID=
# #
# add healthcheck # add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
exit 1
# #
# run neko # run neko
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"] CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]

View file

@ -1,6 +1,6 @@
ARG UBUNTU_RELEASE=20.04 ARG UBUNTU_RELEASE=20.04
ARG CUDA_VERSION=11.4.3 ARG CUDA_VERSION=11.4.3
ARG VIRTUALGL_VERSION=3.1.3-20250409 ARG VIRTUALGL_VERSION=3.1
ARG GSTREAMER_VERSION=1.20 ARG GSTREAMER_VERSION=1.20
# #
@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE
ARG VIRTUALGL_VERSION ARG VIRTUALGL_VERSION
# Make all NVIDIA GPUs visible by default # 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 # 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 # 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 # 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 }" > /etc/vulkan/icd.d/nvidia_icd.json
# #
# install an up-to-date version of VirtualGL # install VirtualGL and make libraries available for preload
RUN apt-get update; \ RUN set -eux; \
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
apt-get update; \ 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 # clean up
apt-get clean -y; \ 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 # copy runtime configs
@ -261,9 +262,7 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer
# #
# add healthcheck # add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
exit 1
# #
# run neko # run neko

View file

@ -1,6 +1,6 @@
ARG UBUNTU_RELEASE=22.04 ARG UBUNTU_RELEASE=22.04
ARG CUDA_VERSION=12.2.0 ARG CUDA_VERSION=12.2.0
ARG VIRTUALGL_VERSION=3.1.3-20250409 ARG VIRTUALGL_VERSION=3.1
ARG GSTREAMER_VERSION=1.22 ARG GSTREAMER_VERSION=1.22
# #
@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE
ARG VIRTUALGL_VERSION ARG VIRTUALGL_VERSION
# Make all NVIDIA GPUs visible by default # 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 # 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 # 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 # 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 }" > /etc/vulkan/icd.d/nvidia_icd.json
# #
# install an up-to-date version of VirtualGL # install VirtualGL and make libraries available for preload
RUN apt-get update; \ RUN set -eux; \
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
apt-get update; \ 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 # clean up
apt-get clean -y; \ 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 # copy runtime configs
@ -253,9 +254,7 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer
# #
# add healthcheck # add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \ HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
exit 1
# #
# run neko # run neko

View file

@ -33,7 +33,7 @@ redirect_stderr=true
[program:neko] [program:neko]
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s" 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 stopsignal=INT
stopwaitsecs=3 stopwaitsecs=3
autorestart=true autorestart=true

Some files were not shown because too many files have changed in this diff Show more