diff --git a/files/docker/index/adminer-theme-switcher.php b/files/docker/index/adminer-theme-switcher.php
new file mode 100644
index 0000000..11b6a60
--- /dev/null
+++ b/files/docker/index/adminer-theme-switcher.php
@@ -0,0 +1,191 @@
+
+ * @link https://github.com/felladrin/adminer-theme-switcher
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
+ * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
+ */
+class AdminerThemeSwitcher
+{
+ protected static $themeList;
+
+ protected static $option;
+
+ public static $prompt = 'Type the number of the theme you want to use: ';
+
+ public static function run()
+ {
+ if (static::isRunningFromCommandLine()) {
+ static::printListAvailableThemes();
+ static::readOptionFromCommandLine();
+ static::switchTheme();
+ return;
+ }
+
+ if (static::hasNotSelectedAnOptionFromBrowserYet()) {
+ static::printListAvailableThemes();
+ static::printJavascriptPrompt();
+ } else {
+ static::readOptionFromBrowser();
+ static::switchTheme();
+ }
+ }
+
+ public static function isRunningFromCommandLine()
+ {
+ return (php_sapi_name() === 'cli');
+ }
+
+ public static function isRunningOnWindows()
+ {
+ return (PHP_OS == 'WINNT');
+ }
+
+ public static function getLineEnding()
+ {
+ return (static::isRunningFromCommandLine() ? PHP_EOL : '
');
+ }
+
+ public static function getThemeList()
+ {
+ if (!empty(static::$themeList)) {
+ return static::$themeList;
+ }
+
+ $context = stream_context_create([
+ 'http' => [
+ 'method' => 'GET',
+ 'header' => [
+ 'User-Agent: PHP'
+ ]
+ ],
+ 'ssl' => [
+ "verify_peer" => false,
+ "verify_peer_name" => false
+ ]
+ ]);
+
+ $urlOfThemesDirFromGithubRepo = 'https://api.github.com/repos/vrana/adminer/contents/designs';
+
+ $jsonThemeList = file_get_contents($urlOfThemesDirFromGithubRepo, false, $context);
+
+ static::$themeList = ($jsonThemeList ? json_decode($jsonThemeList) : false);
+
+ return static::$themeList;
+ }
+
+ public static function downloadTheme($themeIndex = null)
+ {
+ if (is_null($themeIndex)) {
+ $themeIndex = static::$option;
+ }
+
+ $themeName = static::getThemeList()[$themeIndex]->name;
+ $urlOfCssFileFromGithubRepo = "https://raw.githubusercontent.com/vrana/adminer/master/designs/{$themeName}/adminer.css";
+ $cssContent = file_get_contents($urlOfCssFileFromGithubRepo);
+ $filePath = __DIR__ . '/adminer.css';
+ $fileExists = file_exists('adminer.css');
+ if (file_put_contents($filePath, $cssContent) !== false) {
+ if (!$fileExists) {
+ chmod($filePath, 0777);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function printListAvailableThemes()
+ {
+ echo "List of available Adminer Themes:" . static::getLineEnding() . static::getLineEnding();
+
+ if (static::getThemeList()) {
+ foreach (static::getThemeList() as $index => $theme) {
+ if ($theme->type !== 'dir') {
+ continue;
+ }
+
+ echo "[{$index}] {$theme->name}" . static::getLineEnding();
+ }
+ }
+
+ echo static::getLineEnding();
+ }
+
+ public static function readOptionFromCommandLine()
+ {
+ if (static::isRunningOnWindows()) {
+ echo static::$prompt;
+ static::$option = stream_get_line(STDIN, 1024, static::getLineEnding());
+ } else {
+ static::$option = readline(static::$prompt);
+ }
+
+ return static::$option;
+ }
+
+ public static function printResultOf($download)
+ {
+ if ($download) {
+ echo 'Theme switched successfully!' . static::getLineEnding();
+ } else {
+ echo 'Something went wrong with the download. Try again!' . static::getLineEnding();
+ }
+ }
+
+ public static function printJavascriptPrompt()
+ {
+ echo '';
+ }
+
+ public static function readOptionFromBrowser()
+ {
+ if (isset($_GET['option'])) {
+ static::$option = $_GET['option'];
+ }
+
+ return static::$option;
+ }
+
+ public static function hasNotSelectedAnOptionFromBrowserYet()
+ {
+ return !isset($_GET['option']);
+ }
+
+ public static function hasSelectedAValidOption()
+ {
+ return is_numeric(static::$option) && static::$option < count(static::getThemeList());
+ }
+
+ public static function printInvalidOptionErrorMessage()
+ {
+ $errorMessage = static::$option . " is not a number from the options! Try again!";
+
+ if (static::isRunningFromCommandLine()) {
+ echo $errorMessage . static::getLineEnding();
+ static::run();
+ } else {
+ echo '';
+ echo '';
+ }
+ }
+
+ public static function switchTheme()
+ {
+ if (static::hasSelectedAValidOption()) {
+ static::printResultOf(static::downloadTheme());
+ } else {
+ static::printInvalidOptionErrorMessage();
+ }
+ }
+}
+
+AdminerThemeSwitcher::run();
diff --git a/files/docker/index/docker-stack.yaml b/files/docker/index/docker-stack.yaml
new file mode 100644
index 0000000..a7a9e42
--- /dev/null
+++ b/files/docker/index/docker-stack.yaml
@@ -0,0 +1,65 @@
+secrets:
+ index_db_pass:
+ external: true
+
+networks:
+ reverse_proxy:
+ external: true
+
+volumes:
+ index_db:
+ adminer_plugins:
+
+services:
+ adminer:
+ image: ghcr.io/shyim/adminerevo:latest
+ restart: unless-stopped
+ networks:
+ - default
+ - reverse_proxy
+ volumes:
+ - adminer_plugins:/var/www/html/plugins-custom
+ environment:
+ ADMINER_DEFAULT_DRIVER: pgsql
+ ADMINER_DEFAULT_SERVER: tasks.db
+ ADMINER_DEFAULT_USER: postgres
+ ADMINER_PLUGINS: tables-filter tinymce edit-calendar edit-foreign enum-option enum-types file-upload slugify struct-comments
+ ADMINER_DESIGN: 'pepa-linha-dark'
+ deploy:
+ rollback_config:
+ failure_action: continue
+ update_config:
+ delay: 2s
+ failure_action: rollback
+ order: start-first
+ placement:
+ constraints:
+ - node.labels.services_kind==${SERVICE_KIND:-common}
+ labels:
+ - traefik.enable=true
+ - traefik.http.routers.index.rule=Host(`index.alecodes.page`)
+ - traefik.http.services.index.loadbalancer.server.port=8080
+
+ db:
+ image: postgres
+ restart: unless-stopped
+ secrets:
+ - index_db_pass
+ volumes:
+ - index_db:/var/lib/postgresql/data
+ - type: tmpfs
+ target: /dev/shm
+ tmpfs:
+ size: 134217728 # 128*2^20 bytes = 128Mb
+ environment:
+ POSTGRES_PASSWORD_FILE: /run/secrets/index_db_pass
+ deploy:
+ rollback_config:
+ failure_action: continue
+ update_config:
+ delay: 2s
+ failure_action: rollback
+ order: start-first
+ placement:
+ constraints:
+ - node.labels.services_kind==${SERVICE_KIND:-common}