Release 2.26 which incorporates the following changes
- Add a ENV_DIR for docker containers to allow changing the env file location changes where the rest of the configuration is bootstrapped from - Add log_dir and snapshot_dir in env file configuration to determine the log and snapshot directories - Massively cleaned up README and provide better examples
This commit is contained in:
parent
fc7abd4ba1
commit
f6db46234a
15 changed files with 340 additions and 297 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,7 +1,15 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## 2.2.6 - UNRELEASED
|
## 2.2.6 - 2023/04/06
|
||||||
* ...
|
|
||||||
|
_Quick update for an outdated application to make it more useful for docker setups. Keep in mind that PHP 7.4 which is used by ts3web is still EOL!_
|
||||||
|
|
||||||
|
* Made it possible to use environment variable to define location of bootstrapping `env` file in docker containers (natively, set php-fpm `ENV[]` properly!)
|
||||||
|
* `ENV_DIR`: location of the **PARENT** folder which has the `env` file inside
|
||||||
|
* Falls back to application `$appDir/config/env` file if not set
|
||||||
|
* Added new `env` file variables to easily configure
|
||||||
|
* `log_dir`: location of the log dir, _without trailing slash_
|
||||||
|
* `snapshot_dir` location of the snapshots dir, _without trailing slash_
|
||||||
|
|
||||||
## 2.2.5 - 2022/12/17
|
## 2.2.5 - 2022/12/17
|
||||||
* Updated `Dockerfile` (as long as alpine 3.15 shipping PHP7 is kept updated, `ts3web` docker images are kept)
|
* Updated `Dockerfile` (as long as alpine 3.15 shipping PHP7 is kept updated, `ts3web` docker images are kept)
|
||||||
|
|
25
Dockerfile
25
Dockerfile
|
@ -2,7 +2,11 @@ FROM alpine:3.15
|
||||||
|
|
||||||
LABEL maintainer="Varakh<varakh@varakh.de>"
|
LABEL maintainer="Varakh<varakh@varakh.de>"
|
||||||
|
|
||||||
ENV APP_HOME /var/www/html/application
|
ENV APP_HOME=/var/www/html/application \
|
||||||
|
PHP_MEMORY_LIMIT=512M \
|
||||||
|
MAX_UPLOAD=1024M \
|
||||||
|
PHP_MAX_FILE_UPLOAD=200 \
|
||||||
|
PHP_MAX_POST=1024M
|
||||||
|
|
||||||
# setup folder structure
|
# setup folder structure
|
||||||
RUN mkdir -p ${APP_HOME}/data/snapshots && \
|
RUN mkdir -p ${APP_HOME}/data/snapshots && \
|
||||||
|
@ -10,6 +14,9 @@ RUN mkdir -p ${APP_HOME}/data/snapshots && \
|
||||||
touch ${APP_HOME}/log/application.log && \
|
touch ${APP_HOME}/log/application.log && \
|
||||||
mkdir -p ${APP_HOME}/config
|
mkdir -p ${APP_HOME}/config
|
||||||
|
|
||||||
|
# entrypoint
|
||||||
|
ADD docker/configure.php /configure.php
|
||||||
|
|
||||||
# add upstream application
|
# add upstream application
|
||||||
ADD src ${APP_HOME}/src
|
ADD src ${APP_HOME}/src
|
||||||
ADD public ${APP_HOME}/public
|
ADD public ${APP_HOME}/public
|
||||||
|
@ -17,16 +24,11 @@ ADD composer.json ${APP_HOME}/composer.json
|
||||||
ADD composer.lock ${APP_HOME}/composer.lock
|
ADD composer.lock ${APP_HOME}/composer.lock
|
||||||
ADD data ${APP_HOME}/data
|
ADD data ${APP_HOME}/data
|
||||||
ADD config ${APP_HOME}/config
|
ADD config ${APP_HOME}/config
|
||||||
RUN mv ${APP_HOME}/config/env.example ${APP_HOME}/config/env
|
|
||||||
|
|
||||||
# php.ini
|
|
||||||
ENV PHP_MEMORY_LIMIT 512M
|
|
||||||
ENV MAX_UPLOAD 1024M
|
|
||||||
ENV PHP_MAX_FILE_UPLOAD 200
|
|
||||||
ENV PHP_MAX_POST 1024M
|
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
RUN apk add --update --no-cache \
|
RUN cp ${APP_HOME}/config/env.example ${APP_HOME}/config/env && \
|
||||||
|
apk add --update --no-cache \
|
||||||
|
bash \
|
||||||
nginx \
|
nginx \
|
||||||
s6 \
|
s6 \
|
||||||
curl \
|
curl \
|
||||||
|
@ -71,10 +73,13 @@ RUN apk add --update --no-cache \
|
||||||
# Add nginx config
|
# Add nginx config
|
||||||
ADD docker/nginx.conf /etc/nginx/nginx.conf
|
ADD docker/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Add required environment variables to php-fpm
|
||||||
|
RUN echo "env[ENV_DIR]=%%%ENV_DIR%%%" >> /etc/php7/php-fpm.d/www.conf
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
# add overlay
|
# add overlay
|
||||||
ADD docker/s6 /etc/s6/
|
ADD docker/s6 /etc/s6/
|
||||||
|
|
||||||
# expose start
|
# expose start
|
||||||
CMD exec s6-svscan /etc/s6/
|
CMD /usr/bin/php /configure.php && exec s6-svscan /etc/s6/
|
||||||
|
|
379
README.md
379
README.md
|
@ -10,275 +10,153 @@ The minimalistic approach of this application is intentional.
|
||||||
There are many TeamSpeak 3 web interfaces out. Why should I pick ts3web? Free, simple, stateless, easy to extend,
|
There are many TeamSpeak 3 web interfaces out. Why should I pick ts3web? Free, simple, stateless, easy to extend,
|
||||||
standard bootstrap theme.
|
standard bootstrap theme.
|
||||||
|
|
||||||
The main git repository is hosted at _[https://git.myservermanager.com/varakh/ts3web](https://git.myservermanager.com/varakh/ts3web)_.
|
The main git repository is hosted at
|
||||||
|
_[https://git.myservermanager.com/varakh/ts3web](https://git.myservermanager.com/varakh/ts3web)_.
|
||||||
Other repositories are mirrors and pull requests, issues, and planning are managed there.
|
Other repositories are mirrors and pull requests, issues, and planning are managed there.
|
||||||
|
|
||||||
Contributions are very welcome!
|
Contributions are very welcome!
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
TeamSpeak has a detailed interface for permissions and uploading files, therefore the following features are not
|
||||||
|
supported:
|
||||||
|
|
||||||
|
* uploading files (only viewing and deleting, use the official client for uploading)
|
||||||
|
* editing permissions (only viewing, use the client for editing)
|
||||||
|
|
||||||
## READ BEFORE USING
|
## READ BEFORE USING
|
||||||
|
|
||||||
This web interface makes **heavy use of TeamSpeak 3 server query commands**. Please ensure that you _increase query
|
This web interface makes **heavy use of TeamSpeak 3 server query commands**. Please ensure that you _increase query
|
||||||
limits_ and whitelist the requesting IP address to a `whitelist.txt` when in a docker environment (see below).
|
limits_ and whitelist the requesting IP address to a `whitelist.txt` when in a docker environment (see below).
|
||||||
|
|
||||||
Please read the next FAQ section carefully!
|
Please read the next sections carefully.
|
||||||
|
|
||||||
## F.A.Q
|
## Setup
|
||||||
|
|
||||||
<a name="flood"></a>
|
_ts3web_ can be deployed natively or via docker. It's recommended to use docker with docker-compose. Those steps are
|
||||||
|
outlined below.
|
||||||
|
|
||||||
### How to overcome server query limit?
|
### docker-compose
|
||||||
|
|
||||||
You might get one of these messages:
|
General remarks:
|
||||||
|
|
||||||
> I always get `flood client` message when clicking anywhere in the web interface.
|
* By default, `/var/www/html/application/config/env` is used for bootstrapping necessary configuration, can be set to another parent directory with `ENV_DIR` in docker environment variable, e.g., `ENV_DIR=/data`
|
||||||
|
* By default, `/var/www/html/application/data/snapshots` is used for storing snapshots, path can be changed in the `env` file with `snapshot_dir`
|
||||||
|
* By default, `/var/www/html/application/log` is used for storing and reading logs, path can be changed in the `env` file with `log_dir`
|
||||||
|
* Other `env` configuration values are outlined in the [env.example](./config/env.example) file which is also present in the default docker image.
|
||||||
|
|
||||||
The web UI uses query commands _a lot_! When your instance is up and running, you should be able to change the following
|
It's recommended to use the `network=HOST` option for the docker setup, and it will be the only example in this `README` file.
|
||||||
setting, e.g. directly in your database (MySQL or sqlite).
|
|
||||||
|
|
||||||
```ini
|
Let's create the docker volumes first. You could also use automatically generated ones by the `docker-compose` file, you
|
||||||
serverinstance_serverquery_flood_commands = 9999
|
would need to remove the `external: true` in the `volumes` section of the `docker-compose.yml` then.
|
||||||
serverinstance_serverquery_max_connections_per_ip = 999
|
|
||||||
serverinstance_serverquery_flood_time = 1
|
Create the external volumes:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker volume create ts3-vol
|
||||||
|
docker volume create ts3web-vol
|
||||||
```
|
```
|
||||||
|
|
||||||
<a name="whitelist"></a>
|
Paste the example `docker-compose.yml` into a file on your machine:
|
||||||
|
|
||||||
> I always get `TSException: Error: host isn't a ts3 instance!` when selecting a server.
|
```yaml
|
||||||
|
|
||||||
You're probably on a docker environment and the TeamSpeak server is queried through the web UI which
|
|
||||||
resides behind a web server, so the TeamSpeak server thinks that the _remote web server IP address_ invokes the query
|
|
||||||
commands and thus blacklists it.
|
|
||||||
|
|
||||||
You need define an exception for you server's IP in a [`whitelist.txt` file](#whitelist-text-file) and include it in
|
|
||||||
your TeamSpeak application.
|
|
||||||
|
|
||||||
You can also add the desired IP to `query_ip_allowlist.txt` and `query_ip_whitelist.txt` within the TeamSpeak 3 Server
|
|
||||||
data directory (in example it's `./ts3server` where your `docker-compose.yml` resides).
|
|
||||||
|
|
||||||
<a name="dockerperms"></a>
|
|
||||||
|
|
||||||
### I always get `no write permissions` or something similar when trying to save snapshots or when a log entry is created.
|
|
||||||
|
|
||||||
This probably happens when you're in the docker setup. Ensure that host binds have permissions set up properly. The user
|
|
||||||
which is used in the docker container is `nobody` with id `65534`. If, e.g. logs are host bound, then execute
|
|
||||||
`chown -R 65534:65534 host/path/to/log`. The same holds true for snapshots.
|
|
||||||
|
|
||||||
In the below `docker-compose.yml` file it's advised to execute the following command when you're in the _same_ directory
|
|
||||||
as the `docker-compose.yml` file resides in: `chown -R 65534:65534 env snapshots log`.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The main configuration file for the *web interface* is the `env` file located in `config/`. There's an example file
|
|
||||||
called `env.example` which you **need** to copy to `config/env`. Defaults will assume you're running your TeamSpeak
|
|
||||||
server on `localhost` with default port. Docker deployments can and *should* host bind this file into the container
|
|
||||||
directly and just maintain the `env` file.
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
The application can be deployed two different ways. See below for more information. For each deployment type a running
|
|
||||||
TeamSpeak 3 instance is a prerequisite.
|
|
||||||
|
|
||||||
In the `docker-compose.yml` [example](#docker-compose), a setup together with a teamspeak server instance is shown.
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
**Important. Read before setup!**
|
|
||||||
|
|
||||||
- [README](#readme)
|
|
||||||
- [READ BEFORE USING](#read-before-using)
|
|
||||||
- [F.A.Q](#faq)
|
|
||||||
- [How to overcome server query limit?](#how-to-overcome-server-query-limit)
|
|
||||||
- [I always get `no write permissions` or something similar when trying to save snapshots or when a log entry is created.](#i-always-get-no-write-permissions-or-something-similar-when-trying-to-save-snapshots-or-when-a-log-entry-is-created)
|
|
||||||
- [Configuration](#configuration)
|
|
||||||
- [Deployment](#deployment)
|
|
||||||
- [Docker](#docker)
|
|
||||||
- [docker standalone](#docker-standalone)
|
|
||||||
- [docker-compose](#docker-compose)
|
|
||||||
- [whitelist text file](#whitelist-text-file)
|
|
||||||
- [As native PHP application](#as-native-php-application)
|
|
||||||
- [Install:](#install)
|
|
||||||
- [Upgrade:](#upgrade)
|
|
||||||
- [Reverse proxy](#reverse-proxy)
|
|
||||||
- [Limitations](#limitations)
|
|
||||||
- [Development](#development)
|
|
||||||
- [Release](#release)
|
|
||||||
- [Prepare next development cycle](#prepare-next-development-cycle)
|
|
||||||
- [Helpers](#helpers)
|
|
||||||
- [Translations](#translations)
|
|
||||||
- [Theme](#theme)
|
|
||||||
|
|
||||||
#### docker standalone
|
|
||||||
|
|
||||||
The following section outlines a manual setup. Feel free to use the provided `docker-compose.yml` as quick setup.
|
|
||||||
|
|
||||||
1. Create docker volumes for `snapshots`, `log` and `env`. Alternative is to host bind them into your containers.
|
|
||||||
2. Create a docker network with a fixed IP range or later use host network.
|
|
||||||
3. Depending on your setup, you need to change `teamspeak_host` of your `env` file to point either to `your IP` or to a
|
|
||||||
`fixed docker IP` which your teamspeak uses. `localhost` is not valid if you're using it in docker. If you're unsure,
|
|
||||||
please take a look at the example `docker-compose.yml` files.
|
|
||||||
4. Start a container using the docker image `varakh/ts3web` and provide the following bindings for volumes:
|
|
||||||
* `{env_file_volume|host_file}:/var/www/html/applicationconfig/env`
|
|
||||||
* `{snapshot_volume|host_folder}:/var/www/html/application/data/snapshots`
|
|
||||||
* `{log_volume|host_folder}:/var/www/html/application/log`
|
|
||||||
5. [Ensure that you're whitelisting the IP from which the webinterface will issue commands.](#whitelist-text-file)
|
|
||||||
6. Run the `docker run` command including your settings, volumes and networks (if
|
|
||||||
any): `docker run --name teamspeak_web -v ./env:/var/www/html/application/config/env -p 8181:80 varakh/ts3web:latest`
|
|
||||||
.
|
|
||||||
|
|
||||||
#### docker-compose
|
|
||||||
|
|
||||||
In order for TeamSpeak to show correct IP and country flags, the `network_mode = "host"` is advised. It's also possible
|
|
||||||
to set everything up without using the host network mode and use fixed IPs.
|
|
||||||
|
|
||||||
The examples will use host binds for volumes. Feel free to adapt the `docker-compose.yml` template and use docker
|
|
||||||
volumes instead if you like.
|
|
||||||
|
|
||||||
Ensure to [apply permissions](#i-always-get-no-write-permissions-or-something-similar-when-trying-to-save-snapshots-or-when-a-log-entry-is-created) for volumes though.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>docker host mode example</summary>
|
|
||||||
|
|
||||||
```
|
|
||||||
version: '2.1'
|
version: '2.1'
|
||||||
networks:
|
networks:
|
||||||
teamspeak:
|
teamspeak:
|
||||||
external: false
|
external: false
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
container_name: teamspeak_app
|
container_name: teamspeak_app
|
||||||
image: teamspeak:latest
|
image: teamspeak:latest
|
||||||
volumes:
|
volumes:
|
||||||
- ./ts3server:/var/ts3server
|
- ts3-vol:/var/ts3server
|
||||||
- ./whitelist.txt:/whitelist.txt
|
# ports are all public here in the HOST mode example
|
||||||
ports:
|
|
||||||
- 10011:10011
|
|
||||||
- 30033:30033
|
|
||||||
- 9987:9987/udp
|
|
||||||
environment:
|
environment:
|
||||||
- TS3SERVER_LICENSE=accept
|
- TS3SERVER_LICENSE=accept
|
||||||
- TS3SERVER_IP_WHITELIST=/whitelist.txt
|
- TS3SERVER_IP_WHITELIST=/var/ts3server/whitelist.txt
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
web:
|
web:
|
||||||
container_name: teamspeak_web
|
container_name: teamspeak_web
|
||||||
image: varakh/ts3web:latest
|
image: varakh/ts3web:latest
|
||||||
volumes:
|
volumes:
|
||||||
- ./env:/var/www/html/application/config/env
|
- ts3web-vol:/data
|
||||||
- ./snapshots:/var/www/html/application/data/snapshots
|
environment:
|
||||||
- ./log:/var/www/html/application/log
|
# volume needs to contain the env file!
|
||||||
|
- ENV_DIR=/data
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:8181:80
|
- 127.0.0.1:8181:80
|
||||||
depends_on:
|
depends_on:
|
||||||
- app
|
- app
|
||||||
restart: always
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- teamspeak
|
- teamspeak
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>docker without host mode example</summary>
|
|
||||||
|
|
||||||
```
|
|
||||||
version: '2.1'
|
|
||||||
networks:
|
|
||||||
teamspeak:
|
|
||||||
driver: bridge
|
|
||||||
ipam:
|
|
||||||
config:
|
|
||||||
- subnet: 10.5.0.0/16
|
|
||||||
gateway: 10.5.0.1
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
container_name: teamspeak_app
|
|
||||||
image: teamspeak:latest
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./ts3server:/var/ts3server
|
ts3-vol:
|
||||||
- ./whitelist.txt:/whitelist.txt
|
ts3web-vol:
|
||||||
environment:
|
|
||||||
- TS3SERVER_LICENSE=accept
|
|
||||||
- TS3SERVER_IP_WHITELIST=/whitelist.txt
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- 10011:10011
|
|
||||||
- 30033:30033
|
|
||||||
- 9987:9987/udp
|
|
||||||
networks:
|
|
||||||
teamspeak:
|
|
||||||
ipv4_address: 10.5.0.5
|
|
||||||
web:
|
|
||||||
container_name: teamspeak_web
|
|
||||||
image: varakh/ts3web:latest
|
|
||||||
volumes:
|
|
||||||
- ./env:/var/www/html/application/config/env
|
|
||||||
- ./snapshots:/var/www/html/application/data/snapshots
|
|
||||||
- ./log:/var/www/html/application/log
|
|
||||||
ports:
|
|
||||||
- 127.0.0.1:8181:80
|
|
||||||
depends_on:
|
|
||||||
- app
|
|
||||||
restart: always
|
|
||||||
networks:
|
|
||||||
teamspeak:
|
|
||||||
ipv4_address: 10.5.0.6
|
|
||||||
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### whitelist text file
|
|
||||||
|
|
||||||
The following illustrates a valid `whitelist.txt` file which can be used for the above `docker-compose` setups. You need
|
|
||||||
to replace `your-public-ip` with the TeamSpeak's public IP address if required or remove the fixed internal docker IP if
|
|
||||||
you're on 'host' mode.
|
|
||||||
|
|
||||||
```
|
|
||||||
127.0.0.1
|
|
||||||
::1
|
|
||||||
10.5.0.5
|
|
||||||
your-public-ip
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now execute `docker-compose up -d` to start those containers. If you like to update, do `docker-compose down`,
|
Let's populate our docker volumes **before** we start!
|
||||||
`docker-compose pull` and then `docker-compose up -d` again.
|
|
||||||
|
|
||||||
Your TeamSpeak 3 Server will be available under `public-server-ip:9987`. The web interface will be available on
|
```shell
|
||||||
`127.0.0.1:8181`. You need to add a [reverse proxy](#reverseproxy) and probably you also want SSL configured if you
|
docker run -d --rm --name ts3web_creator -v ts3web-vol:/mnt alpine tail -f /dev/null
|
||||||
expose it via domain. For testing purposes, change `- 127.0.0.1:8181:80` to `- 8181:80`. The web interface will then be
|
docker exec -it ts3web_creator sh
|
||||||
available under
|
|
||||||
`public-server-ip:8181`.
|
|
||||||
|
|
||||||
This is **not recommended**! Secure your setup properly via [reverse proxy and SSL](#reverse-proxy).
|
# inside the container, edit the env by copying the example to the docker volume mount path at /data/env
|
||||||
|
cp /var/www/html/application/config/env /data/env
|
||||||
|
|
||||||
### As native PHP application
|
# edit the env file to your liking
|
||||||
|
#
|
||||||
|
# the teamspeak_host should point to your public ip address and must be whitelisted inside the teamspeak server itself
|
||||||
|
vi env
|
||||||
|
|
||||||
**Prerequisite**: `php`, `composer` and probably `php-fpm` installed on the server.
|
# create necessary directories and set permissions
|
||||||
|
mkdir -p /data/log
|
||||||
|
touch /data/log/application.log
|
||||||
|
mkdir -p /data/snapshots
|
||||||
|
chown -R 65534:65534 /data
|
||||||
|
|
||||||
#### Install:
|
# exit the container
|
||||||
|
exit
|
||||||
|
|
||||||
* Clone repository
|
# on the host system, stop the creator container
|
||||||
* Change directory to project home
|
docker stop ts3web_creator
|
||||||
* Execute `composer install`
|
```
|
||||||
* `composer install`
|
|
||||||
* Do the configuration by coping the `env.example` file (see information above)
|
|
||||||
* Use a web server _or_ run directly via the embedded PHP server: `php -S localhost:8080 -t public public/index.php`.
|
|
||||||
* Point your browser to [localhost:8080](http://localhost:8080)
|
|
||||||
* Apply any [whitelist.txt](#whitelist-text-file) changes if you configured `teamspeak_host` differently
|
|
||||||
than `localhost`
|
|
||||||
|
|
||||||
#### Upgrade:
|
Let's populate the teamspeak container with a proper `whitelist.txt` file. See [ensure that you're whitelisting the IP from which the webinterface will issue commands](#whitelisttextfile).
|
||||||
|
|
||||||
* Change directory to project home
|
```shell
|
||||||
* `git pull`
|
docker run -d --rm --name ts3_creator -v ts3-vol:/mnt alpine tail -f /dev/null
|
||||||
* `composer update`
|
docker exec -it ts3_creator sh
|
||||||
|
|
||||||
|
# edit the whitelist.txt file at /var/ts3server/whitelist.txt
|
||||||
|
# it should contain the following entries
|
||||||
|
#
|
||||||
|
# 127.0.0.1
|
||||||
|
# ::1
|
||||||
|
# your-public-ip
|
||||||
|
#
|
||||||
|
vi /var/ts3server/whitelist.txt
|
||||||
|
|
||||||
|
# exit the container
|
||||||
|
exit
|
||||||
|
|
||||||
|
# on the host system, stop the creator container
|
||||||
|
docker stop ts3_creator
|
||||||
|
```
|
||||||
|
|
||||||
|
Maybe you like to copy valid license files into the `ts3-vol` docker volume before stopping.
|
||||||
|
|
||||||
|
_Finally_, start the stack with `docker-compose up -d`. Please see the **Reverse proxy** section for a nginx example.
|
||||||
|
|
||||||
### Reverse proxy
|
### Reverse proxy
|
||||||
|
|
||||||
Here's an example on how to configure a reverse proxy for the web interface docker container
|
Here's an example on how to configure a reverse proxy for the web interface docker container
|
||||||
|
|
||||||
```
|
```shell
|
||||||
root .../public;
|
root .../public;
|
||||||
index index.php;
|
index index.php;
|
||||||
|
|
||||||
|
@ -305,17 +183,84 @@ Here's an example on how to configure a reverse proxy for the web interface dock
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Limitations
|
## Native application
|
||||||
|
|
||||||
TeamSpeak has a detailed interface for permissions and uploading files, therefore the following features are not
|
**Prerequisite**: `php`, `composer` and probably `php-fpm` installed on the server.
|
||||||
supported:
|
|
||||||
|
|
||||||
* uploading files (only viewing and deleting, use the official client for uploading)
|
### Install
|
||||||
* editing permissions (only viewing, use the client for editing)
|
|
||||||
|
* Clone repository
|
||||||
|
* Change directory to project home
|
||||||
|
* Execute `composer install`
|
||||||
|
* `composer install`
|
||||||
|
* Do the configuration by coping the `env.example` file (see information above)
|
||||||
|
* Use a web server _or_ run directly via the embedded PHP server: `php -S localhost:8080 -t public public/index.php`.
|
||||||
|
* Point your browser to [localhost:8080](http://localhost:8080)
|
||||||
|
* Apply any [whitelist.txt](#whitelist-text-file) changes if you configured `teamspeak_host` differently
|
||||||
|
than `localhost`
|
||||||
|
|
||||||
|
### Upgrade
|
||||||
|
|
||||||
|
* Change directory to project home
|
||||||
|
* `git pull`
|
||||||
|
* `composer update`
|
||||||
|
|
||||||
|
## Troubleshooting / F.A.Q
|
||||||
|
|
||||||
|
<a name="flood"></a>
|
||||||
|
### How to overcome server query limit?
|
||||||
|
|
||||||
|
You might get one of these messages:
|
||||||
|
|
||||||
|
> I always get `flood client` message when clicking anywhere in the web interface.
|
||||||
|
|
||||||
|
The web UI uses query commands _a lot_! When your instance is up and running, you should be able to change the following
|
||||||
|
setting, e.g. directly in your database (MySQL or sqlite).
|
||||||
|
|
||||||
|
```ini
|
||||||
|
serverinstance_serverquery_flood_commands = 9999
|
||||||
|
serverinstance_serverquery_max_connections_per_ip = 999
|
||||||
|
serverinstance_serverquery_flood_time = 1
|
||||||
|
```
|
||||||
|
|
||||||
|
<a name="whitelist"></a>
|
||||||
|
> I always get `TSException: Error: host isn't a ts3 instance!` when selecting a server.
|
||||||
|
|
||||||
|
You're probably on a docker environment and the TeamSpeak server is queried through the web UI which
|
||||||
|
resides behind a web server, so the TeamSpeak server thinks that the _remote web server IP address_ invokes the query
|
||||||
|
commands and thus blacklists it.
|
||||||
|
|
||||||
|
You need define an exception for you server's IP in a [`whitelist.txt`](#whitelisttextfile) file and include it in
|
||||||
|
your TeamSpeak application.
|
||||||
|
|
||||||
|
You can also add the desired IP to `query_ip_allowlist.txt` and `query_ip_whitelist.txt` within the TeamSpeak 3 Server
|
||||||
|
data directory.
|
||||||
|
|
||||||
|
<a name="dockerperms"></a>
|
||||||
|
### I always get `no write permissions` or something similar when trying to save snapshots or when a log entry is created.
|
||||||
|
|
||||||
|
This probably happens when you're in the docker setup. Ensure that host binds have permissions set up properly and also files inside any
|
||||||
|
docker volume has the correct permissions. The user which is used in the docker container is `nobody` with id `65534`.
|
||||||
|
|
||||||
|
Change owner permissions recursively with `chown -R 65534:65534 /path/to/dir`.
|
||||||
|
|
||||||
|
<a name="whitelisttextfile"></a>
|
||||||
|
### What's a whitelist.txt and why do I need it?
|
||||||
|
|
||||||
|
The following illustrates a valid `whitelist.txt` file which can be used for the above `docker-compose` setups. You need
|
||||||
|
to replace `your-public-ip` with the TeamSpeak's public IP address if required or remove the fixed internal docker IP if
|
||||||
|
you're on 'host' mode.
|
||||||
|
|
||||||
|
```
|
||||||
|
127.0.0.1
|
||||||
|
::1
|
||||||
|
10.5.0.5
|
||||||
|
your-public-ip
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
If you're willing to contribute, here's some information.
|
Contributions are welcome!
|
||||||
|
|
||||||
### Release
|
### Release
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Constants
|
||||||
/**
|
/**
|
||||||
* Version tag
|
* Version tag
|
||||||
*/
|
*/
|
||||||
const VERSION = '2.2.6-SNAPSHOT';
|
const VERSION = '2.2.6';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return constant by its class name
|
* Return constant by its class name
|
||||||
|
|
|
@ -8,12 +8,27 @@ class EnvConstants
|
||||||
/**
|
/**
|
||||||
* Example env file
|
* Example env file
|
||||||
*/
|
*/
|
||||||
const ENV_FILE_EXAMPLE = "env.example";
|
const FILE_NAME_EXAMPLE_ENV = "env.example";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom env file
|
* Custom env file
|
||||||
*/
|
*/
|
||||||
const ENV_FILE = "env";
|
const FILE_NAME_ENV = "env";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the env file
|
||||||
|
*/
|
||||||
|
const ENV_DIR = "ENV_DIR";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the logs directory
|
||||||
|
*/
|
||||||
|
const LOG_DIR = "log_dir";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the snapshots directory
|
||||||
|
*/
|
||||||
|
const SNAPSHOT_DIR = "snapshot_dir";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Site title
|
* Site title
|
||||||
|
@ -31,7 +46,7 @@ class EnvConstants
|
||||||
const SITE_DATE_FORMAT = "site_date_format";
|
const SITE_DATE_FORMAT = "site_date_format";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* THeme
|
* Theme
|
||||||
*/
|
*/
|
||||||
const THEME = "theme";
|
const THEME = "theme";
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,16 @@ theme="bootstrap4" # values: bootstrap4 (foldernames are used to determine theme
|
||||||
theme_cache=false # values: true|false (cache view/twig. makes it faster, disable for debug)
|
theme_cache=false # values: true|false (cache view/twig. makes it faster, disable for debug)
|
||||||
|
|
||||||
# teamspeak
|
# teamspeak
|
||||||
teamspeak_host="localhost" # 'localhost' or 'name_of_docker_container' if running locally
|
teamspeak_host="app" # 'localhost' or 'name_of_docker_container|name_of_the_docker-compose_service' if running locally
|
||||||
teamspeak_query_port=10011
|
teamspeak_query_port=10011
|
||||||
teamspeak_user="serveradmin"
|
teamspeak_user="serveradmin"
|
||||||
teamspeak_tree_view="true" # show a tree view in the details of online clients if a server has been selected
|
teamspeak_tree_view="true" # show a tree view in the details of online clients if a server has been selected
|
||||||
teamspeak_log_lines=100 # show this amount of latest log lines
|
teamspeak_log_lines=100 # show this amount of latest log lines
|
||||||
|
|
||||||
# log
|
# log
|
||||||
|
log_dir=/var/www/html/application/log
|
||||||
log_name="ts3web" # values: all strings
|
log_name="ts3web" # values: all strings
|
||||||
log_level="INFO" # values: DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
|
log_level="INFO" # values: DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY
|
||||||
|
|
||||||
|
# snapshots
|
||||||
|
snapshot_dir=/var/www/html/application/data/snapshots
|
||||||
|
|
22
docker/configure.php
Normal file
22
docker/configure.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo "Adapting ENV_DIR..." . PHP_EOL;
|
||||||
|
|
||||||
|
$path = '/etc/php7/php-fpm.d/www.conf';
|
||||||
|
$env = "ENV_DIR";
|
||||||
|
|
||||||
|
$fileContent = file_get_contents($path);
|
||||||
|
$fileContent = preg_replace("/%%%" . strtoupper($env) . "%%%/", env($env, "/var/www/html/application/config"), $fileContent);
|
||||||
|
file_put_contents($path, $fileContent);
|
||||||
|
|
||||||
|
function env($name, $default = null)
|
||||||
|
{
|
||||||
|
$v = getenv($name) ?: $default;
|
||||||
|
|
||||||
|
if ($v === null) {
|
||||||
|
return "''";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "'" . $v . "'";
|
||||||
|
}
|
|
@ -64,6 +64,7 @@ Carbon::setToStringFormat(getenv(EnvConstants::SITE_DATE_FORMAT));
|
||||||
// logger
|
// logger
|
||||||
try {
|
try {
|
||||||
$logger = BootstrapHelper::bootLogger();
|
$logger = BootstrapHelper::bootLogger();
|
||||||
|
$logger->info("Starting ts3web version " . Constants::VERSION);
|
||||||
|
|
||||||
$container['logger'] = function () use ($logger) {
|
$container['logger'] = function () use ($logger) {
|
||||||
return $logger;
|
return $logger;
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class SnapshotCreateAction extends AbstractAction
|
||||||
|
|
||||||
$fileSystem = new Filesystem();
|
$fileSystem = new Filesystem();
|
||||||
$name = Carbon::now()->getTimestamp();
|
$name = Carbon::now()->getTimestamp();
|
||||||
$path = FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
$path = FileHelper::getSnapshotsDir() . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
||||||
|
|
||||||
if ($fileSystem->exists($path)) {
|
if ($fileSystem->exists($path)) {
|
||||||
$this->flash->addMessage('error', $this->translator->trans('file.exists'));
|
$this->flash->addMessage('error', $this->translator->trans('file.exists'));
|
||||||
|
|
|
@ -16,14 +16,14 @@ final class SnapshotDeleteAction extends AbstractAction
|
||||||
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
||||||
|
|
||||||
$fileSystem = new Filesystem();
|
$fileSystem = new Filesystem();
|
||||||
$path = FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
$path = FileHelper::getSnapshotsDir() . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
||||||
|
|
||||||
if (!$fileSystem->exists($path)) {
|
if (!$fileSystem->exists($path)) {
|
||||||
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
$fileSystem->remove($path);
|
$fileSystem->remove($path);
|
||||||
$serverPath = FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid;
|
$serverPath = FileHelper::getSnapshotsDir() . DIRECTORY_SEPARATOR . $sid;
|
||||||
|
|
||||||
if (count(FileHelper::getFiles($serverPath)) == 0) {
|
if (count(FileHelper::getFiles($serverPath)) == 0) {
|
||||||
$fileSystem->remove($serverPath);
|
$fileSystem->remove($serverPath);
|
||||||
|
|
|
@ -15,7 +15,7 @@ final class SnapshotDeployAction extends AbstractAction
|
||||||
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
||||||
|
|
||||||
$fileSystem = new Filesystem();
|
$fileSystem = new Filesystem();
|
||||||
$path = FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
$path = FileHelper::getSnapshotsDir() . DIRECTORY_SEPARATOR . $sid . DIRECTORY_SEPARATOR . $name;
|
||||||
|
|
||||||
if (!$fileSystem->exists($path)) {
|
if (!$fileSystem->exists($path)) {
|
||||||
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
$this->flash->addMessage('error', $this->translator->trans('file.notexists'));
|
||||||
|
|
|
@ -12,7 +12,7 @@ final class SnapshotsAction extends AbstractAction
|
||||||
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
$this->ts->login($this->auth->getIdentity()['user'], $this->auth->getIdentity()['password']);
|
||||||
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
$this->ts->getInstance()->selectServer($sid, 'serverId');
|
||||||
|
|
||||||
$snapshots = FileHelper::getFiles(FileHelper::SNAPSHOTS_PATH . DIRECTORY_SEPARATOR . $sid);
|
$snapshots = FileHelper::getFiles(FileHelper::getSnapshotsDir() . DIRECTORY_SEPARATOR . $sid);
|
||||||
|
|
||||||
$this->view->render($response, 'snapshots.twig', [
|
$this->view->render($response, 'snapshots.twig', [
|
||||||
'title' => $this->translator->trans('snapshots.title'),
|
'title' => $this->translator->trans('snapshots.title'),
|
||||||
|
|
|
@ -22,20 +22,39 @@ class BootstrapHelper
|
||||||
*/
|
*/
|
||||||
public static function bootEnvironment()
|
public static function bootEnvironment()
|
||||||
{
|
{
|
||||||
$envPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'config';
|
$envDir = getenv(EnvConstants::ENV_DIR, true);
|
||||||
$envFileExample = $envPath . DIRECTORY_SEPARATOR . EnvConstants::ENV_FILE_EXAMPLE;
|
$externalEnvDir = true;
|
||||||
$envFile = $envPath . DIRECTORY_SEPARATOR . EnvConstants::ENV_FILE;
|
|
||||||
|
if ($envDir === false) {
|
||||||
|
$envDir = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'config';
|
||||||
|
$externalEnvDir = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$envFile = $envDir . DIRECTORY_SEPARATOR . EnvConstants::FILE_NAME_ENV;
|
||||||
|
|
||||||
|
if ($externalEnvDir === false) {
|
||||||
|
$envFileFallback = $envDir . DIRECTORY_SEPARATOR . EnvConstants::FILE_NAME_EXAMPLE_ENV;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$fileSystem = new Filesystem();
|
$fileSystem = new Filesystem();
|
||||||
if (!$fileSystem->exists($envFile)) {
|
if (!$fileSystem->exists($envFile)) {
|
||||||
$fileSystem->copy($envFileExample, $envFile);
|
$fileSystem->copy($envFileFallback, $envFile);
|
||||||
}
|
}
|
||||||
} catch (IOException $e) {
|
} catch (IOException $e) {
|
||||||
die('Could not copy example env file ' . $envFileExample . ' to ' . $envFile);
|
die('Could not copy example env file ' . $envFileFallback . ' to ' . $envFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$fileSystem = new Filesystem();
|
||||||
|
if (!$fileSystem->exists($envFile)) {
|
||||||
|
die('env file ' . $envFile . ' does not exist');
|
||||||
|
}
|
||||||
|
} catch (IOException $e) {
|
||||||
|
die('Could determine env file ' . $envFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$env = new Dotenv($envPath, EnvConstants::ENV_FILE);
|
$env = new Dotenv($envDir, EnvConstants::FILE_NAME_ENV);
|
||||||
$res = $env->load();
|
$res = $env->load();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -142,9 +161,15 @@ class BootstrapHelper
|
||||||
*/
|
*/
|
||||||
public static function getLogDir()
|
public static function getLogDir()
|
||||||
{
|
{
|
||||||
|
$logDir = getenv(EnvConstants::LOG_DIR);
|
||||||
|
|
||||||
|
if ($logDir === false) {
|
||||||
return __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'log';
|
return __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'log';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $logDir;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns log file
|
* Returns log file
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
use Symfony\Component\Finder\Finder;
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
class FileHelper
|
class FileHelper
|
||||||
{
|
{
|
||||||
const SNAPSHOTS_PATH = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'snapshots';
|
private const SNAPSHOTS_PATH = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'snapshots';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all files in FILES_DIR
|
* Constructs the snapshots directory
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getSnapshotsDir(): string
|
||||||
|
{
|
||||||
|
$snapshotDir = getenv(EnvConstants::SNAPSHOT_DIR);
|
||||||
|
|
||||||
|
if ($snapshotDir === false) {
|
||||||
|
return FileHelper::SNAPSHOTS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $snapshotDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all files in a directory
|
||||||
*
|
*
|
||||||
* @param $directory
|
* @param $directory
|
||||||
* @return array
|
* @return array
|
||||||
|
@ -38,7 +54,7 @@ class FileHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output human readable file size
|
* Output human-readable file size
|
||||||
*
|
*
|
||||||
* @param $bytes
|
* @param $bytes
|
||||||
* @param int $decimals
|
* @param int $decimals
|
||||||
|
@ -52,7 +68,7 @@ class FileHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output human readable bandwidth size
|
* Output human-readable bandwidth size
|
||||||
*
|
*
|
||||||
* @param $bytes
|
* @param $bytes
|
||||||
* @param int $decimals
|
* @param int $decimals
|
||||||
|
|
|
@ -38,6 +38,8 @@ class TSInstance
|
||||||
$this->host = getenv(EnvConstants::TEAMSPEAK_HOST);
|
$this->host = getenv(EnvConstants::TEAMSPEAK_HOST);
|
||||||
$this->queryPort = getenv(EnvConstants::TEAMSPEAK_QUERY_PORT);
|
$this->queryPort = getenv(EnvConstants::TEAMSPEAK_QUERY_PORT);
|
||||||
|
|
||||||
|
$this->logger->debug(sprintf('Trying to connect to to %s:%s', $this->host, $this->queryPort));
|
||||||
|
|
||||||
$ts = new ts3admin($this->host, $this->queryPort);
|
$ts = new ts3admin($this->host, $this->queryPort);
|
||||||
$ts = new TS3AdminProxy($ts, $logger);
|
$ts = new TS3AdminProxy($ts, $logger);
|
||||||
|
|
||||||
|
|
Reference in a new issue