feat: единая админка runtime-конфигов и indpic (#22)
deploy-preland / deploy zoomstage.online (push) Successful in 1m1s
deploy-preland / deploy zoomstage.online (push) Successful in 1m1s
This commit is contained in:
1 parent
5b6b2d2053
commit
9515c1c62b
52 files changed
+2459
-34
No files matched your search
@@ -0,0 +1,18 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName admin.zoomstage.online
|
||||
DocumentRoot /app/public
|
||||
<Directory /app/public>
|
||||
AllowOverride None
|
||||
Options -Indexes
|
||||
Require all granted
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</Directory>
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
Header always set Referrer-Policy "same-origin"
|
||||
Header always set X-Frame-Options "DENY"
|
||||
ErrorLog /proc/self/fd/2
|
||||
# Не логировать параметры запросов, тела форм и секреты.
|
||||
CustomLog /proc/self/fd/1 "%h %t %m %U %>s %b"
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/** Read-only экспорт существующих групп; выполняется внутри текущего API-контейнера. */
|
||||
declare(strict_types=1);
|
||||
define('APP_ROOT', getenv('APP_ROOT') ?: getcwd());
|
||||
require APP_ROOT . '/vendor/autoload.php';
|
||||
require APP_ROOT . '/vendor/yiisoft/yii2/Yii.php';
|
||||
$db = Yii::createObject(require APP_ROOT . '/config/database.php');
|
||||
$transaction = $db->beginTransaction(yii\db\Transaction::REPEATABLE_READ);
|
||||
try {
|
||||
$db->createCommand('SET TRANSACTION READ ONLY')->execute();
|
||||
$db->createCommand("SET LOCAL statement_timeout = '5s'")->execute();
|
||||
$groups = [];
|
||||
foreach (['degr' => 'degr_groups', 'tp' => 'tarif_groups', 'cons' => 'consent_groups'] as $marker => $table) {
|
||||
$query = (new yii\db\Query())->select(['number', 'name'])
|
||||
->from('{{%' . $table . '}}')->where(['between', 'number', 1, 9])->orderBy(['number' => SORT_ASC]);
|
||||
if ($marker !== 'degr') {
|
||||
$query->andWhere(['deleted_at' => null]);
|
||||
}
|
||||
$groups[$marker] = array_map(static fn(array $row): array => [
|
||||
'number' => (int) $row['number'], 'name' => (string) $row['name'],
|
||||
], $query->all($db));
|
||||
}
|
||||
$transaction->commit();
|
||||
echo json_encode(['host' => 'zoomzilla.ru', 'generated_at' => gmdate(DATE_ATOM), 'groups' => $groups],
|
||||
JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
|
||||
} catch (Throwable $exception) {
|
||||
if ($transaction->isActive) {
|
||||
$transaction->rollBack();
|
||||
}
|
||||
// Сообщения PDO могут содержать DSN: наружу только безопасный результат.
|
||||
fwrite(STDERR, "Каталог групп не обновлён.\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name admin.zoomstage.online;
|
||||
location ^~ /.well-known/acme-challenge/ { root /var/www/letsencrypt; }
|
||||
location / { return 301 https://$host$request_uri; }
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name admin.zoomstage.online;
|
||||
ssl_certificate /etc/letsencrypt/live/admin.zoomstage.online/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/admin.zoomstage.online/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
client_max_body_size 130m;
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18084;
|
||||
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 $scheme;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
expose_php=Off
|
||||
display_errors=Off
|
||||
log_errors=On
|
||||
upload_max_filesize=128M
|
||||
post_max_size=130M
|
||||
max_execution_time=60
|
||||
memory_limit=512M
|
||||
session.cookie_httponly=1
|
||||
session.cookie_samesite=Strict
|
||||
session.use_strict_mode=1
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
umask 077
|
||||
base=/srv/preland-admin
|
||||
# Подготовка root-процесса находится вне app-writable mount: UID приложения
|
||||
# не может подменить файл ссылкой между проверкой, chown и chmod.
|
||||
temporary=$(mktemp "$base/secrets/catalog.XXXXXX")
|
||||
trap 'rm -f "$temporary"' EXIT HUP INT TERM
|
||||
if ! /usr/bin/docker exec -i config-api-1 php < "$base/deploy/export-catalog.php" > "$temporary" 2>/dev/null; then
|
||||
echo 'Каталог групп не обновлён; прежний снимок сохранён.' >&2
|
||||
exit 1
|
||||
fi
|
||||
/usr/bin/python3 - "$temporary" <<'PY'
|
||||
import json, sys
|
||||
with open(sys.argv[1], encoding='utf-8') as stream:
|
||||
value = json.load(stream)
|
||||
assert value['host'] == 'zoomzilla.ru'
|
||||
assert set(value['groups']) == {'degr', 'tp', 'cons'}
|
||||
assert all(isinstance(items, list) for items in value['groups'].values())
|
||||
PY
|
||||
chown 997:987 "$temporary"
|
||||
chmod 600 "$temporary"
|
||||
# -T заменяет саму ссылку назначения, не следует в указанный ею каталог.
|
||||
mv -fT "$temporary" "$base/state/catalog.json"
|
||||
Reference in new issue
Block a user