diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 2df6bfb18..67a054c45 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -45,7 +45,7 @@ jobs: uses: actions/cache@v2 with: path: vendor - key: ${{ runner.OS }}-build-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.OS }}-build-${{ hashFiles('composer.lock') }} - name: Set console permissions and clear static assets. run: | @@ -117,8 +117,8 @@ jobs: with: context: . load: true - tags: ghcr.io/azuracast/web:latest - cache-from: type=registry,ref=ghcr.io/azuracast/web:buildcache + tags: ghcr.io/azuracast/azuracast:latest + cache-from: type=registry,ref=ghcr.io/azuracast/azuracast:buildcache - name: Set up functional test environment. run: | @@ -126,13 +126,12 @@ jobs: cp azuracast.sample.env azuracast.env cp docker-compose.sample.yml docker-compose.yml cp docker-compose.testing.yml docker-compose.override.yml - docker-compose run --rm --user="azuracast" web azuracast_install - name: Run functional test suite. run: | chmod 777 tests/_output/ chmod 777 tests/_support/_generated - docker-compose run --rm --user="azuracast" web composer codeception-no-coverage + docker-compose run --rm web azuracast_ci - name: Stop all running containers. run: | @@ -202,8 +201,8 @@ jobs: uses: docker/metadata-action@v3 with: images: | - azuracast/azuracast_web_v2 - ghcr.io/azuracast/web + azuracast/azuracast + ghcr.io/azuracast/azuracast tags: | type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} type=ref,event=branch @@ -217,5 +216,5 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=ghcr.io/azuracast/web:buildcache - cache-to: type=registry,ref=ghcr.io/azuracast/web:buildcache,mode=max + cache-from: type=registry,ref=ghcr.io/azuracast/azuracast:buildcache + cache-to: type=registry,ref=ghcr.io/azuracast/azuracast:buildcache,mode=max diff --git a/Dockerfile b/Dockerfile index 49e3e4d62..2197e3f70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,11 @@ +# +# Icecast build stage (for later copy) +# +FROM ghcr.io/azuracast/icecast-kh-ac:2.4.0-kh15-ac2 AS icecast + +# +# Golang dependencies build step +# FROM golang:1.17-buster AS dockerize RUN apt-get update \ @@ -5,22 +13,49 @@ RUN apt-get update \ RUN go install github.com/jwilder/dockerize@latest +# # Final build image -FROM ubuntu:focal +# +FROM mariadb:10.5-focal ENV TZ="UTC" # Add Dockerize COPY --from=dockerize /go/bin/dockerize /usr/local/bin -# Run base build process -COPY ./util/docker/web /bd_build/ +# Import Icecast-KH from build container +COPY --from=icecast /usr/local/bin/icecast /usr/local/bin/icecast +COPY --from=icecast /usr/local/share/icecast /usr/local/share/icecast +# Run base build process +COPY ./util/docker/common /bd_build/ RUN chmod a+x /bd_build/*.sh \ && /bd_build/prepare.sh \ && /bd_build/add_user.sh \ - && /bd_build/setup.sh \ - && /bd_build/cleanup.sh \ + && /bd_build/cleanup.sh + +# Build each set of dependencies in their own step for cacheability. +COPY ./util/docker/stations /bd_build/stations/ +RUN bash /bd_build/stations/setup.sh \ + && bash /bd_build/cleanup.sh \ + && rm -rf /bd_build/stations + +COPY ./util/docker/web /bd_build/web/ +RUN bash /bd_build/web/setup.sh \ + && bash /bd_build/cleanup.sh \ + && rm -rf /bd_build/web + +COPY ./util/docker/mariadb /bd_build/mariadb/ +RUN bash /bd_build/mariadb/setup.sh \ + && bash /bd_build/cleanup.sh \ + && rm -rf /bd_build/mariadb + +COPY ./util/docker/redis /bd_build/redis/ +RUN bash /bd_build/redis/setup.sh \ + && bash /bd_build/cleanup.sh \ + && rm -rf /bd_build/redis + +RUN bash /bd_build/post_setup.sh \ && rm -rf /bd_build # @@ -42,7 +77,8 @@ COPY --chown=azuracast:azuracast . . RUN composer dump-autoload --optimize --classmap-authoritative \ && touch /var/azuracast/.docker -VOLUME ["/var/azuracast/www_tmp", "/var/azuracast/uploads", "/var/azuracast/backups", "/var/azuracast/sftpgo/persist"] +VOLUME ["/var/azuracast/stations", "/var/azuracast/www_tmp", "/var/azuracast/uploads", "/var/azuracast/backups", "/var/azuracast/sftpgo/persist", "/var/azuracast/servers/shoutcast2"] +ENV PATH="${PATH}:/var/azuracast/servers/shoutcast2" # # END Operations as `azuracast` user @@ -50,18 +86,19 @@ VOLUME ["/var/azuracast/www_tmp", "/var/azuracast/uploads", "/var/azuracast/back USER root EXPOSE 80 2022 +EXPOSE 8000-8999 # Sensible default environment variables. ENV LANG="en_US.UTF-8" \ + DOCKER_IS_STANDALONE="true" \ APPLICATION_ENV="production" \ - ENABLE_ADVANCED_FEATURES="false" \ - MYSQL_HOST="mariadb" \ + MYSQL_HOST="localhost" \ MYSQL_PORT=3306 \ MYSQL_USER="azuracast" \ MYSQL_PASSWORD="azur4c457" \ MYSQL_DATABASE="azuracast" \ ENABLE_REDIS="true" \ - REDIS_HOST="redis" \ + REDIS_HOST="localhost" \ REDIS_PORT=6379 \ REDIS_DB=1 \ NGINX_RADIO_PORTS="default" \ @@ -75,5 +112,5 @@ ENV LANG="en_US.UTF-8" \ PROFILING_EXTENSION_HTTP_IP_WHITELIST=* # Entrypoint and default command -ENTRYPOINT ["/usr/local/bin/uptime_wait"] -CMD ["/usr/local/bin/my_init"] +ENTRYPOINT ["/usr/local/bin/my_init"] +CMD ["--no-main-command"] diff --git a/Makefile b/Makefile index 81eaa842f..a69efd01a 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ build: # Rebuild all containers and restart update: # Update everything (i.e. after a branch update) docker-compose build $(MAKE) down - docker-compose run --rm --user=azuracast web composer install - docker-compose run --rm --user=azuracast web azuracast_cli azuracast:setup:initialize + docker-compose run --rm web gosu azuracast composer install + docker-compose run --rm web azuracast_cli azuracast:setup:initialize $(MAKE) frontend-build $(MAKE) up diff --git a/azuracast.dev.env b/azuracast.dev.env index 738455b8e..3b19d4908 100644 --- a/azuracast.dev.env +++ b/azuracast.dev.env @@ -4,7 +4,6 @@ APPLICATION_ENV=development LOG_LEVEL=debug -ENABLE_ADVANCED_FEATURES=true COMPOSER_PLUGIN_MODE=false # Limit station port range diff --git a/azuracast.sample.env b/azuracast.sample.env index 06dc07074..324d498fb 100644 --- a/azuracast.sample.env +++ b/azuracast.sample.env @@ -39,17 +39,17 @@ AUTO_ASSIGN_PORT_MAX=8499 # The host to connect to. Leave this as the default value unless you're connecting # to an external database server. -# Default: mariadb -MYSQL_HOST=mariadb +# Default: localhost +# MYSQL_HOST=localhost # The port to connect to. Leave this as the default value unless you're connecting # to an external database server. # Default: 3306 -MYSQL_PORT=3306 +# MYSQL_PORT=3306 # The username AzuraCast will use to connect to the database. # Default: azuracast -MYSQL_USER=azuracast +# MYSQL_USER=azuracast # The password AzuraCast will use to connect to the database. # By default, the database is not exposed to the Internet at all and this is only @@ -59,7 +59,7 @@ MYSQL_PASSWORD=azur4c457 # The name of the AzuraCast database. # Default: azuracast -MYSQL_DATABASE=azuracast +# MYSQL_DATABASE=azuracast # Automatically generate a random root password upon the first database spin-up. # This password will be visible in the mariadb container's logs. @@ -71,12 +71,12 @@ MYSQL_RANDOM_ROOT_PASSWORD=yes # To read the slow query log once enabled, run: # docker-compose exec mariadb slow_queries # Default: 0 -MYSQL_SLOW_QUERY_LOG=0 +# MYSQL_SLOW_QUERY_LOG=0 # Set the amount of allowed connections to the database. This value should be increased # if you are seeing the `Too many connections` error in the logs. # Default: 100 -MYSQL_MAX_CONNECTIONS=100 +# MYSQL_MAX_CONNECTIONS=100 # # Redis Configuration @@ -90,8 +90,8 @@ MYSQL_MAX_CONNECTIONS=100 # ENABLE_REDIS=true # Name of the Redis host. -# Default: redis -# REDIS_HOST=redis +# Default: localhost +# REDIS_HOST=localhost # Port to connect to on the Redis host. # Default: 6379 diff --git a/bin/uptime_wait b/bin/uptime_wait old mode 100755 new mode 100644 index f3d5c0cda..ed2bf3f3c --- a/bin/uptime_wait +++ b/bin/uptime_wait @@ -92,8 +92,10 @@ class UptimeWait protected function checkDatabase(): bool { try { + $defaultHost = $this->isStandalone() ? 'localhost' : 'mariadb'; + $dbOptions = [ - 'host' => $_ENV['MYSQL_HOST'] ?? 'mariadb', + 'host' => $_ENV['MYSQL_HOST'] ?? $defaultHost, 'port' => (int)($_ENV['MYSQL_PORT'] ?? 3306), 'dbname' => $_ENV['MYSQL_DATABASE'] ?? 'azuracast', 'user' => $_ENV['MYSQL_USER'] ?? 'azuracast', @@ -119,8 +121,10 @@ class UptimeWait protected function checkRedis(): bool { + $defaultHost = $this->isStandalone() ? 'localhost' : 'redis'; + $enableRedis = $this->envToBool($_ENV['ENABLE_REDIS'] ?? true); - $redisHost = $_ENV['REDIS_HOST'] ?? 'redis'; + $redisHost = $_ENV['REDIS_HOST'] ?? $defaultHost; $redisPort = (int)($_ENV['REDIS_PORT'] ?? 6379); $redisDb = (int)($_ENV['REDIS_DB'] ?? 1); @@ -157,6 +161,11 @@ class UptimeWait || '1' === $value; } + protected function isStandalone(): bool + { + return $this->envToBool($_ENV['DOCKER_IS_STANDALONE'] ?? false); + } + protected function println(string $line): void { echo $line . "\n"; diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 8ffee98e1..f364a2647 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,28 +1,13 @@ -services : - web : - build : - context : . - volumes : +services: + web: + build: + context: . + ports: + - "127.0.0.1:3306:3306" + - "127.0.0.1:6379:6379" + volumes: - ./util/local_ssl:/etc/nginx/certs - ./vendor:/var/azuracast/www/vendor - .:/var/azuracast/www extra_hosts: - "host.docker.internal:host-gateway" - - mariadb : - build : - context : ../docker-azuracast-db - ports : - - "127.0.0.1:3306:3306" - - redis : - build : - context : ../docker-azuracast-redis - ports : - - "127.0.0.1:6379:6379" - - stations : - build : - context : ../docker-azuracast-radio - volumes : - - ./util/local_ssl:/etc/nginx/certs diff --git a/docker-compose.installer.yml b/docker-compose.installer.yml index 4ce557b8f..a2fa7e94e 100644 --- a/docker-compose.installer.yml +++ b/docker-compose.installer.yml @@ -1,10 +1,10 @@ services : installer : - container_name : azuracast_installer - image : 'ghcr.io/azuracast/web:${AZURACAST_VERSION:-latest}' - volumes : + container_name: azuracast_installer + image: 'ghcr.io/azuracast/web:${AZURACAST_VERSION:-latest}' + volumes: - './:/installer' - restart : 'no' - user : root - entrypoint : docker_installer - command : install + restart: 'no' + user: root + entrypoint: minimal_init + command: docker_installer install diff --git a/docker-compose.sample.yml b/docker-compose.sample.yml index 64878808d..d24b31f94 100644 --- a/docker-compose.sample.yml +++ b/docker-compose.sample.yml @@ -12,83 +12,14 @@ services: web: - container_name: azuracast_web - image: "ghcr.io/azuracast/web:${AZURACAST_VERSION:-latest}" + container_name: azuracast + image: "ghcr.io/azuracast/azuracast:${AZURACAST_VERSION:-latest}" # Want to customize the HTTP/S ports? Follow the instructions here: # https://docs.azuracast.com/en/administration/docker#using-non-standard-ports ports: - '${AZURACAST_HTTP_PORT:-80}:80' - '${AZURACAST_HTTPS_PORT:-443}:443' - '${AZURACAST_SFTP_PORT:-2022}:2022' - depends_on: - - mariadb - - stations - - redis - env_file: azuracast.env - environment: - LANG: ${LANG:-en_US.UTF-8} - AZURACAST_DC_REVISION: 13 - AZURACAST_VERSION: ${AZURACAST_VERSION:-latest} - AZURACAST_SFTP_PORT: ${AZURACAST_SFTP_PORT:-2022} - NGINX_TIMEOUT: ${NGINX_TIMEOUT:-1800} - LETSENCRYPT_HOST: ${LETSENCRYPT_HOST:-} - LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL:-} - PUID: ${AZURACAST_PUID:-1000} - PGID: ${AZURACAST_PGID:-1000} - volumes: - - letsencrypt:/etc/nginx/certs - - letsencrypt_acme:/etc/acme.sh - - www_vendor:/var/azuracast/www/vendor - - www_uploads:/var/azuracast/uploads - - tmp_data:/var/azuracast/www_tmp - - station_data:/var/azuracast/stations - - shoutcast2_install:/var/azuracast/servers/shoutcast2 - - geolite_install:/var/azuracast/geoip - - sftpgo_data:/var/azuracast/sftpgo/persist - - backups:/var/azuracast/backups - networks: - - frontend - - backend - restart: unless-stopped - ulimits: &default-ulimits - nofile: - soft: 65536 - hard: 65536 - logging: &default-logging - options: - max-size: "1m" - max-file: "5" - - mariadb: - container_name: azuracast_mariadb - image: "ghcr.io/azuracast/db:${AZURACAST_VERSION:-latest}" - volumes: - - db_data:/var/lib/mysql - env_file: azuracast.env - networks: - - backend - restart: unless-stopped - logging: *default-logging - - redis: - container_name: azuracast_redis - image: "ghcr.io/azuracast/redis:${AZURACAST_VERSION:-latest}" - sysctls: - net.core.somaxconn: 1024 - volumes: - - redis_data:/data - networks: - - backend - restart: unless-stopped - logging: *default-logging - - stations: - container_name: azuracast_stations - image: "ghcr.io/azuracast/radio:${AZURACAST_VERSION:-latest}" - environment: - PUID: ${AZURACAST_PUID:-1000} - PGID: ${AZURACAST_PGID:-1000} - ports: # This default mapping is the outgoing and incoming ports for the first 50 stations. # You can override this port mapping in your own docker-compose.override.yml file. # For instructions, see: @@ -240,24 +171,38 @@ services: - '8490:8490' - '8495:8495' - '8496:8496' + env_file: azuracast.env + environment: + LANG: ${LANG:-en_US.UTF-8} + AZURACAST_DC_REVISION: 14 + AZURACAST_VERSION: ${AZURACAST_VERSION:-latest} + AZURACAST_SFTP_PORT: ${AZURACAST_SFTP_PORT:-2022} + NGINX_TIMEOUT: ${NGINX_TIMEOUT:-1800} + LETSENCRYPT_HOST: ${LETSENCRYPT_HOST:-} + LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL:-} + PUID: ${AZURACAST_PUID:-1000} + PGID: ${AZURACAST_PGID:-1000} volumes: + - letsencrypt:/etc/nginx/certs + - letsencrypt_acme:/etc/acme.sh + - www_vendor:/var/azuracast/www/vendor + - www_uploads:/var/azuracast/uploads + - tmp_data:/var/azuracast/www_tmp - station_data:/var/azuracast/stations - shoutcast2_install:/var/azuracast/servers/shoutcast2 - - letsencrypt:/etc/nginx/certs - - tmp_data:/var/azuracast/www_tmp - networks: - - frontend - - backend - init: true + - geolite_install:/var/azuracast/geoip + - sftpgo_data:/var/azuracast/sftpgo/persist + - backups:/var/azuracast/backups + - db_data:/var/lib/mysql restart: unless-stopped - ulimits: *default-ulimits - logging: *default-logging - -networks: - frontend: - driver: bridge - backend: - driver: bridge + ulimits: &default-ulimits + nofile: + soft: 65536 + hard: 65536 + logging: &default-logging + options: + max-size: "1m" + max-file: "5" volumes: db_data: { } @@ -270,5 +215,4 @@ volumes: www_vendor: { } www_uploads: { } tmp_data: { } - redis_data: { } backups: { } diff --git a/docker.sh b/docker.sh index 540e35bde..e62bf71a0 100755 --- a/docker.sh +++ b/docker.sh @@ -378,6 +378,7 @@ run-installer() { fi curl -fsSL https://raw.githubusercontent.com/AzuraCast/AzuraCast/$AZURACAST_RELEASE_BRANCH/docker-compose.installer.yml -o docker-compose.installer.yml + docker-compose -p azuracast_installer -f docker-compose.installer.yml pull docker-compose -p azuracast_installer -f docker-compose.installer.yml run --rm installer install "$@" @@ -454,14 +455,6 @@ install-dev() { fi fi - if [[ ! -d ../docker-azuracast-radio ]]; then - if ask "Clone related repositories?" Y; then - git clone https://github.com/AzuraCast/docker-azuracast-db.git ../docker-azuracast-db - git clone https://github.com/AzuraCast/docker-azuracast-redis.git ../docker-azuracast-redis - git clone https://github.com/AzuraCast/docker-azuracast-radio.git ../docker-azuracast-radio - fi - fi - if [[ ! -f docker-compose.yml ]]; then cp docker-compose.sample.yml docker-compose.yml fi @@ -485,7 +478,7 @@ install-dev() { fi docker-compose build - docker-compose run --rm --user="azuracast" web azuracast_install "$@" + docker-compose run --rm web azuracast_install "$@" docker-compose -f frontend/docker-compose.yml build docker-compose -f frontend/docker-compose.yml run --rm frontend npm run dev-build @@ -563,7 +556,7 @@ update() { docker volume rm azuracast_tmp_data docker volume rm azuracast_redis_data - docker-compose run --rm --user="azuracast" web azuracast_update "$@" + docker-compose run --rm web azuracast_update "$@" docker-compose up -d if ask "Clean up all stopped Docker containers and images to save space?" Y; then @@ -654,7 +647,7 @@ backup() { .env --file .env set AZURACAST_PGID="$(id -g)" fi - docker-compose run --rm web azuracast_cli azuracast:backup "/var/azuracast/backups/${BACKUP_FILENAME}" "$@" + docker-compose exec web azuracast_cli azuracast:backup "/var/azuracast/backups/${BACKUP_FILENAME}" "$@" # Move from Docker volume to local filesystem docker run --rm -v "azuracast_backups:/backup_src" \ diff --git a/src/Environment.php b/src/Environment.php index b11d4e7eb..b2da8ae1e 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -34,6 +34,7 @@ class Environment public const ASSET_URL = 'ASSETS_URL'; public const DOCKER_REVISION = 'AZURACAST_DC_REVISION'; + public const DOCKER_IS_STANDALONE = 'DOCKER_IS_STANDALONE'; public const LANG = 'LANG'; @@ -208,22 +209,32 @@ class Environment return ($compareVersion >= $version); } - public function getUriToWeb(): UriInterface + public function isDockerStandalone(): bool { - if ($this->isDocker()) { - return $this->isDockerRevisionAtLeast(5) - ? new Uri('http://web') - : new Uri('http://nginx'); + if (!$this->isDocker()) { + return false; } - return new Uri('http://127.0.0.1'); + return self::envToBool($this->data[self::DOCKER_IS_STANDALONE] ?? false); + } + + public function getUriToWeb(): UriInterface + { + return match (true) { + $this->isDockerStandalone() => new Uri('http://127.0.0.1'), + $this->isDockerRevisionAtLeast(5) => new Uri('http://web'), + $this->isDocker() => new Uri('http://nginx'), + default => new Uri('http://127.0.0.1') + }; } public function getUriToStations(): UriInterface { - return $this->isDocker() - ? new Uri('http://stations') - : new Uri('http://127.0.0.1'); + return match (true) { + $this->isDockerStandalone() => new Uri('http://127.0.0.1'), + $this->isDocker() => new Uri('http://stations'), + default => new Uri('http://127.0.0.1'), + }; } public function getLang(): ?string @@ -296,11 +307,17 @@ class Environment */ public function getDatabaseSettings(): array { + $defaultHost = match (true) { + $this->isDockerStandalone() => 'localhost', + $this->isDocker() => 'mariadb', + default => 'localhost' + }; + return [ - 'host' => $this->data[self::DB_HOST] ?? ($this->isDocker() ? 'mariadb' : 'localhost'), - 'port' => (int)($this->data[self::DB_PORT] ?? 3306), - 'dbname' => $this->data[self::DB_NAME] ?? 'azuracast', - 'user' => $this->data[self::DB_USER] ?? 'azuracast', + 'host' => $this->data[self::DB_HOST] ?? $defaultHost, + 'port' => (int)($this->data[self::DB_PORT] ?? 3306), + 'dbname' => $this->data[self::DB_NAME] ?? 'azuracast', + 'user' => $this->data[self::DB_USER] ?? 'azuracast', 'password' => $this->data[self::DB_PASSWORD] ?? 'azur4c457', ]; } @@ -315,10 +332,16 @@ class Environment */ public function getRedisSettings(): array { + $defaultHost = match (true) { + $this->isDockerStandalone() => 'localhost', + $this->isDocker() => 'redis', + default => 'localhost' + }; + return [ - 'host' => $this->data[self::REDIS_HOST] ?? ($this->isDocker() ? 'redis' : 'localhost'), + 'host' => $this->data[self::REDIS_HOST] ?? $defaultHost, 'port' => (int)($this->data[self::REDIS_PORT] ?? 6379), - 'db' => (int)($this->data[self::REDIS_DB] ?? 1), + 'db' => (int)($this->data[self::REDIS_DB] ?? 1), ]; } @@ -337,6 +360,15 @@ class Environment return $this->data[self::PROFILING_EXTENSION_HTTP_KEY] ?? 'dev'; } + public static function getDefaultsForEnvironment(Environment $existingEnv): self + { + return new self([ + self::IS_CLI => $existingEnv->isCli(), + self::IS_DOCKER => $existingEnv->isDocker(), + self::DOCKER_IS_STANDALONE => $existingEnv->isDockerStandalone(), + ]); + } + public static function envToBool(mixed $value): bool { if (is_bool($value)) { diff --git a/src/Installer/Command/InstallCommand.php b/src/Installer/Command/InstallCommand.php index 6a8028af5..d4d8fb130 100644 --- a/src/Installer/Command/InstallCommand.php +++ b/src/Installer/Command/InstallCommand.php @@ -102,11 +102,11 @@ class InstallCommand extends Command $locale = SupportedLocales::getValidLocale($azuracastEnv[Environment::LANG] ?? null); $locale->register($this->environment); - $envConfig = EnvFile::getConfiguration(); - $env->setFromDefaults(); + $envConfig = EnvFile::getConfiguration($this->environment); + $env->setFromDefaults($this->environment); - $azuracastEnvConfig = AzuraCastEnvFile::getConfiguration(); - $azuracastEnv->setFromDefaults(); + $azuracastEnvConfig = AzuraCastEnvFile::getConfiguration($this->environment); + $azuracastEnv->setFromDefaults($this->environment); // Apply values passed via flags if (null !== $releaseChannel) { @@ -156,6 +156,16 @@ class InstallCommand extends Command $azuracastEnv['MYSQL_RANDOM_ROOT_PASSWORD'] = 'yes'; } + // Special fixes for transitioning to standalone installations. + if ($this->environment->isDockerStandalone()) { + if ('mariadb' === $azuracastEnv['MYSQL_HOST']) { + unset($azuracastEnv['MYSQL_HOST']); + } + if ('redis' === $azuracastEnv['REDIS_HOST']) { + unset($azuracastEnv['REDIS_HOST']); + } + } + // Display header messages if ($isNewInstall) { $io->title( @@ -253,8 +263,8 @@ class InstallCommand extends Command __('Writing configuration files...') ); - $envStr = $env->writeToFile(); - $azuracastEnvStr = $azuracastEnv->writeToFile(); + $envStr = $env->writeToFile($this->environment); + $azuracastEnvStr = $azuracastEnv->writeToFile($this->environment); if ($io->isVerbose()) { $io->section($env->getBasename()); @@ -289,10 +299,12 @@ class InstallCommand extends Command $sampleFile = $this->environment->getBaseDirectory() . '/docker-compose.sample.yml'; $yaml = Yaml::parseFile($sampleFile); + $isStandalone = $this->environment->isDockerStandalone(); + // Parse port listing and convert into YAML format. $ports = $env['AZURACAST_STATION_PORTS'] ?? ''; - $envConfig = $env::getConfiguration(); + $envConfig = $env::getConfiguration($this->environment); $defaultPorts = $envConfig['AZURACAST_STATION_PORTS']['default']; if (!empty($ports) && 0 !== strcmp($ports, $defaultPorts)) { @@ -316,7 +328,18 @@ class InstallCommand extends Command } if (!empty($yamlPorts)) { - $yaml['services']['stations']['ports'] = $yamlPorts; + if ($isStandalone) { + $existingPorts = []; + foreach ($yaml['services']['ports'] as $port) { + if (str_starts_with('$', $port)) { + $existingPorts[] = $port; + } + } + + $yaml['services']['web']['ports'] = array_merge($existingPorts, $yamlPorts); + } else { + $yaml['services']['stations']['ports'] = $yamlPorts; + } } if (!empty($nginxRadioPorts)) { $nginxRadioPortsStr = '(' . implode('|', $nginxRadioPorts) . ')'; @@ -329,9 +352,11 @@ class InstallCommand extends Command } // Remove Redis if it's not enabled. - $enableRedis = $azuracastEnv->getAsBool(Environment::ENABLE_REDIS, true); - if (!$enableRedis) { - unset($yaml['services']['redis']); + if (!$isStandalone) { + $enableRedis = $azuracastEnv->getAsBool(Environment::ENABLE_REDIS, true); + if (!$enableRedis) { + unset($yaml['services']['redis']); + } } // Remove privileged-mode settings if not enabled. diff --git a/src/Installer/EnvFiles/AbstractEnvFile.php b/src/Installer/EnvFiles/AbstractEnvFile.php index f8f560c00..8ea4dc5ef 100644 --- a/src/Installer/EnvFiles/AbstractEnvFile.php +++ b/src/Installer/EnvFiles/AbstractEnvFile.php @@ -32,12 +32,12 @@ abstract class AbstractEnvFile implements ArrayAccess return basename($this->path); } - public function setFromDefaults(): void + public function setFromDefaults(Environment $environment): void { $currentVars = array_filter($this->data); $defaults = []; - foreach (static::getConfiguration() as $key => $keyInfo) { + foreach (static::getConfiguration($environment) as $key => $keyInfo) { if (isset($keyInfo['default'])) { $defaults[$key] = $keyInfo['default'] ?? null; } @@ -82,7 +82,7 @@ abstract class AbstractEnvFile implements ArrayAccess unset($this->data[$offset]); } - public function writeToFile(): string + public function writeToFile(Environment $environment): string { $values = array_filter($this->data); @@ -93,7 +93,7 @@ abstract class AbstractEnvFile implements ArrayAccess '', ]; - foreach (static::getConfiguration() as $key => $keyInfo) { + foreach (static::getConfiguration($environment) as $key => $keyInfo) { $envFile[] = '# ' . ($keyInfo['name'] ?? $key); if (!empty($keyInfo['description'])) { @@ -180,7 +180,7 @@ abstract class AbstractEnvFile implements ArrayAccess /** * @return mixed[] */ - abstract public static function getConfiguration(): array; + abstract public static function getConfiguration(Environment $environment): array; abstract public static function buildPathFromBase(string $baseDir): string; diff --git a/src/Installer/EnvFiles/AzuraCastEnvFile.php b/src/Installer/EnvFiles/AzuraCastEnvFile.php index f07161b04..195c4eaa3 100644 --- a/src/Installer/EnvFiles/AzuraCastEnvFile.php +++ b/src/Installer/EnvFiles/AzuraCastEnvFile.php @@ -14,12 +14,12 @@ use function __; class AzuraCastEnvFile extends AbstractEnvFile { /** @inheritDoc */ - public static function getConfiguration(): array + public static function getConfiguration(Environment $environment): array { static $config = null; if (null === $config) { - $emptyEnv = new Environment([]); + $emptyEnv = Environment::getDefaultsForEnvironment($environment); $defaults = $emptyEnv->toArray(); $langOptions = []; diff --git a/src/Installer/EnvFiles/EnvFile.php b/src/Installer/EnvFiles/EnvFile.php index 1d97defdd..0e03fe6bb 100644 --- a/src/Installer/EnvFiles/EnvFile.php +++ b/src/Installer/EnvFiles/EnvFile.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Installer\EnvFiles; +use App\Environment; use App\Radio\Configuration; use function __; @@ -11,7 +12,7 @@ use function __; class EnvFile extends AbstractEnvFile { /** @inheritDoc */ - public static function getConfiguration(): array + public static function getConfiguration(Environment $environment): array { static $config = null; diff --git a/src/Radio/Backend/Liquidsoap/ConfigWriter.php b/src/Radio/Backend/Liquidsoap/ConfigWriter.php index a774e8185..3e69eb5c6 100644 --- a/src/Radio/Backend/Liquidsoap/ConfigWriter.php +++ b/src/Radio/Backend/Liquidsoap/ConfigWriter.php @@ -125,7 +125,11 @@ class ConfigWriter implements EventSubscriberInterface $configDir = $station->getRadioConfigDir(); $pidfile = $configDir . DIRECTORY_SEPARATOR . 'liquidsoap.pid'; - $telnetBindAddr = $this->environment->isDocker() ? '0.0.0.0' : '127.0.0.1'; + $telnetBindAddr = match (true) { + $this->environment->isDockerStandalone() => '127.0.0.1', + $this->environment->isDocker() => '0.0.0.0', + default => '127.0.0.1', + }; $telnetPort = $this->liquidsoap->getTelnetPort($station); $stationTz = self::cleanUpString($station->getTimezone()); diff --git a/util/docker/web/add_user.sh b/util/docker/common/add_user.sh similarity index 80% rename from util/docker/web/add_user.sh rename to util/docker/common/add_user.sh index bb427e0f5..a0ba41907 100644 --- a/util/docker/web/add_user.sh +++ b/util/docker/common/add_user.sh @@ -13,7 +13,8 @@ adduser --home /var/azuracast --disabled-password --gecos "" azuracast usermod -aG docker_env azuracast usermod -aG www-data azuracast -mkdir -p /var/azuracast/www /var/azuracast/backups /var/azuracast/www_tmp \ +mkdir -p /var/azuracast/www /var/azuracast/stations /var/azuracast/servers/shoutcast2 \ + /var/azuracast/backups /var/azuracast/www_tmp \ /var/azuracast/uploads /var/azuracast/geoip /var/azuracast/dbip chown -R azuracast:azuracast /var/azuracast diff --git a/util/docker/web/buildconfig b/util/docker/common/buildconfig similarity index 100% rename from util/docker/web/buildconfig rename to util/docker/common/buildconfig diff --git a/util/docker/web/cleanup.sh b/util/docker/common/cleanup.sh similarity index 100% rename from util/docker/web/cleanup.sh rename to util/docker/common/cleanup.sh diff --git a/util/docker/common/post_setup.sh b/util/docker/common/post_setup.sh new file mode 100644 index 000000000..618d53d9a --- /dev/null +++ b/util/docker/common/post_setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +chmod -R a+x /usr/local/bin +chmod -R +x /etc/my_init.d +chmod -R +x /etc/my_init.pre_shutdown.d +chmod -R +x /etc/my_init.post_shutdown.d + +ln -s /etc/service.minimal/* /etc/service +ln -s /etc/service.full/* /etc/service + +chmod -R +x /etc/service.minimal +chmod -R +x /etc/service.full +chmod -R +x /etc/service diff --git a/util/docker/web/prepare.sh b/util/docker/common/prepare.sh similarity index 87% rename from util/docker/web/prepare.sh rename to util/docker/common/prepare.sh index 47dc6ff72..a7186ae92 100644 --- a/util/docker/web/prepare.sh +++ b/util/docker/common/prepare.sh @@ -67,4 +67,14 @@ chmod 700 /etc/container_environment groupadd -g 8377 docker_env chown :docker_env /etc/container_environment.sh /etc/container_environment.json chmod 640 /etc/container_environment.sh /etc/container_environment.json -ln -s /etc/container_environment.sh /etc/profile.d/ \ No newline at end of file +ln -s /etc/container_environment.sh /etc/profile.d/ + +# Install runit and other common scripts. +$minimal_apt_get_install runit gosu curl wget tar zip unzip git rsync tzdata gpg-agent openssh-client + +# Add scripts +cp -rT /bd_build/scripts/ /usr/local/bin +chmod -R a+x /usr/local/bin + +mkdir -p /etc/service.full/ +mkdir -p /etc/service.minimal/ diff --git a/util/docker/web/scripts/my_init b/util/docker/common/scripts/my_init similarity index 88% rename from util/docker/web/scripts/my_init rename to util/docker/common/scripts/my_init index 19ec16258..4ae505280 100644 --- a/util/docker/web/scripts/my_init +++ b/util/docker/common/scripts/my_init @@ -30,41 +30,33 @@ terminated_child_processes = {} _find_unsafe = re.compile(r'[^\w@%+=:,./-]').search - class AlarmException(Exception): pass - def error(message): if log_level >= LOG_LEVEL_ERROR: sys.stderr.write("*** %s\n" % message) - def warn(message): if log_level >= LOG_LEVEL_WARN: sys.stderr.write("*** %s\n" % message) - def info(message): if log_level >= LOG_LEVEL_INFO: sys.stderr.write("*** %s\n" % message) - def debug(message): if log_level >= LOG_LEVEL_DEBUG: sys.stderr.write("*** %s\n" % message) - def ignore_signals_and_raise_keyboard_interrupt(signame): signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) raise KeyboardInterrupt(signame) - def raise_alarm_exception(): raise AlarmException('Alarm') - def listdir(path): try: result = os.stat(path) @@ -75,14 +67,12 @@ def listdir(path): else: return [] - def is_exe(path): try: return os.path.isfile(path) and os.access(path, os.X_OK) except OSError: return False - def import_envvars(clear_existing_environment=True, override_existing_environment=True): if not os.path.exists("/etc/container_environment"): return @@ -101,7 +91,6 @@ def import_envvars(clear_existing_environment=True, override_existing_environmen if override_existing_environment or name not in os.environ: os.environ[name] = value - def export_envvars(to_dir=True): if not os.path.exists("/etc/container_environment"): return @@ -118,7 +107,6 @@ def export_envvars(to_dir=True): with open("/etc/container_environment.json", "w") as f: f.write(json.dumps(dict(os.environ))) - def shquote(s): """Return a shell-escaped version of the string *s*.""" if not s: @@ -130,16 +118,13 @@ def shquote(s): # the string $'b is then quoted as '$'"'"'b' return "'" + s.replace("'", "'\"'\"'") + "'" - def sanitize_shenvname(s): """Return string with [0-9a-zA-Z_] characters""" return re.sub(SHENV_NAME_WHITELIST_REGEX, "_", s) - # Waits for the child process with the given PID, while at the same time # reaping any other child processes that have exited (e.g. adopted child # processes that have terminated). - def waitpid_reap_other_children(pid): global terminated_child_processes @@ -172,7 +157,6 @@ def waitpid_reap_other_children(pid): raise return status - def stop_child_process(name, pid, signo=signal.SIGTERM, time_limit=KILL_PROCESS_TIMEOUT): info("Shutting down %s (PID %d)..." % (name, pid)) try: @@ -198,7 +182,6 @@ def stop_child_process(name, pid, signo=signal.SIGTERM, time_limit=KILL_PROCESS_ finally: signal.alarm(0) - def run_command_killable(*argv): filename = argv[0] status = None @@ -216,13 +199,11 @@ def run_command_killable(*argv): error("%s failed with status %d\n" % (filename, os.WEXITSTATUS(status))) sys.exit(1) - def run_command_killable_and_import_envvars(*argv): run_command_killable(*argv) import_envvars() export_envvars(False) - def kill_all_processes(time_limit): info("Killing all processes...") try: @@ -264,7 +245,6 @@ def run_startup_files(): info("Running /etc/rc.local...") run_command_killable_and_import_envvars("/etc/rc.local") - def run_pre_shutdown_scripts(): debug("Running pre-shutdown scripts...") @@ -275,7 +255,6 @@ def run_pre_shutdown_scripts(): info("Running %s..." % filename) run_command_killable(filename) - def run_post_shutdown_scripts(): debug("Running post-shutdown scripts...") @@ -286,31 +265,26 @@ def run_post_shutdown_scripts(): info("Running %s..." % filename) run_command_killable(filename) - -def start_runit(): +def start_runit(runit_services_dir): info("Booting runit daemon...") - pid = os.spawnl(os.P_NOWAIT, "/usr/bin/runsvdir", "/usr/bin/runsvdir", - "-P", "/etc/service") + pid = os.spawnl(os.P_NOWAIT, "/usr/bin/runsvdir", "/usr/bin/runsvdir", "-P", runit_services_dir) info("Runit started as PID %d" % pid) return pid - def wait_for_runit_or_interrupt(pid): status = waitpid_reap_other_children(pid) return (True, status) - -def shutdown_runit_services(quiet=False): +def shutdown_runit_services(runit_services_dir, quiet=False): if not quiet: debug("Begin shutting down runit services...") - os.system("/usr/bin/sv -w %d force-stop /etc/service/* > /dev/null" % KILL_PROCESS_TIMEOUT) + os.system("/usr/bin/sv -w %d force-stop %s/* > /dev/null" % (KILL_PROCESS_TIMEOUT, runit_services_dir)) - -def wait_for_runit_services(): +def wait_for_runit_services(runit_services_dir): debug("Waiting for runit services to exit...") done = False while not done: - done = os.system("/usr/bin/sv status /etc/service/* | grep -q '^run:'") != 0 + done = os.system("/usr/bin/sv status %s/* | grep -q '^run:'" % runit_services_dir) != 0 if not done: time.sleep(0.1) # According to https://github.com/phusion/baseimage-docker/issues/315 @@ -318,32 +292,28 @@ def wait_for_runit_services(): # not to shutdown services that are already being started. # So during shutdown we repeatedly instruct Runit to shutdown # services. - shutdown_runit_services(True) - - -def install_insecure_key(): - info("Installing insecure SSH key for user root") - run_command_killable("/usr/sbin/enable_insecure_key") - + shutdown_runit_services(runit_services_dir, True) def main(args): import_envvars(False, False) export_envvars() - if args.enable_insecure_key: - install_insecure_key() - if not args.skip_startup_files: run_startup_files() runit_exited = False exit_code = None - + if not args.skip_runit: - runit_pid = start_runit() + runit_services_dir = '/etc/service.minimal' + if len(args.main_command) == 0 or args.no_main_command: + runit_services_dir = '/etc/service' + + runit_pid = start_runit(runit_services_dir) + try: exit_status = None - if len(args.main_command) == 0: + if len(args.main_command) == 0 or args.no_main_command: runit_exited, exit_code = wait_for_runit_or_interrupt(runit_pid) if runit_exited: if exit_code is None: @@ -353,52 +323,63 @@ def main(args): exit_status = os.WEXITSTATUS(exit_code) info("Runit exited with status %d" % exit_status) else: - info("Running %s..." % " ".join(args.main_command)) - pid = os.spawnvp(os.P_NOWAIT, args.main_command[0], args.main_command) + main_command = ["uptime_wait"] + args.main_command + + info("Running %s..." % " ".join(main_command)) + pid = os.spawnvp(os.P_NOWAIT, main_command[0], main_command) try: exit_code = waitpid_reap_other_children(pid) if exit_code is None: - info("%s exited with unknown status." % args.main_command[0]) + info("%s exited with unknown status." % main_command[0]) exit_status = 1 else: exit_status = os.WEXITSTATUS(exit_code) - info("%s exited with status %d." % (args.main_command[0], exit_status)) + info("%s exited with status %d." % (main_command[0], exit_status)) except KeyboardInterrupt: - stop_child_process(args.main_command[0], pid) + stop_child_process(main_command[0], pid) raise except BaseException: warn("An error occurred. Aborting.") - stop_child_process(args.main_command[0], pid) + stop_child_process(main_command[0], pid) raise sys.exit(exit_status) finally: if not args.skip_runit: run_pre_shutdown_scripts() - shutdown_runit_services() + shutdown_runit_services(runit_services_dir) + if not runit_exited: stop_child_process("runit daemon", runit_pid) - wait_for_runit_services() + + wait_for_runit_services(runit_services_dir) run_post_shutdown_scripts() # Parse options. parser = argparse.ArgumentParser(description='Initialize the system.') + parser.add_argument('main_command', metavar='MAIN_COMMAND', type=str, nargs='*', help='The main command to run. (default: runit)') -parser.add_argument('--enable-insecure-key', dest='enable_insecure_key', + +parser.add_argument('--no-main-command', dest='no_main_command', action='store_const', const=True, default=False, - help='Install the insecure SSH key') + help='Flag to provide as main command in the absence of one.') + parser.add_argument('--skip-startup-files', dest='skip_startup_files', action='store_const', const=True, default=False, help='Skip running /etc/my_init.d/* and /etc/rc.local') + parser.add_argument('--skip-runit', dest='skip_runit', action='store_const', const=True, default=False, help='Do not run runit services') + parser.add_argument('--no-kill-all-on-exit', dest='kill_all_on_exit', action='store_const', const=False, default=True, help='Don\'t kill all processes on the system upon exiting') + parser.add_argument('--quiet', dest='log_level', action='store_const', const=LOG_LEVEL_WARN, default=LOG_LEVEL_INFO, help='Only print warnings and errors') + args = parser.parse_args() log_level = args.log_level @@ -410,6 +391,7 @@ if args.skip_runit and len(args.main_command) == 0: signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGTERM')) signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt('SIGINT')) signal.signal(signal.SIGALRM, lambda signum, frame: raise_alarm_exception()) + try: main(args) except KeyboardInterrupt: @@ -417,4 +399,4 @@ except KeyboardInterrupt: exit(2) finally: if args.kill_all_on_exit: - kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT) \ No newline at end of file + kill_all_processes(KILL_ALL_PROCESSES_TIMEOUT) diff --git a/util/docker/mariadb/mariadb/db.cnf.tmpl b/util/docker/mariadb/mariadb/db.cnf.tmpl new file mode 100644 index 000000000..7a58e49c9 --- /dev/null +++ b/util/docker/mariadb/mariadb/db.cnf.tmpl @@ -0,0 +1,11 @@ +[mysqld] +character-set-server=utf8mb4 + +slow_query_log = {{ default .Env.MYSQL_SLOW_QUERY_LOG "0" }} +slow_query_log_file = /var/log/mysql/slow.log +long_query_time = 0.2 + +max_connections = {{ default .Env.MYSQL_MAX_CONNECTIONS "100" }} + +[client] +default-character-set=utf8mb4 diff --git a/util/docker/mariadb/mariadb/db.sql b/util/docker/mariadb/mariadb/db.sql new file mode 100644 index 000000000..887014568 --- /dev/null +++ b/util/docker/mariadb/mariadb/db.sql @@ -0,0 +1,719 @@ +-- MySQL dump 10.17 Distrib 10.3.22-MariaDB, for debian-linux-gnu (x86_64) +-- +-- Host: mariadb Database: azuracast +-- ------------------------------------------------------ +-- Server version 10.4.13-MariaDB-1:10.4.13+maria~bionic-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES UTF8MB4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `azuracast`; +USE `azuracast`; + +ALTER DATABASE `azuracast` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci; + +-- +-- Table structure for table `analytics` +-- + +DROP TABLE IF EXISTS `analytics`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `analytics` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) DEFAULT NULL, + `type` varchar(15) NOT NULL, + `timestamp` int(11) NOT NULL, + `number_min` int(11) NOT NULL, + `number_max` int(11) NOT NULL, + `number_avg` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_EAC2E68821BDB235` (`station_id`), + KEY `search_idx` (`type`,`timestamp`), + CONSTRAINT `FK_EAC2E68821BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `api_keys` +-- + +DROP TABLE IF EXISTS `api_keys`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `api_keys` ( + `id` varchar(16) NOT NULL, + `user_id` int(11) NOT NULL, + `verifier` varchar(128) NOT NULL, + `comment` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_9579321FA76ED395` (`user_id`), + CONSTRAINT `FK_9579321FA76ED395` FOREIGN KEY (`user_id`) REFERENCES `users` (`uid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `app_migrations` +-- + +DROP TABLE IF EXISTS `app_migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `app_migrations` ( + `version` varchar(191) NOT NULL, + `executed_at` datetime DEFAULT NULL, + `execution_time` int(11) DEFAULT NULL, + PRIMARY KEY (`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `app_migrations` +-- + +LOCK TABLES `app_migrations` WRITE; +/*!40000 ALTER TABLE `app_migrations` DISABLE KEYS */; +INSERT INTO `app_migrations` VALUES ('App\\Entity\\Migration\\Version20161003041904',NULL,NULL),('App\\Entity\\Migration\\Version20161006030903',NULL,NULL),('App\\Entity\\Migration\\Version20161007021719',NULL,NULL),('App\\Entity\\Migration\\Version20161007195027',NULL,NULL),('App\\Entity\\Migration\\Version20161117000718',NULL,NULL),('App\\Entity\\Migration\\Version20161117161959',NULL,NULL),('App\\Entity\\Migration\\Version20161120032434',NULL,NULL),('App\\Entity\\Migration\\Version20161122035237',NULL,NULL),('App\\Entity\\Migration\\Version20170412210654',NULL,NULL),('App\\Entity\\Migration\\Version20170414205418',NULL,NULL),('App\\Entity\\Migration\\Version20170423202805',NULL,NULL),('App\\Entity\\Migration\\Version20170424042111',NULL,NULL),('App\\Entity\\Migration\\Version20170502202418',NULL,NULL),('App\\Entity\\Migration\\Version20170510082607',NULL,NULL),('App\\Entity\\Migration\\Version20170510085226',NULL,NULL),('App\\Entity\\Migration\\Version20170510091820',NULL,NULL),('App\\Entity\\Migration\\Version20170512023527',NULL,NULL),('App\\Entity\\Migration\\Version20170512082741',NULL,NULL),('App\\Entity\\Migration\\Version20170512094523',NULL,NULL),('App\\Entity\\Migration\\Version20170516073708',NULL,NULL),('App\\Entity\\Migration\\Version20170516205418',NULL,NULL),('App\\Entity\\Migration\\Version20170516214120',NULL,NULL),('App\\Entity\\Migration\\Version20170516215536',NULL,NULL),('App\\Entity\\Migration\\Version20170518100549',NULL,NULL),('App\\Entity\\Migration\\Version20170522052114',NULL,NULL),('App\\Entity\\Migration\\Version20170524090814',NULL,NULL),('App\\Entity\\Migration\\Version20170606173152',NULL,NULL),('App\\Entity\\Migration\\Version20170618013019',NULL,NULL),('App\\Entity\\Migration\\Version20170619044014',NULL,NULL),('App\\Entity\\Migration\\Version20170619171323',NULL,NULL),('App\\Entity\\Migration\\Version20170622223025',NULL,NULL),('App\\Entity\\Migration\\Version20170719045113',NULL,NULL),('App\\Entity\\Migration\\Version20170803050109',NULL,NULL),('App\\Entity\\Migration\\Version20170823204230',NULL,NULL),('App\\Entity\\Migration\\Version20170829030442',NULL,NULL),('App\\Entity\\Migration\\Version20170906080352',NULL,NULL),('App\\Entity\\Migration\\Version20170917175534',NULL,NULL),('App\\Entity\\Migration\\Version20171022005913',NULL,NULL),('App\\Entity\\Migration\\Version20171103075821',NULL,NULL),('App\\Entity\\Migration\\Version20171104014701',NULL,NULL),('App\\Entity\\Migration\\Version20171124184831',NULL,NULL),('App\\Entity\\Migration\\Version20171128121012',NULL,NULL),('App\\Entity\\Migration\\Version20171208093239',NULL,NULL),('App\\Entity\\Migration\\Version20171214104226',NULL,NULL),('App\\Entity\\Migration\\Version20180203201032',NULL,NULL),('App\\Entity\\Migration\\Version20180203203751',NULL,NULL),('App\\Entity\\Migration\\Version20180203214656',NULL,NULL),('App\\Entity\\Migration\\Version20180204210633',NULL,NULL),('App\\Entity\\Migration\\Version20180206105454',NULL,NULL),('App\\Entity\\Migration\\Version20180211192448',NULL,NULL),('App\\Entity\\Migration\\Version20180320052444',NULL,NULL),('App\\Entity\\Migration\\Version20180320061801',NULL,NULL),('App\\Entity\\Migration\\Version20180320070100',NULL,NULL),('App\\Entity\\Migration\\Version20180320163622',NULL,NULL),('App\\Entity\\Migration\\Version20180320171318',NULL,NULL),('App\\Entity\\Migration\\Version20180324053351',NULL,NULL),('App\\Entity\\Migration\\Version20180412055024',NULL,NULL),('App\\Entity\\Migration\\Version20180415235105',NULL,NULL),('App\\Entity\\Migration\\Version20180417041534',NULL,NULL),('App\\Entity\\Migration\\Version20180425025237',NULL,NULL),('App\\Entity\\Migration\\Version20180425050351',NULL,NULL),('App\\Entity\\Migration\\Version20180428062526',NULL,NULL),('App\\Entity\\Migration\\Version20180429013130',NULL,NULL),('App\\Entity\\Migration\\Version20180506022642',NULL,NULL),('App\\Entity\\Migration\\Version20180608130900',NULL,NULL),('App\\Entity\\Migration\\Version20180716185805',NULL,NULL),('App\\Entity\\Migration\\Version20180818223558',NULL,NULL),('App\\Entity\\Migration\\Version20180826011103',NULL,NULL),('App\\Entity\\Migration\\Version20180826043500',NULL,NULL),('App\\Entity\\Migration\\Version20180830003036',NULL,NULL),('App\\Entity\\Migration\\Version20180909035413',NULL,NULL),('App\\Entity\\Migration\\Version20180909060758',NULL,NULL),('App\\Entity\\Migration\\Version20180909174026',NULL,NULL),('App\\Entity\\Migration\\Version20181016144143',NULL,NULL),('App\\Entity\\Migration\\Version20181025232600',NULL,NULL),('App\\Entity\\Migration\\Version20181120100629',NULL,NULL),('App\\Entity\\Migration\\Version20181126073334',NULL,NULL),('App\\Entity\\Migration\\Version20181202180617',NULL,NULL),('App\\Entity\\Migration\\Version20181211220707',NULL,NULL),('App\\Entity\\Migration\\Version20190124132556',NULL,NULL),('App\\Entity\\Migration\\Version20190128035353',NULL,NULL),('App\\Entity\\Migration\\Version20190314074747',NULL,NULL),('App\\Entity\\Migration\\Version20190314203550',NULL,NULL),('App\\Entity\\Migration\\Version20190315002523',NULL,NULL),('App\\Entity\\Migration\\Version20190324040155',NULL,NULL),('App\\Entity\\Migration\\Version20190326051220',NULL,NULL),('App\\Entity\\Migration\\Version20190331215627','2020-06-09 02:19:27',NULL),('App\\Entity\\Migration\\Version20190402224811','2020-06-09 02:19:27',NULL),('App\\Entity\\Migration\\Version20190429025906','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190429040410','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190513163051','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190517122806','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190624135222','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190715231530','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190719220017','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190810234058','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190813210707','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190818003805','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20190930201744','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20191024185005','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20191101065730','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20191101075303','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200105190343','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200123004338','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200124183957','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200127071620','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200129010322','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200130094654','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200213052842','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200216121137','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200217114139','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200310204315','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200321174535','2020-06-09 02:19:28',NULL),('App\\Entity\\Migration\\Version20200402212036','2020-06-09 02:19:29',NULL),('App\\Entity\\Migration\\Version20200417082209','2020-06-09 02:19:29',NULL),('App\\Entity\\Migration\\Version20200503005148','2020-06-09 02:19:29',NULL),('App\\Entity\\Migration\\Version20200514061004','2020-06-09 02:19:29',NULL),('App\\Entity\\Migration\\Version20200604073027','2020-06-09 02:19:29',NULL),('App\\Entity\\Migration\\Version20200604075356','2020-07-04 17:05:25',137); +/*!40000 ALTER TABLE `app_migrations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `audit_log` +-- + +DROP TABLE IF EXISTS `audit_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `audit_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `timestamp` int(11) NOT NULL, + `operation` smallint(6) NOT NULL, + `class` varchar(255) NOT NULL, + `identifier` varchar(255) NOT NULL, + `target_class` varchar(255) DEFAULT NULL, + `target` varchar(255) DEFAULT NULL, + `changes` longtext NOT NULL COMMENT '(DC2Type:array)', + `user` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `custom_field` +-- + +DROP TABLE IF EXISTS `custom_field`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_field` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `short_name` varchar(100) DEFAULT NULL, + `auto_assign` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `listener` +-- + +DROP TABLE IF EXISTS `listener`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `listener` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `listener_uid` int(11) NOT NULL, + `listener_ip` varchar(45) NOT NULL, + `listener_user_agent` varchar(255) NOT NULL, + `timestamp_start` int(11) NOT NULL, + `timestamp_end` int(11) NOT NULL, + `listener_hash` varchar(32) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_959C342221BDB235` (`station_id`), + KEY `idx_timestamps` (`timestamp_end`,`timestamp_start`), + CONSTRAINT `FK_959C342221BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `relays` +-- + +DROP TABLE IF EXISTS `relays`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `relays` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `base_url` varchar(255) NOT NULL, + `name` varchar(100) DEFAULT NULL, + `is_visible_on_public_pages` tinyint(1) NOT NULL, + `nowplaying` longtext DEFAULT NULL COMMENT '(DC2Type:array)', + `created_at` int(11) NOT NULL, + `updated_at` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `role` +-- + +DROP TABLE IF EXISTS `role`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `role` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `role_permissions` +-- + +DROP TABLE IF EXISTS `role_permissions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `role_permissions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `role_id` int(11) NOT NULL, + `station_id` int(11) DEFAULT NULL, + `action_name` varchar(50) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `role_permission_unique_idx` (`role_id`,`action_name`,`station_id`), + KEY `IDX_1FBA94E6D60322AC` (`role_id`), + KEY `IDX_1FBA94E621BDB235` (`station_id`), + CONSTRAINT `FK_1FBA94E621BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_1FBA94E6D60322AC` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settings` +-- + +DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `settings` ( + `setting_key` varchar(64) NOT NULL, + `setting_value` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + PRIMARY KEY (`setting_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sftp_user` +-- + +DROP TABLE IF EXISTS `sftp_user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `sftp_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) DEFAULT NULL, + `username` varchar(32) NOT NULL, + `password` varchar(255) NOT NULL, + `public_keys` longtext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `username_idx` (`username`), + KEY `IDX_3C32EA3421BDB235` (`station_id`), + CONSTRAINT `FK_3C32EA3421BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `song_history` +-- + +DROP TABLE IF EXISTS `song_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `song_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `song_id` varchar(50) NOT NULL, + `station_id` int(11) NOT NULL, + `timestamp_start` int(11) NOT NULL, + `listeners_start` int(11) DEFAULT NULL, + `timestamp_end` int(11) NOT NULL, + `listeners_end` smallint(6) DEFAULT NULL, + `delta_total` smallint(6) NOT NULL, + `delta_positive` smallint(6) NOT NULL, + `delta_negative` smallint(6) NOT NULL, + `delta_points` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `playlist_id` int(11) DEFAULT NULL, + `timestamp_cued` int(11) DEFAULT NULL, + `request_id` int(11) DEFAULT NULL, + `unique_listeners` smallint(6) DEFAULT NULL, + `media_id` int(11) DEFAULT NULL, + `duration` int(11) DEFAULT NULL, + `sent_to_autodj` tinyint(1) NOT NULL, + `autodj_custom_uri` varchar(255) DEFAULT NULL, + `streamer_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_2AD16164A0BDB2F3` (`song_id`), + KEY `IDX_2AD1616421BDB235` (`station_id`), + KEY `IDX_2AD161646BBD148` (`playlist_id`), + KEY `IDX_2AD16164EA9FDD75` (`media_id`), + KEY `IDX_2AD1616425F432AD` (`streamer_id`), + KEY `IDX_2AD16164427EB8A5` (`request_id`), + KEY `idx_timestamp_cued` (`timestamp_cued`), + KEY `idx_timestamp_start` (`timestamp_start`), + KEY `idx_timestamp_end` (`timestamp_end`), + CONSTRAINT `FK_2AD1616421BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_2AD1616425F432AD` FOREIGN KEY (`streamer_id`) REFERENCES `station_streamers` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_2AD16164427EB8A5` FOREIGN KEY (`request_id`) REFERENCES `station_requests` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_2AD161646BBD148` FOREIGN KEY (`playlist_id`) REFERENCES `station_playlists` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_2AD16164A0BDB2F3` FOREIGN KEY (`song_id`) REFERENCES `songs` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_2AD16164EA9FDD75` FOREIGN KEY (`media_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `songs` +-- + +DROP TABLE IF EXISTS `songs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `songs` ( + `id` varchar(50) NOT NULL, + `text` varchar(150) DEFAULT NULL, + `artist` varchar(150) DEFAULT NULL, + `title` varchar(150) DEFAULT NULL, + `created` int(11) NOT NULL, + `play_count` int(11) NOT NULL, + `last_played` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `search_idx` (`text`,`artist`,`title`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station` +-- + +DROP TABLE IF EXISTS `station`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL, + `frontend_type` varchar(100) DEFAULT NULL, + `frontend_config` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `backend_type` varchar(100) DEFAULT NULL, + `backend_config` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `description` longtext DEFAULT NULL, + `automation_settings` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `automation_timestamp` int(11) DEFAULT NULL, + `enable_requests` tinyint(1) NOT NULL, + `request_delay` int(11) DEFAULT NULL, + `enable_streamers` tinyint(1) NOT NULL, + `needs_restart` tinyint(1) NOT NULL, + `request_threshold` int(11) DEFAULT NULL, + `url` varchar(255) DEFAULT NULL, + `radio_media_dir` varchar(255) DEFAULT NULL, + `radio_base_dir` varchar(255) DEFAULT NULL, + `has_started` tinyint(1) NOT NULL, + `nowplaying` longtext DEFAULT NULL COMMENT '(DC2Type:array)', + `adapter_api_key` varchar(150) DEFAULT NULL, + `nowplaying_timestamp` int(11) DEFAULT NULL, + `enable_public_page` tinyint(1) NOT NULL, + `short_name` varchar(100) DEFAULT NULL, + `current_streamer_id` int(11) DEFAULT NULL, + `is_streamer_live` tinyint(1) NOT NULL, + `is_enabled` tinyint(1) NOT NULL, + `api_history_items` smallint(6) NOT NULL, + `disconnect_deactivate_streamer` int(11) DEFAULT 0, + `genre` varchar(150) DEFAULT NULL, + `storage_quota` bigint(20) DEFAULT NULL, + `storage_used` bigint(20) DEFAULT NULL, + `timezone` varchar(100) DEFAULT NULL, + `default_album_art_url` varchar(255) DEFAULT NULL, + `enable_on_demand` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_9F39F8B19B209974` (`current_streamer_id`), + KEY `idx_short_name` (`short_name`), + CONSTRAINT `FK_9F39F8B19B209974` FOREIGN KEY (`current_streamer_id`) REFERENCES `station_streamers` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_media` +-- + +DROP TABLE IF EXISTS `station_media`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_media` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `song_id` varchar(50) DEFAULT NULL, + `title` varchar(200) DEFAULT NULL, + `artist` varchar(200) DEFAULT NULL, + `album` varchar(200) DEFAULT NULL, + `length` int(11) NOT NULL, + `length_text` varchar(10) DEFAULT NULL, + `path` varchar(500) DEFAULT NULL, + `mtime` int(11) DEFAULT NULL, + `amplify` decimal(3,1) DEFAULT NULL, + `fade_overlap` decimal(3,1) DEFAULT NULL, + `fade_in` decimal(3,1) DEFAULT NULL, + `fade_out` decimal(3,1) DEFAULT NULL, + `cue_in` decimal(5,1) DEFAULT NULL, + `cue_out` decimal(5,1) DEFAULT NULL, + `isrc` varchar(15) DEFAULT NULL, + `lyrics` longtext DEFAULT NULL, + `unique_id` varchar(25) DEFAULT NULL, + `art_updated_at` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `path_unique_idx` (`path`,`station_id`), + KEY `IDX_32AADE3A21BDB235` (`station_id`), + KEY `IDX_32AADE3AA0BDB2F3` (`song_id`), + KEY `search_idx` (`title`,`artist`,`album`), + CONSTRAINT `FK_32AADE3A21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_32AADE3AA0BDB2F3` FOREIGN KEY (`song_id`) REFERENCES `songs` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_media_custom_field` +-- + +DROP TABLE IF EXISTS `station_media_custom_field`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_media_custom_field` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `media_id` int(11) NOT NULL, + `field_id` int(11) NOT NULL, + `field_value` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_35DC02AAEA9FDD75` (`media_id`), + KEY `IDX_35DC02AA443707B0` (`field_id`), + CONSTRAINT `FK_35DC02AA443707B0` FOREIGN KEY (`field_id`) REFERENCES `custom_field` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_35DC02AAEA9FDD75` FOREIGN KEY (`media_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_mounts` +-- + +DROP TABLE IF EXISTS `station_mounts`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_mounts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `name` varchar(100) NOT NULL, + `is_default` tinyint(1) NOT NULL, + `fallback_mount` varchar(100) DEFAULT NULL, + `enable_autodj` tinyint(1) NOT NULL, + `autodj_format` varchar(10) DEFAULT NULL, + `autodj_bitrate` smallint(6) DEFAULT NULL, + `frontend_config` longtext DEFAULT NULL, + `relay_url` varchar(255) DEFAULT NULL, + `is_public` tinyint(1) NOT NULL, + `authhash` varchar(255) DEFAULT NULL, + `custom_listen_url` varchar(255) DEFAULT NULL, + `display_name` varchar(255) DEFAULT NULL, + `is_visible_on_public_pages` tinyint(1) NOT NULL, + `listeners_unique` int(11) NOT NULL, + `listeners_total` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_4DDF64AD21BDB235` (`station_id`), + CONSTRAINT `FK_4DDF64AD21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_playlist_folders` +-- + +DROP TABLE IF EXISTS `station_playlist_folders`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_playlist_folders` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) DEFAULT NULL, + `playlist_id` int(11) DEFAULT NULL, + `path` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_15190AE921BDB235` (`station_id`), + KEY `IDX_15190AE96BBD148` (`playlist_id`), + CONSTRAINT `FK_15190AE921BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_15190AE96BBD148` FOREIGN KEY (`playlist_id`) REFERENCES `station_playlists` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_playlist_media` +-- + +DROP TABLE IF EXISTS `station_playlist_media`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_playlist_media` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `playlist_id` int(11) NOT NULL, + `media_id` int(11) NOT NULL, + `weight` int(11) NOT NULL, + `last_played` int(11) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_EA70D7796BBD148` (`playlist_id`), + KEY `IDX_EA70D779EA9FDD75` (`media_id`), + CONSTRAINT `FK_EA70D7796BBD148` FOREIGN KEY (`playlist_id`) REFERENCES `station_playlists` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_EA70D779EA9FDD75` FOREIGN KEY (`media_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_playlists` +-- + +DROP TABLE IF EXISTS `station_playlists`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_playlists` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `name` varchar(200) NOT NULL, + `type` varchar(50) NOT NULL, + `is_enabled` tinyint(1) NOT NULL, + `play_per_songs` smallint(6) NOT NULL, + `play_per_minutes` smallint(6) NOT NULL, + `weight` smallint(6) NOT NULL, + `include_in_automation` tinyint(1) NOT NULL, + `source` varchar(50) NOT NULL, + `include_in_requests` tinyint(1) NOT NULL, + `playback_order` varchar(50) NOT NULL, + `remote_url` varchar(255) DEFAULT NULL, + `remote_type` varchar(25) DEFAULT NULL, + `is_jingle` tinyint(1) NOT NULL, + `play_per_hour_minute` smallint(6) NOT NULL, + `remote_timeout` smallint(6) NOT NULL, + `backend_options` varchar(255) DEFAULT NULL, + `played_at` int(11) NOT NULL, + `queue` longtext DEFAULT NULL COMMENT '(DC2Type:array)', + `include_in_on_demand` tinyint(1) NOT NULL, + `avoid_duplicates` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_DC827F7421BDB235` (`station_id`), + CONSTRAINT `FK_DC827F7421BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_remotes` +-- + +DROP TABLE IF EXISTS `station_remotes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_remotes` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, + `enable_autodj` tinyint(1) NOT NULL, + `autodj_format` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `autodj_bitrate` smallint(6) DEFAULT NULL, + `custom_listen_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `mount` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `source_username` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `source_password` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `source_port` smallint(5) unsigned DEFAULT NULL, + `source_mount` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `is_public` tinyint(1) NOT NULL, + `display_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `is_visible_on_public_pages` tinyint(1) NOT NULL, + `relay_id` int(11) DEFAULT NULL, + `listeners_unique` int(11) NOT NULL, + `listeners_total` int(11) NOT NULL, + `admin_password` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_779D0E8A21BDB235` (`station_id`), + KEY `IDX_779D0E8A68A482E` (`relay_id`), + CONSTRAINT `FK_779D0E8A21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_779D0E8A68A482E` FOREIGN KEY (`relay_id`) REFERENCES `relays` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_requests` +-- + +DROP TABLE IF EXISTS `station_requests`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_requests` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `track_id` int(11) NOT NULL, + `timestamp` int(11) NOT NULL, + `played_at` int(11) NOT NULL, + `ip` varchar(40) NOT NULL, + `skip_delay` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `IDX_F71F0C0721BDB235` (`station_id`), + KEY `IDX_F71F0C075ED23C43` (`track_id`), + CONSTRAINT `FK_F71F0C0721BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_F71F0C075ED23C43` FOREIGN KEY (`track_id`) REFERENCES `station_media` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_schedules` +-- + +DROP TABLE IF EXISTS `station_schedules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_schedules` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `playlist_id` int(11) DEFAULT NULL, + `streamer_id` int(11) DEFAULT NULL, + `start_time` smallint(6) NOT NULL, + `end_time` smallint(6) NOT NULL, + `start_date` varchar(10) DEFAULT NULL, + `end_date` varchar(10) DEFAULT NULL, + `days` varchar(50) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_B3BFB2956BBD148` (`playlist_id`), + KEY `IDX_B3BFB29525F432AD` (`streamer_id`), + CONSTRAINT `FK_B3BFB29525F432AD` FOREIGN KEY (`streamer_id`) REFERENCES `station_streamers` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_B3BFB2956BBD148` FOREIGN KEY (`playlist_id`) REFERENCES `station_playlists` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_streamer_broadcasts` +-- + +DROP TABLE IF EXISTS `station_streamer_broadcasts`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_streamer_broadcasts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) DEFAULT NULL, + `streamer_id` int(11) DEFAULT NULL, + `timestamp_start` int(11) NOT NULL, + `timestamp_end` int(11) NOT NULL, + `recording_path` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `IDX_76169D6621BDB235` (`station_id`), + KEY `IDX_76169D6625F432AD` (`streamer_id`), + CONSTRAINT `FK_76169D6621BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_76169D6625F432AD` FOREIGN KEY (`streamer_id`) REFERENCES `station_streamers` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_streamers` +-- + +DROP TABLE IF EXISTS `station_streamers`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_streamers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `streamer_username` varchar(50) NOT NULL, + `streamer_password` varchar(255) NOT NULL, + `comments` longtext DEFAULT NULL, + `is_active` tinyint(1) NOT NULL, + `display_name` varchar(255) DEFAULT NULL, + `reactivate_at` int(11) DEFAULT NULL, + `enforce_schedule` tinyint(1) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `username_unique_idx` (`station_id`,`streamer_username`), + KEY `IDX_5170063E21BDB235` (`station_id`), + CONSTRAINT `FK_5170063E21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `station_webhooks` +-- + +DROP TABLE IF EXISTS `station_webhooks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `station_webhooks` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `station_id` int(11) NOT NULL, + `type` varchar(100) NOT NULL, + `is_enabled` tinyint(1) NOT NULL, + `triggers` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `config` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + `name` varchar(100) DEFAULT NULL, + `metadata` longtext DEFAULT NULL COMMENT '(DC2Type:json_array)', + PRIMARY KEY (`id`), + KEY `IDX_1516958B21BDB235` (`station_id`), + CONSTRAINT `FK_1516958B21BDB235` FOREIGN KEY (`station_id`) REFERENCES `station` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_has_role` +-- + +DROP TABLE IF EXISTS `user_has_role`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_has_role` ( + `user_id` int(11) NOT NULL, + `role_id` int(11) NOT NULL, + PRIMARY KEY (`user_id`,`role_id`), + KEY `IDX_EAB8B535A76ED395` (`user_id`), + KEY `IDX_EAB8B535D60322AC` (`role_id`), + CONSTRAINT `FK_EAB8B535A76ED395` FOREIGN KEY (`user_id`) REFERENCES `users` (`uid`) ON DELETE CASCADE, + CONSTRAINT `FK_EAB8B535D60322AC` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `uid` int(11) NOT NULL AUTO_INCREMENT, + `email` varchar(100) DEFAULT NULL, + `auth_password` varchar(255) DEFAULT NULL, + `name` varchar(100) DEFAULT NULL, + `locale` varchar(25) DEFAULT NULL, + `created_at` int(11) NOT NULL, + `updated_at` int(11) NOT NULL, + `theme` varchar(25) DEFAULT NULL, + `two_factor_secret` varchar(255) DEFAULT NULL, + PRIMARY KEY (`uid`), + UNIQUE KEY `email_idx` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2020-07-07 20:12:22 diff --git a/util/docker/mariadb/scripts/db_client b/util/docker/mariadb/scripts/db_client new file mode 100644 index 000000000..ea35e0afe --- /dev/null +++ b/util/docker/mariadb/scripts/db_client @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +MYSQL_USER="${MYSQL_USER:-azuracast}" +MYSQL_PASSWORD="${MYSQL_PASSWORD:-azur4c457}" +MYSQL_DATABASE="${MYSQL_DATABASE:-azuracast}" + +exec gosu mysql mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -D ${MYSQL_DATABASE} \ No newline at end of file diff --git a/util/docker/mariadb/scripts/db_import_file b/util/docker/mariadb/scripts/db_import_file new file mode 100644 index 000000000..ce24813f5 --- /dev/null +++ b/util/docker/mariadb/scripts/db_import_file @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +MYSQL_USER="${MYSQL_USER:-azuracast}" +MYSQL_PASSWORD="${MYSQL_PASSWORD:-azur4c457}" +MYSQL_DATABASE="${MYSQL_DATABASE:-azuracast}" + +cat $1 | mysql -u${MYSQL_USER} -p${MYSQL_PASSWORD} -D ${MYSQL_DATABASE} \ No newline at end of file diff --git a/util/docker/mariadb/scripts/db_reset_root_password b/util/docker/mariadb/scripts/db_reset_root_password new file mode 100644 index 000000000..3669a2042 --- /dev/null +++ b/util/docker/mariadb/scripts/db_reset_root_password @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +if [ "$(id -u)" = "0" ]; then + echo "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" +fi + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then + mysql_error "Both $var and $fileVar are set (but are exclusive)" + fi + local val="$def" + if [ "${!var:-}" ]; then + val="${!var}" + elif [ "${!fileVar:-}" ]; then + val="$(< "${!fileVar}")" + fi + export "$var"="$val" + unset "$fileVar" +} + +# set MARIADB_xyz from MYSQL_xyz when MARIADB_xyz is unset +# and make them the same value (so user scripts can use either) +_mariadb_file_env() { + local var="$1"; shift + local maria="MARIADB_${var#MYSQL_}" + file_env "$var" "$@" + file_env "$maria" "${!var}" + if [ "${!maria:-}" ]; then + export "$var"="${!maria}" + fi +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local newline=$'\n' + local escaped=${1//\\/\\\\} + escaped="${escaped//$newline/\\n}" + echo "${escaped//\'/\\\'}" +} + +mysql_get_config() { + local conf="$1"; shift + mysqld --verbose --help 2>/dev/null \ + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' +} + +# Build env vars +DATADIR="$(mysql_get_config 'datadir')" +SOCKET="$(mysql_get_config 'socket')" + +_mariadb_file_env 'MYSQL_ROOT_HOST' '%' +_mariadb_file_env 'MYSQL_ROOT_PASSWORD' + +: "${MARIADB_ALLOW_EMPTY_ROOT_PASSWORD:=${MYSQL_ALLOW_EMPTY_PASSWORD:-}}" +export MYSQL_ALLOW_EMPTY_PASSWORD="$MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" MARIADB_ALLOW_EMPTY_ROOT_PASSWORD +: "${MARIADB_RANDOM_ROOT_PASSWORD:=${MYSQL_RANDOM_ROOT_PASSWORD:-}}" +export MYSQL_RANDOM_ROOT_PASSWORD="$MARIADB_RANDOM_ROOT_PASSWORD" MARIADB_RANDOM_ROOT_PASSWORD +: "${MARIADB_INITDB_SKIP_TZINFO:=${MYSQL_INITDB_SKIP_TZINFO:-}}" +export MYSQL_INITDB_SKIP_TZINFO="$MARIADB_INITDB_SKIP_TZINFO" MARIADB_INITDB_SKIP_TZINFO + +if [ -z "$MARIADB_ROOT_PASSWORD" -a -z "$MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" -a -z "$MARIADB_RANDOM_ROOT_PASSWORD" ]; then + mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MARIADB_ROOT_PASSWORD, MARIADB_ALLOW_EMPTY_ROOT_PASSWORD and MARIADB_RANDOM_ROOT_PASSWORD' +fi + +# Spin up temp server +echo "Starting temporary MariaDB server..." + +mysqld --skip-networking --skip-grant-tables --socket="${SOCKET}" & + +echo "Waiting for server startup" +for i in {30..0}; do + if mysql --protocol=socket -hlocalhost -uroot --socket="${SOCKET}" --database=mysql <<<'SELECT 1'; then + break + fi + sleep 1 +done +if [ "$i" = 0 ]; then + echo "Unable to start temporary server." + exit 1 +fi + +# Try password reset +echo "Resetting root password..." + +if [ -n "$MARIADB_RANDOM_ROOT_PASSWORD" ]; then + MARIADB_ROOT_PASSWORD="$(pwgen --numerals --capitalize --symbols --remove-chars="'\\" -1 32)" + export MARIADB_ROOT_PASSWORD MYSQL_ROOT_PASSWORD=$MARIADB_ROOT_PASSWORD + echo "GENERATED ROOT PASSWORD: $MARIADB_ROOT_PASSWORD" +fi + +# Sets root password and creates root users for non-localhost hosts +rootCreate= +rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + +# default root to listen for connections from anywhere +if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then + # no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + read -r -d '' rootCreate <<-EOSQL || true + ALTER USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; + EOSQL +fi + +# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set +# --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. +mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" --database=mysql --binary-mode <<-EOSQL + FLUSH PRIVILEGES ; + ALTER USER 'root'@'localhost' IDENTIFIED BY '${rootPasswordEscaped}' ; + GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; + + ${rootCreate} +EOSQL + +echo "Password reset complete." diff --git a/util/docker/mariadb/scripts/db_slow_queries b/util/docker/mariadb/scripts/db_slow_queries new file mode 100644 index 000000000..8d4ed06b6 --- /dev/null +++ b/util/docker/mariadb/scripts/db_slow_queries @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ ${MYSQL_SLOW_QUERY_LOG} != 1 ]; then + echo "MariaDB Slow Query Log is not currently enabled on this instance." + echo "Set MYSQL_SLOW_QUERY_LOG=1 in your environment variables (azuracast.env), then restart to enable." + exit 1 +fi + +exec gosu mysql mysqldumpslow /var/log/mysql/slow.log \ No newline at end of file diff --git a/util/docker/mariadb/scripts/db_upgrade b/util/docker/mariadb/scripts/db_upgrade new file mode 100644 index 000000000..fd73cfb39 --- /dev/null +++ b/util/docker/mariadb/scripts/db_upgrade @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +if [ "$(id -u)" = "0" ]; then + echo "Switching to dedicated user 'mysql'" + exec gosu mysql "$BASH_SOURCE" "$@" +fi + +mysql_get_config() { + local conf="$1"; shift + mysqld --verbose --help 2>/dev/null \ + | awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }' +} + +DATADIR="$(mysql_get_config 'datadir')" +SOCKET="$(mysql_get_config 'socket')" + +echo "Starting temporary MariaDB server..." + +mysqld --skip-networking --skip-grant-tables --socket="${SOCKET}" & + +echo "Waiting for server startup" +for i in {30..0}; do + if mysql --protocol=socket -hlocalhost -uroot --socket="${SOCKET}" --database=mysql <<<'SELECT 1'; then + break + fi + sleep 1 +done +if [ "$i" = 0 ]; then + echo "Unable to start temporary server." + exit 1 +fi + +echo "Upgrading instance (if necessary)..." + +mariadb-upgrade --verbose --protocol=socket -hlocalhost -uroot --socket="${SOCKET}" + +echo "Upgrade complete; Shutting down temporary MariaDB server..." \ No newline at end of file diff --git a/util/docker/mariadb/service.minimal/mariadb/check b/util/docker/mariadb/service.minimal/mariadb/check new file mode 100644 index 000000000..8a76ad12b --- /dev/null +++ b/util/docker/mariadb/service.minimal/mariadb/check @@ -0,0 +1,3 @@ +#!/bin/bash + +exec mysqladmin ping -h localhost diff --git a/util/docker/mariadb/service.minimal/mariadb/run b/util/docker/mariadb/service.minimal/mariadb/run new file mode 100644 index 000000000..27c3f3d3a --- /dev/null +++ b/util/docker/mariadb/service.minimal/mariadb/run @@ -0,0 +1,5 @@ +#!/bin/bash + +dockerize -template /etc/mysql/db.cnf.tmpl:/etc/mysql/conf.d/db.cnf + +exec gosu mysql mysqld diff --git a/util/docker/mariadb/setup.sh b/util/docker/mariadb/setup.sh new file mode 100644 index 000000000..95ab9b402 --- /dev/null +++ b/util/docker/mariadb/setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +apt-get update + +# Install common scripts +cp -rT /bd_build/mariadb/scripts/ /usr/local/bin + +cp -rT /bd_build/mariadb/startup_scripts/. /etc/my_init.d/ + +cp -rT /bd_build/mariadb/service.minimal/. /etc/service.minimal/ + +# cp -rT /bd_build/mariadb/service.full/. /etc/service.full/ + +# Run service setup for all setup scripts +for f in /bd_build/mariadb/setup/*.sh; do + bash "$f" -H +done diff --git a/util/docker/mariadb/setup/mariadb.sh b/util/docker/mariadb/setup/mariadb.sh new file mode 100644 index 000000000..7ac1d3a0b --- /dev/null +++ b/util/docker/mariadb/setup/mariadb.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +$minimal_apt_get_install tzdata + +echo "1" >> /etc/container_environment/MARIADB_AUTO_UPGRADE + +cp /bd_build/mariadb/mariadb/db.sql /docker-entrypoint-initdb.d/00-azuracast.sql +cp /bd_build/mariadb/mariadb/db.cnf.tmpl /etc/mysql/db.cnf.tmpl + +mv /usr/local/bin/healthcheck.sh /usr/local/bin/db_healthcheck.sh +mv /usr/local/bin/docker-entrypoint.sh /usr/local/bin/db_entrypoint.sh diff --git a/util/docker/mariadb/startup_scripts/00_disable_mariadb.sh b/util/docker/mariadb/startup_scripts/00_disable_mariadb.sh new file mode 100644 index 000000000..b9c769421 --- /dev/null +++ b/util/docker/mariadb/startup_scripts/00_disable_mariadb.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# If the MariaDB host is anything but localhost, disable MariaDB on this container. +if [ "$MYSQL_HOST" != "localhost" ]; then + echo "MariaDB host is not localhost; disabling MariaDB..." + + rm -rf /etc/service/mariadb + rm -rf /etc/service.minimal/mariadb +fi diff --git a/util/docker/mariadb/startup_scripts/05_setup_db.sh b/util/docker/mariadb/startup_scripts/05_setup_db.sh new file mode 100644 index 000000000..289d8a558 --- /dev/null +++ b/util/docker/mariadb/startup_scripts/05_setup_db.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +if [ ! -f /etc/service/mariadb/run ]; then + echo "MariaDB disabled. Skipping DB initialization..." + exit 0 +fi + +source /usr/local/bin/db_entrypoint.sh + +set -- mysqld + +mysql_note "Initial DB setup..." + +mysql_check_config "$@" +# Load various environment variables +docker_setup_env "$@" +docker_create_db_directories + +# If container is started as root user, restart as dedicated mysql user +if [ "$(id -u)" = "0" ]; then + mysql_note "Switching to dedicated user 'mysql'" + exec gosu mysql "${BASH_SOURCE[0]}" "$@" +fi + +# there's no database, so it needs to be initialized +if [ -z "$DATABASE_ALREADY_EXISTS" ]; then + docker_verify_minimum_env + + # check dir permissions to reduce likelihood of half-initialized database + ls /docker-entrypoint-initdb.d/ > /dev/null + + docker_init_database_dir "$@" + + mysql_note "Starting temporary server" + docker_temp_server_start "$@" + mysql_note "Temporary server started." + + docker_setup_db + docker_process_init_files /docker-entrypoint-initdb.d/* + + mysql_note "Stopping temporary server" + docker_temp_server_stop + mysql_note "Temporary server stopped" + + echo + mysql_note "MariaDB init process done. Ready for start up." + echo +# MDEV-27636 mariadb_upgrade --check-if-upgrade-is-needed cannot be run offline +#elif mysql_upgrade --check-if-upgrade-is-needed; then +elif _check_if_upgrade_is_needed; then + docker_mariadb_upgrade "$@" +fi diff --git a/util/docker/redis/redis/redis.conf b/util/docker/redis/redis/redis.conf new file mode 100644 index 000000000..a8b5daede --- /dev/null +++ b/util/docker/redis/redis/redis.conf @@ -0,0 +1,6 @@ +save "" + +appendonly no + +maxmemory 128mb +maxmemory-policy volatile-lfu diff --git a/util/docker/redis/service.minimal/redis/run b/util/docker/redis/service.minimal/redis/run new file mode 100644 index 000000000..ce627af64 --- /dev/null +++ b/util/docker/redis/service.minimal/redis/run @@ -0,0 +1,3 @@ +#!/bin/bash + +exec gosu redis redis-server /etc/redis/redis.conf diff --git a/util/docker/redis/setup.sh b/util/docker/redis/setup.sh new file mode 100644 index 000000000..ca36e1ef8 --- /dev/null +++ b/util/docker/redis/setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +apt-get update + +# Install common scripts +# cp -rT /bd_build/redis/scripts/ /usr/local/bin + +cp -rT /bd_build/redis/startup_scripts/. /etc/my_init.d/ + +cp -rT /bd_build/redis/service.minimal/. /etc/service.minimal/ + +# cp -rT /bd_build/redis/service.full/. /etc/service.full/ + +# Run service setup for all setup scripts +for f in /bd_build/redis/setup/*.sh; do + bash "$f" -H +done diff --git a/util/docker/redis/setup/redis.sh b/util/docker/redis/setup/redis.sh new file mode 100644 index 000000000..8bd153292 --- /dev/null +++ b/util/docker/redis/setup/redis.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +add-apt-repository -y ppa:chris-lea/redis-server +apt-get update + +$minimal_apt_get_install redis-server + +cp /bd_build/redis/redis/redis.conf /etc/redis/redis.conf +chown redis:redis /etc/redis/redis.conf diff --git a/util/docker/redis/startup_scripts/00_disable_redis.sh b/util/docker/redis/startup_scripts/00_disable_redis.sh new file mode 100644 index 000000000..2aa0f0efd --- /dev/null +++ b/util/docker/redis/startup_scripts/00_disable_redis.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +bool() { + case "$1" in + Y* | y* | true | TRUE | 1) return 0 ;; + esac + return 1 +} + +# If Redis is expressly disabled or the host is anything but localhost, disable Redis on this container. +ENABLE_REDIS=${ENABLE_REDIS:-false} + +if [ "$REDIS_HOST" != "localhost" ] || [ bool "$ENABLE_REDIS" ]; then + echo "Redis is disabled or host is not localhost; disabling Redis..." + rm -rf /etc/service/redis + rm -rf /etc/service.minimal/redis +fi diff --git a/util/docker/stations/liquidsoap/build_as_azuracast.sh b/util/docker/stations/liquidsoap/build_as_azuracast.sh new file mode 100644 index 000000000..0c5eef666 --- /dev/null +++ b/util/docker/stations/liquidsoap/build_as_azuracast.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e +set -x + +opam init --disable-sandboxing -a --bare && opam switch create 4.13.1 + +# Pin specific commit of Liquidsoap +opam pin add --no-action liquidsoap https://github.com/savonet/liquidsoap.git#af311dc8ee57e3e7d3f637ea23af4096fd57820d + +opam install -y ladspa.0.2.2 ffmpeg.1.1.1 ffmpeg-avutil.1.1.1 ffmpeg-avcodec.1.1.1 ffmpeg-avdevice.1.1.1 \ + ffmpeg-av.1.1.1 ffmpeg-avfilter.1.1.1 ffmpeg-swresample.1.1.1 ffmpeg-swscale.1.1.1 frei0r.0.1.2 \ + samplerate.0.1.6 taglib.0.3.9 mad.0.5.2 faad.0.5.0 fdkaac.0.3.2 lame.0.3.5 vorbis.0.8.0 cry.0.6.6 \ + flac.0.3.0 opus.0.2.1 dtools.0.4.4 duppy.0.9.2 ocurl.0.9.2 ssl.0.5.10 \ + liquidsoap + +# Have Liquidsoap build its own chroot. +mkdir -p /tmp/liquidsoap + +/var/azuracast/.opam/4.13.1/bin/liquidsoap /bd_build/liquidsoap/build_chroot.liq || true + +# Clear entire OPAM directory +rm -rf /var/azuracast/.opam + +cp -r /tmp/liquidsoap/var/azuracast/.opam /var/azuracast/.opam +rm -rf /tmp/liquidsoap + diff --git a/util/docker/stations/liquidsoap/build_chroot.liq b/util/docker/stations/liquidsoap/build_chroot.liq new file mode 100644 index 000000000..298137afc --- /dev/null +++ b/util/docker/stations/liquidsoap/build_chroot.liq @@ -0,0 +1,2 @@ +liquidsoap.chroot.make('/tmp/liquidsoap') +shutdown() diff --git a/util/docker/stations/service.minimal/supervisord/run b/util/docker/stations/service.minimal/supervisord/run new file mode 100644 index 000000000..623f36583 --- /dev/null +++ b/util/docker/stations/service.minimal/supervisord/run @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf diff --git a/util/docker/stations/setup.sh b/util/docker/stations/setup.sh new file mode 100644 index 000000000..7419834a3 --- /dev/null +++ b/util/docker/stations/setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +apt-get update + +# Install common scripts +# cp -rT /bd_build/stations/scripts/ /usr/local/bin + +# cp -rT /bd_build/stations/startup_scripts/. /etc/my_init.d/ + +cp -rT /bd_build/stations/service.minimal/. /etc/service.minimal/ + +# cp -rT /bd_build/stations/service.full/. /etc/service.full/ + +# Run service setup for all setup scripts +for f in /bd_build/stations/setup/*.sh; do + bash "$f" -H +done diff --git a/util/docker/web/setup/flac.sh b/util/docker/stations/setup/flac.sh similarity index 100% rename from util/docker/web/setup/flac.sh rename to util/docker/stations/setup/flac.sh diff --git a/util/docker/stations/setup/icecast.sh b/util/docker/stations/setup/icecast.sh new file mode 100644 index 000000000..9d9f71cb0 --- /dev/null +++ b/util/docker/stations/setup/icecast.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +# Only install Icecast deps (Icecast is handled by another container). +$minimal_apt_get_install libxml2 libxslt1-dev libvorbis-dev + +# SSL self-signed cert generation +$minimal_apt_get_install openssl diff --git a/util/docker/stations/setup/liquidsoap.sh b/util/docker/stations/setup/liquidsoap.sh new file mode 100644 index 000000000..372097494 --- /dev/null +++ b/util/docker/stations/setup/liquidsoap.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +# Packages required by Liquidsoap +$minimal_apt_get_install libao-dev libasound2-dev libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev \ + libavutil-dev libfaad-dev libfdk-aac-dev libflac-dev libfreetype-dev libgd-dev libjack-dev \ + libjpeg-dev liblo-dev libmad0-dev libmagic-dev libmp3lame-dev libopus-dev libpng-dev libportaudio2 \ + libpulse-dev libsamplerate0-dev libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev libshine-dev libsoundtouch-dev libspeex-dev \ + libsrt-dev libswresample-dev libswscale-dev libtag1-dev libtheora-dev libtiff-dev libx11-dev libxpm-dev bubblewrap ffmpeg + +# Optional audio plugins +$minimal_apt_get_install frei0r-plugins-dev ladspa-sdk multimedia-audio-plugins swh-plugins tap-plugins lsp-plugins-ladspa + +# Per-architecture LS installs +ARCHITECTURE=amd64 + +if [ "$(uname -m)" = "aarch64" ]; then + ARCHITECTURE=arm64 +fi + +# Adding this comment to trigger an uncached re-pull of this deb file. + +wget -O /tmp/liquidsoap.deb "https://github.com/savonet/liquidsoap/releases/download/v2.0.3/liquidsoap_2.0.3-ubuntu-focal-2_${ARCHITECTURE}.deb" + +dpkg -i /tmp/liquidsoap.deb +apt-get install -y -f --no-install-recommends +rm -f /tmp/liquidsoap.deb +ln -s /usr/bin/liquidsoap /usr/local/bin/liquidsoap + +# To do a pinned install, uncomment and customize below +# else +# $minimal_apt_get_install build-essential libssl-dev libcurl4-openssl-dev m4 ocaml opam autoconf automake +# +# sudo -u azuracast bash ../liquidsoap/build_as_azuracast.sh +# ln -s /var/azuracast/.opam/4.13.1/bin/liquidsoap /usr/local/bin/liquidsoap +# chmod a+x /usr/local/bin/liquidsoap +# apt-get purge -y build-essential libssl-dev libcurl4-openssl-dev m4 ocaml opam autoconf automake +# fi diff --git a/util/docker/stations/setup/supervisor.sh b/util/docker/stations/setup/supervisor.sh new file mode 100644 index 000000000..8b4f9b3ce --- /dev/null +++ b/util/docker/stations/setup/supervisor.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +source /bd_build/buildconfig +set -x + +# $minimal_apt_get_install python3-minimal python3-pip +# pip3 install setuptools supervisor + +$minimal_apt_get_install supervisor + +# mkdir -p /etc/supervisor +cp /bd_build/stations/supervisor/supervisord.conf /etc/supervisor/supervisord.conf diff --git a/util/docker/web/setup/vorbis.sh b/util/docker/stations/setup/vorbis.sh similarity index 100% rename from util/docker/web/setup/vorbis.sh rename to util/docker/stations/setup/vorbis.sh diff --git a/util/docker/stations/supervisor/supervisord.conf b/util/docker/stations/supervisor/supervisord.conf new file mode 100644 index 000000000..137bf8c1f --- /dev/null +++ b/util/docker/stations/supervisor/supervisord.conf @@ -0,0 +1,25 @@ +[unix_http_server] +file=/tmp/supervisor.sock ; (the path to the socket file) + +[inet_http_server] ; inet (TCP) server disabled by default +port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) + +[supervisord] +user=root +logfile=/var/azuracast/www_tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) +logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) +logfile_backups=10 ; (num of main logfile rotation backups;default 10) +loglevel=info ; (log level;default info; others: debug,warn,trace) +pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +nodaemon=true ; (start in foreground if true;default false) +minfds=1024 ; (min. avail startup file descriptors;default 1024) +minprocs=200 ; (min. avail process descriptors;default 200) + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket + +[include] +files = /var/azuracast/stations/*/config/supervisord.conf conf.d/* diff --git a/util/docker/web/nginx/azuracast.conf.tmpl b/util/docker/web/nginx/azuracast.conf.tmpl index 84924fa75..67694ea69 100644 --- a/util/docker/web/nginx/azuracast.conf.tmpl +++ b/util/docker/web/nginx/azuracast.conf.tmpl @@ -1,11 +1,9 @@ {{if isTrue .Env.ENABLE_REDIS }} upstream redis_server { - nchan_redis_server "redis://redis:6379"; + nchan_redis_server "redis://localhost:6379"; } {{end}} -resolver 127.0.0.11; - server { listen 9010; @@ -130,7 +128,7 @@ server { proxy_connect_timeout 60; proxy_set_header Host localhost:$1; - proxy_pass http://stations:$1/$3?$args; + proxy_pass http://127.0.0.1:$1/$3?$args; } # Reverse proxy the Liquidsoap harbor inputs to allow for streaming. @@ -140,11 +138,9 @@ server { location ~ ^/radio/{{ .Env.NGINX_WEBDJ_PORTS }}(/?)(.*)$ { {{ end }} - resolver 127.0.0.11; - include proxy_params; - proxy_pass http://stations:$1/$3; + proxy_pass http://127.0.0.1:$1/$3; } # pub/sub endpoints diff --git a/util/docker/web/php/www.conf.tmpl b/util/docker/web/php/www.conf.tmpl index 0ba470797..1896836dc 100644 --- a/util/docker/web/php/www.conf.tmpl +++ b/util/docker/web/php/www.conf.tmpl @@ -15,6 +15,7 @@ pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3 pm.max_requests = 100 +pm.status_path = /status pm.process_idle_timeout = 60s chdir = / diff --git a/util/docker/web/runit/beanstalkd/run b/util/docker/web/runit/beanstalkd/run deleted file mode 100644 index 91ca77b06..000000000 --- a/util/docker/web/runit/beanstalkd/run +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -echo 'Spinning up Beanstalkd process...' - -exec sudo -E -u azuracast beanstalkd -p 11300 -z 262140 diff --git a/util/docker/web/runit/php-fpm/run b/util/docker/web/runit/php-fpm/run deleted file mode 100644 index a087e05f7..000000000 --- a/util/docker/web/runit/php-fpm/run +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -source /etc/php/.version - -exec /usr/sbin/php-fpm${PHP_VERSION} -F --fpm-config /etc/php/${PHP_VERSION}/fpm/php-fpm.conf -c /etc/php/${PHP_VERSION}/fpm/ diff --git a/util/docker/web/runit/php-nowplaying/run b/util/docker/web/runit/php-nowplaying/run deleted file mode 100644 index 1797f18c0..000000000 --- a/util/docker/web/runit/php-nowplaying/run +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -exec sudo -E -u azuracast php /var/azuracast/www/bin/console azuracast:sync:nowplaying diff --git a/util/docker/web/runit/php-worker/run b/util/docker/web/runit/php-worker/run deleted file mode 100644 index 9cb618615..000000000 --- a/util/docker/web/runit/php-worker/run +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -exec sudo -E -u azuracast php /var/azuracast/www/bin/console queue:process --worker-name=app_worker_0 diff --git a/util/docker/web/scripts/azuracast_ci b/util/docker/web/scripts/azuracast_ci new file mode 100644 index 000000000..4399b2e8e --- /dev/null +++ b/util/docker/web/scripts/azuracast_ci @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +if [ $(whoami) != 'azuracast' ]; then + echo 'This script must be run as the "azuracast" user. Rerunning...' + exec gosu azuracast azuracast_ci "$@" +fi + +azuracast_install || exit 1 + +cd /var/azuracast/www +composer codeception-no-coverage diff --git a/util/docker/web/scripts/azuracast_cli b/util/docker/web/scripts/azuracast_cli index 2f389527b..90313b5e4 100644 --- a/util/docker/web/scripts/azuracast_cli +++ b/util/docker/web/scripts/azuracast_cli @@ -2,7 +2,7 @@ if [ `whoami` != 'azuracast' ]; then echo 'This script must be run as the "azuracast" user. Rerunning...' - exec sudo -E -u azuracast azuracast_cli "$@" + exec gosu azuracast azuracast_cli "$@" fi cd /var/azuracast/www diff --git a/util/docker/web/scripts/azuracast_install b/util/docker/web/scripts/azuracast_install index d640c53dd..c4dfd8fb8 100644 --- a/util/docker/web/scripts/azuracast_install +++ b/util/docker/web/scripts/azuracast_install @@ -9,7 +9,7 @@ bool() { if [ $(whoami) != 'azuracast' ]; then echo 'This script must be run as the "azuracast" user. Rerunning...' - exec sudo -E -u azuracast azuracast_install "$@" + exec gosu azuracast azuracast_install "$@" fi echo "AzuraCast Setup" diff --git a/util/docker/web/scripts/azuracast_restore b/util/docker/web/scripts/azuracast_restore index 2bc34a3dc..f37fa3cc5 100644 --- a/util/docker/web/scripts/azuracast_restore +++ b/util/docker/web/scripts/azuracast_restore @@ -9,7 +9,7 @@ bool() { if [ $(whoami) != 'azuracast' ]; then echo 'This script must be run as the "azuracast" user. Rerunning...' - exec sudo -E -u azuracast azuracast_restore "$@" + exec gosu azuracast azuracast_restore "$@" fi echo "AzuraCast Backup Restore" diff --git a/util/docker/web/scripts/cron_task b/util/docker/web/scripts/cron_task index 0c96cc75c..f46a1f407 100644 --- a/util/docker/web/scripts/cron_task +++ b/util/docker/web/scripts/cron_task @@ -2,4 +2,4 @@ source /etc/container_environment.sh -exec sudo -E -u azuracast "$@" >/proc/1/fd/1 2>/proc/1/fd/2 +exec gosu azuracast "$@" >/proc/1/fd/1 2>/proc/1/fd/2 diff --git a/util/docker/web/scripts/temp_cleanup b/util/docker/web/scripts/temp_cleanup index 9bd0e8718..53f30aca2 100644 --- a/util/docker/web/scripts/temp_cleanup +++ b/util/docker/web/scripts/temp_cleanup @@ -1,6 +1,8 @@ #!/usr/bin/env bash exec /usr/sbin/tmpreaper 12h --protect '.tmpreaper' --verbose \ + /tmp \ /tmp/app_nginx_client \ /tmp/app_fastcgi_temp \ + /var/azuracast/stations/*/temp \ > /proc/1/fd/1 2> /proc/1/fd/2 diff --git a/util/docker/web/scripts/uptime_wait b/util/docker/web/scripts/uptime_wait index e9ec1f1c9..1035e55c5 100644 --- a/util/docker/web/scripts/uptime_wait +++ b/util/docker/web/scripts/uptime_wait @@ -1,10 +1,6 @@ #!/bin/bash set -e -if [[ -f /var/azuracast/www/bin/uptime_wait ]]; then - if ! php /var/azuracast/www/bin/uptime_wait; then - exit 1 - fi -fi +gosu azuracast php /var/azuracast/www/bin/uptime_wait || exit 1 exec "$@" diff --git a/util/docker/web/runit/acme/run b/util/docker/web/service.full/acme/run similarity index 99% rename from util/docker/web/runit/acme/run rename to util/docker/web/service.full/acme/run index 4d37da545..32550eb07 100644 --- a/util/docker/web/runit/acme/run +++ b/util/docker/web/service.full/acme/run @@ -1,5 +1,7 @@ #!/bin/bash +sv -w 45 check nginx || exit 1 + # Acme loading script # Uses code from: # https://github.com/nginx-proxy/acme-companion/blob/main/app/letsencrypt_service diff --git a/util/docker/web/service.full/beanstalkd/run b/util/docker/web/service.full/beanstalkd/run new file mode 100644 index 000000000..b527151bc --- /dev/null +++ b/util/docker/web/service.full/beanstalkd/run @@ -0,0 +1,5 @@ +#!/bin/bash + +echo 'Spinning up Beanstalkd process...' + +exec gosu azuracast beanstalkd -p 11300 -z 262140 diff --git a/util/docker/web/runit/cron/run b/util/docker/web/service.full/cron/run similarity index 78% rename from util/docker/web/runit/cron/run rename to util/docker/web/service.full/cron/run index 29a0f3117..346a19f29 100644 --- a/util/docker/web/runit/cron/run +++ b/util/docker/web/service.full/cron/run @@ -1,8 +1,10 @@ #!/bin/sh +sv -w 30 check php-fpm || exit 1 + # Touch cron files to fix 'NUMBER OF HARD LINKS > 1' issue. See https://github.com/phusion/baseimage-docker/issues/198 touch -c /var/spool/cron/crontabs/* touch -c /etc/crontab touch -c /etc/cron.*/* -exec /usr/sbin/cron -f \ No newline at end of file +exec /usr/sbin/cron -f diff --git a/util/docker/web/runit/nginx/run b/util/docker/web/service.full/nginx/run similarity index 93% rename from util/docker/web/runit/nginx/run rename to util/docker/web/service.full/nginx/run index 7abaeedfc..36982b7e2 100644 --- a/util/docker/web/runit/nginx/run +++ b/util/docker/web/service.full/nginx/run @@ -1,5 +1,7 @@ #!/bin/bash +sv -w 30 check php-fpm || exit 1 + bool() { case "$1" in Y* | y* | true | TRUE | 1) return 0 ;; diff --git a/util/docker/web/service.full/php-fpm/check b/util/docker/web/service.full/php-fpm/check new file mode 100644 index 000000000..b1df7881d --- /dev/null +++ b/util/docker/web/service.full/php-fpm/check @@ -0,0 +1,3 @@ +#!/bin/bash + +exec php-fpm-healthcheck diff --git a/util/docker/web/service.full/php-fpm/run b/util/docker/web/service.full/php-fpm/run new file mode 100644 index 000000000..be807388b --- /dev/null +++ b/util/docker/web/service.full/php-fpm/run @@ -0,0 +1,25 @@ +#!/bin/bash + +source /etc/container_environment.sh + +if [ -f /etc/service/mariadb/run ]; then + sv -w 30 check mariadb || exit 1 +fi + +if [ -f /etc/service/redis/run ]; then + sv -w 30 check redis || exit 1 +fi + +# Set up PHP config +dockerize -template "/etc/php/${PHP_VERSION}/fpm/05-azuracast.ini.tmpl:/etc/php/${PHP_VERSION}/fpm/conf.d/05-azuracast.ini" \ + -template "/etc/php/${PHP_VERSION}/fpm/www.conf.tmpl:/etc/php/${PHP_VERSION}/fpm/pool.d/www.conf" \ + cp /etc/php/${PHP_VERSION}/fpm/conf.d/05-azuracast.ini /etc/php/${PHP_VERSION}/cli/conf.d/05-azuracast.ini + +# Wait for services to spin up. +gosu azuracast php /var/azuracast/www/bin/uptime_wait || exit 1 + +# Initialize before running FPM +gosu azuracast azuracast_cli azuracast:setup:initialize || exit 1 + +# Run PHP-FPM +exec /usr/sbin/php-fpm${PHP_VERSION} -F --fpm-config /etc/php/${PHP_VERSION}/fpm/php-fpm.conf -c /etc/php/${PHP_VERSION}/fpm/ diff --git a/util/docker/web/service.full/php-nowplaying/run b/util/docker/web/service.full/php-nowplaying/run new file mode 100644 index 000000000..4ecbbd197 --- /dev/null +++ b/util/docker/web/service.full/php-nowplaying/run @@ -0,0 +1,5 @@ +#!/bin/bash + +sv -w 30 check php-fpm || exit 1 + +exec gosu azuracast php /var/azuracast/www/bin/console azuracast:sync:nowplaying diff --git a/util/docker/web/service.full/php-worker/run b/util/docker/web/service.full/php-worker/run new file mode 100644 index 000000000..c3a5e4dc7 --- /dev/null +++ b/util/docker/web/service.full/php-worker/run @@ -0,0 +1,5 @@ +#!/bin/bash + +sv -w 30 check php-fpm || exit 1 + +exec gosu azuracast php /var/azuracast/www/bin/console queue:process --worker-name=app_worker_0 diff --git a/util/docker/web/runit/sftpgo/run b/util/docker/web/service.full/sftpgo/run similarity index 100% rename from util/docker/web/runit/sftpgo/run rename to util/docker/web/service.full/sftpgo/run diff --git a/util/docker/web/setup.sh b/util/docker/web/setup.sh index 6084adb94..54135a24e 100644 --- a/util/docker/web/setup.sh +++ b/util/docker/web/setup.sh @@ -3,24 +3,18 @@ set -e source /bd_build/buildconfig set -x +apt-get update + # Install common scripts -cp -rT /bd_build/scripts/ /usr/local/bin -chmod -R a+x /usr/local/bin +cp -rT /bd_build/web/scripts/ /usr/local/bin -# Install runit -$minimal_apt_get_install runit +cp -rT /bd_build/web/startup_scripts/. /etc/my_init.d/ -# Install runit scripts -cp -rT /bd_build/startup_scripts/. /etc/my_init.d/ -cp -rT /bd_build/runit/. /etc/service/ +# cp -rT /bd_build/web/service.minimal/. /etc/service.minimal/ -chmod -R +x /etc/service -chmod -R +x /etc/my_init.d - -# Install scripts commonly used during setup. -$minimal_apt_get_install curl wget tar zip unzip git rsync tzdata gpg-agent openssh-client +cp -rT /bd_build/web/service.full/. /etc/service.full/ # Run service setup for all setup scripts -for f in /bd_build/setup/*.sh; do +for f in /bd_build/web/setup/*.sh; do bash "$f" -H -done \ No newline at end of file +done diff --git a/util/docker/web/setup/cron.sh b/util/docker/web/setup/cron.sh index 7499f50fc..51bf06f20 100644 --- a/util/docker/web/setup/cron.sh +++ b/util/docker/web/setup/cron.sh @@ -18,5 +18,5 @@ rm -f /etc/cron.daily/dpkg rm -f /etc/cron.daily/password rm -f /etc/cron.weekly/fstrim -cp -r /bd_build/cron/. /etc/cron.d/ +cp -r /bd_build/web/cron/. /etc/cron.d/ chmod -R 600 /etc/cron.d/* diff --git a/util/docker/web/setup/nginx.sh b/util/docker/web/setup/nginx.sh index 60f69f0bc..afe8a8fdd 100644 --- a/util/docker/web/setup/nginx.sh +++ b/util/docker/web/setup/nginx.sh @@ -6,9 +6,9 @@ set -x $minimal_apt_get_install nginx nginx-common nginx-extras openssl # Install nginx and configuration -cp /bd_build/nginx/proxy_params.conf /etc/nginx/proxy_params -cp /bd_build/nginx/nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl -cp /bd_build/nginx/azuracast.conf.tmpl /etc/nginx/azuracast.conf.tmpl +cp /bd_build/web/nginx/proxy_params.conf /etc/nginx/proxy_params +cp /bd_build/web/nginx/nginx.conf.tmpl /etc/nginx/nginx.conf.tmpl +cp /bd_build/web/nginx/azuracast.conf.tmpl /etc/nginx/azuracast.conf.tmpl mkdir -p /etc/nginx/azuracast.conf.d/ diff --git a/util/docker/web/setup/php.sh b/util/docker/web/setup/php.sh index e2e252970..1e6fbcfc5 100644 --- a/util/docker/web/setup/php.sh +++ b/util/docker/web/setup/php.sh @@ -5,6 +5,8 @@ set -x PHP_VERSION=8.1 +echo "${PHP_VERSION}" >> /etc/container_environment/PHP_VERSION + add-apt-repository -y ppa:ondrej/php apt-get update @@ -18,14 +20,19 @@ $minimal_apt_get_install php${PHP_VERSION}-fpm php${PHP_VERSION}-cli php${PHP_VE mkdir -p /run/php touch /run/php/php${PHP_VERSION}-fpm.pid -echo "PHP_VERSION=${PHP_VERSION}" >>/etc/php/.version - -cp /bd_build/php/php.ini.tmpl /etc/php/${PHP_VERSION}/fpm/05-azuracast.ini.tmpl -cp /bd_build/php/www.conf.tmpl /etc/php/${PHP_VERSION}/fpm/www.conf.tmpl +cp /bd_build/web/php/php.ini.tmpl /etc/php/${PHP_VERSION}/fpm/05-azuracast.ini.tmpl +cp /bd_build/web/php/www.conf.tmpl /etc/php/${PHP_VERSION}/fpm/www.conf.tmpl # Install Composer curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer +# Download PHP-FPM healthcheck script +$minimal_apt_get_install libfcgi-bin + +wget -O /usr/local/bin/php-fpm-healthcheck \ + https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \ + && chmod +x /usr/local/bin/php-fpm-healthcheck + # Install PHP SPX profiler $minimal_apt_get_install php${PHP_VERSION}-dev zlib1g-dev build-essential diff --git a/util/docker/web/setup/sftpgo.sh b/util/docker/web/setup/sftpgo.sh index 497daee6f..41e54bc2b 100644 --- a/util/docker/web/setup/sftpgo.sh +++ b/util/docker/web/setup/sftpgo.sh @@ -10,7 +10,7 @@ $minimal_apt_get_install sftpgo mkdir -p /var/azuracast/sftpgo/persist /var/azuracast/sftpgo/backups -cp /bd_build/sftpgo/sftpgo.json /var/azuracast/sftpgo/sftpgo.json +cp /bd_build/web/sftpgo/sftpgo.json /var/azuracast/sftpgo/sftpgo.json touch /var/azuracast/sftpgo/sftpgo.db chown -R azuracast:azuracast /var/azuracast/sftpgo diff --git a/util/docker/web/startup_scripts/02_php.sh b/util/docker/web/startup_scripts/02_php.sh deleted file mode 100644 index 130ac6d71..000000000 --- a/util/docker/web/startup_scripts/02_php.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# Copy the php.ini template to its destination. -source /etc/php/.version - -dockerize -template "/etc/php/${PHP_VERSION}/fpm/05-azuracast.ini.tmpl:/etc/php/${PHP_VERSION}/fpm/conf.d/05-azuracast.ini" \ - -template "/etc/php/${PHP_VERSION}/fpm/www.conf.tmpl:/etc/php/${PHP_VERSION}/fpm/pool.d/www.conf" \ - cp /etc/php/${PHP_VERSION}/fpm/conf.d/05-azuracast.ini /etc/php/${PHP_VERSION}/cli/conf.d/05-azuracast.ini diff --git a/util/docker/web/startup_scripts/15_initialize.sh b/util/docker/web/startup_scripts/15_initialize.sh deleted file mode 100644 index 55109b8cc..000000000 --- a/util/docker/web/startup_scripts/15_initialize.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -set -ex - -exec sudo -E -u azuracast azuracast_cli azuracast:setup:initialize