Merge branch 'master' of github.com:m1k1o/neko into sk_lang

This commit is contained in:
Miroslav Šedivý 2020-04-20 21:05:55 +02:00
commit 1ce276f313
32 changed files with 478 additions and 268 deletions

View File

@ -1,160 +1,4 @@
FROM debian:stretch-slim FROM nurdism/neko:dev
#
# cluster fuck of packages for neko, node, go and gstreamer
RUN set -eux; apt-get update; apt-get install -y --no-install-recommends \
autoconf ca-certificates curl netbase wget \
bzr git mercurial openssh-client subversion procps cmake automake bzip2 dpkg-dev file g++ gcc \
libbz2-dev libc6-dev libcurl4-openssl-dev libdb-dev libevent-dev libffi-dev libgdbm-dev libglib2.0-dev libgmp-dev \
libjpeg-dev libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmaxminddb-dev libncurses5-dev libncursesw5-dev \
libpng-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev \
make patch unzip xz-utils zlib1g-dev pkg-config \
build-essential perl python autopoint bison flex \
gettext openssl libopus-dev libvpx-dev libpulse-dev libx11-dev libxv-dev libxt-dev libxrandr-dev \
libxfixes-dev apt-utils x11vnc libxtst-dev dialog \
pulseaudio openbox chromium firefox-esr dbus-x11 xserver-xorg-video-dummy supervisor; \
if ! command -v gpg > /dev/null; then \
apt-get install -y --no-install-recommends gnupg dirmngr; \
fi
#
# Install libclipboard
RUN set -eux; \
cd /tmp ; \
git clone https://github.com/jtanx/libclipboard ; \
cd libclipboard ; \
cmake . ; \
make -j4; \
make install; \
rm -rf /tmp/libclipboard
#
# Set up env for gst
ENV PATH=/gst/local/bin:$PATH
ENV LD_LIBRARY_PATH=/gst/local/lib:$LD_LIBRARY_PATH
ENV PKG_CONFIG_PATH=/gst/local/lib/pkgconfig:$PKG_CONFIG_PATH
#
# Add node
ENV NODE_VERSION 12.14.1
RUN set -eux; \
ARCH= ; dpkgArch="$(dpkg --print-architecture)" \
;case "${dpkgArch##*-}" in \
amd64) ARCH='x64';; \
ppc64el) ARCH='ppc64le';; \
s390x) ARCH='s390x';; \
arm64) ARCH='arm64';; \
armhf) ARCH='armv7l';; \
i386) ARCH='x86';; \
*) echo "unsupported architecture"; exit 1 ;; \
esac;\
# gpg keys listed at https://github.com/nodejs/node#release-keys
for key in \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
FD3A5288F042B6850C66B31F09FE44734EB7990E \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
B9AE9905FFD7803F25714661B63B535A4C206CA9 \
77984A986EBC2AA786BC0F66B01FBB92821C587A \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
4ED778F539E3634C779C87C6D7062848A1AB005C \
A48C2BEE680E841632CD4E44F07496B3EB3C1762 \
B9E2F5981AA6E0CD28160D9FF13993A75599653C \
; do \
gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \
gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \
gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key"; \
done; \
curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz"; \
curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"; \
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc; \
grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - ; \
tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner; \
rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt; \
ln -s /usr/local/bin/node /usr/local/bin/nodejs
#
# Add golang
ENV GOLANG_VERSION 1.14.1
RUN set -eux; \
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64) goRelArch='linux-amd64'; goRelSha256='2f49eb17ce8b48c680cdb166ffd7389702c0dec6effa090c324804a5cac8a7f8' ;; \
armhf) goRelArch='linux-armv6l'; goRelSha256='04f10e345dae0d7c6c32ffd6356b47f2d4d0e8a0cb757f4ef48ead6c5bef206f' ;; \
arm64) goRelArch='linux-arm64'; goRelSha256='5d8f2c202f35481617e24e63cca30c6afb1ec2585006c4a6ecf16c5f4928ab3c' ;; \
i386) goRelArch='linux-386'; goRelSha256='92d465accdebbe2d0749b2f90c22ecb1fd2492435144923f88ce410cd56b6546' ;; \
ppc64el) goRelArch='linux-ppc64le'; goRelSha256='6559201d452ee2782dfd684d59c05e3ecf789dc40a7ec0ad9ae2dd9f489c0fe1' ;; \
s390x) goRelArch='linux-s390x'; goRelSha256='af009bd6e7729c441fec78af427743fefbf11f919c562e01b37836d835f74226' ;; \
*) goRelArch='src'; goRelSha256='2ad2572115b0d1b4cb4c138e6b3a31cee6294cb48af75ee86bec3dca04507676'; \
echo >&2; echo >&2 "warning: current architecture ($dpkgArch) does not have a corresponding Go binary release; will be building from source"; echo >&2 ;; \
esac; \
url="https://golang.org/dl/go${GOLANG_VERSION}.${goRelArch}.tar.gz"; \
wget -O go.tgz "$url"; \
echo "${goRelSha256} *go.tgz" | sha256sum -c -; \
tar -C /usr/local -xzf go.tgz; \
rm go.tgz; \
if [ "$goRelArch" = 'src' ]; then \
echo >&2; \
echo >&2 'error: UNIMPLEMENTED'; \
echo >&2 'TODO install golang-any from jessie-backports for GOROOT_BOOTSTRAP (and uninstall after build)'; \
echo >&2; \
exit 1; \
fi; \
export PATH="/usr/local/go/bin:$PATH"; \
go version
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
#
# Install Go tools w/module support
RUN mkdir -p /tmp/gotools \
&& cd /tmp/gotools \
&& GOPATH=/tmp/gotools GO111MODULE=on go get -v golang.org/x/tools/gopls@latest 2>&1 \
&& GOPATH=/tmp/gotools GO111MODULE=on go get -v \
honnef.co/go/tools/...@latest \
golang.org/x/tools/cmd/gorename@latest \
golang.org/x/tools/cmd/goimports@latest \
golang.org/x/tools/cmd/guru@latest \
golang.org/x/lint/golint@latest \
github.com/mdempsky/gocode@latest \
github.com/cweill/gotests/...@latest \
github.com/haya14busa/goplay/cmd/goplay@latest \
github.com/sqs/goreturns@latest \
github.com/josharian/impl@latest \
github.com/davidrjenni/reftools/cmd/fillstruct@latest \
github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \
github.com/ramya-rao-a/go-outline@latest \
github.com/acroca/go-symbols@latest \
github.com/godoctor/godoctor@latest \
github.com/rogpeppe/godef@latest \
github.com/zmb3/gogetdoc@latest \
github.com/fatih/gomodifytags@latest \
github.com/mgechev/revive@latest \
github.com/go-delve/delve/cmd/dlv@latest 2>&1 \
#
# Build Go tools w/o module support
&& GOPATH=/tmp/gotools go get -v github.com/alecthomas/gometalinter 2>&1 \
#
# Build gocode-gomod
&& GOPATH=/tmp/gotools go get -x -d github.com/stamblerre/gocode 2>&1 \
&& GOPATH=/tmp/gotools go build -o gocode-gomod github.com/stamblerre/gocode \
#
# Install Go tools
&& mv /tmp/gotools/bin/* /usr/local/bin/ \
&& mv gocode-gomod /usr/local/bin/ \
#
# Install golangci-lint
&& curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin 2>&1 \
#
# Setup paths
&& mkdir -p "$GOPATH/src" "$GOPATH/bin" "$GOPATH/pkg/mod" \
&& chmod -R 777 "$GOPATH"
#
# Turn on go modules
ENV GO111MODULE=on
# Use the "remoteUser" property in devcontainer.json to use it. On Linux, the container # Use the "remoteUser" property in devcontainer.json to use it. On Linux, the container
# user's GID/UIDs will be updated to match your local UID/GID (when using the dockerFile property). # user's GID/UIDs will be updated to match your local UID/GID (when using the dockerFile property).

View File

@ -7,7 +7,8 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/.." DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/.."
build_gst() { build_gst() {
if [ ! -d /gst ]; then if [ ! -L /gst || ! -d /gst ]; then
sudo rm -rf /gst;
sudo mkdir -p /workspace/.build/gst sudo mkdir -p /workspace/.build/gst
sudo ln -s /workspace/.build/gst /gst sudo ln -s /workspace/.build/gst /gst
sudo chown -R neko /workspace/.build sudo chown -R neko /workspace/.build
@ -39,23 +40,33 @@ build_gst() {
done' done'
} }
build_dev() {
set -eux; \
sudo docker build -f files/dev/Dockerfile -t nurdism/neko:dev $DIR ; \
sudo docker push nurdism/neko:dev;
}
build_deps() {
set -eux; \
sudo docker build -f files/deps/Dockerfile -t nurdism/neko:deps $DIR ; \
sudo docker push nurdism/neko:deps;
}
build_base() { build_base() {
set -eux; \ set -eux; \
cd $DIR/server; go get; ./build; \ cd $DIR/server; go get -v -t -d . ; ./build; \
cd $DIR/client; npm install; npm run build; \ cd $DIR/client; npm install; npm run build; \
cd $DIR; sudo docker build -f Dockerfile -t nurdism/neko:base . ; cd $DIR; sudo docker build -f .docker/files/base/Dockerfile -t nurdism/neko:base $DIR ;
} }
build_firefox() { build_firefox() {
set -eux; \ set -eux; \
cd $DIR/.docker/files/firefox; \ sudo docker build -f files/firefox/Dockerfile -t nurdism/neko:firefox -t nurdism/neko:latest $DIR ;
sudo docker build -f Dockerfile -t nurdism/neko:firefox -t nurdism/neko:latest . ;
} }
build_chromium() { build_chromium() {
set -eux; \ set -eux; \
cd $DIR/.docker/files/chromium; \ sudo docker build -f files/chromium/Dockerfile -t nurdism/neko:chromium $DIR ;
sudo docker build -f Dockerfile -t nurdism/neko:chromium . ;
} }
build_docker() { build_docker() {
@ -82,6 +93,9 @@ set -ex
case $1 in case $1 in
push) build_push ;; push) build_push ;;
docker) build_docker ;; docker) build_docker ;;
base) build_base ;;
deps) build_deps ;;
dev) build_dev ;;
gst) build_gst ;; gst) build_gst ;;
*) build_docker ;; *) build_docker ;;
esac esac

View File

@ -1,4 +1,4 @@
FROM debian:stretch-slim FROM nurdism/neko:deps
# #
# avoid warnings by switching to noninteractive # avoid warnings by switching to noninteractive
@ -11,8 +11,7 @@ ARG USER_GID=$USER_UID
# #
# install neko dependencies # install neko dependencies
RUN set -eux; apt-get update; \ RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends wget ca-certificates pulseaudio openbox dbus-x11 xserver-xorg-video-dummy supervisor; \ apt-get install -y --no-install-recommends wget ca-certificates; \
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx4; \
# #
# create a non-root user # create a non-root user
groupadd --gid $USER_GID $USERNAME; \ groupadd --gid $USER_GID $USERNAME; \
@ -36,41 +35,28 @@ RUN set -eux; apt-get update; \
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/*
#
# add gst to env
ENV PATH=/gst/local/bin:$PATH
ENV LD_LIBRARY_PATH=/gst/local/lib:$LD_LIBRARY_PATH
ENV PKG_CONFIG_PATH=/gst/local/lib/pkgconfig:$PKG_CONFIG_PATH
#
# copy gst
COPY .build/gst/local /gst/local/
# #
# env # env
ENV USER=$USERNAME ENV USER=$USERNAME
ENV DISPLAY=:99.0 ENV DISPLAY=:99.0
#
# copy configuation files
COPY .docker/files/dbus /usr/bin/dbus
COPY .docker/files/openbox.xml /etc/neko/openbox.xml
COPY .docker/files/neko/supervisord.conf /etc/neko/supervisord/neko.conf
COPY .docker/files/supervisord.conf /etc/neko/supervisord.conf
COPY .docker/files/xorg.conf /etc/neko/xorg.conf
COPY .docker/files/default.pa /etc/pulse/default.pa
#
# neko files
COPY client/dist/ /var/www
COPY server/bin/neko /usr/bin/neko
#
# neko env
ENV NEKO_PASSWORD=neko ENV NEKO_PASSWORD=neko
ENV NEKO_PASSWORD_ADMIN=admin ENV NEKO_PASSWORD_ADMIN=admin
ENV NEKO_BIND=:8080 ENV NEKO_BIND=:8080
#
# neko config
COPY .docker/files/dbus /usr/bin/dbus
COPY .docker/files/openbox.xml /etc/neko/openbox.xml
COPY .docker/files/supervisord.conf /etc/neko/supervisord.conf
COPY .docker/files/xorg.conf /etc/neko/xorg.conf
COPY .docker/files/default.pa /etc/pulse/default.pa
COPY .docker/files/neko/supervisord.conf /etc/neko/supervisord/neko.conf
#
# neko dist
COPY client/dist/ /var/www
COPY server/bin/neko /usr/bin/neko
# #
# run neko # run neko
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"] CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]

View File

@ -19,6 +19,6 @@ RUN set -eux; apt-get update; \
# #
# copy configuation files # copy configuation files
COPY supervisord.conf /etc/neko/supervisord/chromium.conf COPY .docker/files/chromium/supervisord.conf /etc/neko/supervisord/chromium.conf
COPY preferences.json /usr/share/chromium/master_preferences COPY .docker/files/chromium/preferences.json /usr/share/chromium/master_preferences
COPY policies.json /etc/chromium/policies/managed/policies.json COPY .docker/files/chromium/policies.json /etc/chromium/policies/managed/policies.json

View File

@ -0,0 +1,21 @@
FROM debian:stretch-slim
#
# install neko dependencies
RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends pulseaudio openbox dbus-x11 xserver-xorg-video-dummy supervisor; \
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx4; \
#
# clean up
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
#
# add gst to env
ENV PATH=/gst/local/bin:$PATH
ENV LD_LIBRARY_PATH=/gst/local/lib:$LD_LIBRARY_PATH
ENV PKG_CONFIG_PATH=/gst/local/lib/pkgconfig:$PKG_CONFIG_PATH
#
# copy gst
COPY .build/gst/local /gst/local/

View File

@ -0,0 +1,191 @@
FROM debian:stretch-slim
#
# cluster fuck of packages for neko, node, go and gstreamer
RUN set -eux; apt-get update; apt-get install -y --no-install-recommends \
apt-transport-https gnupg-agent software-properties-common lsb-release \
autoconf ca-certificates curl netbase wget gnupg dirmngr libatomic1 \
libghc-zlib-dev libexpat1-dev \
bzr mercurial openssh-client subversion procps cmake automake bzip2 dpkg-dev file g++ gcc \
libbz2-dev libc6-dev libcurl4-openssl-dev libdb-dev libevent-dev libffi-dev libgdbm-dev libglib2.0-dev libgmp-dev \
libjpeg-dev libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmaxminddb-dev libncurses5-dev libncursesw5-dev \
libpng-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev \
make patch unzip xz-utils zlib1g-dev pkg-config \
build-essential perl python autopoint bison flex \
gettext openssl libopus-dev libvpx-dev libpulse-dev libx11-dev libxv-dev libxt-dev libxrandr-dev \
libxfixes-dev apt-utils x11vnc libxtst-dev dialog \
pulseaudio openbox chromium firefox-esr dbus-x11 xserver-xorg-video-dummy supervisor;
#
# add git
ENV GIT_VERSION 2.21.0
RUN set -eux; \
cd /tmp/; \
wget https://github.com/git/git/archive/v$GIT_VERSION.zip -O latestgit.zip; \
unzip latestgit.zip; \
cd git-$GIT_VERSION; \
make prefix=/usr/local all; \
make prefix=/usr/local install; \
rm -rf /tmp/latestgit.zip /tmp/git-$GIT_VERSION;
#
# install libclipboard
RUN set -eux; \
cd /tmp; \
git clone https://github.com/jtanx/libclipboard; \
cd libclipboard; \
cmake . ; \
make -j4; \
make install; \
rm -rf /tmp/libclipboard;
#
# set up env for gst
ENV PATH=/gst/local/bin:$PATH
ENV LD_LIBRARY_PATH=/gst/local/lib:$LD_LIBRARY_PATH
ENV PKG_CONFIG_PATH=/gst/local/lib/pkgconfig:$PKG_CONFIG_PATH
#
# copy gst
COPY .build/gst/local /gst/local/
#
# add node
ENV NODE_VERSION 12.16.2
RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \
&& case "${dpkgArch##*-}" in \
amd64) ARCH='x64';; \
ppc64el) ARCH='ppc64le';; \
s390x) ARCH='s390x';; \
arm64) ARCH='arm64';; \
armhf) ARCH='armv7l';; \
i386) ARCH='x86';; \
*) echo "unsupported architecture"; exit 1 ;; \
esac \
&& set -ex \
&& for key in \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
FD3A5288F042B6850C66B31F09FE44734EB7990E \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
B9AE9905FFD7803F25714661B63B535A4C206CA9 \
77984A986EBC2AA786BC0F66B01FBB92821C587A \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
4ED778F539E3634C779C87C6D7062848A1AB005C \
A48C2BEE680E841632CD4E44F07496B3EB3C1762 \
B9E2F5981AA6E0CD28160D9FF13993A75599653C \
; do \
gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \
gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \
gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \
done \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& apt-mark auto '.*' > /dev/null \
&& find /usr/local -type f -executable -exec ldd '{}' ';' \
| awk '/=>/ { print $(NF-1) }' \
| sort -u \
| xargs -r dpkg-query --search \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
# smoke tests
&& node --version \
&& npm --version
#
# add golang
ENV GOLANG_VERSION 1.14.2
RUN set -eux; \
dpkgArch="$(dpkg --print-architecture)"; \
case "${dpkgArch##*-}" in \
amd64) goRelArch='linux-amd64'; goRelSha256='6272d6e940ecb71ea5636ddb5fab3933e087c1356173c61f4a803895e947ebb3' ;; \
armhf) goRelArch='linux-armv6l'; goRelSha256='eb4550ba741506c2a4057ea4d3a5ad7ed5a887de67c7232f1e4795464361c83c' ;; \
arm64) goRelArch='linux-arm64'; goRelSha256='bb6d22fe5806352c3d0826676654e09b6e41eb1af52e8d506d3fa85adf7f8d88' ;; \
i386) goRelArch='linux-386'; goRelSha256='cab5f51e6ffb616c6ee963c3d0650ca4e3c4108307c44f2baf233fcb8ff098f6' ;; \
ppc64el) goRelArch='linux-ppc64le'; goRelSha256='48c22268c81ced9084a43bbe2c1596d3e636b5560b30a32434a7f15e561de160' ;; \
s390x) goRelArch='linux-s390x'; goRelSha256='501cc919648c9d85b901963303c5061ea6814c80f0d35fda9e62980d3ff58cf4' ;; \
*) goRelArch='src'; goRelSha256='98de84e69726a66da7b4e58eac41b99cbe274d7e8906eeb8a5b7eb0aadee7f7c'; \
echo >&2; echo >&2 "warning: current architecture ($dpkgArch) does not have a corresponding Go binary release; will be building from source"; echo >&2 ;; \
esac; \
url="https://golang.org/dl/go${GOLANG_VERSION}.${goRelArch}.tar.gz"; \
wget -O go.tgz "$url"; \
echo "${goRelSha256} *go.tgz" | sha256sum -c -; \
tar -C /usr/local -xzf go.tgz; \
rm go.tgz; \
if [ "$goRelArch" = 'src' ]; then \
echo >&2; \
echo >&2 'error: UNIMPLEMENTED'; \
echo >&2 'TODO install golang-any from jessie-backports for GOROOT_BOOTSTRAP (and uninstall after build)'; \
echo >&2; \
exit 1; \
fi; \
export PATH="/usr/local/go/bin:$PATH"; \
go version
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
#
# install Go tools w/module support
RUN mkdir -p /tmp/gotools \
&& cd /tmp/gotools \
&& GOPATH=/tmp/gotools GO111MODULE=on go get -v golang.org/x/tools/gopls@latest 2>&1 \
&& GOPATH=/tmp/gotools GO111MODULE=on go get -v \
honnef.co/go/tools/...@latest \
golang.org/x/tools/cmd/gorename@latest \
golang.org/x/tools/cmd/goimports@latest \
golang.org/x/tools/cmd/guru@latest \
golang.org/x/lint/golint@latest \
github.com/mdempsky/gocode@latest \
github.com/cweill/gotests/...@latest \
github.com/haya14busa/goplay/cmd/goplay@latest \
github.com/sqs/goreturns@latest \
github.com/josharian/impl@latest \
github.com/davidrjenni/reftools/cmd/fillstruct@latest \
github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \
github.com/ramya-rao-a/go-outline@latest \
github.com/acroca/go-symbols@latest \
github.com/godoctor/godoctor@latest \
github.com/rogpeppe/godef@latest \
github.com/zmb3/gogetdoc@latest \
github.com/fatih/gomodifytags@latest \
github.com/mgechev/revive@latest \
github.com/go-delve/delve/cmd/dlv@latest 2>&1 \
#
# build Go tools w/o module support
&& GOPATH=/tmp/gotools go get -v github.com/alecthomas/gometalinter 2>&1 \
#
# build gocode-gomod
&& GOPATH=/tmp/gotools go get -x -d github.com/stamblerre/gocode 2>&1 \
&& GOPATH=/tmp/gotools go build -o gocode-gomod github.com/stamblerre/gocode \
#
# install Go tools
&& mv /tmp/gotools/bin/* /usr/local/bin/ \
&& mv gocode-gomod /usr/local/bin/ \
#
# install golangci-lint
&& curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin 2>&1 \
#
# setup paths
&& mkdir -p "$GOPATH/src" "$GOPATH/bin" "$GOPATH/pkg/mod" \
&& chmod -R 777 "$GOPATH"
#
# turn on go modules
ENV GO111MODULE=on
#
# install docker
RUN set -eux; \
curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | (OUT=$(apt-key add - 2>&1) || echo $OUT); \
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable"; \
apt-get update; apt-get install -y docker-ce-cli;

View File

@ -16,7 +16,7 @@ RUN set -eux; apt-get update; \
# #
# copy configuation files # copy configuation files
COPY supervisord.conf /etc/neko/supervisord/firefox.conf COPY .docker/files/firefox/supervisord.conf /etc/neko/supervisord/firefox.conf
COPY neko.js /usr/lib/firefox-esr/mozilla.cfg COPY .docker/files/firefox/neko.js /usr/lib/firefox-esr/mozilla.cfg
COPY autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js COPY .docker/files/firefox/autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js
COPY policies.json /usr/lib/firefox-esr/distribution/policies.json COPY .docker/files/firefox/policies.json /usr/lib/firefox-esr/distribution/policies.json

0
.examples/README.md Normal file
View File

View File

View File

@ -0,0 +1,16 @@
version: "2.0"
services:
neko:
image: nurdism/neko:firefox
restart: always
shm_size: "1gb"
ports:
- "80:8080"
- "59000-59100:59000-59100/udp"
environment:
DISPLAY: :99.0
NEKO_PASSWORD: neko
NEKO_PASSWORD_ADMIN: admin
NEKO_BIND: :8080
NEKO_EPR: 59000-59100
NEKO_NAT1TO1: 192.168.0.X

View File

32
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: build
on:
pull_request:
branches: [ master ]
paths:
- 'client/**'
- 'server/**'
jobs:
build:
name: Build
runs-on: ubuntu-latest
container: nurdism/neko:dev
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Fetch unshallow
run: git fetch --prune --tags --unshallow
- name: Get go dependencies and build server
working-directory: server
run: |
go get -v -t -d .
./build
- name: Get npm dependencies and build Client
working-directory: client
run: |
npm install
npm run build

44
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: deploy
on:
push:
branches: [ master ]
paths:
- 'client/**'
- 'server/**'
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
container: nurdism/neko:dev
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Fetch unshallow
run: git fetch --prune --tags --unshallow
- name: Get go dependencies and build server
working-directory: server
run: |
go get -v -t -d .
./build
- name: Get npm dependencies and build Client
working-directory: client
run: |
npm install
npm run build
- name: Build the base image
run: docker build -f ./.docker/files/base/Dockerfile -t nurdism/neko:base .
- name: Build the latest image
run: docker build -f ./.docker/files/firefox/Dockerfile -t nurdism/neko:latest .
- name: Push the Docker images
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login ${{ secrets.DOCKER_REGISTRY_URL }} -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push nurdism/neko:base
docker push nurdism/neko:latest

View File

@ -6,6 +6,7 @@
<img src="https://img.shields.io/docker/pulls/nurdism/neko" alt="pulls"> <img src="https://img.shields.io/docker/pulls/nurdism/neko" alt="pulls">
<img src="https://img.shields.io/github/issues/nurdism/neko" alt="issues"> <img src="https://img.shields.io/github/issues/nurdism/neko" alt="issues">
<a href="https://discord.gg/3U6hWpC" ><img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord"><a/> <a href="https://discord.gg/3U6hWpC" ><img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord"><a/>
<a href="https://github.com/nurdism/neko/actions" ><img src="https://github.com/nurdism/neko/workflows/deploy/badge.svg" alt="build"><a/>
</p> </p>
<br/> <br/>
<br/> <br/>

View File

@ -36,7 +36,7 @@
</li> </li>
</template> </template>
<template v-if="admin"> <template v-if="admin && !child.data.member.admin">
<li class="seperator" /> <li class="seperator" />
<li> <li>
<span @click="kick(child.data.member)" style="color: #f04747;">{{ $t('context.kick') }}</span> <span @click="kick(child.data.member)" style="color: #f04747;">{{ $t('context.kick') }}</span>
@ -165,12 +165,12 @@
kick(member: Member) { kick(member: Member) {
this.$swal({ this.$swal({
title: this.$t('context.confirm.kick_title', { name: member.displayname }), title: this.$t('context.confirm.kick_title', { name: member.displayname }) as string,
text: this.$t('context.confirm.kick_text', { name: member.displayname }), text: this.$t('context.confirm.kick_text', { name: member.displayname }) as string,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: this.$t('context.confirm.button_yes'), confirmButtonText: this.$t('context.confirm.button_yes') as string,
cancelButtonText: this.$t('context.confirm.button_cancel'), cancelButtonText: this.$t('context.confirm.button_cancel') as string,
}).then(({ value }) => { }).then(({ value }) => {
if (value) { if (value) {
this.$accessor.user.kick(member) this.$accessor.user.kick(member)
@ -180,12 +180,12 @@
ban(member: Member) { ban(member: Member) {
this.$swal({ this.$swal({
title: this.$t('context.confirm.ban_title', { name: member.displayname }), title: this.$t('context.confirm.ban_title', { name: member.displayname }) as string,
text: this.$t('context.confirm.ban_text', { name: member.displayname }), text: this.$t('context.confirm.ban_text', { name: member.displayname }) as string,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: this.$t('context.confirm.button_yes'), confirmButtonText: this.$t('context.confirm.button_yes') as string,
cancelButtonText: this.$t('context.confirm.button_cancel'), cancelButtonText: this.$t('context.confirm.button_cancel') as string,
}).then(({ value }) => { }).then(({ value }) => {
if (value) { if (value) {
this.$accessor.user.ban(member) this.$accessor.user.ban(member)
@ -195,12 +195,12 @@
mute(member: Member) { mute(member: Member) {
this.$swal({ this.$swal({
title: this.$t('context.confirm.mute_title', { name: member.displayname }), title: this.$t('context.confirm.mute_title', { name: member.displayname }) as string,
text: this.$t('context.confirm.mute_text', { name: member.displayname }), text: this.$t('context.confirm.mute_text', { name: member.displayname }) as string,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: this.$t('context.confirm.button_yes'), confirmButtonText: this.$t('context.confirm.button_yes') as string,
cancelButtonText: this.$t('context.confirm.button_cancel'), cancelButtonText: this.$t('context.confirm.button_cancel') as string,
}).then(({ value }) => { }).then(({ value }) => {
if (value) { if (value) {
this.$accessor.user.mute(member) this.$accessor.user.mute(member)
@ -210,12 +210,12 @@
unmute(member: Member) { unmute(member: Member) {
this.$swal({ this.$swal({
title: this.$t('context.confirm.unmute_title', { name: member.displayname }), title: this.$t('context.confirm.unmute_title', { name: member.displayname }) as string,
text: this.$t('context.confirm.unmute_text', { name: member.displayname }), text: this.$t('context.confirm.unmute_text', { name: member.displayname }) as string,
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: this.$t('context.confirm.button_yes'), confirmButtonText: this.$t('context.confirm.button_yes') as string,
cancelButtonText: this.$t('context.confirm.button_cancel'), cancelButtonText: this.$t('context.confirm.button_cancel') as string,
}).then(({ value }) => { }).then(({ value }) => {
if (value) { if (value) {
this.$accessor.user.unmute(member) this.$accessor.user.unmute(member)

View File

@ -4,11 +4,11 @@
<ul> <ul>
<li :class="{ active: tab === 'chat' }" @click.stop.prevent="change('chat')"> <li :class="{ active: tab === 'chat' }" @click.stop.prevent="change('chat')">
<i class="fas fa-comment-alt" /> <i class="fas fa-comment-alt" />
<span>{{ $t('chat') }}</span> <span>{{ $t('side.chat') }}</span>
</li> </li>
<li :class="{ active: tab === 'settings' }" @click.stop.prevent="change('settings')"> <li :class="{ active: tab === 'settings' }" @click.stop.prevent="change('settings')">
<i class="fas fa-sliders-h" /> <i class="fas fa-sliders-h" />
<span>{{ $t('settings') }}</span> <span>{{ $t('side.settings') }}</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -161,6 +161,7 @@
private observer = new ResizeObserver(this.onResise.bind(this)) private observer = new ResizeObserver(this.onResise.bind(this))
private focused = false private focused = false
private fullscreen = false private fullscreen = false
private activeKeys: Set<number> = new Set()
get admin() { get admin() {
return this.$accessor.user.admin return this.$accessor.user.admin
@ -333,12 +334,14 @@
}) })
document.addEventListener('focusin', this.onFocus.bind(this)) document.addEventListener('focusin', this.onFocus.bind(this))
document.addEventListener('focusout', this.onBlur.bind(this))
} }
beforeDestroy() { beforeDestroy() {
this.observer.disconnect() this.observer.disconnect()
this.$accessor.video.setPlayable(false) this.$accessor.video.setPlayable(false)
document.removeEventListener('focusin', this.onFocus.bind(this)) document.removeEventListener('focusin', this.onFocus.bind(this))
document.removeEventListener('focusout', this.onBlur.bind(this))
} }
play() { play() {
@ -401,6 +404,17 @@
} }
} }
onBlur() {
if (!this.focused || !this.hosting || this.locked) {
return
}
for (let key of this.activeKeys) {
this.$client.sendData('keyup', { key })
this.activeKeys.delete(key)
}
}
onMousePos(e: MouseEvent) { onMousePos(e: MouseEvent) {
const { w, h } = this.$accessor.video.resolution const { w, h } = this.$accessor.video.resolution
const rect = this._overlay.getBoundingClientRect() const rect = this._overlay.getBoundingClientRect()
@ -466,15 +480,15 @@
// frick you firefox // frick you firefox
getCode(e: KeyboardEvent): number { getCode(e: KeyboardEvent): number {
let key = e.keyCode let key = e.keyCode
if (key === 59 && e.key === ';') { if (key === 59 && (e.key === ';' || e.key === ':')) {
key = 186 key = 186
} }
if (key === 61 && e.key === '=') { if (key === 61 && (e.key === '=' || e.key === '+')) {
key = 187 key = 187
} }
if (key === 173 && e.key === '-') { if (key === 173 && (e.key === '-' || e.key === '_')) {
key = 189 key = 189
} }
@ -486,7 +500,9 @@
return return
} }
this.$client.sendData('keydown', { key: this.getCode(e) }) let key = this.getCode(e)
this.$client.sendData('keydown', { key })
this.activeKeys.add(key)
} }
onKeyUp(e: KeyboardEvent) { onKeyUp(e: KeyboardEvent) {
@ -494,7 +510,9 @@
return return
} }
this.$client.sendData('keyup', { key: this.getCode(e) }) let key = this.getCode(e)
this.$client.sendData('keyup', { key })
this.activeKeys.delete(key)
} }
onResise() { onResise() {

View File

@ -1,13 +1,13 @@
export const chat = 'Chat'
export const settings = 'Settings'
export const logout = 'logout' export const logout = 'logout'
export const unsupported = 'this browser does not support webrtc' export const unsupported = 'this browser does not support webrtc'
export const admin_loggedin = 'You are logged in as an admin' export const admin_loggedin = 'You are logged in as an admin'
export const you = 'You' export const you = 'You'
export const ok = 'ok'
export const send_a_message = 'Send a message' export const send_a_message = 'Send a message'
export const connected = 'connected'
export const disconnected = 'disconnected' export const side = {
chat: 'Chat',
settings: 'Settings',
}
export const connect = { export const connect = {
title: 'Please Login', title: 'Please Login',
@ -37,7 +37,7 @@ export const context = {
unmute_text: 'Are you sure you want to unmute {name}?', unmute_text: 'Are you sure you want to unmute {name}?',
button_yes: 'Yes', button_yes: 'Yes',
button_cancel: 'Cancel', button_cancel: 'Cancel',
} },
} }
export const controls = { export const controls = {
@ -63,11 +63,15 @@ export const setting = {
} }
export const connection = { export const connection = {
success: 'Successfully connected', logged_out: 'You have been logged out!',
connected: 'Successfully connected',
disconnected: 'You have been disconnected',
button_confirm: 'Ok',
} }
export const notifications = { export const notifications = {
logged_out: '{name} logged out!', connected: '{name} connected',
disconnected: '{name} disconnected',
controls_taken: '{name} took the controls', controls_taken: '{name} took the controls',
controls_taken_force: 'force took the controls', controls_taken_force: 'force took the controls',
controls_taken_steal: 'took the controls from {name}', controls_taken_steal: 'took the controls from {name}',

View File

@ -54,9 +54,9 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
this.disconnect() this.disconnect()
this.cleanup() this.cleanup()
this.$vue.$swal({ this.$vue.$swal({
title: this.$vue.$t('notifications.logged_out', { name: this.$vue.$t('you') }), title: this.$vue.$t('connection.logged_out'),
icon: 'info', icon: 'info',
confirmButtonText: this.$vue.$t('ok') as string, confirmButtonText: this.$vue.$t('connection.button_confirm') as string,
}) })
} }
@ -75,7 +75,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
this.$vue.$notify({ this.$vue.$notify({
group: 'neko', group: 'neko',
type: 'success', type: 'success',
title: this.$vue.$t('connection.success') as string, title: this.$vue.$t('connection.connected') as string,
duration: 5000, duration: 5000,
speed: 1000, speed: 1000,
}) })
@ -86,7 +86,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
this.$vue.$notify({ this.$vue.$notify({
group: 'neko', group: 'neko',
type: 'error', type: 'error',
title: this.$vue.$t('disconnected') as string, title: this.$vue.$t('connection.disconnected') as string,
text: reason ? reason.message : undefined, text: reason ? reason.message : undefined,
duration: 5000, duration: 5000,
speed: 1000, speed: 1000,
@ -111,10 +111,10 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
protected [EVENT.SYSTEM.DISCONNECT]({ message }: DisconnectPayload) { protected [EVENT.SYSTEM.DISCONNECT]({ message }: DisconnectPayload) {
this.onDisconnected(new Error(message)) this.onDisconnected(new Error(message))
this.$vue.$swal({ this.$vue.$swal({
title: this.$vue.$t('disconnected'), title: this.$vue.$t('connection.disconnected'),
text: message, text: message,
icon: 'error', icon: 'error',
confirmButtonText: this.$vue.$t('ok') as string, confirmButtonText: this.$vue.$t('connection.button_confirm') as string,
}) })
} }
@ -125,7 +125,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
this.$accessor.user.setMembers(members) this.$accessor.user.setMembers(members)
this.$accessor.chat.newMessage({ this.$accessor.chat.newMessage({
id: this.id, id: this.id,
content: this.$vue.$t('connected') as string, content: this.$vue.$t('notifications.connected', { name: '' }) as string,
type: 'event', type: 'event',
created: new Date(), created: new Date(),
}) })
@ -137,7 +137,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
if (member.id !== this.id) { if (member.id !== this.id) {
this.$accessor.chat.newMessage({ this.$accessor.chat.newMessage({
id: member.id, id: member.id,
content: this.$vue.$t('connected') as string, content: this.$vue.$t('notifications.connected', { name: '' }) as string,
type: 'event', type: 'event',
created: new Date(), created: new Date(),
}) })
@ -152,7 +152,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
this.$accessor.chat.newMessage({ this.$accessor.chat.newMessage({
id: member.id, id: member.id,
content: this.$vue.$t('disconnected') as string, content: this.$vue.$t('notifications.disconnected', { name: '' }) as string,
type: 'event', type: 'event',
created: new Date(), created: new Date(),
}) })

View File

@ -25,7 +25,7 @@
```shell ```shell
sudo ufw allow 80/tcp # if you have ufw installed/enabled sudo ufw allow 80/tcp # if you have ufw installed/enabled
sudo ufw allow 59000:59100/udp sudo ufw allow 59000:59100/udp
wget https://raw.githubusercontent.com/nurdism/neko/master/docker-compose.yaml wget https://raw.githubusercontent.com/nurdism/neko/master/.examples/simple/docker-compose.yaml
sudo docker-compose up -d sudo docker-compose up -d
``` ```
5. Visit the IP address server in your browser and login, the default password is `neko` 5. Visit the IP address server in your browser and login, the default password is `neko`

View File

@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
set -ex
BUILD_TIME=`date -u +'%Y-%m-%dT%H:%M:%SZ'` BUILD_TIME=`date -u +'%Y-%m-%dT%H:%M:%SZ'`
GIT_COMMIT=`git rev-parse --short HEAD` GIT_COMMIT=`git rev-parse --short HEAD`
GIT_BRANCH=`git rev-parse --symbolic-full-name --abbrev-ref HEAD` GIT_BRANCH=`git rev-parse --symbolic-full-name --abbrev-ref HEAD`

View File

@ -2,6 +2,7 @@ package broadcast
import ( import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"n.eko.moe/neko/internal/gst" "n.eko.moe/neko/internal/gst"
"n.eko.moe/neko/internal/types/config" "n.eko.moe/neko/internal/types/config"
) )
@ -14,8 +15,8 @@ type BroadcastManager struct {
func New(config *config.Broadcast) *BroadcastManager { func New(config *config.Broadcast) *BroadcastManager {
return &BroadcastManager{ return &BroadcastManager{
logger: log.With().Str("module", "remote").Logger(), logger: log.With().Str("module", "remote").Logger(),
config: config config: config,
} }
} }
@ -28,13 +29,15 @@ func (manager *BroadcastManager) Start() {
) )
if err != nil { if err != nil {
manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline") manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline")
} }
manager.pipeline.Start() manager.pipeline.Start()
} }
func (manager *BroadcastManager) Shutdown() error { func (manager *BroadcastManager) Shutdown() error {
if (manager.pipeline != nil) { if manager.pipeline != nil {
manager.pipeline.Stop() manager.pipeline.Stop()
} }
return nil
} }

View File

@ -68,7 +68,7 @@ func init() {
func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineRTMP string) (*Pipeline, error) { func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineRTMP string) (*Pipeline, error) {
video := fmt.Sprintf(videoSrc, pipelineDisplay) video := fmt.Sprintf(videoSrc, pipelineDisplay)
audio := fmt.Sprintf(audioSrc, pipelineDevice) audio := fmt.Sprintf(audioSrc, pipelineDevice)
return CreatePipeline(fmt.Sprintf("%s ! x264enc ! flv. ! %s ! faac ! flv. ! flvmux name='flv' ! rtmpsink location='%s'", video, audio, pipelineRTMP)) return CreatePipeline(fmt.Sprintf("%s ! x264enc ! flv. ! %s ! faac ! flv. ! flvmux name='flv' ! rtmpsink location='%s'", video, audio, pipelineRTMP), "", 0)
} }
// CreateAppPipeline creates a GStreamer Pipeline // CreateAppPipeline creates a GStreamer Pipeline
@ -199,11 +199,11 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri
return nil, fmt.Errorf("unknown codec %s", codecName) return nil, fmt.Errorf("unknown codec %s", codecName)
} }
return CreatePipeline(pipelineStr) return CreatePipeline(pipelineStr, codecName, clockRate)
} }
// CreatePipeline creates a GStreamer Pipeline // CreatePipeline creates a GStreamer Pipeline
func CreatePipeline(pipelineStr string) (*Pipeline, error) { func CreatePipeline(pipelineStr string, codecName string, clockRate float32) (*Pipeline, error) {
pipelineStrUnsafe := C.CString(pipelineStr) pipelineStrUnsafe := C.CString(pipelineStr)
defer C.free(unsafe.Pointer(pipelineStrUnsafe)) defer C.free(unsafe.Pointer(pipelineStrUnsafe))

View File

@ -88,6 +88,21 @@ func (manager *SessionManager) Get(id string) (types.Session, bool) {
return session, ok return session, ok
} }
func (manager *SessionManager) Admins() []*types.Member {
members := []*types.Member{}
for _, session := range manager.members {
if !session.connected || !session.admin {
continue
}
member := session.Member()
if member != nil {
members = append(members, member)
}
}
return members
}
func (manager *SessionManager) Members() []*types.Member { func (manager *SessionManager) Members() []*types.Member {
members := []*types.Member{} members := []*types.Member{}
for _, session := range manager.members { for _, session := range manager.members {
@ -113,7 +128,7 @@ func (manager *SessionManager) Destroy(id string) error {
manager.remote.StopStream() manager.remote.StopStream()
} }
manager.emmiter.Emit("destroyed", id) manager.emmiter.Emit("destroyed", id, session)
return err return err
} }
@ -155,9 +170,9 @@ func (manager *SessionManager) OnHostCleared(listener func(id string)) {
}) })
} }
func (manager *SessionManager) OnDestroy(listener func(id string)) { func (manager *SessionManager) OnDestroy(listener func(id string, session types.Session)) {
manager.emmiter.On("destroyed", func(payload ...interface{}) { manager.emmiter.On("destroyed", func(payload ...interface{}) {
listener(payload[0].(string)) listener(payload[0].(string), payload[1].(*Session))
}) })
} }

View File

@ -6,7 +6,7 @@ import (
) )
type Broadcast struct { type Broadcast struct {
Enabled string Enabled bool
Display string Display string
Device string Device string
AudioParams string AudioParams string

View File

@ -124,8 +124,8 @@ func (s *Remote) Set() {
if len(res) > 0 { if len(res) > 0 {
width, err1 := strconv.ParseInt(res[1], 10, 64) width, err1 := strconv.ParseInt(res[1], 10, 64)
height, err2 := strconv.ParseInt(res[1], 10, 64) height, err2 := strconv.ParseInt(res[2], 10, 64)
rate, err3 := strconv.ParseInt(res[1], 10, 64) rate, err3 := strconv.ParseInt(res[3], 10, 64)
if err1 == nil && err2 == nil && err3 == nil { if err1 == nil && err2 == nil && err3 == nil {
s.ScreenWidth = int(width) s.ScreenWidth = int(width)

View File

@ -8,6 +8,7 @@ import (
type WebSocket struct { type WebSocket struct {
Password string Password string
AdminPassword string AdminPassword string
Proxy bool
} }
func (WebSocket) Init(cmd *cobra.Command) error { func (WebSocket) Init(cmd *cobra.Command) error {
@ -21,10 +22,16 @@ func (WebSocket) Init(cmd *cobra.Command) error {
return err return err
} }
cmd.PersistentFlags().Bool("proxy", false, "enable reverse proxy mode")
if err := viper.BindPFlag("proxy", cmd.PersistentFlags().Lookup("proxy")); err != nil {
return err
}
return nil return nil
} }
func (s *WebSocket) Set() { func (s *WebSocket) Set() {
s.Password = viper.GetString("password") s.Password = viper.GetString("password")
s.AdminPassword = viper.GetString("password_admin") s.AdminPassword = viper.GetString("password_admin")
s.Proxy = viper.GetBool("proxy")
} }

View File

@ -36,12 +36,13 @@ type SessionManager interface {
Has(id string) bool Has(id string) bool
Get(id string) (Session, bool) Get(id string) (Session, bool)
Members() []*Member Members() []*Member
Admins() []*Member
Destroy(id string) error Destroy(id string) error
Clear() error Clear() error
Broadcast(v interface{}, exclude interface{}) error Broadcast(v interface{}, exclude interface{}) error
OnHost(listener func(id string)) OnHost(listener func(id string))
OnHostCleared(listener func(id string)) OnHostCleared(listener func(id string))
OnDestroy(listener func(id string)) OnDestroy(listener func(id string, session Session))
OnCreated(listener func(id string, session Session)) OnCreated(listener func(id string, session Session))
OnConnected(listener func(id string, session Session)) OnConnected(listener func(id string, session Session))
} }

View File

@ -230,7 +230,7 @@ func (h *MessageHandler) adminKick(id string, session types.Session, payload *me
return nil return nil
} }
if err := target.Kick("You have been kicked"); err != nil { if err := target.Kick("kicked"); err != nil {
return err return err
} }
@ -280,7 +280,7 @@ func (h *MessageHandler) adminBan(id string, session types.Session, payload *mes
h.banned[address[0]] = true h.banned[address[0]] = true
if err := target.Kick("You have been banned"); err != nil { if err := target.Kick("banned"); err != nil {
return err return err
} }

View File

@ -29,19 +29,26 @@ func (h *MessageHandler) Connected(id string, socket *WebSocket) (bool, string,
ok, banned := h.banned[address] ok, banned := h.banned[address]
if ok && banned { if ok && banned {
h.logger.Debug().Str("address", address).Msg("banned") h.logger.Debug().Str("address", address).Msg("banned")
return false, "This IP has been banned", nil return false, "banned", nil
} }
} }
if h.locked { if h.locked {
h.logger.Debug().Msg("server locked") session, ok := h.sessions.Get(id)
return false, "Server is currently locked", nil if !ok || !session.Admin() {
h.logger.Debug().Msg("server locked")
return false, "locked", nil
}
} }
return true, "", nil return true, "", nil
} }
func (h *MessageHandler) Disconnected(id string) error { func (h *MessageHandler) Disconnected(id string) error {
if h.locked && len(h.sessions.Admins()) == 0 {
h.locked = false
}
return h.sessions.Destroy(id) return h.sessions.Destroy(id)
} }

View File

@ -70,7 +70,7 @@ func (ws *WebSocketHandler) Start() error {
} }
}) })
ws.sessions.OnDestroy(func(id string) { ws.sessions.OnDestroy(func(id string, session types.Session) {
if err := ws.handler.SessionDestroyed(id); err != nil { if err := ws.handler.SessionDestroyed(id); err != nil {
ws.logger.Warn().Str("id", id).Err(err).Msg("session destroyed with and error") ws.logger.Warn().Str("id", id).Err(err).Msg("session destroyed with and error")
} else { } else {
@ -131,7 +131,7 @@ func (ws *WebSocketHandler) Upgrade(w http.ResponseWriter, r *http.Request) erro
if err = connection.WriteJSON(message.Disconnect{ if err = connection.WriteJSON(message.Disconnect{
Event: event.SYSTEM_DISCONNECT, Event: event.SYSTEM_DISCONNECT,
Message: "invalid password", Message: "invalid_password",
}); err != nil { }); err != nil {
ws.logger.Error().Err(err).Msg("failed to send disconnect") ws.logger.Error().Err(err).Msg("failed to send disconnect")
} }
@ -191,7 +191,11 @@ func (ws *WebSocketHandler) Upgrade(w http.ResponseWriter, r *http.Request) erro
} }
func (ws *WebSocketHandler) authenticate(r *http.Request) (string, string, bool, error) { func (ws *WebSocketHandler) authenticate(r *http.Request) (string, string, bool, error) {
ip := utils.ReadUserIP(r) ip := r.RemoteAddr
if ws.conf.Proxy {
ip = utils.ReadUserIP(r)
}
id, err := utils.NewUID(32) id, err := utils.NewUID(32)
if err != nil { if err != nil {