From 13741413b440a1077954d0fb9cf60f6ca6fbdae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Jaussoin?= Date: Mon, 22 Sep 2025 22:55:42 +0200 Subject: [PATCH] Containerfile and podman-compose test scripts --- .github/workflows/docker.yml | 20 --------- CHANGELOG.md | 1 + Containerfile | 43 ++++++++++++++++++ README.md | 36 +++++++++++---- config/daemon.php | 2 +- etc/nginx/conf.d/movim-websocket.conf | 15 +++++++ etc/nginx/conf.d/movim.conf | 6 ++- .../s6-rc.d/movim-daemon/dependencies | 1 + etc/s6-overlay/s6-rc.d/movim-daemon/run | 2 + etc/s6-overlay/s6-rc.d/movim-daemon/type | 1 + etc/s6-overlay/s6-rc.d/movim-migrations/type | 1 + etc/s6-overlay/s6-rc.d/movim-migrations/up | 2 + linker.php | 4 +- podman-compose.yml | 24 ++++++++++ src/Movim/Console/DaemonCommand.php | 38 ++++++++++------ src/Movim/Daemon/Core.php | 7 +-- src/Movim/Daemon/Session.php | 44 +++++++++---------- templater.php | 15 +++---- 18 files changed, 177 insertions(+), 85 deletions(-) delete mode 100644 .github/workflows/docker.yml create mode 100644 Containerfile create mode 100644 etc/nginx/conf.d/movim-websocket.conf create mode 100644 etc/s6-overlay/s6-rc.d/movim-daemon/dependencies create mode 100644 etc/s6-overlay/s6-rc.d/movim-daemon/run create mode 100644 etc/s6-overlay/s6-rc.d/movim-daemon/type create mode 100644 etc/s6-overlay/s6-rc.d/movim-migrations/type create mode 100755 etc/s6-overlay/s6-rc.d/movim-migrations/up create mode 100644 podman-compose.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 5ddfec932..000000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,20 +0,0 @@ -# a docker image is built on each commit and pushed to docker hub -name: Docker Image CI - -on: - push: - branches: ["master"] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: clone movim_docker repo - run: git clone --depth 1 https://github.com/movim/movim_docker/ - - name: Build the docker image - run: cd movim_docker/ && docker build -t movim/movim:master --build-arg MOVIM_VERSION=master . - - name: Authenticate to docker hub - run: docker login -u edhelas -p ${{ secrets.DOCKER_HUB_TOKEN }} - - name: push docker image to docker hub - run: docker push movim/movim:master diff --git a/CHANGELOG.md b/CHANGELOG.md index 29732cea0..ae871a638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ v0.32 (master) * Add a templater worker and start to offload some widget calls to it * Refactor and simplify the internal events system, enforce Packet everywhere * Remove the necessity to launch the Login page to cache the BASE_URI for the daemon launch +* Containerfile and podman-compose test scripts v0.31 --------------------------- diff --git a/Containerfile b/Containerfile new file mode 100644 index 000000000..febd28647 --- /dev/null +++ b/Containerfile @@ -0,0 +1,43 @@ +FROM serversideup/php:8.4-fpm-nginx-alpine + +USER root + +# S6 +COPY ./etc/s6-overlay/s6-rc.d/movim-migrations/ /etc/s6-overlay/s6-rc.d/movim-migrations/ +COPY ./etc/s6-overlay/s6-rc.d/movim-daemon/ /etc/s6-overlay/s6-rc.d/movim-daemon/ +RUN touch /etc/s6-overlay/s6-rc.d/user/contents.d/movim-migrations +RUN touch /etc/s6-overlay/s6-rc.d/user/contents.d/movim-daemon + +# nginx websocket +COPY /etc/nginx/conf.d/movim-websocket.conf /etc/nginx/server-opts.d/ + +# PHP +RUN install-php-extensions imagick gd + +# Movim + +ENV SSL_MODE=full +ENV PHP_OPCACHE_ENABLE=1 +ENV DAEMON_INTERFACE=127.0.0.1 +ENV DAEMON_PORT=8083 +ENV NGINX_WEBROOT=/var/www/html/public + +ADD . /var/www/html +WORKDIR /var/www/html + +RUN cd /var/www/html +RUN composer install +# Ensure that the host .env is not copied +RUN rm -rf /var/www/.env +# Setup some directories +RUN rm -rf cache; \ + mkdir cache; \ + chown -R www-data:www-data cache; \ + rm -rf public/cache; \ + mkdir public/cache; \ + chown -R www-data:www-data public/cache; \ + rm -rf log; \ + mkdir log; \ + chown -R www-data:www-data log/ + +USER www-data \ No newline at end of file diff --git a/README.md b/README.md index 954d50029..585362e7b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -

Movim

-

+

Movim

Federated blogging and chat platform

![build ci badge](https://github.com/movim/movim/actions/workflows/main.yml/badge.svg?event=push) @@ -12,10 +11,30 @@ Movim is a federated blogging and chat platform that acts as a web frontend for ![movim screenshot](https://movim.eu/img/home.webp) -Installation ------------- -Please refer to the installation instructions in the [INSTALL.md](INSTALL.md) file, -or check out the [Movim Wiki](https://github.com/movim/movim/wiki) for more information. +Deployment +---------- +Please refer to the installation instructions in the [INSTALL.md](INSTALL.md) file, or check out the [Movim Wiki](https://github.com/movim/movim/wiki) for more information. + +Quick Test +---------- + +You can try out Movim on your local machine in a container using [Podman (main website)](https://podman.io/). Podman is a FOSS alternative to Docker that is available on all the main distributions. + +⚠️ __This setup is only for tests purpose, the containers are not optimized and most of the caches are disabled. To deploy your own Movim instance use the [INSTALL.md](INSTALL.md) tutorial.__ + +Install `podman-compose` and clone the repository before trying the next steps. + +Launch the podman-compose script + + podman-compose up + +After a few minutes it will launch a local test instance with a blank database. + +You can then access in your browser at the following URL: + + https://127.0.0.1:8443/ + +The container is using a self-signed certificate, accept to get to the login page. Security report --------------- @@ -25,8 +44,9 @@ See [SECURITY.md](./SECURITY.md). Support Us ---------- You can help Movim by: -* Doing a one time donation using Paypal: [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=8QHPJDAQXT9UC) -* Helping us covering our monthly costs on our official Patreon page: [![Donate](https://img.shields.io/badge/Patreon-Become%20a%20Patron-orange.svg)](https://www.patreon.com/movim) + +* Doing a one time donation using PayPal [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=8QHPJDAQXT9UC) +* Helping us covering our monthly costs on our official Patreon page [![Donate](https://img.shields.io/badge/Patreon-Become%20a%20Patron-orange.svg)](https://www.patreon.com/movim) Links ----- diff --git a/config/daemon.php b/config/daemon.php index 880b402be..3cf30c886 100644 --- a/config/daemon.php +++ b/config/daemon.php @@ -7,7 +7,7 @@ return [ 'url' => env('DAEMON_URL', null), 'port' => env('DAEMON_PORT', 8080), - 'interface' => env('DAEMON_INTERFACE', 'localhost'), + 'interface' => env('DAEMON_INTERFACE', '127.0.0.1'), 'debug' => env('DAEMON_DEBUG', false), 'verbose' => env('DAEMON_VERBOSE', false), ]; diff --git a/etc/nginx/conf.d/movim-websocket.conf b/etc/nginx/conf.d/movim-websocket.conf new file mode 100644 index 000000000..89b3d4aec --- /dev/null +++ b/etc/nginx/conf.d/movim-websocket.conf @@ -0,0 +1,15 @@ +location /ws/ { + proxy_pass http://127.0.0.1:8083/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + + proxy_read_timeout 1800s; + proxy_send_timeout 1800s; + + proxy_redirect off; +} diff --git a/etc/nginx/conf.d/movim.conf b/etc/nginx/conf.d/movim.conf index fedce336f..048127763 100644 --- a/etc/nginx/conf.d/movim.conf +++ b/etc/nginx/conf.d/movim.conf @@ -1,6 +1,8 @@ server { - listen 443 ssl http2; - listen [::]:443 ssl http2; + listen 443 ssl; + listen [::]:443 ssl; + + http2 on; # Where Movim public directory is setup root /var/www/movim/public; diff --git a/etc/s6-overlay/s6-rc.d/movim-daemon/dependencies b/etc/s6-overlay/s6-rc.d/movim-daemon/dependencies new file mode 100644 index 000000000..bd6e9df68 --- /dev/null +++ b/etc/s6-overlay/s6-rc.d/movim-daemon/dependencies @@ -0,0 +1 @@ +movim-migrations \ No newline at end of file diff --git a/etc/s6-overlay/s6-rc.d/movim-daemon/run b/etc/s6-overlay/s6-rc.d/movim-daemon/run new file mode 100644 index 000000000..c33ee4fb4 --- /dev/null +++ b/etc/s6-overlay/s6-rc.d/movim-daemon/run @@ -0,0 +1,2 @@ +#!/command/with-contenv sh +php /var/www/html/daemon.php start \ No newline at end of file diff --git a/etc/s6-overlay/s6-rc.d/movim-daemon/type b/etc/s6-overlay/s6-rc.d/movim-daemon/type new file mode 100644 index 000000000..1780f9f44 --- /dev/null +++ b/etc/s6-overlay/s6-rc.d/movim-daemon/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/etc/s6-overlay/s6-rc.d/movim-migrations/type b/etc/s6-overlay/s6-rc.d/movim-migrations/type new file mode 100644 index 000000000..3d92b15f2 --- /dev/null +++ b/etc/s6-overlay/s6-rc.d/movim-migrations/type @@ -0,0 +1 @@ +oneshot \ No newline at end of file diff --git a/etc/s6-overlay/s6-rc.d/movim-migrations/up b/etc/s6-overlay/s6-rc.d/movim-migrations/up new file mode 100755 index 000000000..ac8f75f3f --- /dev/null +++ b/etc/s6-overlay/s6-rc.d/movim-migrations/up @@ -0,0 +1,2 @@ +#!/command/with-contenv sh +composer -d /var/www/html/ movim:migrate \ No newline at end of file diff --git a/linker.php b/linker.php index 7dfdc55ed..3ac98da3a 100644 --- a/linker.php +++ b/linker.php @@ -275,7 +275,7 @@ $xmppBehaviour = function (React\Socket\Connection $stream) use (&$xmppSocket, $ Wrapper::getInstance()->iterate('socket_connected'); - if (getenv('verbose')) { + if (config('daemon.verbose')) { logOut(colorize('XMPP socket launched', 'blue')); logOut(" launched : " . \humanSize(memory_get_usage())); } @@ -325,7 +325,7 @@ $xmppBehaviour = function (React\Socket\Connection $stream) use (&$xmppSocket, $ }; $wsConnector = new \Ratchet\Client\Connector($loop); -$wsConnector('ws://127.0.0.1:' . getenv('port'), [], [ +$wsConnector('ws://127.0.0.1:' . config('daemon.port'), [], [ 'MOVIM_SESSION_ID' => getenv('sid'), 'MOVIM_DAEMON_KEY' => getenv('key') ])->then(function (Ratchet\Client\WebSocket $socket) use (&$wsSocket, $wsSocketBehaviour) { diff --git a/podman-compose.yml b/podman-compose.yml new file mode 100644 index 000000000..5834f5392 --- /dev/null +++ b/podman-compose.yml @@ -0,0 +1,24 @@ +services: + db: + image: 'postgres:latest' + environment: + POSTGRES_USER: movim + POSTGRES_PASSWORD: movim + POSTGRES_DB: movim + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 2s + movim: + build: + context: ./ + dockerfile: Containerfile + depends_on: + db: + condition: service_healthy + environment: + DB_HOST: db + DAEMON_URL: https://127.0.0.1:8443/ + DAEMON_DEBUG: true + DAEMON_VERBOSE: true + ports: + - 8443:8443 \ No newline at end of file diff --git a/src/Movim/Console/DaemonCommand.php b/src/Movim/Console/DaemonCommand.php index 8b56b27eb..4fae20c76 100644 --- a/src/Movim/Console/DaemonCommand.php +++ b/src/Movim/Console/DaemonCommand.php @@ -52,7 +52,7 @@ class DaemonCommand extends Command if ($manager->printStatus('movim')['hasDownMigration']) { $output->writeln('The database needs to be migrated before running the daemon'); $output->writeln('To migrate the database run'); - $output->writeln('php vendor/bin/phinx migrate'); + $output->writeln('composer movim:migrate'); exit; } @@ -76,8 +76,8 @@ class DaemonCommand extends Command # URI in practice. $schemeEnforcedURL = parse_url(config('daemon.url'), PHP_URL_SCHEME) ? config('daemon.url') - : 'http://' . ltrim(config('daemon.url'), '/'); - if (Validator::url()->notEmpty()->isValid($schemeEnforcedURL)) { + : 'https://' . ltrim(config('daemon.url'), '/'); + if (filter_var($schemeEnforcedURL, FILTER_VALIDATE_URL)) { $baseuri = rtrim(config('daemon.url'), '/') . '/'; } } else { @@ -93,15 +93,15 @@ class DaemonCommand extends Command } - $clearTemplatesCache = new Process('exec ' . PHP_BINARY . ' daemon.php clearTemplatesCache'); + $clearTemplatesCache = new Process('exec ' . PHP_BINARY . ' ' . DOCUMENT_ROOT . '/daemon.php clearTemplatesCache'); $clearTemplatesCache->start($loop); $clearTemplatesCache->on('exit', fn($out) => $output->writeln('Templates cache cleared')); - $compileLanguages = new Process('exec ' . PHP_BINARY . ' daemon.php compileLanguages'); + $compileLanguages = new Process('exec ' . PHP_BINARY . ' ' . DOCUMENT_ROOT . '/daemon.php compileLanguages'); $compileLanguages->start($loop); $compileLanguages->on('exit', fn($out) => $output->writeln('Compiled po files')); - $compileStickers = new Process('exec ' . PHP_BINARY . ' daemon.php compileStickers'); + $compileStickers = new Process('exec ' . PHP_BINARY . ' ' . DOCUMENT_ROOT . '/daemon.php compileStickers'); $compileStickers->start($loop); $compileStickers->on('exit', fn($out) => $output->writeln('Stickers compiled')); @@ -113,7 +113,7 @@ class DaemonCommand extends Command } if (isOpcacheEnabled()) { - $compileOpcache = new Process('exec ' . PHP_BINARY . ' daemon.php compileOpcache'); + $compileOpcache = new Process('exec ' . PHP_BINARY . ' ' . DOCUMENT_ROOT . '/daemon.php compileOpcache'); $compileOpcache->start($loop); $compileOpcache->on('exit', fn($out) => $output->writeln('Files compiled in Opcache')); } else { @@ -131,18 +131,30 @@ class DaemonCommand extends Command $socketApi = new SocketServer('unix://' . API_SOCKET); new Api($socketApi, $core); - $resolverWorker = new Process('exec ' . PHP_BINARY . ' resolver.php'); + // Resolver + + $resolverWorker = new Process('exec ' . PHP_BINARY . ' resolver.php', cwd: DOCUMENT_ROOT); $resolverWorker->start($loop); $resolverWorker->on('exit', fn() => $output->writeln('Resolver Worker crashed')); $output->writeln('Resolver Worker launched'); + // Templater + $templaterWorker = new Process( 'exec ' . PHP_BINARY . ' templater.php', - null, - [ - 'key' => $core->getKey(), - 'baseuri' => $baseuri, - 'port' => config('daemon.port') + cwd: DOCUMENT_ROOT, + env: [ + 'baseuri' => $baseuri, + 'DAEMON_DEBUG' => config('daemon.debug'), + 'DAEMON_PORT' => config('daemon.port'), + 'DAEMON_VERBOSE'=> config('daemon.verbose'), + 'DB_DATABASE' => config('database.database'), + 'DB_DRIVER' => config('database.driver'), + 'DB_HOST' => config('database.host'), + 'DB_PASSWORD' => config('database.password'), + 'DB_PORT' => config('database.port'), + 'DB_USERNAME' => config('database.username'), + 'key' => $core->getKey(), ] ); $templaterWorker->start($loop); diff --git a/src/Movim/Daemon/Core.php b/src/Movim/Daemon/Core.php index 9487fa5a2..a757534a5 100644 --- a/src/Movim/Daemon/Core.php +++ b/src/Movim/Daemon/Core.php @@ -114,7 +114,7 @@ class Core implements MessageComponentInterface "--- " . colorize("Server Configuration - Caddy", 'purple') . " ---" . "\n"; echo colorize("Add this in your configuration file", 'yellow') . "\nhandle /ws/* { - reverse_proxy localhost:8080 + reverse_proxy localhost:{$port} } "; @@ -149,11 +149,8 @@ class Core implements MessageComponentInterface $this->loop, $sid, $this->baseuri, - config('daemon.port'), $this->key, - $language, - config('daemon.verbose'), - config('daemon.debug') + $language ); } diff --git a/src/Movim/Daemon/Session.php b/src/Movim/Daemon/Session.php index e50156d5b..1c09a7740 100644 --- a/src/Movim/Daemon/Session.php +++ b/src/Movim/Daemon/Session.php @@ -22,37 +22,27 @@ class Session public ?Process $process; public ?ConnectionInterface $internalSocket = null; - private int $port; // Daemon Websocket port private string $key; // Daemon secure key public bool $registered = false; public bool $started = false; private $state; - private $verbose; - private $debug; private $language; public function __construct( LoopInterface $loop, string $sid, string $baseuri, - int $port, string $key, - $language = false, - $verbose = false, - $debug = false + $language = false ) { $this->sid = $sid; $this->baseuri = $baseuri; $this->language = $language; - $this->port = $port; $this->key = $key; - $this->verbose = $verbose; - $this->debug = $debug; - $this->clients = new \SplObjectStorage; $this->register($loop); @@ -63,7 +53,7 @@ class Session { $this->clients->attach($conn); - if ($this->verbose) { + if (config('daemon.verbose')) { echo colorize($this->sid, 'yellow') . " : " . colorize($conn->resourceId . " connected\n", 'green'); } @@ -76,7 +66,7 @@ class Session { $this->internalSocket = $conn; - if ($this->verbose) { + if (config('daemon.verbose')) { echo colorize($this->sid, 'yellow') . " : " . colorize($conn->resourceId . " internal connected\n", 'green'); } } @@ -85,7 +75,7 @@ class Session { $this->clients->detach($conn); - if ($this->verbose) { + if (config('daemon.verbose')) { echo colorize($this->sid, 'yellow') . " : " . colorize($conn->resourceId . " deconnected\n", 'red'); } @@ -121,22 +111,28 @@ class Session // Launching the linker $this->process = new Process( 'exec ' . PHP_BINARY . ' ' . $configuration . ' -d=memory_limit=512M linker.php ' . $this->sid, - null, - [ - 'sid' => $this->sid, - 'baseuri' => $this->baseuri, - 'language' => $this->language, - 'verbose' => $this->verbose, - 'debug' => $this->debug, - 'key' => $this->key, - 'port' => $this->port + cwd: DOCUMENT_ROOT, + env: [ + 'baseuri' => $this->baseuri, + 'DAEMON_DEBUG' => config('daemon.debug'), + 'DAEMON_PORT' => config('daemon.port'), + 'DAEMON_VERBOSE'=> config('daemon.verbose'), + 'DB_DATABASE' => config('database.database'), + 'DB_DRIVER' => config('database.driver'), + 'DB_HOST' => config('database.host'), + 'DB_PASSWORD' => config('database.password'), + 'DB_PORT' => config('database.port'), + 'DB_USERNAME' => config('database.username'), + 'key' => $this->key, + 'language' => $this->language, + 'sid' => $this->sid, ] ); $this->process->start($loop); // The linker died, we close properly the session $this->process->on('exit', function ($output) { - if ($this->verbose) { + if (config('daemon.verbose')) { echo colorize($this->sid, 'yellow') . " : " . colorize("linker killed \n", 'red'); } diff --git a/templater.php b/templater.php index acc452f3b..007e3dbef 100644 --- a/templater.php +++ b/templater.php @@ -30,12 +30,7 @@ $handler = function (ServerRequestInterface $request) use ($templater) { $data = json_decode((string)$request->getBody()); return new Promise(function () use ($data, $templater) { - $templater->callWidget($data->jid, $data->widget, $data->method, $data->data);/*->then(function ($resolvedData) use ($data) { - global $wsTemplaterSocket; - - $resolvedData['sid'] = $data->sid; - $wsTemplaterSocket->send(json_encode($resolvedData)); - });*/ + $templater->callWidget($data->jid, $data->widget, $data->method, $data->data); }); }; @@ -45,19 +40,19 @@ $server->on('error', function (\Throwable $e) { }); $path = 'unix://' . TEMPLATER_SOCKET; -//$path = '127.0.0.1:8899'; $server->listen(new SocketServer($path)); - /** * Authenticated Websocket to the main Daemon */ $wsConnector = new \Ratchet\Client\Connector($loop); -$wsConnector('ws://127.0.0.1:' . getenv('port'), [], [ +$wsConnector('ws://127.0.0.1:' . config('daemon.port'), [], [ 'MOVIM_DAEMON_KEY' => getenv('key'), - 'MOVIM_TEMPLATER' => 'hop', + 'MOVIM_TEMPLATER' => 'templater', ])->then(function (Ratchet\Client\WebSocket $socket) use (&$wsTemplaterSocket) { $wsTemplaterSocket = $socket; +}, function ($e) { + \logError($e->getMessage()); }); $loop->run();