diff --git a/sodew-bash-main (1).zip b/sodew-bash-main (1).zip new file mode 100644 index 0000000..ed4fb21 Binary files /dev/null and b/sodew-bash-main (1).zip differ diff --git a/sodew-bash-main/.gitignore b/sodew-bash-main/.gitignore new file mode 100644 index 0000000..8a93bc4 --- /dev/null +++ b/sodew-bash-main/.gitignore @@ -0,0 +1,9 @@ +.zed/ +_tmp/ +data/ +tools/registry/images +config.sh +_gen.sh +CLAUDE.md +codebook.toml +.env diff --git a/sodew-bash-main/README.md b/sodew-bash-main/README.md new file mode 100644 index 0000000..51d5ef0 --- /dev/null +++ b/sodew-bash-main/README.md @@ -0,0 +1,123 @@ +# KUBE 7.1.563 + +Bash script for deploying/backups and restore SODEW infrastructures. + + +> [!WARNING] +> Documentation is outdated + + +### Install HELM +``` +curl -fsSL https://get.helm.sh/helm-v3.16.2-linux-amd64.tar.gz | tar -xz +sudo mv linux-amd64/helm /usr/local/bin/helm + +helm version +``` + +--- + +- [File structure](docs/file_scructure.md) +- [Options](docs/options.md) +- Usage + - [Backup](docs/backup.md) + - [Restore](docs/restore.md) + - [Storage](docs/storage.md) + - [k3s](docs/k3s.md) + - [MariaDB](docs/resources/mariadb.md) + - [Redis](docs/resources/redis.md) + - [OpenLiteSpeed](docs/resources/openlitespeed.md) + - [Postfix](docs/resources/postfix.md) + - [Worker](docs/resources/worker.md) + - [Site/Wordpress](docs/site.md) + - [Transfer](docs/transfer.md) + - [Shell](docs/shell.md) + - [Log](docs/log.md) + - [Cron](docs/cron.md) + - [Test](docs/test.md) + - [Install](docs/install.md) + - [Script](docs/script.md) +- [Mail server](docs/mta.md) + +## Getting started + +Before you start, you must create a configuration file: +```bash +cp config.sh.default config.sh +``` +and make the necessary changes. + +If necessary, you can set your own password values for MariaDB, Redis, rSync, OpenLiteSpeed ... +This can be done by specifying values for the corresponding variables or by writing values to the corresponding files in the `data` directory. If there are no files, just run the script without parameters. The files with passwords will be created automatically. After that you can make the appropriate edits. + +## Usage + +The script requires elevated permissions to work (sudoers group). + +## Scheme open ports +```mermaid +flowchart LR; + subgraph MD[MariaDB replica] + MDM[mariadb-master-0] + MDS[mariadb-slave-0] + end + subgraph RD[Redis cluster] + RD0[redis-0] + RD1[redis-1] + RD2[redis-2] + RS0[redis-sentinel-0] + RS1[redis-sentinel-1] + RS2[redis-sentinel-2] + end + subgraph OLS[OpenLiteSpeed] + OL1[openlitespeed-0] + OL2[openlitespeed-1] + end + PTF[postfix-xxxxxxxxx-xxxxx] + + MDM <==> MDS + RD0 <==> RD1 <==> RD2 <==> RD0 + RS0 <==> RS1 <==> RS2 <==> RS0 + + P3306([3306]) + P3306 --mysql--> MD + + P6379([6379]) + P26379([26379]) + P6379 & P26379 --redis--> RD + + P80([80/443]) + P80 --http/https--> OLS + + P7080([7080/31080]) + P7080 --admin panel--> OLS + + P25([25]) + P587([587]) + P25 --smtp--> PTF + P587 --smtp=tls--> PTF +``` +## Scheme use ports +```mermaid +%%graph LR; +flowchart LR; + subgraph K3S[Server] + MD[MariaDB] + RD[Redis] + + PTF[Postfix] + subgraph OLS[OpenLiteSpeed] + WP[Sites on Wordpress] + ADM[Admin Panel] + end + end + NET[Internet] + + NET --80,443--> WP + NET --31080--> ADM + NET --25--> PTF + + WP --3306--> MD + WP --6379--> RD + WP --587--> PTF +`````` diff --git a/sodew-bash-main/assets/domains.list b/sodew-bash-main/assets/domains.list new file mode 100644 index 0000000..e13a961 --- /dev/null +++ b/sodew-bash-main/assets/domains.list @@ -0,0 +1,51 @@ +#mta.krumax.cz +#api.krumax.cz +#us1.krumax.cz +us2.krumax.cz +us3.krumax.cz +us4.krumax.cz +us5.krumax.cz +us6.krumax.cz +us7.krumax.cz +us8.krumax.cz +us9.krumax.cz +us10.krumax.cz +us11.krumax.cz +us12.krumax.cz +us13.krumax.cz +us14.krumax.cz +us15.krumax.cz +us16.krumax.cz +us17.krumax.cz +us18.krumax.cz +us19.krumax.cz +us20.krumax.cz +us21.krumax.cz +us22.krumax.cz +us23.krumax.cz +us24.krumax.cz +us25.krumax.cz +us26.krumax.cz +us27.krumax.cz +us28.krumax.cz +us29.krumax.cz +us30.krumax.cz +us31.krumax.cz +us32.krumax.cz +us33.krumax.cz +us34.krumax.cz +us35.krumax.cz +us36.krumax.cz +us37.krumax.cz +us38.krumax.cz +us39.krumax.cz +us40.krumax.cz +us41.krumax.cz +us42.krumax.cz +us43.krumax.cz +us44.krumax.cz +us45.krumax.cz +us46.krumax.cz +us47.krumax.cz +us48.krumax.cz +us49.krumax.cz diff --git a/sodew-bash-main/assets/k3s/Chart.yaml b/sodew-bash-main/assets/k3s/Chart.yaml new file mode 100644 index 0000000..b30be40 --- /dev/null +++ b/sodew-bash-main/assets/k3s/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: stack +version: 0.1.0 +type: application diff --git a/sodew-bash-main/assets/k3s/profiles/cpu-16c.yaml b/sodew-bash-main/assets/k3s/profiles/cpu-16c.yaml new file mode 100644 index 0000000..7d16f47 --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/cpu-16c.yaml @@ -0,0 +1,16 @@ +profiles: + mariadbMaster: + cpuRequest: 1000m + cpuLimit: 4000m + threadPoolSize: 4 + mariadbSlave: + cpuRequest: 250m + cpuLimit: 1000m + threadPoolSize: 1 + redis: + cpuRequest: 250m + cpuLimit: 1000m + maxClients: 20000 + openlitespeed: + cpuRequest: 3000m + cpuLimit: 7000m diff --git a/sodew-bash-main/assets/k3s/profiles/cpu-32c.yaml b/sodew-bash-main/assets/k3s/profiles/cpu-32c.yaml new file mode 100644 index 0000000..87cb0fc --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/cpu-32c.yaml @@ -0,0 +1,16 @@ +profiles: + mariadbMaster: + cpuRequest: 2000m + cpuLimit: 8000m + threadPoolSize: 6 + mariadbSlave: + cpuRequest: 500m + cpuLimit: 2000m + threadPoolSize: 1 + redis: + cpuRequest: 750m + cpuLimit: 4000m + maxClients: 30000 + openlitespeed: + cpuRequest: 5000m + cpuLimit: 12000m diff --git a/sodew-bash-main/assets/k3s/profiles/cpu-4c.yaml b/sodew-bash-main/assets/k3s/profiles/cpu-4c.yaml new file mode 100644 index 0000000..a23a917 --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/cpu-4c.yaml @@ -0,0 +1,16 @@ +profiles: + mariadbMaster: + cpuRequest: 500m + cpuLimit: 1500m + threadPoolSize: 2 + mariadbSlave: + cpuRequest: 100m + cpuLimit: 500m + threadPoolSize: 1 + redis: + cpuRequest: 100m + cpuLimit: 500m + maxClients: 8000 + openlitespeed: + cpuRequest: 750m + cpuLimit: 2000m diff --git a/sodew-bash-main/assets/k3s/profiles/cpu-8c.yaml b/sodew-bash-main/assets/k3s/profiles/cpu-8c.yaml new file mode 100644 index 0000000..a0c9fba --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/cpu-8c.yaml @@ -0,0 +1,16 @@ +profiles: + mariadbMaster: + cpuRequest: 750m + cpuLimit: 2500m + threadPoolSize: 3 + mariadbSlave: + cpuRequest: 200m + cpuLimit: 750m + threadPoolSize: 1 + redis: + cpuRequest: 150m + cpuLimit: 750m + maxClients: 12000 + openlitespeed: + cpuRequest: 1250m + cpuLimit: 3000m diff --git a/sodew-bash-main/assets/k3s/profiles/memory-128g.yaml b/sodew-bash-main/assets/k3s/profiles/memory-128g.yaml new file mode 100644 index 0000000..46d698d --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/memory-128g.yaml @@ -0,0 +1,26 @@ +profiles: + mariadbMaster: + memoryRequest: 32Gi + memoryLimit: 48Gi + maxConnections: 600 + innodbBufferPoolSize: 30G + innodbBufferPoolInstances: 16 + tableOpenCache: 24000 + tableOpenCacheInstances: 16 + tmpTableSize: 64M + mariadbSlave: + memoryRequest: 8Gi + memoryLimit: 12Gi + maxConnections: 50 + innodbBufferPoolSize: 8G + innodbBufferPoolInstances: 8 + tableOpenCache: 8000 + tableOpenCacheInstances: 8 + tmpTableSize: 64M + redis: + memoryRequest: 2Gi + memoryLimit: 4Gi + maxmemory: 3500mb + openlitespeed: + memoryRequest: 8Gi + memoryLimit: 24Gi diff --git a/sodew-bash-main/assets/k3s/profiles/memory-16g.yaml b/sodew-bash-main/assets/k3s/profiles/memory-16g.yaml new file mode 100644 index 0000000..dc9e1d6 --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/memory-16g.yaml @@ -0,0 +1,26 @@ +profiles: + mariadbMaster: + memoryRequest: 4Gi + memoryLimit: 6Gi + maxConnections: 100 + innodbBufferPoolSize: 4G + innodbBufferPoolInstances: 2 + tableOpenCache: 3000 + tableOpenCacheInstances: 4 + tmpTableSize: 8M + mariadbSlave: + memoryRequest: 1Gi + memoryLimit: 1.5Gi + maxConnections: 30 + innodbBufferPoolSize: 1G + innodbBufferPoolInstances: 1 + tableOpenCache: 1200 + tableOpenCacheInstances: 2 + tmpTableSize: 8M + redis: + memoryRequest: 128Mi + memoryLimit: 512Mi + maxmemory: 350mb + openlitespeed: + memoryRequest: 1.5Gi + memoryLimit: 2Gi diff --git a/sodew-bash-main/assets/k3s/profiles/memory-32g.yaml b/sodew-bash-main/assets/k3s/profiles/memory-32g.yaml new file mode 100644 index 0000000..c177b26 --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/memory-32g.yaml @@ -0,0 +1,26 @@ +profiles: + mariadbMaster: + memoryRequest: 8Gi + memoryLimit: 12Gi + maxConnections: 200 + innodbBufferPoolSize: 8G + innodbBufferPoolInstances: 4 + tableOpenCache: 6000 + tableOpenCacheInstances: 8 + tmpTableSize: 16M + mariadbSlave: + memoryRequest: 2Gi + memoryLimit: 3Gi + maxConnections: 50 + innodbBufferPoolSize: 2G + innodbBufferPoolInstances: 2 + tableOpenCache: 2500 + tableOpenCacheInstances: 4 + tmpTableSize: 16M + redis: + memoryRequest: 256Mi + memoryLimit: 1Gi + maxmemory: 700mb + openlitespeed: + memoryRequest: 4Gi + memoryLimit: 6Gi diff --git a/sodew-bash-main/assets/k3s/profiles/memory-64g.yaml b/sodew-bash-main/assets/k3s/profiles/memory-64g.yaml new file mode 100644 index 0000000..77a344f --- /dev/null +++ b/sodew-bash-main/assets/k3s/profiles/memory-64g.yaml @@ -0,0 +1,26 @@ +profiles: + mariadbMaster: + memoryRequest: 16Gi + memoryLimit: 24Gi + maxConnections: 350 + innodbBufferPoolSize: 16G + innodbBufferPoolInstances: 8 + tableOpenCache: 12000 + tableOpenCacheInstances: 8 + tmpTableSize: 32M + mariadbSlave: + memoryRequest: 4Gi + memoryLimit: 6Gi + maxConnections: 50 + innodbBufferPoolSize: 4G + innodbBufferPoolInstances: 4 + tableOpenCache: 4000 + tableOpenCacheInstances: 8 + tmpTableSize: 32M + redis: + memoryRequest: 512Mi + memoryLimit: 2Gi + maxmemory: 1500mb + openlitespeed: + memoryRequest: 8Gi + memoryLimit: 12Gi diff --git a/sodew-bash-main/assets/k3s/templates/debug.yaml b/sodew-bash-main/assets/k3s/templates/debug.yaml new file mode 100644 index 0000000..e9783b9 --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/debug.yaml @@ -0,0 +1,19 @@ +{{- if .Values.debugHelm }} + +--- +apiVersion: v1 +kind: Pod +metadata: { name: debug, namespace: "{{ .Values.namespace }}" } +spec: + containers: + - name: debug + image: nicolaka/netshoot:latest + command: ["sh","-c","sleep infinity"] + stdin: true + tty: true + securityContext: + capabilities: + add: ["NET_RAW","NET_ADMIN"] # for tcpdump/mtr + restartPolicy: Never + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/mariadb.yaml b/sodew-bash-main/assets/k3s/templates/mariadb.yaml new file mode 100644 index 0000000..2f74e18 --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/mariadb.yaml @@ -0,0 +1,300 @@ +{{- if .Values.mariadbHelm }} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.mariadbMaster }}-config", namespace: "{{ .Values.namespace }}" } +data: + replication.cnf: | + [mysqld] + skip_name_resolve=1 + server-id=1 + + # --- log --- + log_bin=mysql-bin + binlog_format=ROW + binlog_expire_logs_seconds=604800 + max_binlog_total_size=32212254720 + + # --- durability --- + innodb_flush_log_at_trx_commit=1 + sync_binlog=1 + + # --- connection / thread pool --- + max_connections={{ .Values.profiles.mariadbMaster.maxConnections }} + thread_handling=pool-of-threads + thread_pool_size={{ .Values.profiles.mariadbMaster.threadPoolSize }} + thread_pool_max_threads=128 + thread_pool_stall_limit=500 + + innodb_buffer_pool_size={{ .Values.profiles.mariadbMaster.innodbBufferPoolSize }} + innodb_buffer_pool_instances={{ .Values.profiles.mariadbMaster.innodbBufferPoolInstances }} + innodb_flush_method=O_DIRECT + innodb_flush_neighbors=0 + innodb_io_capacity=2000 + innodb_io_capacity_max=4000 + innodb_log_file_size=1G + innodb_log_buffer_size=64M + + # --- cache --- + query_cache_type=0 + query_cache_size=0 + table_open_cache={{ .Values.profiles.mariadbMaster.tableOpenCache }} + table_open_cache_instances={{ .Values.profiles.mariadbMaster.tableOpenCacheInstances }} + table_definition_cache={{ .Values.profiles.mariadbMaster.tableOpenCache }} + open_files_limit=65535 + + # --- buffers --- + tmp_table_size={{ .Values.profiles.mariadbMaster.tmpTableSize }} + max_heap_table_size={{ .Values.profiles.mariadbMaster.tmpTableSize }} + sort_buffer_size=2M + join_buffer_size=2M + read_buffer_size=256K + read_rnd_buffer_size=512K + + # --- timeouts --- + wait_timeout=60 + interactive_timeout=300 + + max_allowed_packet=64M + + slow_query_log=1 + long_query_time=1 + slow_query_log_file=/var/lib/mysql/slow.log + log_error=/var/lib/mysql/error.log + +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.mariadbSlave }}-config", namespace: "{{ .Values.namespace }}" } +data: + replication.cnf: | + [mysqld] + skip_name_resolve=1 + server-id=2 + read_only=1 + + # --- log --- + relay-log=relay-log + relay_log_purge=1 + relay_log_recovery=1 + + # --- connection / thread pool --- + max_connections={{ .Values.profiles.mariadbSlave.maxConnections }} + thread_handling=pool-of-threads + thread_pool_size={{ .Values.profiles.mariadbSlave.threadPoolSize }} + thread_pool_max_threads=32 + thread_pool_stall_limit=500 + + # --- InnoDB --- + innodb_buffer_pool_size={{ .Values.profiles.mariadbSlave.innodbBufferPoolSize }} + innodb_buffer_pool_instances={{ .Values.profiles.mariadbSlave.innodbBufferPoolInstances }} + innodb_flush_method=O_DIRECT + innodb_flush_neighbors=0 + innodb_io_capacity=1000 + innodb_io_capacity_max=2000 + innodb_log_file_size=512M + innodb_log_buffer_size=32M + + # --- durability (for standby recovery replica) --- + innodb_flush_log_at_trx_commit=1 + sync_binlog=1 + + # --- cache --- + query_cache_type=0 + query_cache_size=0 + table_open_cache={{ .Values.profiles.mariadbSlave.tableOpenCache }} + table_open_cache_instances={{ .Values.profiles.mariadbSlave.tableOpenCacheInstances }} + table_definition_cache={{ .Values.profiles.mariadbSlave.tableOpenCache }} + open_files_limit=65535 + + # --- buffers --- + tmp_table_size={{ .Values.profiles.mariadbSlave.tmpTableSize }} + max_heap_table_size={{ .Values.profiles.mariadbSlave.tmpTableSize }} + sort_buffer_size=1M + join_buffer_size=1M + read_buffer_size=256K + read_rnd_buffer_size=512K + + # --- timeouts --- + wait_timeout=60 + interactive_timeout=300 + + max_allowed_packet=64M + + # --- logs --- + slow_query_log=0 + log_error=/var/lib/mysql/error.log + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.mariadbMaster }}-pv" } +spec: + capacity: { storage: 100Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.mariadbMasterPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.mariadbMaster }}-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.mariadbMaster }}-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 100Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.mariadbSlave }}-pv" } +spec: + capacity: { storage: 100Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.mariadbSlavePath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.mariadbSlave }}-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.mariadbSlave }}-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 100Gi } } + storageClassName: "" + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: { name: "{{ .Values.mariadbMaster }}", namespace: "{{ .Values.namespace }}" } +spec: + serviceName: "{{ .Values.mariadbMaster }}-headless" + replicas: 1 + selector: { matchLabels: { app: "{{ .Values.mariadbMaster }}" } } + template: + metadata: { labels: { app: "{{ .Values.mariadbMaster }}" } } + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: "{{ .Values.mariadbMaster }}" + image: mariadb:12.2 + resources: + requests: { cpu: "{{ .Values.profiles.mariadbMaster.cpuRequest }}", memory: "{{ .Values.profiles.mariadbMaster.memoryRequest }}" } + limits: { cpu: "{{ .Values.profiles.mariadbMaster.cpuLimit }}", memory: "{{ .Values.profiles.mariadbMaster.memoryLimit }}" } + ports: + - { containerPort: 3306 } + env: + - { name: MARIADB_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.mariadb }}-secret", key: root-password } } } + - { name: LD_PRELOAD, value: /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 } + readinessProbe: + exec: + command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent'] + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + exec: + command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent'] + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + volumeMounts: + - { name: "{{ .Values.mariadbMaster }}-volume", mountPath: /var/lib/mysql } + - { name: "{{ .Values.mariadbMaster }}-config-volume", mountPath: /etc/mysql/conf.d/replication.cnf, subPath: replication.cnf } + volumes: + - name: "{{ .Values.mariadbMaster }}-volume" + persistentVolumeClaim: { claimName: "{{ .Values.mariadbMaster }}-pvc" } + - name: "{{ .Values.mariadbMaster }}-config-volume" + configMap: { name: "{{ .Values.mariadbMaster }}-config" } + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: { name: "{{ .Values.mariadbSlave }}", namespace: "{{ .Values.namespace }}" } +spec: + serviceName: "{{ .Values.mariadbSlave }}-headless" + replicas: 1 + selector: { matchLabels: { app: "{{ .Values.mariadbSlave }}" } } + template: + metadata: { labels: { app: "{{ .Values.mariadbSlave }}" } } + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: "{{ .Values.mariadbSlave }}" + image: mariadb:12.2 + resources: + requests: { cpu: "{{ .Values.profiles.mariadbSlave.cpuRequest }}", memory: "{{ .Values.profiles.mariadbSlave.memoryRequest }}" } + limits: { cpu: "{{ .Values.profiles.mariadbSlave.cpuLimit }}", memory: "{{ .Values.profiles.mariadbSlave.memoryLimit }}" } + ports: + - { containerPort: 3306 } + env: + - { name: MARIADB_ROOT_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.mariadb }}-secret", key: root-password } } } + readinessProbe: + exec: + command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent'] + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + exec: + command: ["sh","-lc",'mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent'] + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + volumeMounts: + - { name: "{{ .Values.mariadbSlave }}-volume", mountPath: /var/lib/mysql } + - { name: "{{ .Values.mariadbSlave }}-config-volume", mountPath: /etc/mysql/conf.d/replication.cnf, subPath: replication.cnf } + volumes: + - name: "{{ .Values.mariadbSlave }}-volume" + persistentVolumeClaim: { claimName: "{{ .Values.mariadbSlave }}-pvc" } + - name: "{{ .Values.mariadbSlave }}-config-volume" + configMap: { name: "{{ .Values.mariadbSlave }}-config" } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.mariadbMaster }}", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.mariadbMaster }}" } + ports: [ { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } ] + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.mariadbSlave }}", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.mariadbSlave }}" } + ports: [ { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } ] + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.mariadbMaster }}-headless", namespace: "{{ .Values.namespace }}" } +spec: + clusterIP: None + selector: { app: "{{ .Values.mariadbMaster }}" } + ports: + - { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.mariadbSlave }}-headless", namespace: "{{ .Values.namespace }}" } +spec: + clusterIP: None + selector: { app: "{{ .Values.mariadbSlave }}" } + ports: + - { name: mysql, protocol: TCP, port: 3306, targetPort: 3306 } + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/metric.yaml b/sodew-bash-main/assets/k3s/templates/metric.yaml new file mode 100644 index 0000000..94c5cad --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/metric.yaml @@ -0,0 +1,128 @@ +{{- if .Values.metricHelm }} + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.metric }}-node-exporter", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.metric }}-node-exporter" } + ports: [ { name: metrics, protocol: TCP, port: 9100, targetPort: 9100 } ] + +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: { name: "{{ .Values.metric }}-node-exporter", namespace: "{{ .Values.namespace }}" } +spec: + selector: + matchLabels: { app: "{{ .Values.metric }}-node-exporter" } + template: + metadata: + labels: { app: "{{ .Values.metric }}-node-exporter" } + spec: + hostNetwork: true + hostPID: true + dnsPolicy: ClusterFirstWithHostNet + tolerations: + - operator: "Exists" + containers: + - name: "{{ .Values.metric }}-node-exporter" + image: quay.io/prometheus/node-exporter:v1.8.2 + args: + - --web.listen-address=:9100 + - --path.procfs=/host/proc + - --path.sysfs=/host/sys + - --path.rootfs=/host/root + - --collector.textfile.directory={{ .Values.metricPromPath }} + ports: [ { containerPort: 9100, hostPort: 9100, name: metrics } ] + resources: + requests: { cpu: "10m", memory: "32Mi" } + limits: { cpu: "150m", memory: "200Mi" } + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + volumeMounts: + - { name: proc, mountPath: /host/proc, readOnly: true } + - { name: sys, mountPath: /host/sys, readOnly: true } + - { name: root, mountPath: /host/root, readOnly: true } + - { name: textfile, mountPath: "{{ .Values.metricPromPath }}", readOnly: true } + volumes: + - name: proc + hostPath: { path: /proc, type: Directory } + - name: sys + hostPath: { path: /sys, type: Directory } + - name: root + hostPath: { path: /, type: Directory } + - name: textfile + hostPath: { path: "{{ .Values.metricPromPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" } + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: { name: "{{ .Values.metric }}-vmagent" } +rules: + - apiGroups: [""] + resources: ["nodes", "nodes/proxy", "services", "endpoints", "pods"] + verbs: ["get", "list", "watch"] + - apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["get", "list", "watch"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: { name: "{{ .Values.metric }}-vmagent" } +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: "{{ .Values.metric }}-vmagent" +subjects: + - kind: ServiceAccount + name: "{{ .Values.metric }}-vmagent" + namespace: "{{ .Values.namespace }}" + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.metric }}-vmagent" } + ports: [ { name: http, protocol: TCP, port: 8429, targetPort: 8429 } ] + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: "{{ .Values.metric }}-vmagent", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 1 + selector: + matchLabels: { app: "{{ .Values.metric }}-vmagent" } + template: + metadata: + labels: { app: "{{ .Values.metric }}-vmagent" } + spec: + serviceAccountName: "{{ .Values.metric }}-vmagent" + containers: + - name: "{{ .Values.metric }}-vmagent" + image: victoriametrics/vmagent:v1.112.0 + args: + - -promscrape.config=/etc/vmagent/vmagent.yml + - -httpListenAddr=:8429 + - -loggerLevel=INFO + - -remoteWrite.url={{ .Values.metricApiUrl }} + - -remoteWrite.label=server={{ .Values.namespace }} + ports: [ { containerPort: 8429, name: http } ] + resources: + requests: { cpu: "30m", memory: "128Mi" } + limits: { cpu: "300m", memory: "512Mi" } + volumeMounts: + - { name: "{{ .Values.metric }}-vmagent-config-volume", mountPath: /etc/vmagent/vmagent.yml, subPath: vmagent.yml, readOnly: true } + volumes: + - name: "{{ .Values.metric }}-vmagent-config-volume" + hostPath: { path: "{{ .Values.metricVmagentPath }}", type: DirectoryOrCreate } + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/openlitespeed.yaml b/sodew-bash-main/assets/k3s/templates/openlitespeed.yaml new file mode 100644 index 0000000..233f1c0 --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/openlitespeed.yaml @@ -0,0 +1,303 @@ +{{- if .Values.openlitespeedHelm }} + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-vhosts-pv" } +spec: + capacity: { storage: 1Ti } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsVhostsPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-vhosts-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-vhosts-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 1Ti } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-private-pv" } +spec: + capacity: { storage: 5Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsPrivatePath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-private-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-private-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 5Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-public-pv" } +spec: + capacity: { storage: 10Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsPublicPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-public-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-public-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 10Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-config-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsConfigPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-config-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-config-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-admin-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsAdminPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-admin-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-admin-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-phpini-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.olsPhpIniPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-phpini-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-phpini-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.openlitespeed }}-logs-pv" } +spec: + capacity: { storage: 10Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + storageClassName: "" + hostPath: { path: "{{ .Values.olsLogsPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.openlitespeed }}-logs-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.openlitespeed }}-logs-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 10Gi } } + storageClassName: "" + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: "{{ .Values.openlitespeed }}", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 2 + strategy: { type: RollingUpdate, rollingUpdate: { maxSurge: 0, maxUnavailable: 1 } } + selector: { matchLabels: { app: "{{ .Values.openlitespeed }}" } } + template: + metadata: { labels: { app: "{{ .Values.openlitespeed }}" } } + spec: + initContainers: + - name: "{{ .Values.openlitespeed }}-init" + # image: litespeedtech/openlitespeed:1.8.5-lsphp85 + image: git.api.sodew.ai/mk250/openlitespeed:1.8.5-lsphp85-multiphp-latest + command: ["sh", "-c"] + args: + - | + if [ ! -f /mnt/config/httpd_config.conf ]; then + cp -a /usr/local/lsws/conf/. /mnt/config/ + fi + if [ ! -f /mnt/admin/admin_config.conf ]; then + cp -a /usr/local/lsws/admin/conf/. /mnt/admin/ + fi + volumeMounts: + - { name: "{{ .Values.openlitespeed }}-config-volume", mountPath: /mnt/config } + - { name: "{{ .Values.openlitespeed }}-admin-volume", mountPath: /mnt/admin } + shareProcessNamespace: true + containers: + - name: "{{ .Values.openlitespeed }}" + # image: litespeedtech/openlitespeed:1.8.5-lsphp85 + image: git.api.sodew.ai/mk250/openlitespeed:1.8.5-lsphp85-multiphp-latest + ports: + - { containerPort: 80 } + - { containerPort: 7080 } + resources: + requests: { cpu: "{{ .Values.profiles.openlitespeed.cpuRequest }}", memory: "{{ .Values.profiles.openlitespeed.memoryRequest }}" } + limits: { cpu: "{{ .Values.profiles.openlitespeed.cpuLimit }}", memory: "{{ .Values.profiles.openlitespeed.memoryLimit }}" } + readinessProbe: { tcpSocket: { port: 80 }, initialDelaySeconds: 5, periodSeconds: 5, failureThreshold: 3 } + livenessProbe: { tcpSocket: { port: 80 }, initialDelaySeconds: 10, periodSeconds: 10, failureThreshold: 5 } + volumeMounts: + - { name: "{{ .Values.openlitespeed }}-vhosts-volume", mountPath: {{ .Values.olsPodVhostsPath }} } + - { name: "{{ .Values.openlitespeed }}-private-volume", mountPath: {{ .Values.olsPodPrivatePath }}, readOnly: true } + - { name: "{{ .Values.openlitespeed }}-public-volume", mountPath: {{ .Values.olsPodPublicPath }}, readOnly: true } + - { name: "{{ .Values.openlitespeed }}-config-volume", mountPath: /usr/local/lsws/conf } + - { name: "{{ .Values.openlitespeed }}-admin-volume", mountPath: /usr/local/lsws/admin/conf } + - { name: "{{ .Values.openlitespeed }}-phpini-volume", mountPath: /etc/ols-php-ini } + - { name: "{{ .Values.openlitespeed }}-logs-volume", mountPath: /usr/local/lsws/logs } + - { name: "{{ .Values.openlitespeed }}-cache-volume", mountPath: /usr/local/lsws/cachedata } + - { name: "{{ .Values.openlitespeed }}-tmp-volume", mountPath: /tmp/lshttpd } + volumes: + - name: "{{ .Values.openlitespeed }}-vhosts-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-vhosts-pvc" } + - name: "{{ .Values.openlitespeed }}-private-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-private-pvc" } + - name: "{{ .Values.openlitespeed }}-public-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-public-pvc" } + - name: "{{ .Values.openlitespeed }}-config-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-config-pvc" } + - name: "{{ .Values.openlitespeed }}-admin-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-admin-pvc" } + - name: "{{ .Values.openlitespeed }}-phpini-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-phpini-pvc" } + - name: "{{ .Values.openlitespeed }}-logs-volume" + persistentVolumeClaim: { claimName: "{{ .Values.openlitespeed }}-logs-pvc" } + - name: "{{ .Values.openlitespeed }}-cache-volume" + emptyDir: { sizeLimit: 100Gi } + - name: "{{ .Values.openlitespeed }}-tmp-volume" + emptyDir: { sizeLimit: 100Gi } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.openlitespeed }}", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.openlitespeed }}" } + ports: + - { name: http, protocol: TCP, port: 80, targetPort: 80 } + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: { name: "{{ .Values.openlitespeed }}-ingress", namespace: "{{ .Values.namespace }}" } +spec: + ingressClassName: traefik + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: { name: "{{ .Values.openlitespeed }}", port: { number: 80 } } + +# ------------------------- +# Admin panel +# ------------------------- +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.openlitespeed }}-admin", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.openlitespeed }}" } + type: ClusterIP + ports: + - { name: admin, protocol: TCP, port: 7080, targetPort: 7080 } + +--- +apiVersion: traefik.io/v1alpha1 +kind: MiddlewareTCP +metadata: { name: "{{ .Values.openlitespeed }}-admin-allowlist", namespace: "{{ .Values.namespace }}" } +spec: + ipAllowList: { sourceRange: [ {{ .Values.olsAdminWhiteList }} ] } + +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: { name: "{{ .Values.openlitespeed }}-admin", namespace: "{{ .Values.namespace }}" } +spec: + entryPoints: [ "olsadmin" ] + routes: + - match: HostSNI(`*`) + middlewares: + - name: "{{ .Values.openlitespeed }}-admin-allowlist" + services: + - name: "{{ .Values.openlitespeed }}-admin" + port: 7080 + tls: + passthrough: true + +--- +apiVersion: helm.cattle.io/v1 +kind: HelmChartConfig +metadata: { name: traefik, namespace: kube-system } +spec: + valuesContent: |- + ports: + olsadmin: + port: 9443 + expose: + default: true + exposedPort: 9443 + protocol: TCP + additionalArguments: + - "--entryPoints.olsadmin.address=:9443/tcp" + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/postfix.yaml b/sodew-bash-main/assets/k3s/templates/postfix.yaml new file mode 100644 index 0000000..1c1779b --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/postfix.yaml @@ -0,0 +1,188 @@ +{{- if .Values.postfixHelm }} + +--- +apiVersion: v1 +kind: Secret +metadata: { name: "{{ .Values.postfix }}-tls", namespace: "{{ .Values.namespace }}" } +type: kubernetes.io/tls +data: + tls.crt: {{ .Values.postfixTlsCrtB64 }} + tls.key: {{ .Values.postfixTlsKeyB64 }} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.postfix }}-sasl", namespace: "{{ .Values.namespace }}" } +data: + smtpd.conf: | + pwcheck_method: auxprop + auxprop_plugin: sasldb + sasldb_path: /config/sasldb2 + mech_list: PLAIN LOGIN + default_realm: {{ .Values.postfixDefaultRealm }} + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.postfix }}-config-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.postfixConfigPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.postfix }}-dkim-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.postfixDkimPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.postfix }}-config-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.postfix }}-config-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.postfix }}-dkim-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.postfix }}-dkim-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.postfix }}-queue-pvc", namespace: "{{ .Values.namespace }}" } +spec: + accessModes: ["ReadWriteOnce"] + resources: { requests: { storage: 5Gi } } + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: "{{ .Values.postfix }}", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 1 + selector: { matchLabels: { app: "{{ .Values.postfix }}" } } + template: + metadata: { labels: { app: "{{ .Values.postfix }}" } } + spec: + enableServiceLinks: false + initContainers: + - name: "{{ .Values.postfix }}-dkim-init" + image: boky/postfix:4.4.0-alpine + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-lc"] + args: + - | + set -e + if [ ! -f /dkim/opendkim.conf ]; then + cp -a /etc/opendkim/* /dkim/ + fi + touch /dkim/TrustedHosts /dkim/SigningTable /dkim/KeyTable + chown opendkim:opendkim /dkim/TrustedHosts /dkim/KeyTable /dkim/SigningTable + chmod 0644 /dkim/TrustedHosts /dkim/KeyTable /dkim/SigningTable + printf '%s\n' 127.0.0.1 localhost 10.42.0.0/16 10.43.0.0/16 > /dkim/TrustedHosts + + volumeMounts: + - name: "{{ .Values.postfix }}-dkim-volume" + mountPath: /dkim + containers: + - name: "{{ .Values.postfix }}" + image: boky/postfix:4.4.0-alpine + ports: + - { containerPort: 25, name: smtp } + - { containerPort: 587, name: submission } + env: + - { name: POSTFIX_myhostname, value: "{{ .Values.postfixHost }}" } + - { name: POSTFIX_smtpd_banner, value: "$myhostname ESMTP" } + - { name: POSTFIX_mynetworks, value: "127.0.0.0/8" } + - { name: POSTFIX_inet_interfaces, value: "all" } + + # Rules + - { name: POSTFIX_smtpd_client_restrictions, value: "permit_mynetworks, permit_sasl_authenticated, reject" } + - { name: POSTFIX_smtpd_relay_restrictions, value: "permit_sasl_authenticated, reject_unauth_destination" } + - { name: POSTFIX_smtpd_sender_restrictions, value: "reject_non_fqdn_sender,reject_unknown_sender_domain,reject_sender_login_mismatch,permit_sasl_authenticated,reject_unauth_destination" } + + # TLS + - { name: POSTFIX_smtpd_tls_cert_file, value: "/etc/ssl/mail/tls.crt" } + - { name: POSTFIX_smtpd_tls_key_file, value: "/etc/ssl/mail/tls.key" } + - { name: POSTFIX_smtpd_tls_security_level, value: "may" } + - { name: POSTFIX_smtp_tls_security_level, value: "may" } + + # SASL (Cyrus, sasldb2) + - { name: POSTFIX_smtpd_sasl_auth_enable, value: "yes" } + - { name: POSTFIX_smtpd_sasl_type, value: "cyrus" } + - { name: POSTFIX_smtpd_sasl_path, value: "smtpd" } + - { name: POSTFIX_cyrus_sasl_config_path, value: "/etc/sasl2" } + + # Maps (Virtual domain/alias and senders) + - { name: POSTFIX_virtual_alias_domains, value: "lmdb:/config/{{ .Values.postfixDomainsFile }}" } + - { name: POSTFIX_virtual_alias_maps, value: "lmdb:/config/{{ .Values.postfixAliasesFile }}" } + - { name: POSTFIX_smtpd_sender_login_maps, value: "lmdb:/config/{{ .Values.postfixSendersFile }}" } + + # DKIM + - { name: DKIM_SELECTOR, value: "{{ .Values.postfixDkimSelector }}" } + - { name: POSTFIX_smtpd_milters, value: "inet:localhost:8891" } + - { name: POSTFIX_non_smtpd_milters, value: "$smtpd_milters" } + - { name: POSTFIX_milter_default_action, value: "accept" } + - { name: POSTFIX_milter_protocol, value: "6" } + + # Other + - { name: ALLOW_EMPTY_SENDER_DOMAINS, value: "true" } + - { name: ALLOWED_SENDER_DOMAINS, value: "" } + + volumeMounts: + - { name: "{{ .Values.postfix }}-tls-volume", mountPath: /etc/ssl/mail, readOnly: true } + - { name: "{{ .Values.postfix }}-sasl-volume", mountPath: /etc/sasl2 } + - { name: "{{ .Values.postfix }}-config-volume", mountPath: /config } + - { name: "{{ .Values.postfix }}-dkim-volume", mountPath: /etc/opendkim } + - { name: "{{ .Values.postfix }}-dkim-volume", mountPath: /etc/opendkim/keys, subPath: keys } + - { name: "{{ .Values.postfix }}-queue-volume", mountPath: /var/spool/postfix } + volumes: + - name: "{{ .Values.postfix }}-tls-volume" + secret: { secretName: "{{ .Values.postfix }}-tls" } + - name: "{{ .Values.postfix }}-sasl-volume" + configMap: { name: "{{ .Values.postfix }}-sasl" } + - name: "{{ .Values.postfix }}-config-volume" + persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-config-pvc" } + - name: "{{ .Values.postfix }}-dkim-volume" + persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-dkim-pvc" } + - name: "{{ .Values.postfix }}-queue-volume" + persistentVolumeClaim: { claimName: "{{ .Values.postfix }}-queue-pvc" } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.postfix }}-public", namespace: "{{ .Values.namespace }}" } +spec: + type: LoadBalancer + externalTrafficPolicy: Local + selector: { app: "{{ .Values.postfix }}" } + ports: [ { name: smtp, port: 25, targetPort: 25 } ] + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.postfix }}", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.postfix }}" } + ports: [ { name: submission, port: 587, targetPort: 587 } ] + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/redis.yaml b/sodew-bash-main/assets/k3s/templates/redis.yaml new file mode 100644 index 0000000..abaf5a9 --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/redis.yaml @@ -0,0 +1,416 @@ +{{- if .Values.redisHelm }} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.redis }}-config", namespace: "{{ .Values.namespace }}" } +data: + redis.conf: | + bind 0.0.0.0 + port 6379 + dir /data + + # --- ACL --- + aclfile /data-acl/{{ .Values.redisFileUsersAcl }} + + # --- all in one DB --- + databases 1 + + # --- network --- + protected-mode yes + tcp-backlog 1024 + tcp-keepalive 300 + timeout 0 + + # --- connections --- + maxclients {{ .Values.profiles.redis.maxClients }} + + # --- memory (tune) --- + maxmemory {{ .Values.profiles.redis.maxmemory }} + maxmemory-policy allkeys-lfu + maxmemory-samples 5 + maxmemory-eviction-tenacity 10 + maxmemory-clients 5% + client-query-buffer-limit 256mb + client-output-buffer-limit normal 0 0 0 + client-output-buffer-limit replica 256mb 64mb 60 + client-output-buffer-limit pubsub 32mb 8mb 60 + + # --- replication --- + replica-serve-stale-data yes + replica-read-only yes + repl-backlog-size 64mb + repl-backlog-ttl 3600 + min-replicas-to-write 0 + #min-replicas-max-lag 10 + + # --- persistence --- + appendonly yes + appendfsync everysec + aof-rewrite-incremental-fsync yes + rdb-save-incremental-fsync yes + save "" + + # --- log --- + slowlog-log-slower-than 10000 + slowlog-max-len 128 + latency-monitor-threshold 0 + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: { name: "{{ .Values.redis }}-users-pv" } +spec: + capacity: { storage: 1Gi } + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + persistentVolumeReclaimPolicy: Retain + hostPath: { path: "{{ .Values.redisPath }}", type: DirectoryOrCreate } + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: "{{ .Values.redis }}-users-pvc", namespace: "{{ .Values.namespace }}" } +spec: + volumeName: "{{ .Values.redis }}-users-pv" + volumeMode: Filesystem + accessModes: [ ReadWriteMany ] + resources: { requests: { storage: 1Gi } } + storageClassName: "" + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" } +spec: + serviceName: "{{ .Values.redis }}" + replicas: 2 + selector: { matchLabels: { app: "{{ .Values.redis }}" } } + persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain } + template: + metadata: { labels: { app: "{{ .Values.redis }}" } } + spec: + terminationGracePeriodSeconds: 30 + initContainers: + - name: init-redis-acl + image: redis:8.6-alpine + resources: + requests: { cpu: "10m", memory: "32Mi" } + limits: { cpu: "100m", memory: "128Mi" } + env: + - { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } } + - { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } } + command: ["/bin/sh","-c"] + args: + - | + set -eu + ACL="/data-acl/{{ .Values.redisFileUsersAcl }}" + HASH=$(printf '%s' "$REDIS_PASSWORD" | sha256sum | awk '{print $1}') + mkdir -p /data-acl + + if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then + tmp="${ACL}.tmp" + + if [ -f "$ACL" ]; then + awk '!(tolower($1)=="user" && $2=="default")' "$ACL" > "$tmp" + else + : > "$tmp" + fi + + # default user is used by replication auth and HAProxy health checks + printf 'user default on sanitize-payload #%s ~* &* +@all\n' "$HASH" >> "$tmp" + mv "$tmp" "$ACL" + chmod 600 "$ACL" + else + i=0 + while [ ! -f "$ACL" ] && [ $i -lt 300 ]; do + sleep 1 + i=$((i+1)) + done + [ -f "$ACL" ] || exit 1 + fi + volumeMounts: + - { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl } + containers: + - name: "{{ .Values.redis }}" + image: redis:8.6-alpine + resources: + requests: { cpu: "{{ .Values.profiles.redis.cpuRequest }}", memory: "{{ .Values.profiles.redis.memoryRequest }}" } + limits: { cpu: "{{ .Values.profiles.redis.cpuLimit }}", memory: "{{ .Values.profiles.redis.memoryLimit }}" } + ports: + - { name: redis, containerPort: 6379 } + env: + - { name: POD_NAME, valueFrom: { fieldRef: { fieldPath: metadata.name } } } + - { name: POD_IP, valueFrom: { fieldRef: { fieldPath: status.podIP } } } + - { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } } + - { name: SENTINEL_HOST, value: "{{ .Values.redisSentinel }}" } + - { name: SENTINEL_PORT, value: "26379" } + - { name: MASTER_NAME, value: "mymaster" } + command: ["/bin/sh","-c"] + args: + - | + set -eu + + POD_DNS_SHORT="${POD_NAME}.{{ .Values.redis }}" + POD_DNS_FQDN="${POD_NAME}.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local" + + get_master() { + REDISCLI_AUTH="$REDIS_PASSWORD" \ + redis-cli -h "$SENTINEL_HOST" -p "$SENTINEL_PORT" \ + SENTINEL get-master-addr-by-name "$MASTER_NAME" 2>/dev/null | tr -d '\r' + } + + out="" + i=0 + while [ $i -lt 20 ]; do + out="$(get_master || true)" + [ -n "$out" ] && break + i=$((i+1)) + sleep 1 + done + + master_host="$(printf '%s\n' "$out" | sed -n '1p')" + master_port="$(printf '%s\n' "$out" | sed -n '2p')" + [ -n "$master_port" ] || master_port="6379" + + is_me_master() { + [ "$master_host" = "$POD_IP" ] || [ "$master_host" = "$POD_DNS_SHORT" ] || [ "$master_host" = "$POD_DNS_FQDN" ] + } + + if [ -n "$master_host" ]; then + if is_me_master; then + exec redis-server /etc/redis/redis.conf --masteruser default --masterauth "$REDIS_PASSWORD" + else + exec redis-server /etc/redis/redis.conf --replicaof "$master_host" "$master_port" --masteruser default --masterauth "$REDIS_PASSWORD" + fi + else + # bootstrap if sentinel is not ready yet + if [ "$POD_NAME" = "{{ .Values.redis }}-0" ]; then + exec redis-server /etc/redis/redis.conf + else + exec redis-server /etc/redis/redis.conf --replicaof {{ .Values.redis }}-0.{{ .Values.redis }} 6379 --masteruser default --masterauth "$REDIS_PASSWORD" + fi + fi + volumeMounts: + - { name: "{{ .Values.redis }}-data-volume", mountPath: /data } + - { name: "{{ .Values.redis }}-config-volume", mountPath: /etc/redis } + - { name: "{{ .Values.redis }}-users-volume", mountPath: /data-acl } + volumes: + - name: "{{ .Values.redis }}-config-volume" + configMap: { name: "{{ .Values.redis }}-config" } + - name: "{{ .Values.redis }}-users-volume" + persistentVolumeClaim: { claimName: "{{ .Values.redis }}-users-pvc" } + volumeClaimTemplates: + - metadata: { name: "{{ .Values.redis }}-data-volume" } + spec: + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 10Gi } } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.redis }}", namespace: "{{ .Values.namespace }}" } +spec: + clusterIP: None + selector: { app: "{{ .Values.redis }}" } + ports: + - { name: redis, protocol: TCP, port: 6379, targetPort: 6379 } + +# ------------------------- +# Sentinel (1) with PVC +# ------------------------- +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.redisSentinel }}-config", namespace: "{{ .Values.namespace }}" } +data: + sentinel.conf.tmpl: | + bind 0.0.0.0 + port 26379 + dir /data + + sentinel deny-scripts-reconfig yes + sentinel resolve-hostnames yes + sentinel announce-hostnames yes + + # quorum=1 (single sentinel) + sentinel monitor mymaster {{ .Values.redis }}-0.{{ .Values.redis }} 6379 1 + sentinel down-after-milliseconds mymaster 5000 + sentinel failover-timeout mymaster 60000 + sentinel parallel-syncs mymaster 1 + + sentinel auth-user mymaster default + sentinel auth-pass mymaster __PASSWORD__ + + requirepass __PASSWORD__ + +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 1 + serviceName: "{{ .Values.redisSentinel }}" + selector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } } + persistentVolumeClaimRetentionPolicy: { whenDeleted: Delete, whenScaled: Retain } + template: + metadata: { labels: { app: "{{ .Values.redisSentinel }}" } } + spec: + containers: + - name: "{{ .Values.redisSentinel }}" + image: redis:8.6-alpine + resources: + requests: { cpu: "50m", memory: "128Mi" } + limits: { cpu: "200m", memory: "256Mi" } + ports: + - { name: sentinel, containerPort: 26379 } + env: + - { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } } + command: ["/bin/sh","-c"] + args: + - | + set -eu + CONF=/data/sentinel.conf + if [ ! -f "$CONF" ]; then + pwd_escaped=$(printf '%s' "$REDIS_PASSWORD" | sed -e 's/[\/&]/\\&/g') + sed "s/__PASSWORD__/${pwd_escaped}/g" /tmpl/sentinel.conf.tmpl > "$CONF" + chmod 600 "$CONF" + fi + exec redis-server "$CONF" --sentinel + volumeMounts: + - { name: "{{ .Values.redisSentinel }}-tmpl", mountPath: /tmpl } + - { name: "{{ .Values.redisSentinel }}-data", mountPath: /data } + volumes: + - name: "{{ .Values.redisSentinel }}-tmpl" + configMap: { name: "{{ .Values.redisSentinel }}-config" } + volumeClaimTemplates: + - metadata: { name: "{{ .Values.redisSentinel }}-data" } + spec: + accessModes: [ ReadWriteOnce ] + resources: { requests: { storage: 1Gi } } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.redisSentinel }}", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.redisSentinel }}" } + ports: + - { name: sentinel, port: 26379, targetPort: 26379 } + +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: { name: "{{ .Values.redisSentinel }}-allow-only-checker", namespace: "{{ .Values.namespace }}" } +spec: + podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } } + policyTypes: + - Ingress + ingress: + - from: + - podSelector: { matchLabels: { app: "{{ .Values.redis }}" } } + - podSelector: { matchLabels: { app: "{{ .Values.redisSentinel }}" } } + ports: + - { protocol: TCP, port: 26379 } + +# ------------------------- +# HAProxy: single master endpoint +# ------------------------- +--- +apiVersion: v1 +kind: ConfigMap +metadata: { name: "{{ .Values.redis }}-haproxy-config", namespace: "{{ .Values.namespace }}" } +data: + haproxy.cfg: | + global + log stdout format raw local0 + maxconn 20000 + + resolvers kubedns + nameserver dns1 10.43.0.10:53 + accepted_payload_size 8192 + resolve_retries 3 + timeout resolve 1s + timeout retry 1s + hold valid 10s + hold obsolete 30s + + defaults + mode tcp + log global + option tcplog + option log-health-checks + timeout connect 5s + timeout client 1m + timeout server 1m + timeout check 1s + + frontend fe_redis_master + bind *:6379 + default_backend be_redis_master + + backend be_redis_master + mode tcp + balance first + option tcp-check + option srvtcpka + timeout queue 2s + timeout connect 2s + timeout check 3s + timeout server 10m + tcp-check connect + tcp-check send-lf "AUTH default $REDIS_PASSWORD\r\n" + tcp-check expect string +OK + tcp-check send INFO\ replication\r\n + tcp-check expect string role:master + tcp-check send QUIT\r\n + tcp-check expect string +OK + server redis0 {{ .Values.redis }}-0.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2 + server redis1 {{ .Values.redis }}-1.{{ .Values.redis }}.{{ .Values.namespace }}.svc.cluster.local:6379 check resolvers kubedns resolve-prefer ipv4 init-addr libc,none inter 5s fall 2 rise 2 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: "{{ .Values.redis }}-haproxy", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 1 + selector: { matchLabels: { app: "{{ .Values.redis }}-haproxy" } } + template: + metadata: + labels: { app: "{{ .Values.redis }}-haproxy" } + spec: + containers: + - name: "{{ .Values.redis }}-haproxy" + image: haproxy:2.9-alpine + resources: + requests: { cpu: "50m", memory: "64Mi" } + limits: { cpu: "500m", memory: "256Mi" } + ports: + - { name: redis, containerPort: 6379 } + env: + - { name: REDIS_PASSWORD, valueFrom: { secretKeyRef: { name: "{{ .Values.redis }}-secret", key: root-password } } } + command: ["/bin/sh","-c"] + args: + - | + set -eu + haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg + exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg -db + readinessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 1, periodSeconds: 2, failureThreshold: 3 } + livenessProbe: { tcpSocket: { port: 6379 }, initialDelaySeconds: 10, periodSeconds: 10, failureThreshold: 3 } + volumeMounts: + # - { name: cfg, mountPath: /usr/local/etc/haproxy/haproxy.cfg, subPath: haproxy.cfg } + - { name: cfg, mountPath: /usr/local/etc/haproxy } + volumes: + - name: cfg + configMap: { name: "{{ .Values.redis }}-haproxy-config" } + +--- +apiVersion: v1 +kind: Service +metadata: { name: "{{ .Values.redis }}-master", namespace: "{{ .Values.namespace }}" } +spec: + selector: { app: "{{ .Values.redis }}-haproxy" } + ports: + - { name: redis, port: 6379, targetPort: 6379 } + +{{- end }} diff --git a/sodew-bash-main/assets/k3s/templates/worker.yaml b/sodew-bash-main/assets/k3s/templates/worker.yaml new file mode 100644 index 0000000..f207980 --- /dev/null +++ b/sodew-bash-main/assets/k3s/templates/worker.yaml @@ -0,0 +1,52 @@ +{{- if .Values.workerHelm }} + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: { name: "{{ .Values.worker }}", namespace: "{{ .Values.namespace }}" } +spec: + replicas: 1 + selector: { matchLabels: { app: "{{ .Values.worker }}" } } + template: + metadata: { labels: { app: "{{ .Values.worker }}" } } + spec: + containers: + - name: "{{ .Values.worker }}" + image: python:3.12-slim + imagePullPolicy: IfNotPresent + resources: + requests: { cpu: "50m", memory: "64Mi" } + limits: { cpu: "200m", memory: "256Mi" } + ports: + - { containerPort: 8080 } + workingDir: /app/agent + command: ["/bin/sh","-c"] + args: + - | + set -e + if [ ! -f /app/agent/worker.py ]; then + echo "ERROR: /app/agent/worker.py not found" >&2 + ls -la /app/agent >&2 || true + exit 1 + fi + exec python -u /app/agent/worker.py + env: + - { name: WORKER_UUID, value: "{{ .Values.workerUuid }}" } + - { name: WORKER_NAME, value: "{{ .Values.workerName }}" } + - { name: WORKER_POOL, value: "{{ .Values.workerPool }}" } + - { name: API_URL, value: "{{ .Values.workerApiUrl }}" } + - { name: GETTING_PAUSE, value: "{{ .Values.workerApiGettingPause }}" } + - { name: SENDING_PAUSE, value: "{{ .Values.workerApiSendingPause }}" } + volumeMounts: + - { name: "{{ .Values.worker }}-agent-volume", mountPath: /app/agent } + - { name: "{{ .Values.worker }}-tasks-volume", mountPath: /app/tasks } + readinessProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 5, timeoutSeconds: 2 } + livenessProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 10, timeoutSeconds: 2, failureThreshold: 3 } + startupProbe: { httpGet: { path: /healthz, port: 8080 }, periodSeconds: 2, timeoutSeconds: 2, failureThreshold: 30 } + volumes: + - name: "{{ .Values.worker }}-agent-volume" + hostPath: { path: "{{ .Values.workerAgentPath }}", type: DirectoryOrCreate } + - name: "{{ .Values.worker }}-tasks-volume" + hostPath: { path: "{{ .Values.workerTasksPath }}", type: DirectoryOrCreate } + +{{- end }} diff --git a/sodew-bash-main/assets/metric/vmagent.yml b/sodew-bash-main/assets/metric/vmagent.yml new file mode 100644 index 0000000..19bff0b --- /dev/null +++ b/sodew-bash-main/assets/metric/vmagent.yml @@ -0,0 +1,42 @@ +global: + scrape_interval: 15s + scrape_timeout: 10s + +scrape_configs: + - job_name: "metric-node-exporter" + kubernetes_sd_configs: + - role: pod + relabel_configs: + - action: keep + source_labels: [__meta_kubernetes_pod_label_app] + regex: metric-node-exporter + + - action: keep + source_labels: [__meta_kubernetes_pod_container_port_number] + regex: "9100" + + - action: replace + source_labels: [__meta_kubernetes_pod_node_name] + target_label: node + + - job_name: "metric-kubelet-cadvisor" + scheme: https + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + tls_config: + insecure_skip_verify: true + kubernetes_sd_configs: + - role: node + relabel_configs: + - target_label: __address__ + replacement: kubernetes.default.svc:443 + + - source_labels: [__meta_kubernetes_node_name] + target_label: __metrics_path__ + replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor + + - source_labels: [__meta_kubernetes_node_name] + target_label: node + metric_relabel_configs: + - source_labels: [__name__] + action: keep + regex: 'container_memory_working_set_bytes|container_memory_usage_bytes|container_memory_rss|container_memory_cache|container_cpu_usage_seconds_total|container_cpu_system_seconds_total|container_cpu_user_seconds_total' diff --git a/sodew-bash-main/assets/openlitespeed/php/00-ols-master.ini b/sodew-bash-main/assets/openlitespeed/php/00-ols-master.ini new file mode 100644 index 0000000..9ab4de6 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/php/00-ols-master.ini @@ -0,0 +1,52 @@ +expose_php = Off +allow_url_fopen = Off +allow_url_include = Off +enable_dl = Off +short_open_tag = Off + +; --- block user_ini --- +user_ini.filename = + +; -- disable functions --- +disable_functions = exec,passthru,shell_exec,system,proc_open,popen,putenv,dl,show_source,highlight_file,symlink,readlink,link,proc_terminate,proc_get_status,pfsockopen,posix_kill,posix_setuid,posix_setgid,posix_setsid,posix_setpgid,posix_getgrnam,posix_getpgid,posix_getpwuid,posix_getpwnam,posix_getrlimit,posix_initgroups,posix_mkfifo,posix_mknod,posix_uname,register_tick_function,openlog,syslog,proc_close,proc_nice,diskfreespace,disk_free_space,disk_total_space,leak,pcntl_alarm,pcntl_async_signals,pcntl_exec,pcntl_fork,pcntl_get_last_error,pcntl_getcpuaffinity,pcntl_getpriority,pcntl_rfork,pcntl_setcpuaffinity,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_signal_get_handler,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_strerror,pcntl_unshare,pcntl_wait,pcntl_waitid,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifexited,pcntl_wifsignaled,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig,sys_getloadavg + +; not use for LSAPI +;pm.max_children = {{children}} + +; --- memory limit --- +max_memory_limit = {{max_memory_limit}} + +; --- default (redefined per vhost) --- +memory_limit = {{memory_limit}} +max_execution_time = {{max_execution_time}} +max_input_time = 30 +max_input_vars = 1000 +output_buffering = 4096 + +; --- uploads --- +post_max_size = {{post_max_size}} +upload_max_filesize = {{upload_max_filesize}} +max_file_uploads = 10 + +; --- log --- (to stderr k3s?) +display_errors = Off +log_errors = On +;error_log = /usr/local/lsws/conf/logs/php_errors.log +error_log = /proc/self/fd/2 + +; --- sessions (redefined per vhost) --- +session.save_path = "/tmp" +session.use_only_cookies = 1 +session.cookie_httponly = 1 +session.cookie_secure = 1 +session.cookie_samesite = "Lax" + +; --- OPcache --- +opcache.enable = 1 +opcache.enable_cli = 0 +opcache.memory_consumption = 256 +opcache.interned_strings_buffer = 16 +opcache.max_accelerated_files = 100000 +opcache.validate_timestamps = 1 +opcache.revalidate_freq = 2 +opcache.jit = "disable" diff --git a/sodew-bash-main/assets/openlitespeed/profiles/memory-128g.config b/sodew-bash-main/assets/openlitespeed/profiles/memory-128g.config new file mode 100644 index 0000000..1fcbcac --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/profiles/memory-128g.config @@ -0,0 +1,6 @@ +children=8 +max_memory_limit=512M +memory_limit=512M +max_execution_time=30 +post_max_size=512M +upload_max_filesize=512M diff --git a/sodew-bash-main/assets/openlitespeed/profiles/memory-16g.config b/sodew-bash-main/assets/openlitespeed/profiles/memory-16g.config new file mode 100644 index 0000000..67b664d --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/profiles/memory-16g.config @@ -0,0 +1,6 @@ +children=2 +max_memory_limit=512M +memory_limit=512M +max_execution_time=30 +post_max_size=512M +upload_max_filesize=512M diff --git a/sodew-bash-main/assets/openlitespeed/profiles/memory-32g.config b/sodew-bash-main/assets/openlitespeed/profiles/memory-32g.config new file mode 100644 index 0000000..abc3617 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/profiles/memory-32g.config @@ -0,0 +1,6 @@ +children=4 +max_memory_limit=512M +memory_limit=512M +max_execution_time=30 +post_max_size=512M +upload_max_filesize=512M diff --git a/sodew-bash-main/assets/openlitespeed/profiles/memory-64g.config b/sodew-bash-main/assets/openlitespeed/profiles/memory-64g.config new file mode 100644 index 0000000..f8f8bb2 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/profiles/memory-64g.config @@ -0,0 +1,6 @@ +children=6 +max_memory_limit=512M +memory_limit=512M +max_execution_time=30 +post_max_size=512M +upload_max_filesize=512M diff --git a/sodew-bash-main/assets/openlitespeed/rules/front-controller.rules b/sodew-bash-main/assets/openlitespeed/rules/front-controller.rules new file mode 100644 index 0000000..c9680be --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/front-controller.rules @@ -0,0 +1,8 @@ +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + +# Front-controller +RewriteRule ^/?index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] diff --git a/sodew-bash-main/assets/openlitespeed/rules/https-www.rules b/sodew-bash-main/assets/openlitespeed/rules/https-www.rules new file mode 100644 index 0000000..33e62ad --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/https-www.rules @@ -0,0 +1,11 @@ +RewriteEngine On + +# no www -> add www + https +RewriteCond %{HTTP_HOST} !^www\. [NC] +RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + +# www, http -> https +RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC] +RewriteCond %{HTTP:Forwarded} !proto=https [NC] +RewriteCond %{HTTPS} !=on +RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] diff --git a/sodew-bash-main/assets/openlitespeed/rules/https.rules b/sodew-bash-main/assets/openlitespeed/rules/https.rules new file mode 100644 index 0000000..00a2278 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/https.rules @@ -0,0 +1,11 @@ +RewriteEngine On + +# www -> non-www + https +RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] +RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L] + +# http -> https +RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC] +RewriteCond %{HTTP:Forwarded} !proto=https [NC] +RewriteCond %{HTTPS} !=on +RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] diff --git a/sodew-bash-main/assets/openlitespeed/rules/laravel.rules b/sodew-bash-main/assets/openlitespeed/rules/laravel.rules new file mode 100644 index 0000000..9cb5914 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/laravel.rules @@ -0,0 +1,7 @@ +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + +# Laravel +RewriteRule . /public/index.php [L] +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_FILENAME} !-f diff --git a/sodew-bash-main/assets/openlitespeed/rules/php.rules b/sodew-bash-main/assets/openlitespeed/rules/php.rules new file mode 100644 index 0000000..000a3cd --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/php.rules @@ -0,0 +1,13 @@ +RewriteEngine On + +RewriteCond %{DOCUMENT_ROOT}/.php81 -f +RewriteRule ^ - [H=application/x-httpd-lsphp81] + +RewriteCond %{DOCUMENT_ROOT}/.php82 -f +RewriteRule ^ - [H=application/x-httpd-lsphp82] + +RewriteCond %{DOCUMENT_ROOT}/.php83 -f +RewriteRule ^ - [H=application/x-httpd-lsphp83] + +RewriteCond %{DOCUMENT_ROOT}/.php84 -f +RewriteRule ^ - [H=application/x-httpd-lsphp84] diff --git a/sodew-bash-main/assets/openlitespeed/rules/unigma.rules b/sodew-bash-main/assets/openlitespeed/rules/unigma.rules new file mode 100644 index 0000000..eba2421 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/rules/unigma.rules @@ -0,0 +1,14 @@ +RewriteEngine On + +# Unigma 1.0.0 fix +RewriteCond %{REQUEST_URI} ^/v(8[1-5])/ [OR] +RewriteCond %{REQUEST_URI} ^/v(8[1-5])/router\.lua [OR] +RewriteCond %{REQUEST_URI} ^/router\.lua +RewriteRule ^ - [L] + +RewriteCond %{REQUEST_URI} !^/router\.lua +RewriteCond %{REQUEST_URI} !^/[^/]+/www/ +RewriteCond %{HTTP_HOST} ^([a-z0-9.-]+)$ +RewriteRule ^/?(.*)$ /%1/www/$1 + +RewriteRule ^/?(.*\.php)$ /router.lua?orig=/$1 [L] diff --git a/sodew-bash-main/assets/openlitespeed/templates/virtual.host.conf b/sodew-bash-main/assets/openlitespeed/templates/virtual.host.conf new file mode 100644 index 0000000..35b71c8 --- /dev/null +++ b/sodew-bash-main/assets/openlitespeed/templates/virtual.host.conf @@ -0,0 +1,46 @@ +allowSymbolLink 1 +enableScript 1 +restrained 1 +setUIDMode 2 +vhRoot /var/www/vhosts/$VH_NAME + +virtualHostConfig { + docRoot $VH_ROOT/www/ + vhDomain $VH_NAME + vhAliases *.$VH_NAME + + index { + useServer 0 + indexFiles index.php + } + + phpIniOverride { + # --- paths --- + php_admin_value upload_tmp_dir $VH_ROOT/tmp + php_admin_value session.save_path $VH_ROOT/session + php_admin_value open_basedir "$VH_ROOT/www:$VH_ROOT/tmp:$VH_ROOT/session:/var/www/private/$VH_NAME:/var/www/public" + php_admin_value auto_prepend_file /var/www/private/$VH_NAME/bootstrap.php + + # --- hard limits --- + php_admin_value memory_limit 512M + php_admin_value max_execution_time 30 + php_admin_value max_input_time 30 + php_admin_value max_input_vars 1000 + php_admin_value post_max_size 128M + php_admin_value upload_max_filesize 128M + } + + rewrite { + enable 1 + autoLoadHtaccess 1 + rules <<credentials();$J=Driver::connect($Fb[0],$Fb[1],$Fb[2]);return(is_object($J)?$J:null);}function +idf_unescape($u){if(!preg_match('~^[`\'"[]~',$u))return$u;$He=substr($u,-1);return +str_replace($He.$He,$He,substr($u,1,-1));}function +q($Q){return +connection()->quote($Q);}function +escape_string($X){return +substr(q($X),1,-1);}function +idx($va,$x,$k=null){return($va&&array_key_exists($x,$va)?$va[$x]:$k);}function +number($X){return +preg_replace('~[^0-9]+~','',$X);}function +number_type(){return'((?$W){unset($Wg[$x][$_e]);if(is_array($W)){$Wg[$x][stripslashes($_e)]=$W;$Wg[]=&$Wg[$x][stripslashes($_e)];}else$Wg[$x][stripslashes($_e)]=($ad?$W:stripslashes($W));}}}}function +bracket_escape($u,$Ca=false){static$Xi=array(':'=>':1',']'=>':2','['=>':3','"'=>':4');return +strtr($u,($Ca?array_flip($Xi):$Xi));}function +min_version($Ej,$Ve="",$g=null){$g=connection($g);$Qh=$g->server_info;if($Ve&&preg_match('~([\d.]+)-MariaDB~',$Qh,$A)){$Qh=$A[1];$Ej=$Ve;}return$Ej&&version_compare($Qh,$Ej)>=0;}function +charset(Db$f){return(min_version("5.5.3",0,$f)?"utf8mb4":"utf8");}function +ini_bool($je){$X=ini_get($je);return(preg_match('~^(on|true|yes)$~i',$X)||(int)$X);}function +sid(){static$J;if($J===null)$J=(SID&&!($_COOKIE&&ini_bool("session.use_cookies")));return$J;}function +set_password($Dj,$N,$V,$F){$_SESSION["pwds"][$Dj][$N][$V]=($_COOKIE["adminer_key"]&&is_string($F)?array(encrypt_string($F,$_COOKIE["adminer_key"])):$F);}function +get_password(){$J=get_session("pwds");if(is_array($J))$J=($_COOKIE["adminer_key"]?decrypt_string($J[0],$_COOKIE["adminer_key"]):false);return$J;}function +get_val($H,$m=0,$tb=null){$tb=connection($tb);$I=$tb->query($H);if(!is_object($I))return +false;$K=$I->fetch_row();return($K?$K[$m]:false);}function +get_vals($H,$d=0){$J=array();$I=connection()->query($H);if(is_object($I)){while($K=$I->fetch_row())$J[]=$K[$d];}return$J;}function +get_key_vals($H,$g=null,$Th=true){$g=connection($g);$J=array();$I=$g->query($H);if(is_object($I)){while($K=$I->fetch_row()){if($Th)$J[$K[0]]=$K[1];else$J[]=$K[0];}}return$J;}function +get_rows($H,$g=null,$l="

"){$tb=connection($g);$J=array();$I=$tb->query($H);if(is_object($I)){while($K=$I->fetch_assoc())$J[]=$K;}elseif(!$I&&!$g&&$l&&(defined('Adminer\PAGE_HEADER')||$l=="-- "))echo$l.error()."\n";return$J;}function +unique_array($K,array$w){foreach($w +as$v){if(preg_match("~PRIMARY|UNIQUE~",$v["type"])){$J=array();foreach($v["columns"]as$x){if(!isset($K[$x]))continue +2;$J[$x]=$K[$x];}return$J;}}}function +escape_key($x){if(preg_match('(^([\w(]+)('.str_replace("_",".*",preg_quote(idf_escape("_"))).')([ \w)]+)$)',$x,$A))return$A[1].idf_escape(idf_unescape($A[2])).$A[3];return +idf_escape($x);}function +where(array$Z,array$n=array()){$J=array();foreach((array)$Z["where"]as$x=>$X){$x=bracket_escape($x,true);$d=escape_key($x);$m=idx($n,$x,array());$Yc=$m["type"];$J[]=$d.(JUSH=="sql"&&$Yc=="json"?" = CAST(".q($X)." AS JSON)":(JUSH=="sql"&&is_numeric($X)&&preg_match('~\.~',$X)?" LIKE ".q($X):(JUSH=="mssql"&&strpos($Yc,"datetime")===false?" LIKE ".q(preg_replace('~[_%[]~','[\0]',$X)):" = ".unconvert_field($m,q($X)))));if(JUSH=="sql"&&preg_match('~char|text~',$Yc)&&preg_match("~[^ -@]~",$X))$J[]="$d = ".q($X)." COLLATE ".charset(connection())."_bin";}foreach((array)$Z["null"]as$x)$J[]=escape_key($x)." IS NULL";return +implode(" AND ",$J);}function +where_check($X,array$n=array()){parse_str($X,$Wa);remove_slashes(array(&$Wa));return +where($Wa,$n);}function +where_link($s,$d,$Y,$Tf="="){return"&where%5B$s%5D%5Bcol%5D=".urlencode($d)."&where%5B$s%5D%5Bop%5D=".urlencode(($Y!==null?$Tf:"IS NULL"))."&where%5B$s%5D%5Bval%5D=".urlencode($Y);}function +convert_fields(array$e,array$n,array$M=array()){$J="";foreach($e +as$x=>$X){if($M&&!in_array(idf_escape($x),$M))continue;$wa=convert_field($n[$x]);if($wa)$J +.=", $wa AS ".idf_escape($x);}return$J;}function +cookie($B,$Y,$Oe=2592000){header("Set-Cookie: $B=".urlencode($Y).($Oe?"; expires=".gmdate("D, d M Y H:i:s",time()+$Oe)." GMT":"")."; path=".preg_replace('~\?.*~','',$_SERVER["REQUEST_URI"]).(HTTPS?"; secure":"")."; HttpOnly; SameSite=lax",false);}function +get_settings($Bb){parse_str($_COOKIE[$Bb],$Uh);return$Uh;}function +get_setting($x,$Bb="adminer_settings"){$Uh=get_settings($Bb);return$Uh[$x];}function +save_settings(array$Uh,$Bb="adminer_settings"){$Y=http_build_query($Uh+get_settings($Bb));cookie($Bb,$Y);$_COOKIE[$Bb]=$Y;}function +restart_session(){if(!ini_bool("session.use_cookies")&&(!function_exists('session_status')||session_status()==1))session_start();}function +stop_session($id=false){$wj=ini_bool("session.use_cookies");if(!$wj||$id){session_write_close();if($wj&&@ini_set("session.use_cookies",'0')===false)session_start();}}function&get_session($x){return$_SESSION[$x][DRIVER][SERVER][$_GET["username"]];}function +set_session($x,$X){$_SESSION[$x][DRIVER][SERVER][$_GET["username"]]=$X;}function +auth_url($Dj,$N,$V,$j=null){$sj=remove_from_uri(implode("|",array_keys(SqlDriver::$drivers))."|username|ext|".($j!==null?"db|":"").($Dj=='mssql'||$Dj=='pgsql'?"":"ns|").session_name());preg_match('~([^?]*)\??(.*)~',$sj,$A);return"$A[1]?".(sid()?SID."&":"").($Dj!="server"||$N!=""?urlencode($Dj)."=".urlencode($N)."&":"").($_GET["ext"]?"ext=".urlencode($_GET["ext"])."&":"")."username=".urlencode($V).($j!=""?"&db=".urlencode($j):"").($A[2]?"&$A[2]":"");}function +is_ajax(){return($_SERVER["HTTP_X_REQUESTED_WITH"]=="XMLHttpRequest");}function +redirect($Re,$if=null){if($if!==null){restart_session();$_SESSION["messages"][preg_replace('~^[^?]*~','',($Re!==null?$Re:$_SERVER["REQUEST_URI"]))][]=$if;}if($Re!==null){if($Re=="")$Re=".";header("Location: $Re");exit;}}function +query_redirect($H,$Re,$if,$fh=true,$Kc=true,$Tc=false,$Ki=""){if($Kc){$ji=microtime(true);$Tc=!connection()->query($H);$Ki=format_time($ji);}$di=($H?adminer()->messageQuery($H,$Ki,$Tc):"");if($Tc){adminer()->error +.=error().$di.script("messagesPrint();")."
";return +false;}if($fh)redirect($Re,$if.$di);return +true;}class +Queries{static$queries=array();static$start=0;}function +queries($H){if(!Queries::$start)Queries::$start=microtime(true);Queries::$queries[]=(preg_match('~;$~',$H)?"DELIMITER ;;\n$H;\nDELIMITER ":$H).";";return +connection()->query($H);}function +apply_queries($H,array$T,$Gc='Adminer\table'){foreach($T +as$R){if(!queries("$H ".$Gc($R)))return +false;}return +true;}function +queries_redirect($Re,$if,$fh){$ah=implode("\n",Queries::$queries);$Ki=format_time(Queries::$start);return +query_redirect($ah,$Re,$if,$fh,false,!$fh,$Ki);}function +format_time($ji){return +sprintf('%.3f s',max(0,microtime(true)-$ji));}function +relative_uri(){return +str_replace(":","%3a",preg_replace('~^[^?]*/([^?]*)~','\1',$_SERVER["REQUEST_URI"]));}function +remove_from_uri($qg=""){return +substr(preg_replace("~(?<=[?&])($qg".(SID?"":"|".session_name()).")=[^&]*&~",'',relative_uri()."&"),0,-1);}function +get_file($x,$Rb=false,$Xb=""){$Zc=$_FILES[$x];if(!$Zc)return +null;foreach($Zc +as$x=>$X)$Zc[$x]=(array)$X;$J='';foreach($Zc["error"]as$x=>$l){if($l)return$l;$B=$Zc["name"][$x];$Si=$Zc["tmp_name"][$x];$yb=file_get_contents($Rb&&preg_match('~\.gz$~',$B)?"compress.zlib://$Si":$Si);if($Rb){$ji=substr($yb,0,3);if(function_exists("iconv")&&preg_match("~^\xFE\xFF|^\xFF\xFE~",$ji))$yb=iconv("utf-16","utf-8",$yb);elseif($ji=="\xEF\xBB\xBF")$yb=substr($yb,3);}$J +.=$yb;if($Xb)$J +.=(preg_match("($Xb\\s*\$)",$yb)?"":$Xb)."\n\n";}return$J;}function +upload_error($l){$df=($l==UPLOAD_ERR_INI_SIZE?ini_get("upload_max_filesize"):0);return($l?'Unable to upload a file.'.($df?" ".sprintf('Maximum allowed file size is %sB.',$df):""):'File does not exist.');}function +repeat_pattern($Cg,$y){return +str_repeat("$Cg{0,65535}",$y/65535)."$Cg{0,".($y%65535)."}";}function +is_utf8($X){return(preg_match('~~u',$X)&&!preg_match('~[\0-\x8\xB\xC\xE-\x1F]~',$X));}function +format_number($X){return +strtr(number_format($X,0,".",','),preg_split('~~u','0123456789',-1,PREG_SPLIT_NO_EMPTY));}function +friendly_url($X){return +preg_replace('~\W~i','-',$X);}function +table_status1($R,$Uc=false){$J=table_status($R,$Uc);return($J?reset($J):array("Name"=>$R));}function +column_foreign_keys($R){$J=array();foreach(adminer()->foreignKeys($R)as$p){foreach($p["source"]as$X)$J[$X][]=$p;}return$J;}function +fields_from_edit(){$J=array();foreach((array)$_POST["field_keys"]as$x=>$X){if($X!=""){$X=bracket_escape($X);$_POST["function"][$X]=$_POST["field_funs"][$x];$_POST["fields"][$X]=$_POST["field_vals"][$x];}}foreach((array)$_POST["fields"]as$x=>$X){$B=bracket_escape($x,true);$J[$B]=array("field"=>$B,"privileges"=>array("insert"=>1,"update"=>1,"where"=>1,"order"=>1),"null"=>1,"auto_increment"=>($x==driver()->primary),);}return$J;}function +dump_headers($Qd,$sf=false){$J=adminer()->dumpHeaders($Qd,$sf);$mg=$_POST["output"];if($mg!="text")header("Content-Disposition: attachment; filename=".adminer()->dumpFilename($Qd).".$J".($mg!="file"&&preg_match('~^[0-9a-z]+$~',$mg)?".$mg":""));session_write_close();if(!ob_get_level())ob_start(null,4096);ob_flush();flush();return$J;}function +dump_csv(array$K){foreach($K +as$x=>$X){if(preg_match('~["\n,;\t]|^0|\.\d*0$~',$X)||$X==="")$K[$x]='"'.str_replace('"','""',$X).'"';}echo +implode(($_POST["format"]=="csv"?",":($_POST["format"]=="tsv"?"\t":";")),$K)."\r\n";}function +apply_sql_function($r,$d){return($r?($r=="unixepoch"?"DATETIME($d, '$r')":($r=="count distinct"?"COUNT(DISTINCT ":strtoupper("$r("))."$d)"):$d);}function +get_temp_dir(){$J=ini_get("upload_tmp_dir");if(!$J){if(function_exists('sys_get_temp_dir'))$J=sys_get_temp_dir();else{$o=@tempnam("","");if(!$o)return'';$J=dirname($o);unlink($o);}}return$J;}function +file_open_lock($o){if(is_link($o))return;$q=@fopen($o,"c+");if(!$q)return;chmod($o,0660);if(!flock($q,LOCK_EX)){fclose($q);return;}return$q;}function +file_write_unlock($q,$Lb){rewind($q);fwrite($q,$Lb);ftruncate($q,strlen($Lb));file_unlock($q);}function +file_unlock($q){flock($q,LOCK_UN);fclose($q);}function +first(array$va){return +reset($va);}function +password_file($h){$o=get_temp_dir()."/adminer.key";if(!$h&&!file_exists($o))return'';$q=file_open_lock($o);if(!$q)return'';$J=stream_get_contents($q);if(!$J){$J=rand_string();file_write_unlock($q,$J);}else +file_unlock($q);return$J;}function +rand_string(){return +md5(uniqid(strval(mt_rand()),true));}function +select_value($X,$_,array$m,$Ji){if(is_array($X)){$J="";foreach($X +as$_e=>$W)$J +.="".($X!=array_values($X)?"".h($_e):"")."".select_value($W,$_,$m,$Ji);return"$J
";}if(!$_)$_=adminer()->selectLink($X,$m);if($_===null){if(is_mail($X))$_="mailto:$X";if(is_url($X))$_=$X;}$J=adminer()->editVal($X,$m);if($J!==null){if(!is_utf8($J))$J="\0";elseif($Ji!=""&&is_shortable($m))$J=shorten_utf8($J,max(0,+$Ji));else$J=h($J);}return +adminer()->selectVal($J,$_,$m,$X);}function +is_mail($uc){$xa='[-a-z0-9!#$%&\'*+/=?^_`{|}~]';$gc='[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])';$Cg="$xa+(\\.$xa+)*@($gc?\\.)+$gc";return +is_string($uc)&&preg_match("(^$Cg(,\\s*$Cg)*\$)i",$uc);}function +is_url($Q){$gc='[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])';return +preg_match("~^(https?)://($gc?\\.)+$gc(:\\d+)?(/.*)?(\\?.*)?(#.*)?\$~i",$Q);}function +is_shortable(array$m){return +preg_match('~char|text|json|lob|geometry|point|linestring|polygon|string|bytea~',$m["type"]);}function +count_rows($R,array$Z,$te,array$wd){$H=" FROM ".table($R).($Z?" WHERE ".implode(" AND ",$Z):"");return($te&&(JUSH=="sql"||count($wd)==1)?"SELECT COUNT(DISTINCT ".implode(", ",$wd).")$H":"SELECT COUNT(*)".($te?" FROM (SELECT 1$H GROUP BY ".implode(", ",$wd).") x":$H));}function +slow_query($H){$j=adminer()->database();$Li=adminer()->queryTimeout();$Yh=driver()->slowQuery($H,$Li);$g=null;if(!$Yh&&support("kill")){$g=connect();if($g&&($j==""||$g->select_db($j))){$Ce=get_val(connection_id(),0,$g);echo +script("const timeout = setTimeout(() => { ajax('".js_escape(ME)."script=kill', function () {}, 'kill=$Ce&token=".get_token()."'); }, 1000 * $Li);");}}ob_flush();flush();$J=@get_key_vals(($Yh?:$H),$g,false);if($g){echo +script("clearTimeout(timeout);");ob_flush();flush();}return$J;}function +get_token(){$dh=rand(1,1e6);return($dh^$_SESSION["token"]).":$dh";}function +verify_token(){list($Ti,$dh)=explode(":",$_POST["token"]);return($dh^$_SESSION["token"])==$Ti;}function +lzw_decompress($Ia){$cc=256;$Ja=8;$gb=array();$qh=0;$rh=0;for($s=0;$s=$Ja){$rh-=$Ja;$gb[]=$qh>>$rh;$qh&=(1<<$rh)-1;$cc++;if($cc>>$Ja)$Ja++;}}$bc=range("\0","\xFF");$J="";$Nj="";foreach($gb +as$s=>$fb){$tc=$bc[$fb];if(!isset($tc))$tc=$Nj.$Nj[0];$J +.=$tc;if($s)$bc[]=$Nj.$tc[0];$Nj=$tc;}return$J;}function +script($ai,$Wi="\n"){return"$ai$Wi";}function +script_src($tj,$Ub=false){return"\n";}function +nonce(){return' nonce="'.get_nonce().'"';}function +input_hidden($B,$Y=""){return"\n";}function +input_token(){return +input_hidden("token",get_token());}function +target_blank(){return' target="_blank" rel="noreferrer noopener"';}function +h($Q){return +str_replace("\0","�",htmlspecialchars($Q,ENT_QUOTES,'utf-8'));}function +nl_br($Q){return +str_replace("\n","
",$Q);}function +checkbox($B,$Y,$Za,$Ee="",$Sf="",$db="",$Ge=""){$J="".($Sf?script("qsl('input').onclick = function () { $Sf };",""):"");return($Ee!=""||$db?"$J".h($Ee)."":$J);}function +optionlist($Xf,$Ih=null,$xj=false){$J="";foreach($Xf +as$_e=>$W){$Yf=array($_e=>$W);if(is_array($W)){$J +.='';$Yf=$W;}foreach($Yf +as$x=>$X)$J +.=''.h($X);if(is_array($W))$J +.='';}return$J;}function +html_select($B,array$Xf,$Y="",$Rf="",$Ge=""){static$Ee=0;$Fe="";if(!$Ge&&substr($Xf[""],0,1)=="("){$Ee++;$Ge="label-$Ee";$Fe="

","$Me",script("qsl('a').onclick = partial(toggle, 'fieldset-$t');",""),"","
\n";}function +selectSearchPrint(array$Z,array$e,array$w){print_fieldset("search",'Search',$Z);foreach($w +as$s=>$v){if($v["type"]=="FULLTEXT")echo"
(".implode(", ",array_map('Adminer\h',$v["columns"])).") AGAINST"," ",script("qsl('input').oninput = selectFieldChange;",""),checkbox("boolean[$s]",1,isset($_GET["boolean"][$s]),"BOOL"),"
\n";}$Ta="this.parentNode.firstChild.onchange();";foreach(array_merge((array)$_GET["where"],array(array()))as$s=>$X){if(!$X||("$X[col]$X[val]"!=""&&in_array($X["op"],adminer()->operators())))echo"
".select_input(" name='where[$s][col]'",$e,$X["col"],($X?"selectFieldChange":"selectAddRow"),"(".'anywhere'.")"),html_select("where[$s][op]",adminer()->operators(),$X["op"],$Ta),"",script("mixin(qsl('input'), {oninput: function () { $Ta }, onkeydown: selectSearchKeydown, onsearch: selectSearchSearch});",""),"
\n";}echo"\n";}function +selectOrderPrint(array$Zf,array$e,array$w){print_fieldset("sort",'Sort',$Zf);$s=0;foreach((array)$_GET["order"]as$x=>$X){if($X!=""){echo"
".select_input(" name='order[$s]'",$e,$X,"selectFieldChange"),checkbox("desc[$s]",1,isset($_GET["desc"][$x]),'descending')."
\n";$s++;}}echo"
".select_input(" name='order[$s]'",$e,"","selectAddRow"),checkbox("desc[$s]",1,false,'descending')."
\n","\n";}function +selectLimitPrint($z){echo"
".'Limit'."
","",script("qsl('input').oninput = selectFieldChange;",""),"
\n";}function +selectLengthPrint($Ji){if($Ji!==null)echo"
".'Text length'."
","","
\n";}function +selectActionPrint(array$w){echo"
".'Action'."
",""," ","\n","const indexColumns = ";$e=array();foreach($w +as$v){$Jb=reset($v["columns"]);if($v["type"]!="FULLTEXT"&&$Jb)$e[$Jb]=1;}$e[""]=1;foreach($e +as$x=>$X)json_row($x);echo";\n","selectFieldChange.call(qs('#form')['select']);\n","\n","
\n";}function +selectCommandPrint(){return!information_schema(DB);}function +selectImportPrint(){return!information_schema(DB);}function +selectEmailPrint(array$vc,array$e){}function +selectColumnsProcess(array$e,array$w){$M=array();$wd=array();foreach((array)$_GET["columns"]as$x=>$X){if($X["fun"]=="count"||($X["col"]!=""&&(!$X["fun"]||in_array($X["fun"],driver()->functions)||in_array($X["fun"],driver()->grouping)))){$M[$x]=apply_sql_function($X["fun"],($X["col"]!=""?idf_escape($X["col"]):"*"));if(!in_array($X["fun"],driver()->grouping))$wd[]=$M[$x];}}return +array($M,$wd);}function +selectSearchProcess(array$n,array$w){$J=array();foreach($w +as$s=>$v){if($v["type"]=="FULLTEXT"&&idx($_GET["fulltext"],$s)!="")$J[]="MATCH (".implode(", ",array_map('Adminer\idf_escape',$v["columns"])).") AGAINST (".q($_GET["fulltext"][$s]).(isset($_GET["boolean"][$s])?" IN BOOLEAN MODE":"").")";}foreach((array)$_GET["where"]as$x=>$X){$hb=$X["col"];if("$hb$X[val]"!=""&&in_array($X["op"],adminer()->operators())){$sb=array();foreach(($hb!=""?array($hb=>$n[$hb]):$n)as$B=>$m){$Og="";$rb=" $X[op]";if(preg_match('~IN$~',$X["op"])){$Wd=process_length($X["val"]);$rb +.=" ".($Wd!=""?$Wd:"(NULL)");}elseif($X["op"]=="SQL")$rb=" $X[val]";elseif(preg_match('~^(I?LIKE) %%$~',$X["op"],$A))$rb=" $A[1] ".adminer()->processInput($m,"%$X[val]%");elseif($X["op"]=="FIND_IN_SET"){$Og="$X[op](".q($X["val"]).", ";$rb=")";}elseif(!preg_match('~NULL$~',$X["op"]))$rb +.=" ".adminer()->processInput($m,$X["val"]);if($hb!=""||(isset($m["privileges"]["where"])&&(preg_match('~^[-\d.'.(preg_match('~IN$~',$X["op"])?',':'').']+$~',$X["val"])||!preg_match('~'.number_type().'|bit~',$m["type"]))&&(!preg_match("~[\x80-\xFF]~",$X["val"])||preg_match('~char|text|enum|set~',$m["type"]))&&(!preg_match('~date|timestamp~',$m["type"])||preg_match('~^\d+-\d+-\d+~',$X["val"]))))$sb[]=$Og.driver()->convertSearch(idf_escape($B),$X,$m).$rb;}$J[]=(count($sb)==1?$sb[0]:($sb?"(".implode(" OR ",$sb).")":"1 = 0"));}}return$J;}function +selectOrderProcess(array$n,array$w){$J=array();foreach((array)$_GET["order"]as$x=>$X){if($X!="")$J[]=(preg_match('~^((COUNT\(DISTINCT |[A-Z0-9_]+\()(`(?:[^`]|``)+`|"(?:[^"]|"")+")\)|COUNT\(\*\))$~',$X)?$X:idf_escape($X)).(isset($_GET["desc"][$x])?" DESC":"");}return$J;}function +selectLimitProcess(){return(isset($_GET["limit"])?intval($_GET["limit"]):50);}function +selectLengthProcess(){return(isset($_GET["text_length"])?"$_GET[text_length]":"100");}function +selectEmailProcess(array$Z,array$kd){return +false;}function +selectQueryBuild(array$M,array$Z,array$wd,array$Zf,$z,$D){return"";}function +messageQuery($H,$Ki,$Tc=false){restart_session();$Jd=&get_session("queries");if(!idx($Jd,$_GET["db"]))$Jd[$_GET["db"]]=array();if(strlen($H)>1e6)$H=preg_replace('~[\x80-\xFF]+$~','',substr($H,0,1e6))."\n…";$Jd[$_GET["db"]][]=array($H,time(),$Ki);$fi="sql-".count($Jd[$_GET["db"]]);$J="".'SQL command'."\n";if(!$Tc&&($Jj=driver()->warnings())){$t="warnings-".count($Jd[$_GET["db"]]);$J="".'Warnings'.", $J\n";}return" ".@date("H:i:s").""." $J';}function +editRowPrint($R,array$n,$K,$rj){}function +editFunctions(array$m){$J=($m["null"]?"NULL/":"");$rj=isset($_GET["select"])||where($_GET);foreach(array(driver()->insertFunctions,driver()->editFunctions)as$x=>$rd){if(!$x||(!isset($_GET["call"])&&$rj)){foreach($rd +as$Cg=>$X){if(!$Cg||preg_match("~$Cg~",$m["type"]))$J +.="/$X";}}if($x&&$rd&&!preg_match('~set|blob|bytea|raw|file|bool~',$m["type"]))$J +.="/SQL";}if($m["auto_increment"]&&!$rj)$J='Auto Increment';return +explode("/",$J);}function +editInput($R,array$m,$ya,$Y){if($m["type"]=="enum")return(isset($_GET["select"])?" ":"").($m["null"]?" ":"").enum_input("radio",$ya,$m,$Y,$Y===0?0:null);return"";}function +editHint($R,array$m,$Y){return"";}function +processInput(array$m,$Y,$r=""){if($r=="SQL")return$Y;$B=$m["field"];$J=q($Y);if(preg_match('~^(now|getdate|uuid)$~',$r))$J="$r()";elseif(preg_match('~^current_(date|timestamp)$~',$r))$J=$r;elseif(preg_match('~^([+-]|\|\|)$~',$r))$J=idf_escape($B)." $r $J";elseif(preg_match('~^[+-] interval$~',$r))$J=idf_escape($B)." $r ".(preg_match("~^(\\d+|'[0-9.: -]') [A-Z_]+\$~i",$Y)?$Y:$J);elseif(preg_match('~^(addtime|subtime|concat)$~',$r))$J="$r(".idf_escape($B).", $J)";elseif(preg_match('~^(md5|sha1|password|encrypt)$~',$r))$J="$r($J)";return +unconvert_field($m,$J);}function +dumpOutput(){$J=array('text'=>'open','file'=>'save');if(function_exists('gzencode'))$J['gz']='gzip';return$J;}function +dumpFormat(){return(support("dump")?array('sql'=>'SQL'):array())+array('csv'=>'CSV,','csv;'=>'CSV;','tsv'=>'TSV');}function +dumpDatabase($j){}function +dumpTable($R,$ni,$xe=0){if($_POST["format"]!="sql"){echo"\xef\xbb\xbf";if($ni)dump_csv(array_keys(fields($R)));}else{if($xe==2){$n=array();foreach(fields($R)as$B=>$m)$n[]=idf_escape($B)." $m[full_type]";$h="CREATE TABLE ".table($R)." (".implode(", ",$n).")";}else$h=create_sql($R,$_POST["auto_increment"],$ni);set_utf8mb4($h);if($ni&&$h){if($ni=="DROP+CREATE"||$xe==1)echo"DROP ".($xe==2?"VIEW":"TABLE")." IF EXISTS ".table($R).";\n";if($xe==1)$h=remove_definer($h);echo"$h;\n\n";}}}function +dumpData($R,$ni,$H){if($ni){$af=(JUSH=="sqlite"?0:1048576);$n=array();$Sd=false;if($_POST["format"]=="sql"){if($ni=="TRUNCATE+INSERT")echo +truncate_sql($R).";\n";$n=fields($R);if(JUSH=="mssql"){foreach($n +as$m){if($m["auto_increment"]){echo"SET IDENTITY_INSERT ".table($R)." ON;\n";$Sd=true;break;}}}}$I=connection()->query($H,1);if($I){$me="";$Na="";$Be=array();$sd=array();$pi="";$Wc=($R!=''?'fetch_assoc':'fetch_row');$Cb=0;while($K=$I->$Wc()){if(!$Be){$Bj=array();foreach($K +as$X){$m=$I->fetch_field();if(idx($n[$m->name],'generated')){$sd[$m->name]=true;continue;}$Be[]=$m->name;$x=idf_escape($m->name);$Bj[]="$x = VALUES($x)";}$pi=($ni=="INSERT+UPDATE"?"\nON DUPLICATE KEY UPDATE ".implode(", ",$Bj):"").";\n";}if($_POST["format"]!="sql"){if($ni=="table"){dump_csv($Be);$ni="INSERT";}dump_csv($K);}else{if(!$me)$me="INSERT INTO ".table($R)." (".implode(", ",array_map('Adminer\idf_escape',$Be)).") VALUES";foreach($K +as$x=>$X){if($sd[$x]){unset($K[$x]);continue;}$m=$n[$x];$K[$x]=($X!==null?unconvert_field($m,preg_match(number_type(),$m["type"])&&!preg_match('~\[~',$m["full_type"])&&is_numeric($X)?$X:q(($X===false?0:$X))):"NULL");}$_h=($af?"\n":" ")."(".implode(",\t",$K).")";if(!$Na)$Na=$me.$_h;elseif(JUSH=='mssql'?$Cb%1000!=0:strlen($Na)+4+strlen($_h)+strlen($pi)<$af)$Na +.=",$_h";else{echo$Na.$pi;$Na=$me.$_h;}}$Cb++;}if($Na)echo$Na.$pi;}elseif($_POST["format"]=="sql")echo"-- ".str_replace("\n"," ",connection()->error)."\n";if($Sd)echo"SET IDENTITY_INSERT ".table($R)." OFF;\n";}}function +dumpFilename($Qd){return +friendly_url($Qd!=""?$Qd:(SERVER!=""?SERVER:"localhost"));}function +dumpHeaders($Qd,$sf=false){$mg=$_POST["output"];$Oc=(preg_match('~sql~',$_POST["format"])?"sql":($sf?"tar":"csv"));header("Content-Type: ".($mg=="gz"?"application/x-gzip":($Oc=="tar"?"application/x-tar":($Oc=="sql"||$mg!="file"?"text/plain":"text/csv")."; charset=utf-8")));if($mg=="gz"){ob_start(function($Q){return +gzencode($Q);},1e6);}return$Oc;}function +dumpFooter(){if($_POST["format"]=="sql")echo"-- ".gmdate("Y-m-d H:i:s e")."\n";}function +importServerPath(){return"adminer.sql";}function +homepage(){echo'

".adminer()->name()." ".VERSION;$_f=$_COOKIE["adminer_version"];echo" ".(version_compare(VERSION,$_f)<0?h($_f):"")."","

\n";if($pf=="auth"){$mg="";foreach((array)$_SESSION["pwds"]as$Dj=>$Rh){foreach($Rh +as$N=>$zj){$B=h(get_setting("vendor-$Dj-$N")?:get_driver($Dj));foreach($zj +as$V=>$F){if($F!==null){$Qb=$_SESSION["db"][$Dj][$N][$V];foreach(($Qb?array_keys($Qb):array(""))as$j)$mg +.="
  • ($B) ".h($V.($N!=""?"@".adminer()->serverName($N):"").($j!=""?" - $j":""))."\n";}}}}if($mg)echo"\n".script("mixin(qs('#logins'), {onmouseover: menuOver, onmouseout: menuOut});");}else{$T=array();if($_GET["ns"]!==""&&!$pf&&DB!=""){connection()->select_db(DB);$T=table_status('',true);}adminer()->syntaxHighlighting($T);adminer()->databasesPrint($pf);$ia=array();if(DB==""||!$pf){if(support("sql")){$ia[]="".'SQL command'."";$ia[]="".'Import'."";}$ia[]="".'Export'."";}$Xd=$_GET["ns"]!==""&&!$pf&&DB!="";if($Xd)$ia[]='".'Create table'."";echo($ia?"

    ".'No tables.'."

    \n";}}}function +syntaxHighlighting(array$T){echo +script_src(preg_replace("~\\?.*~","",ME)."?file=jush.js&version=5.3.0",true);if(support("sql")){echo"\n";if($T){$Qe=array();foreach($T +as$R=>$U)$Qe[]=preg_quote($R,'/');echo"var jushLinks = { ".JUSH.": [ '".js_escape(ME).(support("table")?"table=":"select=")."\$&', /\\b(".implode("|",$Qe).")\\b/g ] };\n";foreach(array("bac","bra","sqlite_quo","mssql_bra")as$X)echo"jushLinks.$X = jushLinks.".JUSH.";\n";if(isset($_GET["sql"])||isset($_GET["trigger"])||isset($_GET["check"])){$_i=array_fill_keys(array_keys($T),array());foreach(driver()->allFields()as$R=>$n){foreach($n +as$m)$_i[$R][]=$m["field"];}echo"addEventListener('DOMContentLoaded', () => { autocompleter = jush.autocompleteSql('".idf_escape("")."', ".json_encode($_i)."); });\n";}}echo"\n";}echo +script("syntaxHighlighting('".preg_replace('~^(\d\.?\d).*~s','\1',connection()->server_info)."', '".connection()->flavor."');");}function +databasesPrint($pf){$i=adminer()->databases();if(DB&&$i&&!in_array(DB,$i))array_unshift($i,DB);echo"
    \n

    \n";hidden_fields_get();$Ob=script("mixin(qsl('select'), {onmousedown: dbMouseDown, onchange: dbChange});");echo"","\n";if(support("scheme")){if($pf!="db"&&DB!=""&&connection()->select_db(DB)){echo"
    ";if($_GET["ns"]!="")set_schema($_GET["ns"]);}}foreach(array("import","sql","schema","dump","privileges")as$X){if(isset($_GET[$X])){echo +input_hidden($X);break;}}echo"

    \n";}function +tablesPrint(array$T){echo"
      ".script("mixin(qs('#tables'), {onmouseover: menuOver, onmouseout: menuOut});");foreach($T +as$R=>$P){$R="$R";$B=adminer()->tableName($P);if($B!=""&&!$P["inherited"])echo'
    • ".'select'." ",(support("table")||support("indexes")?'$B":"$B")."\n";}echo"
    \n";}}class +Plugins{private +static$append=array('dumpFormat'=>true,'dumpOutput'=>true,'editRowPrint'=>true,'editFunctions'=>true,'config'=>true);var$plugins;var$error='';private$hooks=array();function +__construct($Hg){if($Hg===null){$Hg=array();$Ha="adminer-plugins";if(is_dir($Ha)){foreach(glob("$Ha/*.php")as$o)$Yd=include_once"./$o";}$Id=" href='https://www.adminer.org/plugins/#use'".target_blank();if(file_exists("$Ha.php")){$Yd=include_once"./$Ha.php";if(is_array($Yd)){foreach($Yd +as$Gg)$Hg[get_class($Gg)]=$Gg;}else$this->error +.=sprintf('%s must return an array.',"$Ha.php",$Id)."
    ";}foreach(get_declared_classes()as$db){if(!$Hg[$db]&&preg_match('~^Adminer\w~i',$db)){$kh=new +\ReflectionClass($db);$xb=$kh->getConstructor();if($xb&&$xb->getNumberOfRequiredParameters())$this->error +.=sprintf('Configure %s in %s.',$Id,"$db","$Ha.php")."
    ";else$Hg[$db]=new$db;}}}$this->plugins=$Hg;$la=new +Adminer;$Hg[]=$la;$kh=new +\ReflectionObject($la);foreach($kh->getMethods()as$nf){foreach($Hg +as$Gg){$B=$nf->getName();if(method_exists($Gg,$B))$this->hooks[$B][]=$Gg;}}}function +__call($B,array$rg){$ua=array();foreach($rg +as$x=>$X)$ua[]=&$rg[$x];$J=null;foreach($this->hooks[$B]as$Gg){$Y=call_user_func_array(array($Gg,$B),$ua);if($Y!==null){if(!self::$append[$B])return$Y;$J=$Y+(array)$J;}}return$J;}}abstract +class +Plugin{protected$translations=array();function +description(){return$this->lang('');}function +screenshot(){return"";}protected +function +lang($u,$Ff=null){$ua=func_get_args();$ua[0]=idx($this->translations[LANG],$u)?:$u;return +call_user_func_array('Adminer\lang_format',$ua);}}Adminer::$instance=(function_exists('adminer_object')?adminer_object():(is_dir("adminer-plugins")||file_exists("adminer-plugins.php")?new +Plugins(null):new +Adminer));SqlDriver::$drivers=array("server"=>"MySQL / MariaDB")+SqlDriver::$drivers;if(!defined('Adminer\DRIVER')){define('Adminer\DRIVER',"server");if(extension_loaded("mysqli")&&$_GET["ext"]!="pdo"){class +Db +extends +\MySQLi{static$instance;var$extension="MySQLi",$flavor='';function +__construct(){parent::init();}function +attach($N,$V,$F){mysqli_report(MYSQLI_REPORT_OFF);list($Md,$Ig)=explode(":",$N,2);$ii=adminer()->connectSsl();if($ii)$this->ssl_set($ii['key'],$ii['cert'],$ii['ca'],'','');$J=@$this->real_connect(($N!=""?$Md:ini_get("mysqli.default_host")),($N.$V!=""?$V:ini_get("mysqli.default_user")),($N.$V.$F!=""?$F:ini_get("mysqli.default_pw")),null,(is_numeric($Ig)?intval($Ig):ini_get("mysqli.default_port")),(is_numeric($Ig)?null:$Ig),($ii?($ii['verify']!==false?2048:64):0));$this->options(MYSQLI_OPT_LOCAL_INFILE,false);return($J?'':$this->error);}function +set_charset($Va){if(parent::set_charset($Va))return +true;parent::set_charset('utf8');return$this->query("SET NAMES $Va");}function +next_result(){return +self::more_results()&&parent::next_result();}function +quote($Q){return"'".$this->escape_string($Q)."'";}}}elseif(extension_loaded("mysql")&&!((ini_bool("sql.safe_mode")||ini_bool("mysql.allow_local_infile"))&&extension_loaded("pdo_mysql"))){class +Db +extends +SqlDb{private$link;function +attach($N,$V,$F){if(ini_bool("mysql.allow_local_infile"))return +sprintf('Disable %s or enable %s or %s extensions.',"'mysql.allow_local_infile'","MySQLi","PDO_MySQL");$this->link=@mysql_connect(($N!=""?$N:ini_get("mysql.default_host")),("$N$V"!=""?$V:ini_get("mysql.default_user")),("$N$V$F"!=""?$F:ini_get("mysql.default_password")),true,131072);if(!$this->link)return +mysql_error();$this->server_info=mysql_get_server_info($this->link);return'';}function +set_charset($Va){if(function_exists('mysql_set_charset')){if(mysql_set_charset($Va,$this->link))return +true;mysql_set_charset('utf8',$this->link);}return$this->query("SET NAMES $Va");}function +quote($Q){return"'".mysql_real_escape_string($Q,$this->link)."'";}function +select_db($Nb){return +mysql_select_db($Nb,$this->link);}function +query($H,$jj=false){$I=@($jj?mysql_unbuffered_query($H,$this->link):mysql_query($H,$this->link));$this->error="";if(!$I){$this->errno=mysql_errno($this->link);$this->error=mysql_error($this->link);return +false;}if($I===true){$this->affected_rows=mysql_affected_rows($this->link);$this->info=mysql_info($this->link);return +true;}return +new +Result($I);}}class +Result{var$num_rows;private$result;private$offset=0;function +__construct($I){$this->result=$I;$this->num_rows=mysql_num_rows($I);}function +fetch_assoc(){return +mysql_fetch_assoc($this->result);}function +fetch_row(){return +mysql_fetch_row($this->result);}function +fetch_field(){$J=mysql_fetch_field($this->result,$this->offset++);$J->orgtable=$J->table;$J->charsetnr=($J->blob?63:0);return$J;}function +__destruct(){mysql_free_result($this->result);}}}elseif(extension_loaded("pdo_mysql")){class +Db +extends +PdoDb{var$extension="PDO_MySQL";function +attach($N,$V,$F){$Xf=array(\PDO::MYSQL_ATTR_LOCAL_INFILE=>false);$ii=adminer()->connectSsl();if($ii){if($ii['key'])$Xf[\PDO::MYSQL_ATTR_SSL_KEY]=$ii['key'];if($ii['cert'])$Xf[\PDO::MYSQL_ATTR_SSL_CERT]=$ii['cert'];if($ii['ca'])$Xf[\PDO::MYSQL_ATTR_SSL_CA]=$ii['ca'];if(isset($ii['verify']))$Xf[\PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT]=$ii['verify'];}return$this->dsn("mysql:charset=utf8;host=".str_replace(":",";unix_socket=",preg_replace('~:(\d)~',';port=\1',$N)),$V,$F,$Xf);}function +set_charset($Va){return$this->query("SET NAMES $Va");}function +select_db($Nb){return$this->query("USE ".idf_escape($Nb));}function +query($H,$jj=false){$this->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,!$jj);return +parent::query($H,$jj);}}}class +Driver +extends +SqlDriver{static$extensions=array("MySQLi","MySQL","PDO_MySQL");static$jush="sql";var$unsigned=array("unsigned","zerofill","unsigned zerofill");var$operators=array("=","<",">","<=",">=","!=","LIKE","LIKE %%","REGEXP","IN","FIND_IN_SET","IS NULL","NOT LIKE","NOT REGEXP","NOT IN","IS NOT NULL","SQL");var$functions=array("char_length","date","from_unixtime","lower","round","floor","ceil","sec_to_time","time_to_sec","upper");var$grouping=array("avg","count","count distinct","group_concat","max","min","sum");static +function +connect($N,$V,$F){$f=parent::connect($N,$V,$F);if(is_string($f)){if(function_exists('iconv')&&!is_utf8($f)&&strlen($_h=iconv("windows-1250","utf-8",$f))>strlen($f))$f=$_h;return$f;}$f->set_charset(charset($f));$f->query("SET sql_quote_show_create = 1, autocommit = 1");$f->flavor=(preg_match('~MariaDB~',$f->server_info)?'maria':'mysql');add_driver(DRIVER,($f->flavor=='maria'?"MariaDB":"MySQL"));return$f;}function +__construct(Db$f){parent::__construct($f);$this->types=array('Numbers'=>array("tinyint"=>3,"smallint"=>5,"mediumint"=>8,"int"=>10,"bigint"=>20,"decimal"=>66,"float"=>12,"double"=>21),'Date and time'=>array("date"=>10,"datetime"=>19,"timestamp"=>19,"time"=>10,"year"=>4),'Strings'=>array("char"=>255,"varchar"=>65535,"tinytext"=>255,"text"=>65535,"mediumtext"=>16777215,"longtext"=>4294967295),'Lists'=>array("enum"=>65535,"set"=>64),'Binary'=>array("bit"=>20,"binary"=>255,"varbinary"=>65535,"tinyblob"=>255,"blob"=>65535,"mediumblob"=>16777215,"longblob"=>4294967295),'Geometry'=>array("geometry"=>0,"point"=>0,"linestring"=>0,"polygon"=>0,"multipoint"=>0,"multilinestring"=>0,"multipolygon"=>0,"geometrycollection"=>0),);$this->insertFunctions=array("char"=>"md5/sha1/password/encrypt/uuid","binary"=>"md5/sha1","date|time"=>"now",);$this->editFunctions=array(number_type()=>"+/-","date"=>"+ interval/- interval","time"=>"addtime/subtime","char|text"=>"concat",);if(min_version('5.7.8',10.2,$f))$this->types['Strings']["json"]=4294967295;if(min_version('',10.7,$f)){$this->types['Strings']["uuid"]=128;$this->insertFunctions['uuid']='uuid';}if(min_version(9,'',$f)){$this->types['Numbers']["vector"]=16383;$this->insertFunctions['vector']='string_to_vector';}if(min_version(5.1,'',$f))$this->partitionBy=array("HASH","LINEAR HASH","KEY","LINEAR KEY","RANGE","LIST");if(min_version(5.7,10.2,$f))$this->generated=array("STORED","VIRTUAL");}function +unconvertFunction(array$m){return(preg_match("~binary~",$m["type"])?"UNHEX":($m["type"]=="bit"?doc_link(array('sql'=>'bit-value-literals.html'),"b''"):(preg_match("~geometry|point|linestring|polygon~",$m["type"])?"GeomFromText":"")));}function +insert($R,array$O){return($O?parent::insert($R,$O):queries("INSERT INTO ".table($R)." ()\nVALUES ()"));}function +insertUpdate($R,array$L,array$G){$e=array_keys(reset($L));$Og="INSERT INTO ".table($R)." (".implode(", ",$e).") VALUES\n";$Bj=array();foreach($e +as$x)$Bj[$x]="$x = VALUES($x)";$pi="\nON DUPLICATE KEY UPDATE ".implode(", ",$Bj);$Bj=array();$y=0;foreach($L +as$O){$Y="(".implode(", ",$O).")";if($Bj&&(strlen($Og)+$y+strlen($Y)+strlen($pi)>1e6)){if(!queries($Og.implode(",\n",$Bj).$pi))return +false;$Bj=array();$y=0;}$Bj[]=$Y;$y+=strlen($Y)+2;}return +queries($Og.implode(",\n",$Bj).$pi);}function +slowQuery($H,$Li){if(min_version('5.7.8','10.1.2')){if($this->conn->flavor=='maria')return"SET STATEMENT max_statement_time=$Li FOR $H";elseif(preg_match('~^(SELECT\b)(.+)~is',$H,$A))return"$A[1] /*+ MAX_EXECUTION_TIME(".($Li*1000).") */ $A[2]";}}function +convertSearch($u,array$X,array$m){return(preg_match('~char|text|enum|set~',$m["type"])&&!preg_match("~^utf8~",$m["collation"])&&preg_match('~[\x80-\xFF]~',$X['val'])?"CONVERT($u USING ".charset($this->conn).")":$u);}function +warnings(){$I=$this->conn->query("SHOW WARNINGS");if($I&&$I->num_rows){ob_start();print_select_result($I);return +ob_get_clean();}}function +tableHelp($B,$xe=false){$Ue=($this->conn->flavor=='maria');if(information_schema(DB))return +strtolower("information-schema-".($Ue?"$B-table/":str_replace("_","-",$B)."-table.html"));if(DB=="mysql")return($Ue?"mysql$B-table/":"system-schema.html");}function +partitionsInfo($R){$pd="FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = ".q(DB)." AND TABLE_NAME = ".q($R);$I=connection()->query("SELECT PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_ORDINAL_POSITION $pd ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1");$J=array();list($J["partition_by"],$J["partition"],$J["partitions"])=$I->fetch_row();$zg=get_key_vals("SELECT PARTITION_NAME, PARTITION_DESCRIPTION $pd AND PARTITION_NAME != '' ORDER BY PARTITION_ORDINAL_POSITION");$J["partition_names"]=array_keys($zg);$J["partition_values"]=array_values($zg);return$J;}function +hasCStyleEscapes(){static$Qa;if($Qa===null){$gi=get_val("SHOW VARIABLES LIKE 'sql_mode'",1,$this->conn);$Qa=(strpos($gi,'NO_BACKSLASH_ESCAPES')===false);}return$Qa;}function +engines(){$J=array();foreach(get_rows("SHOW ENGINES")as$K){if(preg_match("~YES|DEFAULT~",$K["Support"]))$J[]=$K["Engine"];}return$J;}function +indexAlgorithms(array$ti){return(preg_match('~^(MEMORY|NDB)$~',$ti["Engine"])?array("HASH","BTREE"):array());}}function +idf_escape($u){return"`".str_replace("`","``",$u)."`";}function +table($u){return +idf_escape($u);}function +get_databases($hd){$J=get_session("dbs");if($J===null){$H="SELECT SCHEMA_NAME FROM information_schema.SCHEMATA ORDER BY SCHEMA_NAME";$J=($hd?slow_query($H):get_vals($H));restart_session();set_session("dbs",$J);stop_session();}return$J;}function +limit($H,$Z,$z,$C=0,$Mh=" "){return" $H$Z".($z?$Mh."LIMIT $z".($C?" OFFSET $C":""):"");}function +limit1($R,$H,$Z,$Mh="\n"){return +limit($H,$Z,1,0,$Mh);}function +db_collation($j,array$jb){$J=null;$h=get_val("SHOW CREATE DATABASE ".idf_escape($j),1);if(preg_match('~ COLLATE ([^ ]+)~',$h,$A))$J=$A[1];elseif(preg_match('~ CHARACTER SET ([^ ]+)~',$h,$A))$J=$jb[$A[1]][-1];return$J;}function +logged_user(){return +get_val("SELECT USER()");}function +tables_list(){return +get_key_vals("SELECT TABLE_NAME, TABLE_TYPE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME");}function +count_tables(array$i){$J=array();foreach($i +as$j)$J[$j]=count(get_vals("SHOW TABLES IN ".idf_escape($j)));return$J;}function +table_status($B="",$Uc=false){$J=array();foreach(get_rows($Uc?"SELECT TABLE_NAME AS Name, ENGINE AS Engine, TABLE_COMMENT AS Comment FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ".($B!=""?"AND TABLE_NAME = ".q($B):"ORDER BY Name"):"SHOW TABLE STATUS".($B!=""?" LIKE ".q(addcslashes($B,"%_\\")):""))as$K){if($K["Engine"]=="InnoDB")$K["Comment"]=preg_replace('~(?:(.+); )?InnoDB free: .*~','\1',$K["Comment"]);if(!isset($K["Engine"]))$K["Comment"]="";if($B!="")$K["Name"]=$B;$J[$K["Name"]]=$K;}return$J;}function +is_view(array$S){return$S["Engine"]===null;}function +fk_support(array$S){return +preg_match('~InnoDB|IBMDB2I'.(min_version(5.6)?'|NDB':'').'~i',$S["Engine"]);}function +fields($R){$Ue=(connection()->flavor=='maria');$J=array();foreach(get_rows("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ".q($R)." ORDER BY ORDINAL_POSITION")as$K){$m=$K["COLUMN_NAME"];$U=$K["COLUMN_TYPE"];$td=$K["GENERATION_EXPRESSION"];$Rc=$K["EXTRA"];preg_match('~^(VIRTUAL|PERSISTENT|STORED)~',$Rc,$sd);preg_match('~^([^( ]+)(?:\((.+)\))?( unsigned)?( zerofill)?$~',$U,$Xe);$k=$K["COLUMN_DEFAULT"];if($k!=""){$we=preg_match('~text|json~',$Xe[1]);if(!$Ue&&$we)$k=preg_replace("~^(_\w+)?('.*')$~",'\2',stripslashes($k));if($Ue||$we){$k=($k=="NULL"?null:preg_replace_callback("~^'(.*)'$~",function($A){return +stripslashes(str_replace("''","'",$A[1]));},$k));}if(!$Ue&&preg_match('~binary~',$Xe[1])&&preg_match('~^0x(\w*)$~',$k,$A))$k=pack("H*",$A[1]);}$J[$m]=array("field"=>$m,"full_type"=>$U,"type"=>$Xe[1],"length"=>$Xe[2],"unsigned"=>ltrim($Xe[3].$Xe[4]),"default"=>($sd?($Ue?$td:stripslashes($td)):$k),"null"=>($K["IS_NULLABLE"]=="YES"),"auto_increment"=>($Rc=="auto_increment"),"on_update"=>(preg_match('~\bon update (\w+)~i',$Rc,$A)?$A[1]:""),"collation"=>$K["COLLATION_NAME"],"privileges"=>array_flip(explode(",","$K[PRIVILEGES],where,order")),"comment"=>$K["COLUMN_COMMENT"],"primary"=>($K["COLUMN_KEY"]=="PRI"),"generated"=>($sd[1]=="PERSISTENT"?"STORED":$sd[1]),);}return$J;}function +indexes($R,$g=null){$J=array();foreach(get_rows("SHOW INDEX FROM ".table($R),$g)as$K){$B=$K["Key_name"];$J[$B]["type"]=($B=="PRIMARY"?"PRIMARY":($K["Index_type"]=="FULLTEXT"?"FULLTEXT":($K["Non_unique"]?($K["Index_type"]=="SPATIAL"?"SPATIAL":"INDEX"):"UNIQUE")));$J[$B]["columns"][]=$K["Column_name"];$J[$B]["lengths"][]=($K["Index_type"]=="SPATIAL"?null:$K["Sub_part"]);$J[$B]["descs"][]=null;$J[$B]["algorithm"]=$K["Index_type"];}return$J;}function +foreign_keys($R){static$Cg='(?:`(?:[^`]|``)+`|"(?:[^"]|"")+")';$J=array();$Db=get_val("SHOW CREATE TABLE ".table($R),1);if($Db){preg_match_all("~CONSTRAINT ($Cg) FOREIGN KEY ?\\(((?:$Cg,? ?)+)\\) REFERENCES ($Cg)(?:\\.($Cg))? \\(((?:$Cg,? ?)+)\\)(?: ON DELETE (".driver()->onActions."))?(?: ON UPDATE (".driver()->onActions."))?~",$Db,$Ye,PREG_SET_ORDER);foreach($Ye +as$A){preg_match_all("~$Cg~",$A[2],$ai);preg_match_all("~$Cg~",$A[5],$Di);$J[idf_unescape($A[1])]=array("db"=>idf_unescape($A[4]!=""?$A[3]:$A[4]),"table"=>idf_unescape($A[4]!=""?$A[4]:$A[3]),"source"=>array_map('Adminer\idf_unescape',$ai[0]),"target"=>array_map('Adminer\idf_unescape',$Di[0]),"on_delete"=>($A[6]?:"RESTRICT"),"on_update"=>($A[7]?:"RESTRICT"),);}}return$J;}function +view($B){return +array("select"=>preg_replace('~^(?:[^`]|`[^`]*`)*\s+AS\s+~isU','',get_val("SHOW CREATE VIEW ".table($B),1)));}function +collations(){$J=array();foreach(get_rows("SHOW COLLATION")as$K){if($K["Default"])$J[$K["Charset"]][-1]=$K["Collation"];else$J[$K["Charset"]][]=$K["Collation"];}ksort($J);foreach($J +as$x=>$X)sort($J[$x]);return$J;}function +information_schema($j){return($j=="information_schema")||(min_version(5.5)&&$j=="performance_schema");}function +error(){return +h(preg_replace('~^You have an error.*syntax to use~U',"Syntax error",connection()->error));}function +create_database($j,$c){return +queries("CREATE DATABASE ".idf_escape($j).($c?" COLLATE ".q($c):""));}function +drop_databases(array$i){$J=apply_queries("DROP DATABASE",$i,'Adminer\idf_escape');restart_session();set_session("dbs",null);return$J;}function +rename_database($B,$c){$J=false;if(create_database($B,$c)){$T=array();$Gj=array();foreach(tables_list()as$R=>$U){if($U=='VIEW')$Gj[]=$R;else$T[]=$R;}$J=(!$T&&!$Gj)||move_tables($T,$Gj,$B);drop_databases($J?array(DB):array());}return$J;}function +auto_increment(){$Aa=" PRIMARY KEY";if($_GET["create"]!=""&&$_POST["auto_increment_col"]){foreach(indexes($_GET["create"])as$v){if(in_array($_POST["fields"][$_POST["auto_increment_col"]]["orig"],$v["columns"],true)){$Aa="";break;}if($v["type"]=="PRIMARY")$Aa=" UNIQUE";}}return" AUTO_INCREMENT$Aa";}function +alter_table($R,$B,array$n,array$jd,$ob,$yc,$c,$_a,$E){$b=array();foreach($n +as$m){if($m[1]){$k=$m[1][3];if(preg_match('~ GENERATED~',$k)){$m[1][3]=(connection()->flavor=='maria'?"":$m[1][2]);$m[1][2]=$k;}$b[]=($R!=""?($m[0]!=""?"CHANGE ".idf_escape($m[0]):"ADD"):" ")." ".implode($m[1]).($R!=""?$m[2]:"");}else$b[]="DROP ".idf_escape($m[0]);}$b=array_merge($b,$jd);$P=($ob!==null?" COMMENT=".q($ob):"").($yc?" ENGINE=".q($yc):"").($c?" COLLATE ".q($c):"").($_a!=""?" AUTO_INCREMENT=$_a":"");if($E){$zg=array();if($E["partition_by"]=='RANGE'||$E["partition_by"]=='LIST'){foreach($E["partition_names"]as$x=>$X){$Y=$E["partition_values"][$x];$zg[]="\n PARTITION ".idf_escape($X)." VALUES ".($E["partition_by"]=='RANGE'?"LESS THAN":"IN").($Y!=""?" ($Y)":" MAXVALUE");}}$P +.="\nPARTITION BY $E[partition_by]($E[partition])";if($zg)$P +.=" (".implode(",",$zg)."\n)";elseif($E["partitions"])$P +.=" PARTITIONS ".(+$E["partitions"]);}elseif($E===null)$P +.="\nREMOVE PARTITIONING";if($R=="")return +queries("CREATE TABLE ".table($B)." (\n".implode(",\n",$b)."\n)$P");if($R!=$B)$b[]="RENAME TO ".table($B);if($P)$b[]=ltrim($P);return($b?queries("ALTER TABLE ".table($R)."\n".implode(",\n",$b)):true);}function +alter_indexes($R,$b){$Ua=array();foreach($b +as$X)$Ua[]=($X[2]=="DROP"?"\nDROP INDEX ".idf_escape($X[1]):"\nADD $X[0] ".($X[0]=="PRIMARY"?"KEY ":"").($X[1]!=""?idf_escape($X[1])." ":"")."(".implode(", ",$X[2]).")");return +queries("ALTER TABLE ".table($R).implode(",",$Ua));}function +truncate_tables(array$T){return +apply_queries("TRUNCATE TABLE",$T);}function +drop_views(array$Gj){return +queries("DROP VIEW ".implode(", ",array_map('Adminer\table',$Gj)));}function +drop_tables(array$T){return +queries("DROP TABLE ".implode(", ",array_map('Adminer\table',$T)));}function +move_tables(array$T,array$Gj,$Di){$oh=array();foreach($T +as$R)$oh[]=table($R)." TO ".idf_escape($Di).".".table($R);if(!$oh||queries("RENAME TABLE ".implode(", ",$oh))){$Wb=array();foreach($Gj +as$R)$Wb[table($R)]=view($R);connection()->select_db($Di);$j=idf_escape(DB);foreach($Wb +as$B=>$Fj){if(!queries("CREATE VIEW $B AS ".str_replace(" $j."," ",$Fj["select"]))||!queries("DROP VIEW $j.$B"))return +false;}return +true;}return +false;}function +copy_tables(array$T,array$Gj,$Di){queries("SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'");foreach($T +as$R){$B=($Di==DB?table("copy_$R"):idf_escape($Di).".".table($R));if(($_POST["overwrite"]&&!queries("\nDROP TABLE IF EXISTS $B"))||!queries("CREATE TABLE $B LIKE ".table($R))||!queries("INSERT INTO $B SELECT * FROM ".table($R)))return +false;foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")))as$K){$cj=$K["Trigger"];if(!queries("CREATE TRIGGER ".($Di==DB?idf_escape("copy_$cj"):idf_escape($Di).".".idf_escape($cj))." $K[Timing] $K[Event] ON $B FOR EACH ROW\n$K[Statement];"))return +false;}}foreach($Gj +as$R){$B=($Di==DB?table("copy_$R"):idf_escape($Di).".".table($R));$Fj=view($R);if(($_POST["overwrite"]&&!queries("DROP VIEW IF EXISTS $B"))||!queries("CREATE VIEW $B AS $Fj[select]"))return +false;}return +true;}function +trigger($B,$R){if($B=="")return +array();$L=get_rows("SHOW TRIGGERS WHERE `Trigger` = ".q($B));return +reset($L);}function +triggers($R){$J=array();foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")))as$K)$J[$K["Trigger"]]=array($K["Timing"],$K["Event"]);return$J;}function +trigger_options(){return +array("Timing"=>array("BEFORE","AFTER"),"Event"=>array("INSERT","UPDATE","DELETE"),"Type"=>array("FOR EACH ROW"),);}function +routine($B,$U){$ra=array("bool","boolean","integer","double precision","real","dec","numeric","fixed","national char","national varchar");$bi="(?:\\s|/\\*[\s\S]*?\\*/|(?:#|-- )[^\n]*\n?|--\r?\n)";$_c=driver()->enumLength;$hj="((".implode("|",array_merge(array_keys(driver()->types()),$ra)).")\\b(?:\\s*\\(((?:[^'\")]|$_c)++)\\))?"."\\s*(zerofill\\s*)?(unsigned(?:\\s+zerofill)?)?)(?:\\s*(?:CHARSET|CHARACTER\\s+SET)\\s*['\"]?([^'\"\\s,]+)['\"]?)?";$Cg="$bi*(".($U=="FUNCTION"?"":driver()->inout).")?\\s*(?:`((?:[^`]|``)*)`\\s*|\\b(\\S+)\\s+)$hj";$h=get_val("SHOW CREATE $U ".idf_escape($B),2);preg_match("~\\(((?:$Cg\\s*,?)*)\\)\\s*".($U=="FUNCTION"?"RETURNS\\s+$hj\\s+":"")."(.*)~is",$h,$A);$n=array();preg_match_all("~$Cg\\s*,?~is",$A[1],$Ye,PREG_SET_ORDER);foreach($Ye +as$qg)$n[]=array("field"=>str_replace("``","`",$qg[2]).$qg[3],"type"=>strtolower($qg[5]),"length"=>preg_replace_callback("~$_c~s",'Adminer\normalize_enum',$qg[6]),"unsigned"=>strtolower(preg_replace('~\s+~',' ',trim("$qg[8] $qg[7]"))),"null"=>true,"full_type"=>$qg[4],"inout"=>strtoupper($qg[1]),"collation"=>strtolower($qg[9]),);return +array("fields"=>$n,"comment"=>get_val("SELECT ROUTINE_COMMENT FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE() AND ROUTINE_NAME = ".q($B)),)+($U!="FUNCTION"?array("definition"=>$A[11]):array("returns"=>array("type"=>$A[12],"length"=>$A[13],"unsigned"=>$A[15],"collation"=>$A[16]),"definition"=>$A[17],"language"=>"SQL",));}function +routines(){return +get_rows("SELECT ROUTINE_NAME AS SPECIFIC_NAME, ROUTINE_NAME, ROUTINE_TYPE, DTD_IDENTIFIER FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE()");}function +routine_languages(){return +array();}function +routine_id($B,array$K){return +idf_escape($B);}function +last_id($I){return +get_val("SELECT LAST_INSERT_ID()");}function +explain(Db$f,$H){return$f->query("EXPLAIN ".(min_version(5.1)&&!min_version(5.7)?"PARTITIONS ":"").$H);}function +found_rows(array$S,array$Z){return($Z||$S["Engine"]!="InnoDB"?null:$S["Rows"]);}function +create_sql($R,$_a,$ni){$J=get_val("SHOW CREATE TABLE ".table($R),1);if(!$_a)$J=preg_replace('~ AUTO_INCREMENT=\d+~','',$J);return$J;}function +truncate_sql($R){return"TRUNCATE ".table($R);}function +use_sql($Nb){return"USE ".idf_escape($Nb);}function +trigger_sql($R){$J="";foreach(get_rows("SHOW TRIGGERS LIKE ".q(addcslashes($R,"%_\\")),null,"-- ")as$K)$J +.="\nCREATE TRIGGER ".idf_escape($K["Trigger"])." $K[Timing] $K[Event] ON ".table($K["Table"])." FOR EACH ROW\n$K[Statement];;\n";return$J;}function +show_variables(){return +get_rows("SHOW VARIABLES");}function +show_status(){return +get_rows("SHOW STATUS");}function +process_list(){return +get_rows("SHOW FULL PROCESSLIST");}function +convert_field(array$m){if(preg_match("~binary~",$m["type"]))return"HEX(".idf_escape($m["field"]).")";if($m["type"]=="bit")return"BIN(".idf_escape($m["field"])." + 0)";if(preg_match("~geometry|point|linestring|polygon~",$m["type"]))return(min_version(8)?"ST_":"")."AsWKT(".idf_escape($m["field"]).")";}function +unconvert_field(array$m,$J){if(preg_match("~binary~",$m["type"]))$J="UNHEX($J)";if($m["type"]=="bit")$J="CONVERT(b$J, UNSIGNED)";if(preg_match("~geometry|point|linestring|polygon~",$m["type"])){$Og=(min_version(8)?"ST_":"");$J=$Og."GeomFromText($J, $Og"."SRID($m[field]))";}return$J;}function +support($Vc){return +preg_match('~^(comment|columns|copy|database|drop_col|dump|indexes|kill|privileges|move_col|procedure|processlist|routine|sql|status|table|trigger|variables|view'.(min_version(5.1)?'|event':'').(min_version(8)?'|descidx':'').(min_version('8.0.16','10.2.1')?'|check':'').')$~',$Vc);}function +kill_process($X){return +queries("KILL ".number($X));}function +connection_id(){return"SELECT CONNECTION_ID()";}function +max_connections(){return +get_val("SELECT @@max_connections");}function +types(){return +array();}function +type_values($t){return"";}function +schemas(){return +array();}function +get_schema(){return"";}function +set_schema($Bh,$g=null){return +true;}}define('Adminer\JUSH',Driver::$jush);define('Adminer\SERVER',$_GET[DRIVER]);define('Adminer\DB',$_GET["db"]);define('Adminer\ME',preg_replace('~\?.*~','',relative_uri()).'?'.(sid()?SID.'&':'').(SERVER!==null?DRIVER."=".urlencode(SERVER).'&':'').($_GET["ext"]?"ext=".urlencode($_GET["ext"]).'&':'').(isset($_GET["username"])?"username=".urlencode($_GET["username"]).'&':'').(DB!=""?'db='.urlencode(DB).'&'.(isset($_GET["ns"])?"ns=".urlencode($_GET["ns"])."&":""):''));function +page_header($Ni,$l="",$Ma=array(),$Oi=""){page_headers();if(is_ajax()&&$l){page_messages($l);exit;}if(!ob_get_level())ob_start('ob_gzhandler',4096);$Pi=$Ni.($Oi!=""?": $Oi":"");$Qi=strip_tags($Pi.(SERVER!=""&&SERVER!="localhost"?h(" - ".SERVER):"")." - ".adminer()->name());echo' + + + + +',$Qi,' + +';$Hb=adminer()->css();if(is_int(key($Hb)))$Hb=array_fill_keys($Hb,'light');$Ed=in_array('light',$Hb)||in_array('',$Hb);$Cd=in_array('dark',$Hb)||in_array('',$Hb);$Kb=($Ed?($Cd?null:false):($Cd?:null));$gf=" media='(prefers-color-scheme: dark)'";if($Kb!==false)echo"\n";echo"\n",script_src(preg_replace("~\\?.*~","",ME)."?file=functions.js&version=5.3.0");if(adminer()->head($Kb))echo"\n","\n";foreach($Hb +as$tj=>$qf){$ya=($qf=='dark'&&!$Kb?$gf:($qf=='light'&&$Cd?" media='(prefers-color-scheme: light)'":""));echo"\n";}echo"\nbodyClass();echo"'>\n";$o=get_temp_dir()."/adminer.version";if(!$_COOKIE["adminer_version"]&&function_exists('openssl_verify')&&file_exists($o)&&filemtime($o)+86400>time()){$Ej=unserialize(file_get_contents($o));$Yg="-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwqWOVuF5uw7/+Z70djoK +RlHIZFZPO0uYRezq90+7Amk+FDNd7KkL5eDve+vHRJBLAszF/7XKXe11xwliIsFs +DFWQlsABVZB3oisKCBEuI71J4kPH8dKGEWR9jDHFw3cWmoH3PmqImX6FISWbG3B8 +h7FIx3jEaw5ckVPVTeo5JRm/1DZzJxjyDenXvBQ/6o9DgZKeNDgxwKzH+sw9/YCO +jHnq1cFpOIISzARlrHMa/43YfeNRAm/tsBXjSxembBPo7aQZLAWHmaj5+K19H10B +nCpz9Y++cipkVEiKRGih4ZEvjoFysEOdRLj6WiD/uUNky4xGeA6LaJqh5XpkFkcQ +fQIDAQAB +-----END PUBLIC KEY----- +";if(openssl_verify($Ej["version"],base64_decode($Ej["signature"]),$Yg)==1)$_COOKIE["adminer_version"]=$Ej["version"];}echo +script("mixin(document.body, {onkeydown: bodyKeydown, onclick: bodyClick".(isset($_COOKIE["adminer_version"])?"":", onload: partial(verifyVersion, '".VERSION."', '".js_escape(ME)."', '".get_token()."')")."}); +document.body.classList.replace('nojs', 'js'); +const offlineMessage = '".js_escape('You are offline.')."'; +const thousandsSeparator = '".js_escape(',')."';"),"\n",script("mixin(qs('#help'), {onmouseover: () => { helpOpen = 1; }, onmouseout: helpMouseout});"),"
    \n","".icon("move","","menu","")."".script("qs('#menuopen').onclick = event => { qs('#foot').classList.toggle('foot'); event.stopPropagation(); }");if($Ma!==null){$_=substr(preg_replace('~\b(username|db|ns)=[^&]*&~','',ME),0,-1);echo'

    $Pi

    \n","\n";restart_session();page_messages($l);$i=&get_session("dbs");if(DB!=""&&$i&&!in_array(DB,$i,true))$i=null;stop_session();define('Adminer\PAGE_HEADER',1);}function +page_headers(){header("Content-Type: text/html; charset=utf-8");header("Cache-Control: no-cache");header("X-Frame-Options: deny");header("X-XSS-Protection: 0");header("X-Content-Type-Options: nosniff");header("Referrer-Policy: origin-when-cross-origin");foreach(adminer()->csp(csp())as$Gb){$Gd=array();foreach($Gb +as$x=>$X)$Gd[]="$x $X";header("Content-Security-Policy: ".implode("; ",$Gd));}adminer()->headers();}function +csp(){return +array(array("script-src"=>"'self' 'unsafe-inline' 'nonce-".get_nonce()."' 'strict-dynamic'","connect-src"=>"'self'","frame-src"=>"https://www.adminer.org","object-src"=>"'none'","base-uri"=>"'none'","form-action"=>"'self'",),);}function +get_nonce(){static$Bf;if(!$Bf)$Bf=base64_encode(rand_string());return$Bf;}function +page_messages($l){$sj=preg_replace('~^[^?]*~','',$_SERVER["REQUEST_URI"]);$mf=idx($_SESSION["messages"],$sj);if($mf){echo"
    ".implode("
    \n
    ",$mf)."
    ".script("messagesPrint();");unset($_SESSION["messages"][$sj]);}if($l)echo"
    $l
    \n";if(adminer()->error)echo"
    ".adminer()->error."
    \n";}function +page_footer($pf=""){echo"
    \n\n\n\n",script("setupSubmitHighlight(document);");}function +int32($uf){while($uf>=2147483648)$uf-=4294967296;while($uf<=-2147483649)$uf+=4294967296;return(int)$uf;}function +long2str(array$W,$Ij){$_h='';foreach($W +as$X)$_h +.=pack('V',$X);if($Ij)return +substr($_h,0,end($W));return$_h;}function +str2long($_h,$Ij){$W=array_values(unpack('V*',str_pad($_h,4*ceil(strlen($_h)/4),"\0")));if($Ij)$W[]=strlen($_h);return$W;}function +xxtea_mx($Pj,$Oj,$qi,$_e){return +int32((($Pj>>5&0x7FFFFFF)^$Oj<<2)+(($Oj>>3&0x1FFFFFFF)^$Pj<<4))^int32(($qi^$Oj)+($_e^$Pj));}function +encrypt_string($li,$x){if($li=="")return"";$x=array_values(unpack("V*",pack("H*",md5($x))));$W=str2long($li,true);$uf=count($W)-1;$Pj=$W[$uf];$Oj=$W[0];$Zg=floor(6+52/($uf+1));$qi=0;while($Zg-->0){$qi=int32($qi+0x9E3779B9);$pc=$qi>>2&3;for($og=0;$og<$uf;$og++){$Oj=$W[$og+1];$tf=xxtea_mx($Pj,$Oj,$qi,$x[$og&3^$pc]);$Pj=int32($W[$og]+$tf);$W[$og]=$Pj;}$Oj=$W[0];$tf=xxtea_mx($Pj,$Oj,$qi,$x[$og&3^$pc]);$Pj=int32($W[$uf]+$tf);$W[$uf]=$Pj;}return +long2str($W,false);}function +decrypt_string($li,$x){if($li=="")return"";if(!$x)return +false;$x=array_values(unpack("V*",pack("H*",md5($x))));$W=str2long($li,false);$uf=count($W)-1;$Pj=$W[$uf];$Oj=$W[0];$Zg=floor(6+52/($uf+1));$qi=int32($Zg*0x9E3779B9);while($qi){$pc=$qi>>2&3;for($og=$uf;$og>0;$og--){$Pj=$W[$og-1];$tf=xxtea_mx($Pj,$Oj,$qi,$x[$og&3^$pc]);$Oj=int32($W[$og]-$tf);$W[$og]=$Oj;}$Pj=$W[$uf];$tf=xxtea_mx($Pj,$Oj,$qi,$x[$og&3^$pc]);$Oj=int32($W[0]-$tf);$W[0]=$Oj;$qi=int32($qi-0x9E3779B9);}return +long2str($W,true);}$Eg=array();if($_COOKIE["adminer_permanent"]){foreach(explode(" ",$_COOKIE["adminer_permanent"])as$X){list($x)=explode(":",$X);$Eg[$x]=$X;}}function +add_invalid_login(){$Fa=get_temp_dir()."/adminer.invalid";foreach(glob("$Fa*")?:array($Fa)as$o){$q=file_open_lock($o);if($q)break;}if(!$q)$q=file_open_lock("$Fa-".rand_string());if(!$q)return;$re=unserialize(stream_get_contents($q));$Ki=time();if($re){foreach($re +as$se=>$X){if($X[0]<$Ki)unset($re[$se]);}}$qe=&$re[adminer()->bruteForceKey()];if(!$qe)$qe=array($Ki+30*60,0);$qe[1]++;file_write_unlock($q,serialize($re));}function +check_invalid_login(array&$Eg){$re=array();foreach(glob(get_temp_dir()."/adminer.invalid*")as$o){$q=file_open_lock($o);if($q){$re=unserialize(stream_get_contents($q));file_unlock($q);break;}}$qe=idx($re,adminer()->bruteForceKey(),array());$Af=($qe[1]>29?$qe[0]-time():0);if($Af>0)auth_error(lang_format(array('Too many unsuccessful logins, try again in %d minute.','Too many unsuccessful logins, try again in %d minutes.'),ceil($Af/60)),$Eg);}$za=$_POST["auth"];if($za){session_regenerate_id();$Dj=$za["driver"];$N=$za["server"];$V=$za["username"];$F=(string)$za["password"];$j=$za["db"];set_password($Dj,$N,$V,$F);$_SESSION["db"][$Dj][$N][$V][$j]=true;if($za["permanent"]){$x=implode("-",array_map('base64_encode',array($Dj,$N,$V,$j)));$Tg=adminer()->permanentLogin(true);$Eg[$x]="$x:".base64_encode($Tg?encrypt_string($F,$Tg):"");cookie("adminer_permanent",implode(" ",$Eg));}if(count($_POST)==1||DRIVER!=$Dj||SERVER!=$N||$_GET["username"]!==$V||DB!=$j)redirect(auth_url($Dj,$N,$V,$j));}elseif($_POST["logout"]&&(!$_SESSION["token"]||verify_token())){foreach(array("pwds","db","dbs","queries")as$x)set_session($x,null);unset_permanent($Eg);redirect(substr(preg_replace('~\b(username|db|ns)=[^&]*&~','',ME),0,-1),'Logout successful.'.' '.'Thanks for using Adminer, consider donating.');}elseif($Eg&&!$_SESSION["pwds"]){session_regenerate_id();$Tg=adminer()->permanentLogin();foreach($Eg +as$x=>$X){list(,$cb)=explode(":",$X);list($Dj,$N,$V,$j)=array_map('base64_decode',explode("-",$x));set_password($Dj,$N,$V,decrypt_string(base64_decode($cb),$Tg));$_SESSION["db"][$Dj][$N][$V][$j]=true;}}function +unset_permanent(array&$Eg){foreach($Eg +as$x=>$X){list($Dj,$N,$V,$j)=array_map('base64_decode',explode("-",$x));if($Dj==DRIVER&&$N==SERVER&&$V==$_GET["username"]&&$j==DB)unset($Eg[$x]);}cookie("adminer_permanent",implode(" ",$Eg));}function +auth_error($l,array&$Eg){$Sh=session_name();if(isset($_GET["username"])){header("HTTP/1.1 403 Forbidden");if(($_COOKIE[$Sh]||$_GET[$Sh])&&!$_SESSION["token"])$l='Session expired, please login again.';else{restart_session();add_invalid_login();$F=get_password();if($F!==null){if($F===false)$l +.=($l?'
    ':'').sprintf('Master password expired. Implement %s method to make it permanent.',target_blank(),'permanentLogin()');set_password(DRIVER,SERVER,$_GET["username"],null);}unset_permanent($Eg);}}if(!$_COOKIE[$Sh]&&$_GET[$Sh]&&ini_bool("session.use_only_cookies"))$l='Session support must be enabled.';$rg=session_get_cookie_params();cookie("adminer_key",($_COOKIE["adminer_key"]?:rand_string()),$rg["lifetime"]);if(!$_SESSION["token"])$_SESSION["token"]=rand(1,1e6);page_header('Login',$l,null);echo"
    \n","
    ";if(hidden_fields($_POST,array("auth")))echo"

    ".'The action will be performed after successful login with the same credentials.'."\n";echo"

    \n";adminer()->loginForm();echo"
    \n";page_footer("auth");exit;}if(isset($_GET["username"])&&!class_exists('Adminer\Db')){unset($_SESSION["pwds"][DRIVER]);unset_permanent($Eg);page_header('No extension',sprintf('None of the supported PHP extensions (%s) are available.',implode(", ",Driver::$extensions)),false);page_footer("auth");exit;}$f='';if(isset($_GET["username"])&&is_string(get_password())){list($Md,$Ig)=explode(":",SERVER,2);if(preg_match('~^\s*([-+]?\d+)~',$Ig,$A)&&($A[1]<1024||$A[1]>65535))auth_error('Connecting to privileged ports is not allowed.',$Eg);check_invalid_login($Eg);$Fb=adminer()->credentials();$f=Driver::connect($Fb[0],$Fb[1],$Fb[2]);if(is_object($f)){Db::$instance=$f;Driver::$instance=new +Driver($f);if($f->flavor)save_settings(array("vendor-".DRIVER."-".SERVER=>get_driver(DRIVER)));}}$Se=null;if(!is_object($f)||($Se=adminer()->login($_GET["username"],get_password()))!==true){$l=(is_string($f)?nl_br(h($f)):(is_string($Se)?$Se:'Invalid credentials.')).(preg_match('~^ | $~',get_password())?'
    '.'There is a space in the input password which might be the cause.':'');auth_error($l,$Eg);}if($_POST["logout"]&&$_SESSION["token"]&&!verify_token()){page_header('Logout','Invalid CSRF token. Send the form again.');page_footer("db");exit;}if(!$_SESSION["token"])$_SESSION["token"]=rand(1,1e6);stop_session(true);if($za&&$_POST["token"])$_POST["token"]=get_token();$l='';if($_POST){if(!verify_token()){$je="max_input_vars";$ef=ini_get($je);if(extension_loaded("suhosin")){foreach(array("suhosin.request.max_vars","suhosin.post.max_vars")as$x){$X=ini_get($x);if($X&&(!$ef||$X<$ef)){$je=$x;$ef=$X;}}}$l=(!$_POST["token"]&&$ef?sprintf('Maximum number of allowed fields exceeded. Please increase %s.',"'$je'"):'Invalid CSRF token. Send the form again.'.' '.'If you did not send this request from Adminer then close this page.');}}elseif($_SERVER["REQUEST_METHOD"]=="POST"){$l=sprintf('Too big POST data. Reduce the data or increase the %s configuration directive.',"'post_max_size'");if(isset($_GET["sql"]))$l +.=' '.'You can upload a big SQL file via FTP and import it from server.';}function +print_select_result($I,$g=null,array$dg=array(),$z=0){$Qe=array();$w=array();$e=array();$Ka=array();$ij=array();$J=array();for($s=0;(!$z||$s<$z)&&($K=$I->fetch_row());$s++){if(!$s){echo"
    \n","\n","";for($ye=0;$yefetch_field();$B=$m->name;$cg=(isset($m->orgtable)?$m->orgtable:"");$bg=(isset($m->orgname)?$m->orgname:$B);if($dg&&JUSH=="sql")$Qe[$ye]=($B=="table"?"table=":($B=="possible_keys"?"indexes=":null));elseif($cg!=""){if(isset($m->table))$J[$m->table]=$cg;if(!isset($w[$cg])){$w[$cg]=array();foreach(indexes($cg,$g)as$v){if($v["type"]=="PRIMARY"){$w[$cg]=array_flip($v["columns"]);break;}}$e[$cg]=$w[$cg];}if(isset($e[$cg][$bg])){unset($e[$cg][$bg]);$w[$cg][$bg]=$ye;$Qe[$ye]=$cg;}}if($m->charsetnr==63)$Ka[$ye]=true;$ij[$ye]=$m->type;echo"name!=$bg?" title='".h(($cg!=""?"$cg.":"").$bg)."'":"").">".h($B).($dg?doc_link(array('sql'=>"explain-output.html#explain_".strtolower($B),'mariadb'=>"explain/#the-columns-in-explain-select",)):"");}echo"\n";}echo"";foreach($K +as$x=>$X){$_="";if(isset($Qe[$x])&&!$e[$Qe[$x]]){if($dg&&JUSH=="sql"){$R=$K[array_search("table=",$Qe)];$_=ME.$Qe[$x].urlencode($dg[$R]!=""?$dg[$R]:$R);}else{$_=ME."edit=".urlencode($Qe[$x]);foreach($w[$Qe[$x]]as$hb=>$ye)$_ +.="&where".urlencode("[".bracket_escape($hb)."]")."=".urlencode($K[$ye]);}}elseif(is_url($X))$_=$X;if($X===null)$X="NULL";elseif($Ka[$x]&&!is_utf8($X))$X="".lang_format(array('%d byte','%d bytes'),strlen($X))."";else{$X=h($X);if($ij[$x]==254)$X="$X";}if($_)$X="$X";echo"$X";}}echo($s?"
    \n
    ":"

    ".'No rows.')."\n";return$J;}function +referencable_primary($Kh){$J=array();foreach(table_status('',true)as$vi=>$R){if($vi!=$Kh&&fk_support($R)){foreach(fields($vi)as$m){if($m["primary"]){if($J[$vi]){unset($J[$vi]);break;}$J[$vi]=$m;}}}}return$J;}function +textarea($B,$Y,$L=10,$kb=80){echo"";}function +select_input($ya,array$Xf,$Y="",$Rf="",$Fg=""){$Ci=($Xf?"select":"input");return"<$Ci$ya".($Xf?">

    ".sprintf('%s version: %s through PHP extension %s',get_driver(DRIVER),"".h(connection()->server_info)."","".connection()->extension."")."\n","

    ".sprintf('Logged as: %s',"".h(logged_user())."")."\n";$i=adminer()->databases();if($i){$Dh=support("scheme");$jb=collations();echo"

    \n","\n",script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"),"".(support("database")?"\n";$i=($_GET["dbsize"]?count_tables($i):array_flip($i));foreach($i +as$j=>$T){$vh=h(ME)."db=".urlencode($j);$t=h("Db-".$j);echo"".(support("database")?"
    ":"")."".'Database'.(get_session("dbs")!==null?" - ".'Refresh'."":"")."".'Collation'."".'Tables'."".'Size'." - ".'Compute'."".script("qsl('a').onclick = partial(ajaxSetHtml, '".js_escape(ME)."script=connect');","")."
    ".checkbox("db[]",$j,in_array($j,(array)$_POST["db"]),"","","",$t):""),"".h($j)."";$c=h(db_collation($j,$jb));echo"".(support("database")?"$c":$c),"".($_GET["dbsize"]?$T:"?")."","".($_GET["dbsize"]?db_size($j):"?"),"\n";}echo"
    \n",(support("database")?"\n":""),input_token(),"
    \n",script("tableCheck();");}if(!empty(adminer()->plugins)){echo"
    \n","

    ".'Loaded plugins'."

    \n
      \n";foreach(adminer()->plugins +as$Gg){$Zb=(method_exists($Gg,'description')?$Gg->description():"");if(!$Zb){$kh=new +\ReflectionObject($Gg);if(preg_match('~^/[\s*]+(.+)~',$kh->getDocComment(),$A))$Zb=$A[1];}$Eh=(method_exists($Gg,'screenshot')?$Gg->screenshot():"");echo"
    • ".get_class($Gg)."".h($Zb?": $Zb":"").($Eh?" (".'screenshot'.")":"")."\n";}echo"
    \n";adminer()->pluginsLinks();echo"
    \n";}}page_footer("db");exit;}if(support("scheme")){if(DB!=""&&$_GET["ns"]!==""){if(!isset($_GET["ns"]))redirect(preg_replace('~ns=[^&]*&~','',ME)."ns=".get_schema());if(!set_schema($_GET["ns"])){header("HTTP/1.1 404 Not Found");page_header('Schema'.": ".h($_GET["ns"]),'Invalid schema.',true);page_footer("ns");exit;}}}class +TmpFile{private$handler;var$size;function +__construct(){$this->handler=tmpfile();}function +write($zb){$this->size+=strlen($zb);fwrite($this->handler,$zb);}function +send(){fseek($this->handler,0);fpassthru($this->handler);fclose($this->handler);}}if(isset($_GET["select"])&&($_POST["edit"]||$_POST["clone"])&&!$_POST["save"])$_GET["edit"]=$_GET["select"];if(isset($_GET["callf"]))$_GET["call"]=$_GET["callf"];if(isset($_GET["function"]))$_GET["procedure"]=$_GET["function"];if(isset($_GET["download"])){$a=$_GET["download"];$n=fields($a);header("Content-Type: application/octet-stream");header("Content-Disposition: attachment; filename=".friendly_url("$a-".implode("_",$_GET["where"])).".".friendly_url($_GET["field"]));$M=array(idf_escape($_GET["field"]));$I=driver()->select($a,$M,array(where($_GET,$n)),$M);$K=($I?$I->fetch_row():array());echo +driver()->value($K[0],$n[$_GET["field"]]);exit;}elseif(isset($_GET["table"])){$a=$_GET["table"];$n=fields($a);if(!$n)$l=error()?:'No tables.';$S=table_status1($a);$B=adminer()->tableName($S);page_header(($n&&is_view($S)?$S['Engine']=='materialized view'?'Materialized view':'View':'Table').": ".($B!=""?$B:h($a)),$l);$uh=array();foreach($n +as$x=>$m)$uh+=$m["privileges"];adminer()->selectLinks($S,(isset($uh["insert"])||!support("table")?"":null));$ob=$S["Comment"];if($ob!="")echo"

    ".'Comment'.": ".h($ob)."\n";function +tables_links($T){echo"

    \n";}$ie=driver()->inheritsFrom($a);if($ie){echo"

    ".'Inherits from'."

    \n";tables_links($ie);}elseif($n)adminer()->tableStructurePrint($n,$S);if(support("indexes")&&driver()->supportsIndex($S)){echo"

    ".'Indexes'."

    \n";$w=indexes($a);if($w)adminer()->tableIndexesPrint($w,$S);echo'

    ".'Foreign keys'."

    \n";$ld=foreign_keys($a);if($ld){echo"\n","\n";foreach($ld +as$B=>$p){echo"","
    ".'Source'."".'Target'."".'ON DELETE'."".'ON UPDATE'."
    ".implode(", ",array_map('Adminer\h',$p["source"]))."";$_=($p["db"]!=""?preg_replace('~db=[^&]*~',"db=".urlencode($p["db"]),ME):($p["ns"]!=""?preg_replace('~ns=[^&]*~',"ns=".urlencode($p["ns"]),ME):ME));echo"".($p["db"]!=""&&$p["db"]!=DB?"".h($p["db"]).".":"").($p["ns"]!=""&&$p["ns"]!=$_GET["ns"]?"".h($p["ns"]).".":"").h($p["table"])."","(".implode(", ",array_map('Adminer\h',$p["target"])).")","".h($p["on_delete"]),"".h($p["on_update"]),''.'Alter'.'',"\n";}echo"
    \n";}echo'

    ".'Checks'."

    \n";$Xa=driver()->checkConstraints($a);if($Xa){echo"\n";foreach($Xa +as$x=>$X)echo"","
    ".h($X),"".'Alter'."","\n";echo"
    \n";}echo'

    ".'Triggers'."

    \n";$fj=triggers($a);if($fj){echo"\n";foreach($fj +as$x=>$X)echo"
    ".h($X[0])."".h($X[1])."".h($x)."".'Alter'."\n";echo"
    \n";}echo'

    ".'Partitions'."

    \n";$vg=driver()->partitionsInfo($a);if($vg)echo"

    BY ".h("$vg[partition_by]($vg[partition])")."\n";tables_links($he);}}elseif(isset($_GET["schema"])){page_header('Database schema',"",array(),h(DB.($_GET["ns"]?".$_GET[ns]":"")));$xi=array();$yi=array();$ca=($_GET["schema"]?:$_COOKIE["adminer_schema-".str_replace(".","_",DB)]);preg_match_all('~([^:]+):([-0-9.]+)x([-0-9.]+)(_|$)~',$ca,$Ye,PREG_SET_ORDER);foreach($Ye +as$s=>$A){$xi[$A[1]]=array($A[2],$A[3]);$yi[]="\n\t'".js_escape($A[1])."': [ $A[2], $A[3] ]";}$Ui=0;$Ga=-1;$Bh=array();$jh=array();$Le=array();$sa=driver()->allFields();foreach(table_status('',true)as$R=>$S){if(is_view($S))continue;$Jg=0;$Bh[$R]["fields"]=array();foreach($sa[$R]as$m){$Jg+=1.25;$m["pos"]=$Jg;$Bh[$R]["fields"][$m["field"]]=$m;}$Bh[$R]["pos"]=($xi[$R]?:array($Ui,0));foreach(adminer()->foreignKeys($R)as$X){if(!$X["db"]){$Je=$Ga;if(idx($xi[$R],1)||idx($xi[$X["table"]],1))$Je=min(idx($xi[$R],1,0),idx($xi[$X["table"]],1,0))-1;else$Ga-=.1;while($Le[(string)$Je])$Je-=.0001;$Bh[$R]["references"][$X["table"]][(string)$Je]=array($X["source"],$X["target"]);$jh[$X["table"]][$R][(string)$Je]=$X["target"];$Le[(string)$Je]=true;}}$Ui=max($Ui,$Bh[$R]["pos"][0]+2.5+$Jg);}echo'

    + +qs(\'#schema\').onselectstart = () => false; +const tablePos = {',implode(",",$yi)."\n",'}; +const em = qs(\'#schema\').offsetHeight / ',$Ui,'; +document.onmousemove = schemaMousemove; +document.onmouseup = partialArg(schemaMouseup, \'',js_escape(DB),'\'); + +';foreach($Bh +as$B=>$R){echo"
    ",''.h($B)."",script("qsl('div').onmousedown = schemaMousedown;");foreach($R["fields"]as$m){$X=''.h($m["field"]).'';echo"
    ".($m["primary"]?"$X":$X);}foreach((array)$R["references"]as$Ei=>$lh){foreach($lh +as$Je=>$gh){$Ke=$Je-idx($xi[$B],1);$s=0;foreach($gh[0]as$ai)echo"\n
    "."
    ";}}foreach((array)$jh[$B]as$Ei=>$lh){foreach($lh +as$Je=>$e){$Ke=$Je-idx($xi[$B],1);$s=0;foreach($e +as$Di)echo"\n
    "."
    "."
    ";}}echo"\n
    \n";}foreach($Bh +as$B=>$R){foreach((array)$R["references"]as$Ei=>$lh){foreach($lh +as$Je=>$gh){$of=$Ui;$cf=-10;foreach($gh[0]as$x=>$ai){$Kg=$R["pos"][0]+$R["fields"][$ai]["pos"];$Lg=$Bh[$Ei]["pos"][0]+$Bh[$Ei]["fields"][$gh[1][$x]]["pos"];$of=min($of,$Kg,$Lg);$cf=max($cf,$Kg,$Lg);}echo"
    \n";}}}echo'
    +
    + +';$Pb=array('','USE','DROP+CREATE','CREATE');$zi=array('','DROP+CREATE','CREATE');$Mb=array('','TRUNCATE+INSERT','INSERT');if(JUSH=="sql")$Mb[]='INSERT+UPDATE';$K=get_settings("adminer_export");if(!$K)$K=array("output"=>"text","format"=>"sql","db_style"=>(DB!=""?"":"CREATE"),"table_style"=>"DROP+CREATE","data_style"=>"INSERT");if(!isset($K["events"])){$K["routines"]=$K["events"]=($_GET["dump"]=="");$K["triggers"]=$K["table_style"];}echo"
    ".'Output'."".html_radios("output",adminer()->dumpOutput(),$K["output"])."\n","
    ".'Format'."".html_radios("format",adminer()->dumpFormat(),$K["format"])."\n",(JUSH=="sqlite"?"":"
    ".'Database'."".html_select('db_style',$Pb,$K["db_style"]).(support("type")?checkbox("types",1,$K["types"],'User types'):"").(support("routine")?checkbox("routines",1,$K["routines"],'Routines'):"").(support("event")?checkbox("events",1,$K["events"],'Events'):"")),"
    ".'Tables'."".html_select('table_style',$zi,$K["table_style"]).checkbox("auto_increment",1,$K["auto_increment"],'Auto Increment').(support("trigger")?checkbox("triggers",1,$K["triggers"],'Triggers'):""),"
    ".'Data'."".html_select('data_style',$Mb,$K["data_style"]),'
    +

    +',input_token(),' + +',script("qsl('table').onclick = dumpClick;");$Pg=array();if(DB!=""){$Za=($a!=""?"":" checked");echo"","\n";$Gj="";$Ai=tables_list();foreach($Ai +as$B=>$U){$Og=preg_replace('~_.*~','',$B);$Za=($a==""||$a==(substr($a,-1)=="%"?"$Og%":$B));$Sg="\n";$i=adminer()->databases();if($i){foreach($i +as$j){if(!information_schema($j)){$Og=preg_replace('~_.*~','',$j);echo"
    ".script("qs('#check-tables').onclick = partial(formCheck, /^tables\\[/);",""),"".script("qs('#check-data').onclick = partial(formCheck, /^data\\[/);",""),"
    ".checkbox("tables[]",$B,$Za,$B,"","block");if($U!==null&&!preg_match('~table~i',$U))$Gj +.="$Sg\n";else +echo"$Sg\n";$Pg[$Og]++;}echo$Gj;if($Ai)echo +script("ajaxSetHtml('".js_escape(ME)."script=db');");}else{echo"
    ","",script("qs('#check-databases').onclick = partial(formCheck, /^databases\\[/);",""),"
    ".checkbox("databases[]",$j,$a==""||$a=="$Og%",$j,"","block")."\n";$Pg[$Og]++;}}}else +echo"
    ";}echo'
    +

    +';$bd=true;foreach($Pg +as$x=>$X){if($x!=""&&$X>1){echo($bd?"

    ":" ")."".h($x)."";$bd=false;}}}elseif(isset($_GET["privileges"])){page_header('Privileges');echo'

    \n";hidden_fields_get();echo +input_hidden("db",DB),($ud?"":input_hidden("grant")),"\n","\n";while($K=$I->fetch_assoc())echo'
    ".'Username'."".'Server'."
    '.h($K["User"])."".h($K["Host"]).''.'Edit'."\n";if(!$ud||DB!="")echo"
    \n";echo"
    \n","

    \n";}elseif(isset($_GET["sql"])){if(!$l&&$_POST["export"]){save_settings(array("output"=>$_POST["output"],"format"=>$_POST["format"]),"adminer_import");dump_headers("sql");adminer()->dumpTable("","");adminer()->dumpData("","table",$_POST["query"]);adminer()->dumpFooter();exit;}restart_session();$Kd=&get_session("queries");$Jd=&$Kd[DB];if(!$l&&$_POST["clear"]){$Jd=array();redirect(remove_from_uri("history"));}stop_session();page_header((isset($_GET["import"])?'Import':'SQL command'),$l);$Pe='--'.(JUSH=='sql'?' ':'');if(!$l&&$_POST){$q=false;if(!isset($_GET["import"]))$H=$_POST["query"];elseif($_POST["webfile"]){$ei=adminer()->importServerPath();$q=@fopen((file_exists($ei)?$ei:"compress.zlib://$ei.gz"),"rb");$H=($q?fread($q,1e6):false);}else$H=get_file("sql_file",true,";");if(is_string($H)){if(function_exists('memory_get_usage')&&($hf=ini_bytes("memory_limit"))!="-1")@ini_set("memory_limit",max($hf,strval(2*strlen($H)+memory_get_usage()+8e6)));if($H!=""&&strlen($H)<1e6){$Zg=$H.(preg_match("~;[ \t\r\n]*\$~",$H)?"":";");if(!$Jd||first(end($Jd))!=$Zg){restart_session();$Jd[]=array($Zg,time());set_session("queries",$Kd);stop_session();}}$bi="(?:\\s|/\\*[\s\S]*?\\*/|(?:#|$Pe)[^\n]*\n?|--\r?\n)";$Xb=";";$C=0;$xc=true;$g=connect();if($g&&DB!=""){$g->select_db(DB);if($_GET["ns"]!="")set_schema($_GET["ns"],$g);}$nb=0;$Ec=array();$sg='[\'"'.(JUSH=="sql"?'`#':(JUSH=="sqlite"?'`[':(JUSH=="mssql"?'[':''))).']|/\*|'.$Pe.'|$'.(JUSH=="pgsql"?'|\$[^$]*\$':'');$Vi=microtime(true);$ma=get_settings("adminer_import");$oc=adminer()->dumpFormat();unset($oc["sql"]);while($H!=""){if(!$C&&preg_match("~^$bi*+DELIMITER\\s+(\\S+)~i",$H,$A)){$Xb=preg_quote($A[1]);$H=substr($H,strlen($A[0]));}elseif(!$C&&JUSH=='pgsql'&&preg_match("~^($bi*+COPY\\s+)[^;]+\\s+FROM\\s+stdin;~i",$H,$A)){$Xb="\n\\\\\\.\r?\n";$C=strlen($A[0]);}else{preg_match("($Xb\\s*|$sg)",$H,$A,PREG_OFFSET_CAPTURE,$C);list($nd,$Jg)=$A[0];if(!$nd&&$q&&!feof($q))$H +.=fread($q,1e5);else{if(!$nd&&rtrim($H)=="")break;$C=$Jg+strlen($nd);if($nd&&!preg_match("(^$Xb)",$nd)){$Ra=driver()->hasCStyleEscapes()||(JUSH=="pgsql"&&($Jg>0&&strtolower($H[$Jg-1])=="e"));$Cg=($nd=='/*'?'\*/':($nd=='['?']':(preg_match("~^$Pe|^#~",$nd)?"\n":preg_quote($nd).($Ra?'|\\\\.':''))));while(preg_match("($Cg|\$)s",$H,$A,PREG_OFFSET_CAPTURE,$C)){$_h=$A[0][0];if(!$_h&&$q&&!feof($q))$H +.=fread($q,1e5);else{$C=$A[0][1]+strlen($_h);if(!$_h||$_h[0]!="\\")break;}}}else{$xc=false;$Zg=substr($H,0,$Jg+($Xb[0]=="\n"?3:0));$nb++;$Sg="
    ".adminer()->sqlCommandQuery($Zg)."
    \n";if(JUSH=="sqlite"&&preg_match("~^$bi*+ATTACH\\b~i",$Zg,$A)){echo$Sg,"

    ".'ATTACH queries are not supported.'."\n";$Ec[]=" $nb";if($_POST["error_stops"])break;}else{if(!$_POST["only_errors"]){echo$Sg;ob_flush();flush();}$ji=microtime(true);if(connection()->multi_query($Zg)&&$g&&preg_match("~^$bi*+USE\\b~i",$Zg))$g->query($Zg);do{$I=connection()->store_result();if(connection()->error){echo($_POST["only_errors"]?$Sg:""),"

    ".'Error in query'.(connection()->errno?" (".connection()->errno.")":"").": ".error()."\n";$Ec[]=" $nb";if($_POST["error_stops"])break +2;}else{$Ki=" (".format_time($ji).")".(strlen($Zg)<1000?" ".'Edit'."":"");$oa=connection()->affected_rows;$Jj=($_POST["only_errors"]?"":driver()->warnings());$Kj="warnings-$nb";if($Jj)$Ki +.=", ".'Warnings'."".script("qsl('a').onclick = partial(toggle, '$Kj');","");$Mc=null;$dg=null;$Nc="explain-$nb";if(is_object($I)){$z=$_POST["limit"];$dg=print_select_result($I,$g,array(),$z);if(!$_POST["only_errors"]){echo"

    \n";$Ef=$I->num_rows;echo"
    \n";}}else{if(preg_match("~^$bi*+(CREATE|DROP|ALTER)$bi++(DATABASE|SCHEMA)\\b~i",$Zg)){restart_session();set_session("dbs",null);stop_session();}if(!$_POST["only_errors"])echo"

    ".lang_format(array('Query executed OK, %d row affected.','Query executed OK, %d rows affected.'),$oa)."$Ki\n";}echo($Jj?"

    \n":"");if($Mc){echo"\n";}}$ji=microtime(true);}while(connection()->next_result());}$H=substr($H,$C);$C=0;}}}}if($xc)echo"

    ".'No commands to execute.'."\n";elseif($_POST["only_errors"])echo"

    ".lang_format(array('%d query executed OK.','%d queries executed OK.'),$nb-count($Ec))," (".format_time($Vi).")\n";elseif($Ec&&$nb>1)echo"

    ".'Error in query'.": ".implode("",$Ec)."\n";}else +echo"

    ".upload_error($H)."\n";}echo' +

    +';$Kc="";if(!isset($_GET["import"])){$Zg=$_GET["sql"];if($_POST)$Zg=$_POST["query"];elseif($_GET["history"]=="all")$Zg=$Jd;elseif($_GET["history"]!="")$Zg=idx($Jd[$_GET["history"]],0);echo"

    ";textarea("query",$Zg,20);echo +script(($_POST?"":"qs('textarea').focus();\n")."qs('#form').onsubmit = partial(sqlSubmit, qs('#form'), '".js_escape(remove_from_uri("sql|limit|error_stops|only_errors|history"))."');"),"

    ";adminer()->sqlPrintAfter();echo"$Kc\n",'Limit rows'.": \n";}else{echo"

    ".'File upload'."
    ";$_d=(extension_loaded("zlib")?"[.gz]":"");echo(ini_bool("file_uploads")?"SQL$_d (< ".ini_get("upload_max_filesize")."B): \n$Kc":'File uploads are disabled.'),"
    \n";$Vd=adminer()->importServerPath();if($Vd)echo"
    ".'From server'."
    ",sprintf('Webserver file %s',"".h($Vd)."$_d"),' ',"
    \n";echo"

    ";}echo +checkbox("error_stops",1,($_POST?$_POST["error_stops"]:isset($_GET["import"])||$_GET["error_stops"]),'Stop on error')."\n",checkbox("only_errors",1,($_POST?$_POST["only_errors"]:isset($_GET["import"])||$_GET["only_errors"]),'Show only errors')."\n",input_token();if(!isset($_GET["import"])&&$Jd){print_fieldset("history",'History',$_GET["history"]!="");for($X=end($Jd);$X;$X=prev($Jd)){$x=key($Jd);list($Zg,$Ki,$sc)=$X;echo''.'Edit'.""." ".@date("H:i:s",$Ki).""." ".shorten_utf8(ltrim(str_replace("\n"," ",str_replace("\r","",preg_replace("~^(#|$Pe).*~m",'',$Zg)))),80,"").($sc?" ($sc)":"")."
    \n";}echo"\n","".'Edit all'."\n","\n";}echo'

    +';}elseif(isset($_GET["edit"])){$a=$_GET["edit"];$n=fields($a);$Z=(isset($_GET["select"])?($_POST["check"]&&count($_POST["check"])==1?where_check($_POST["check"][0],$n):""):where($_GET,$n));$rj=(isset($_GET["select"])?$_POST["edit"]:$Z);foreach($n +as$B=>$m){if(!isset($m["privileges"][$rj?"update":"insert"])||adminer()->fieldName($m)==""||$m["generated"])unset($n[$B]);}if($_POST&&!$l&&!isset($_GET["select"])){$Re=$_POST["referer"];if($_POST["insert"])$Re=($rj?null:$_SERVER["REQUEST_URI"]);elseif(!preg_match('~^.+&select=.+$~',$Re))$Re=ME."select=".urlencode($a);$w=indexes($a);$mj=unique_array($_GET["where"],$w);$ch="\nWHERE $Z";if(isset($_POST["delete"]))queries_redirect($Re,'Item has been deleted.',driver()->delete($a,$ch,$mj?0:1));else{$O=array();foreach($n +as$B=>$m){$X=process_input($m);if($X!==false&&$X!==null)$O[idf_escape($B)]=$X;}if($rj){if(!$O)redirect($Re);queries_redirect($Re,'Item has been updated.',driver()->update($a,$O,$ch,$mj?0:1));if(is_ajax()){page_headers();page_messages($l);exit;}}else{$I=driver()->insert($a,$O);$Ie=($I?last_id($I):0);queries_redirect($Re,sprintf('Item%s has been inserted.',($Ie?" $Ie":"")),$I);}}}$K=null;if($_POST["save"])$K=(array)$_POST["fields"];elseif($Z){$M=array();foreach($n +as$B=>$m){if(isset($m["privileges"]["select"])){$wa=($_POST["clone"]&&$m["auto_increment"]?"''":convert_field($m));$M[]=($wa?"$wa AS ":"").idf_escape($B);}}$K=array();if(!support("table"))$M=array("*");if($M){$I=driver()->select($a,$M,array($Z),$M,array(),(isset($_GET["select"])?2:1));if(!$I)$l=error();else{$K=$I->fetch_assoc();if(!$K)$K=false;}if(isset($_GET["select"])&&(!$K||$I->fetch_assoc()))$K=null;}}if(!support("table")&&!$n){if(!$Z){$I=driver()->select($a,array("*"),array(),array("*"));$K=($I?$I->fetch_assoc():false);if(!$K)$K=array(driver()->primary=>"");}if($K){foreach($K +as$x=>$X){if(!$Z)$K[$x]=null;$n[$x]=array("field"=>$x,"null"=>($x!=driver()->primary),"auto_increment"=>($x==driver()->primary));}}}edit_form($a,$n,$K,$rj,$l);}elseif(isset($_GET["create"])){$a=$_GET["create"];$xg=driver()->partitionBy;$_g=driver()->partitionsInfo($a);$ih=referencable_primary($a);$ld=array();foreach($ih +as$vi=>$m)$ld[str_replace("`","``",$vi)."`".str_replace("`","``",$m["field"])]=$vi;$gg=array();$S=array();if($a!=""){$gg=fields($a);$S=table_status1($a);if(count($S)<2)$l='No tables.';}$K=$_POST;$K["fields"]=(array)$K["fields"];if($K["auto_increment_col"])$K["fields"][$K["auto_increment_col"]]["auto_increment"]=true;if($_POST)save_settings(array("comments"=>$_POST["comments"],"defaults"=>$_POST["defaults"]));if($_POST&&!process_fields($K["fields"])&&!$l){if($_POST["drop"])queries_redirect(substr(ME,0,-1),'Table has been dropped.',drop_tables(array($a)));else{$n=array();$sa=array();$vj=false;$jd=array();$fg=reset($gg);$qa=" FIRST";foreach($K["fields"]as$x=>$m){$p=$ld[$m["type"]];$gj=($p!==null?$ih[$p]:$m);if($m["field"]!=""){if(!$m["generated"])$m["default"]=null;$Xg=process_field($m,$gj);$sa[]=array($m["orig"],$Xg,$qa);if(!$fg||$Xg!==process_field($fg,$fg)){$n[]=array($m["orig"],$Xg,$qa);if($m["orig"]!=""||$qa)$vj=true;}if($p!==null)$jd[idf_escape($m["field"])]=($a!=""&&JUSH!="sqlite"?"ADD":" ").format_foreign_key(array('table'=>$ld[$m["type"]],'source'=>array($m["field"]),'target'=>array($gj["field"]),'on_delete'=>$m["on_delete"],));$qa=" AFTER ".idf_escape($m["field"]);}elseif($m["orig"]!=""){$vj=true;$n[]=array($m["orig"]);}if($m["orig"]!=""){$fg=next($gg);if(!$fg)$qa="";}}$E=array();if(in_array($K["partition_by"],$xg)){foreach($K +as$x=>$X){if(preg_match('~^partition~',$x))$E[$x]=$X;}foreach($E["partition_names"]as$x=>$B){if($B==""){unset($E["partition_names"][$x]);unset($E["partition_values"][$x]);}}$E["partition_names"]=array_values($E["partition_names"]);$E["partition_values"]=array_values($E["partition_values"]);if($E==$_g)$E=array();}elseif(preg_match("~partitioned~",$S["Create_options"]))$E=null;$if='Table has been altered.';if($a==""){cookie("adminer_engine",$K["Engine"]);$if='Table has been created.';}$B=trim($K["name"]);queries_redirect(ME.(support("table")?"table=":"select=").urlencode($B),$if,alter_table($a,$B,(JUSH=="sqlite"&&($vj||$jd)?$sa:$n),$jd,($K["Comment"]!=$S["Comment"]?$K["Comment"]:null),($K["Engine"]&&$K["Engine"]!=$S["Engine"]?$K["Engine"]:""),($K["Collation"]&&$K["Collation"]!=$S["Collation"]?$K["Collation"]:""),($K["Auto_increment"]!=""?number($K["Auto_increment"]):""),$E));}}page_header(($a!=""?'Alter table':'Create table'),$l,array("table"=>$a),h($a));if(!$_POST){$ij=driver()->types();$K=array("Engine"=>$_COOKIE["adminer_engine"],"fields"=>array(array("field"=>"","type"=>(isset($ij["int"])?"int":(isset($ij["integer"])?"integer":"")),"on_update"=>"")),"partition_names"=>array(""),);if($a!=""){$K=$S;$K["name"]=$a;$K["fields"]=array();if(!$_GET["auto_increment"])$K["Auto_increment"]="";foreach($gg +as$m){$m["generated"]=$m["generated"]?:(isset($m["default"])?"DEFAULT":"");$K["fields"][]=$m;}if($xg){$K+=$_g;$K["partition_names"][]="";$K["partition_values"][]="";}}}$jb=collations();if(is_array(reset($jb)))$jb=call_user_func_array('array_merge',array_values($jb));$zc=driver()->engines();foreach($zc +as$yc){if(!strcasecmp($yc,$K["Engine"])){$K["Engine"]=$yc;break;}}echo' +
    +

    +';if(support("columns")||$a==""){echo'Table name'.": \n",($zc?html_select("Engine",array(""=>"(".'engine'.")")+$zc,$K["Engine"]).on_help("event.target.value",1).script("qsl('select').onchange = helpClose;")."\n":"");if($jb)echo"".optionlist($jb)."\n",(preg_match("~sqlite|mssql~",JUSH)?"":"\n");echo"\n";}if(support("columns")){echo"

    \n","\n";edit_fields($K["fields"],$jb,"TABLE",$ld);echo"
    \n",script("editFields();"),"
    \n

    \n",'Auto Increment'.": \n",checkbox("defaults",1,($_POST?$_POST["defaults"]:get_setting("defaults")),'Default values',"columnShow(this.checked, 5)","jsonly");$qb=($_POST?$_POST["comments"]:get_setting("comments"));echo(support("comment")?checkbox("comments",1,$qb,'Comment',"editingCommentsClick(this, true);","jsonly").' '.(preg_match('~\n~',$K["Comment"])?"":''):''),'

    + +';}echo' +';if($a!="")echo'',confirm(sprintf('Drop %s?',$a));if($xg&&(JUSH=='sql'||$a=="")){$yg=preg_match('~RANGE|LIST~',$K["partition_by"]);print_fieldset("partition",'Partition by',$K["partition_by"]);echo"

    ".html_select("partition_by",array_merge(array(""),$xg),$K["partition_by"]).on_help("event.target.value.replace(/./, 'PARTITION BY \$&')",1).script("qsl('select').onchange = partitionByChange;"),"()\n",'Partitions'.": \n","\n","\n";foreach($K["partition_names"]as$x=>$X)echo'','\n\n";}echo +input_token(),'

    +';}elseif(isset($_GET["indexes"])){$a=$_GET["indexes"];$de=array("PRIMARY","UNIQUE","INDEX");$S=table_status1($a,true);$ae=driver()->indexAlgorithms($S);if(preg_match('~MyISAM|M?aria'.(min_version(5.6,'10.0.5')?'|InnoDB':'').'~i',$S["Engine"]))$de[]="FULLTEXT";if(preg_match('~MyISAM|M?aria'.(min_version(5.7,'10.2.2')?'|InnoDB':'').'~i',$S["Engine"]))$de[]="SPATIAL";$w=indexes($a);$G=array();if(JUSH=="mongo"){$G=$w["_id_"];unset($de[0]);unset($w["_id_"]);}$K=$_POST;if($K)save_settings(array("index_options"=>$K["options"]));if($_POST&&!$l&&!$_POST["add"]&&!$_POST["drop_col"]){$b=array();foreach($K["indexes"]as$v){$B=$v["name"];if(in_array($v["type"],$de)){$e=array();$Ne=array();$ac=array();$be=(support("partial_indexes")?$v["partial"]:"");$Zd=(in_array($v["algorithm"],$ae)?$v["algorithm"]:"");$O=array();ksort($v["columns"]);foreach($v["columns"]as$x=>$d){if($d!=""){$y=idx($v["lengths"],$x);$Yb=idx($v["descs"],$x);$O[]=idf_escape($d).($y?"(".(+$y).")":"").($Yb?" DESC":"");$e[]=$d;$Ne[]=($y?:null);$ac[]=$Yb;}}$Lc=$w[$B];if($Lc){ksort($Lc["columns"]);ksort($Lc["lengths"]);ksort($Lc["descs"]);if($v["type"]==$Lc["type"]&&array_values($Lc["columns"])===$e&&(!$Lc["lengths"]||array_values($Lc["lengths"])===$Ne)&&array_values($Lc["descs"])===$ac&&$Lc["partial"]==$be&&(!$ae||$Lc["algorithm"]==$Zd)){unset($w[$B]);continue;}}if($e)$b[]=array($v["type"],$B,$O,$Zd,$be);}}foreach($w +as$B=>$Lc)$b[]=array($Lc["type"],$B,"DROP");if(!$b)redirect(ME."table=".urlencode($a));queries_redirect(ME."table=".urlencode($a),'Indexes have been altered.',alter_indexes($a,$b));}page_header('Indexes',$l,array("table"=>$a),h($a));$n=array_keys(fields($a));if($_POST["add"]){foreach($K["indexes"]as$x=>$v){if($v["columns"][count($v["columns"])]!="")$K["indexes"][$x]["columns"][]="";}$v=end($K["indexes"]);if($v["type"]||array_filter($v["columns"],'strlen'))$K["indexes"][]=array("columns"=>array(1=>""));}if(!$K){foreach($w +as$x=>$v){$w[$x]["name"]=$x;$w[$x]["columns"][]="";}$w[]=array("columns"=>array(1=>""));$K["indexes"]=$w;}$Ne=(JUSH=="sql"||JUSH=="mssql");$Vh=($_POST?$_POST["options"]:get_setting("index_options"));echo' +
    +
    + + + +';if($G){echo"
    Index Type +';$Td=" class='idxopts".($Vh?"":" hidden")."'";if($ae)echo"".'Algorithm'.doc_link(array('sql'=>'create-index.html#create-index-storage-engine-index-types','mariadb'=>'storage-engine-index-types/','pgsql'=>'indexes-types.html',));echo'','Columns'.($Ne?" (".'length'.")":"");if($Ne||support("descidx"))echo +checkbox("options",1,$Vh,'Options',"indexOptionsShow(this.checked)","jsonly")."\n";echo'Name +';if(support("partial_indexes"))echo"".'Condition';echo' +
    PRIMARY";foreach($G["columns"]as$x=>$d)echo +select_input(" disabled",$n,$d)," ";echo"\n";}$ye=1;foreach($K["indexes"]as$v){if(!$_POST["drop_col"]||$ye!=key($_POST["drop_col"])){echo"
    ".html_select("indexes[$ye][type]",array(-1=>"")+$de,$v["type"],($ye==count($K["indexes"])?"indexesAddRow.call(this);":""),"label-type");if($ae)echo"".html_select("indexes[$ye][algorithm]",array_merge(array(""),$ae),$v['algorithm'],"label-algorithm");echo"";ksort($v["columns"]);$s=1;foreach($v["columns"]as$x=>$d){echo"".select_input(" name='indexes[$ye][columns][$s]' title='".'Column'."'",($n?array_combine($n,$n):$n),$d,"partial(".($s==count($v["columns"])?"indexesAddColumn":"indexesChangeColumn").", '".js_escape(JUSH=="sql"?"":$_GET["indexes"]."_")."')"),"",($Ne?"":""),(support("descidx")?checkbox("indexes[$ye][descs][$s]",1,idx($v["descs"],$x),'descending'):"")," ";$s++;}echo"\n";if(support("partial_indexes"))echo"\n";echo"".icon("cross","drop_col[$ye]","x",'Remove').script("qsl('button').onclick = partial(editingRemoveRow, 'indexes\$1[type]');");}$ye++;}echo'
    +
    +

    + +',input_token(),'

    +';}elseif(isset($_GET["database"])){$K=$_POST;if($_POST&&!$l&&!$_POST["add"]){$B=trim($K["name"]);if($_POST["drop"]){$_GET["db"]="";queries_redirect(remove_from_uri("db|database"),'Database has been dropped.',drop_databases(array(DB)));}elseif(DB!==$B){if(DB!=""){$_GET["db"]=$B;queries_redirect(preg_replace('~\bdb=[^&]*&~','',ME)."db=".urlencode($B),'Database has been renamed.',rename_database($B,$K["collation"]));}else{$i=explode("\n",str_replace("\r","",$B));$oi=true;$He="";foreach($i +as$j){if(count($i)==1||$j!=""){if(!create_database($j,$K["collation"]))$oi=false;$He=$j;}}restart_session();set_session("dbs",null);queries_redirect(ME."db=".urlencode($He),'Database has been created.',$oi);}}else{if(!$K["collation"])redirect(substr(ME,0,-1));query_redirect("ALTER DATABASE ".idf_escape($B).(preg_match('~^[a-z0-9_]+$~i',$K["collation"])?" COLLATE $K[collation]":""),substr(ME,0,-1),'Database has been altered.');}}page_header(DB!=""?'Alter database':'Create database',$l,array(),h(DB));$jb=collations();$B=DB;if($_POST)$B=$K["name"];elseif(DB!="")$K["collation"]=db_collation(DB,$jb);elseif(JUSH=="sql"){foreach(get_vals("SHOW GRANTS")as$ud){if(preg_match('~ ON (`(([^\\\\`]|``|\\\\.)*)%`\.\*)?~',$ud,$A)&&$A[1]){$B=stripcslashes(idf_unescape("`$A[2]`"));break;}}}echo' +
    +

    +',($_POST["add"]||strpos($B,"\n")?'
    ':'')."\n".($jb?html_select("collation",array(""=>"(".'collation'.")")+$jb,$K["collation"]).doc_link(array('sql'=>"charset-charsets.html",'mariadb'=>"supported-character-sets-and-collations/",'mssql'=>"relational-databases/system-functions/sys-fn-helpcollations-transact-sql",)):""),' +';if(DB!="")echo"".confirm(sprintf('Drop %s?',DB))."\n";elseif(!$_POST["add"]&&$_GET["db"]=="")echo +icon("plus","add[0]","+",'Add next')."\n";echo +input_token(),'

    +';}elseif(isset($_GET["scheme"])){$K=$_POST;if($_POST&&!$l){$_=preg_replace('~ns=[^&]*&~','',ME)."ns=";if($_POST["drop"])query_redirect("DROP SCHEMA ".idf_escape($_GET["ns"]),$_,'Schema has been dropped.');else{$B=trim($K["name"]);$_ +.=urlencode($B);if($_GET["ns"]=="")query_redirect("CREATE SCHEMA ".idf_escape($B),$_,'Schema has been created.');elseif($_GET["ns"]!=$B)query_redirect("ALTER SCHEMA ".idf_escape($_GET["ns"])." RENAME TO ".idf_escape($B),$_,'Schema has been altered.');else +redirect($_);}}page_header($_GET["ns"]!=""?'Alter schema':'Create schema',$l);if(!$K)$K["name"]=$_GET["ns"];echo' +
    +

    + +';if($_GET["ns"]!="")echo"".confirm(sprintf('Drop %s?',$_GET["ns"]))."\n";echo +input_token(),'

    +';}elseif(isset($_GET["call"])){$ba=($_GET["name"]?:$_GET["call"]);page_header('Call'.": ".h($ba),$l);$wh=routine($_GET["call"],(isset($_GET["callf"])?"FUNCTION":"PROCEDURE"));$Wd=array();$lg=array();foreach($wh["fields"]as$s=>$m){if(substr($m["inout"],-3)=="OUT"&&JUSH=='sql')$lg[$s]="@".idf_escape($m["field"])." AS ".idf_escape($m["field"]);if(!$m["inout"]||substr($m["inout"],0,2)=="IN")$Wd[]=$s;}if(!$l&&$_POST){$Sa=array();foreach($wh["fields"]as$x=>$m){$X="";if(in_array($x,$Wd)){$X=process_input($m);if($X===false)$X="''";if(isset($lg[$x]))connection()->query("SET @".idf_escape($m["field"])." = $X");}if(isset($lg[$x]))$Sa[]="@".idf_escape($m["field"]);elseif(in_array($x,$Wd))$Sa[]=$X;}$H=(isset($_GET["callf"])?"SELECT":"CALL")." ".table($ba)."(".implode(", ",$Sa).")";$ji=microtime(true);$I=connection()->multi_query($H);$oa=connection()->affected_rows;echo +adminer()->selectQuery($H,$ji,!$I);if(!$I)echo"

    ".error()."\n";else{$g=connect();if($g)$g->select_db(DB);do{$I=connection()->store_result();if(is_object($I))print_select_result($I,$g);else +echo"

    ".lang_format(array('Routine has been called, %d row affected.','Routine has been called, %d rows affected.'),$oa)." ".@date("H:i:s")."\n";}while(connection()->next_result());if($lg)print_select_result(connection()->query("SELECT ".implode(", ",$lg)));}}echo' +

    +';if($Wd){echo"\n";foreach($Wd +as$x){$m=$wh["fields"][$x];$B=$m["field"];echo"
    ".adminer()->fieldName($m);$Y=idx($_POST["fields"],$B);if($Y!=""){if($m["type"]=="set")$Y=implode(",",$Y);}input($m,$Y,idx($_POST["function"],$B,""));echo"\n";}echo"
    \n";}echo'

    + +',input_token(),'

    + +
    +';function
    +pre_tr($_h){return
    +preg_replace('~^~m','',preg_replace('~\|~','',preg_replace('~\|$~m',"",rtrim($_h))));}$R='(\+--[-+]+\+\n)';$K='(\| .* \|\n)';echo
    +preg_replace_callback("~^$R?$K$R?($K*)$R?~m",function($A){$cd=pre_tr($A[2]);return"\n".($A[1]?"$cd\n":$cd).pre_tr($A[4])."\n
    ";},preg_replace('~(\n( -|mysql)> )(.+)~',"\\1\\3",preg_replace('~(.+)\n---+\n~',"\\1\n",h($wh['comment']))));echo'
    +';}elseif(isset($_GET["foreign"])){$a=$_GET["foreign"];$B=$_GET["name"];$K=$_POST;if($_POST&&!$l&&!$_POST["add"]&&!$_POST["change"]&&!$_POST["change-js"]){if(!$_POST["drop"]){$K["source"]=array_filter($K["source"],'strlen');ksort($K["source"]);$Di=array();foreach($K["source"]as$x=>$X)$Di[$x]=$K["target"][$x];$K["target"]=$Di;}if(JUSH=="sqlite")$I=recreate_table($a,$a,array(),array(),array(" $B"=>($K["drop"]?"":" ".format_foreign_key($K))));else{$b="ALTER TABLE ".table($a);$I=($B==""||queries("$b DROP ".(JUSH=="sql"?"FOREIGN KEY ":"CONSTRAINT ").idf_escape($B)));if(!$K["drop"])$I=queries("$b ADD".format_foreign_key($K));}queries_redirect(ME."table=".urlencode($a),($K["drop"]?'Foreign key has been dropped.':($B!=""?'Foreign key has been altered.':'Foreign key has been created.')),$I);if(!$K["drop"])$l='Source and target columns must have the same data type, there must be an index on the target columns and referenced data must exist.';}page_header('Foreign key',$l,array("table"=>$a),h($a));if($_POST){ksort($K["source"]);if($_POST["add"])$K["source"][]="";elseif($_POST["change"]||$_POST["change-js"])$K["target"]=array();}elseif($B!=""){$ld=foreign_keys($a);$K=$ld[$B];$K["source"][]="";}else{$K["table"]=$a;$K["source"]=array("");}echo' +
    +';$ai=array_keys(fields($a));if($K["db"]!="")connection()->select_db($K["db"]);if($K["ns"]!=""){$hg=get_schema();set_schema($K["ns"]);}$hh=array_keys(array_filter(table_status('',true),'Adminer\fk_support'));$Di=array_keys(fields(in_array($K["table"],$hh)?$K["table"]:reset($hh)));$Rf="this.form['change-js'].value = '1'; this.form.submit();";echo"

    \n";if(support("scheme")){$Ch=array_filter(adminer()->schemas(),function($Bh){return!preg_match('~^information_schema$~i',$Bh);});echo"";if($K["ns"]!="")set_schema($hg);}elseif(JUSH!="sqlite"){$Qb=array();foreach(adminer()->databases()as$j){if(!information_schema($j))$Qb[]=$j;}echo"";}echo +input_hidden("change-js"),'

    + + +';$ye=0;foreach($K["source"]as$x=>$X){echo"","
    SourceTarget
    ".html_select("source[".(+$x)."]",array(-1=>"")+$ai,$X,($ye==count($K["source"])-1?"foreignAddRow.call(this);":""),"label-source"),"".html_select("target[".(+$x)."]",$Di,idx($K["target"],$x),"","label-target");$ye++;}echo'
    +

    + + +',doc_link(array('sql'=>"innodb-foreign-key-constraints.html",'mariadb'=>"foreign-keys/",'pgsql'=>"sql-createtable.html#SQL-CREATETABLE-REFERENCES",'mssql'=>"t-sql/statements/create-table-transact-sql",'oracle'=>"SQLRF01111",)),'

    + +

    +';if($B!="")echo'',confirm(sprintf('Drop %s?',$B));echo +input_token(),'

    +';}elseif(isset($_GET["view"])){$a=$_GET["view"];$K=$_POST;$ig="VIEW";if(JUSH=="pgsql"&&$a!=""){$P=table_status1($a);$ig=strtoupper($P["Engine"]);}if($_POST&&!$l){$B=trim($K["name"]);$wa=" AS\n$K[select]";$Re=ME."table=".urlencode($B);$if='View has been altered.';$U=($_POST["materialized"]?"MATERIALIZED VIEW":"VIEW");if(!$_POST["drop"]&&$a==$B&&JUSH!="sqlite"&&$U=="VIEW"&&$ig=="VIEW")query_redirect((JUSH=="mssql"?"ALTER":"CREATE OR REPLACE")." VIEW ".table($B).$wa,$Re,$if);else{$Fi=$B."_adminer_".uniqid();drop_create("DROP $ig ".table($a),"CREATE $U ".table($B).$wa,"DROP $U ".table($B),"CREATE $U ".table($Fi).$wa,"DROP $U ".table($Fi),($_POST["drop"]?substr(ME,0,-1):$Re),'View has been dropped.',$if,'View has been created.',$a,$B);}}if(!$_POST&&$a!=""){$K=view($a);$K["name"]=$a;$K["materialized"]=($ig!="VIEW");if(!$l)$l=error();}page_header(($a!=""?'Alter view':'Create view'),$l,array("table"=>$a),h($a));echo' +
    +

    Name: +',(support("materializedview")?" ".checkbox("materialized",1,$K["materialized"],'Materialized view'):""),'

    ';textarea("select",$K["select"]);echo'

    + +';if($a!="")echo'',confirm(sprintf('Drop %s?',$a));echo +input_token(),'

    +';}elseif(isset($_GET["event"])){$aa=$_GET["event"];$pe=array("YEAR","QUARTER","MONTH","DAY","HOUR","MINUTE","WEEK","SECOND","YEAR_MONTH","DAY_HOUR","DAY_MINUTE","DAY_SECOND","HOUR_MINUTE","HOUR_SECOND","MINUTE_SECOND");$ki=array("ENABLED"=>"ENABLE","DISABLED"=>"DISABLE","SLAVESIDE_DISABLED"=>"DISABLE ON SLAVE");$K=$_POST;if($_POST&&!$l){if($_POST["drop"])query_redirect("DROP EVENT ".idf_escape($aa),substr(ME,0,-1),'Event has been dropped.');elseif(in_array($K["INTERVAL_FIELD"],$pe)&&isset($ki[$K["STATUS"]])){$Ah="\nON SCHEDULE ".($K["INTERVAL_VALUE"]?"EVERY ".q($K["INTERVAL_VALUE"])." $K[INTERVAL_FIELD]".($K["STARTS"]?" STARTS ".q($K["STARTS"]):"").($K["ENDS"]?" ENDS ".q($K["ENDS"]):""):"AT ".q($K["STARTS"]))." ON COMPLETION".($K["ON_COMPLETION"]?"":" NOT")." PRESERVE";queries_redirect(substr(ME,0,-1),($aa!=""?'Event has been altered.':'Event has been created.'),queries(($aa!=""?"ALTER EVENT ".idf_escape($aa).$Ah.($aa!=$K["EVENT_NAME"]?"\nRENAME TO ".idf_escape($K["EVENT_NAME"]):""):"CREATE EVENT ".idf_escape($K["EVENT_NAME"]).$Ah)."\n".$ki[$K["STATUS"]]." COMMENT ".q($K["EVENT_COMMENT"]).rtrim(" DO\n$K[EVENT_DEFINITION]",";").";"));}}page_header(($aa!=""?'Alter event'.": ".h($aa):'Create event'),$l);if(!$K&&$aa!=""){$L=get_rows("SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ".q(DB)." AND EVENT_NAME = ".q($aa));$K=reset($L);}echo' +
    + +
    Name +
    Start +
    End +
    Every ',html_select("INTERVAL_FIELD",$pe,$K["INTERVAL_FIELD"]),'
    Status',html_select("STATUS",$ki,$K["STATUS"]),'
    Comment +
    ',checkbox("ON_COMPLETION","PRESERVE",$K["ON_COMPLETION"]=="PRESERVE",'On completion preserve'),'
    +

    ';textarea("EVENT_DEFINITION",$K["EVENT_DEFINITION"]);echo'

    + +';if($aa!="")echo'',confirm(sprintf('Drop %s?',$aa));echo +input_token(),'

    +';}elseif(isset($_GET["procedure"])){$ba=($_GET["name"]?:$_GET["procedure"]);$wh=(isset($_GET["function"])?"FUNCTION":"PROCEDURE");$K=$_POST;$K["fields"]=(array)$K["fields"];if($_POST&&!process_fields($K["fields"])&&!$l){$eg=routine($_GET["procedure"],$wh);$Fi="$K[name]_adminer_".uniqid();foreach($K["fields"]as$x=>$m){if($m["field"]=="")unset($K["fields"][$x]);}drop_create("DROP $wh ".routine_id($ba,$eg),create_routine($wh,$K),"DROP $wh ".routine_id($K["name"],$K),create_routine($wh,array("name"=>$Fi)+$K),"DROP $wh ".routine_id($Fi,$K),substr(ME,0,-1),'Routine has been dropped.','Routine has been altered.','Routine has been created.',$ba,$K["name"]);}page_header(($ba!=""?(isset($_GET["function"])?'Alter function':'Alter procedure').": ".h($ba):(isset($_GET["function"])?'Create function':'Create procedure')),$l);if(!$_POST){if($ba=="")$K["language"]="sql";else{$K=routine($_GET["procedure"],$wh);$K["name"]=$ba;}}$jb=get_vals("SHOW CHARACTER SET");sort($jb);$xh=routine_languages();echo($jb?"".optionlist($jb)."":""),' +
    +

    Name: +',($xh?"\n":""),' +

    + +';edit_fields($K["fields"],$jb,$wh);if(isset($_GET["function"])){echo"
    ".'Return type';edit_type("returns",(array)$K["returns"],$jb,array(),(JUSH=="pgsql"?array("void","trigger"):array()));}echo'
    +',script("editFields();"),'
    +

    ';textarea("definition",$K["definition"]);echo'

    + +';if($ba!="")echo'',confirm(sprintf('Drop %s?',$ba));echo +input_token(),'

    +';}elseif(isset($_GET["sequence"])){$da=$_GET["sequence"];$K=$_POST;if($_POST&&!$l){$_=substr(ME,0,-1);$B=trim($K["name"]);if($_POST["drop"])query_redirect("DROP SEQUENCE ".idf_escape($da),$_,'Sequence has been dropped.');elseif($da=="")query_redirect("CREATE SEQUENCE ".idf_escape($B),$_,'Sequence has been created.');elseif($da!=$B)query_redirect("ALTER SEQUENCE ".idf_escape($da)." RENAME TO ".idf_escape($B),$_,'Sequence has been altered.');else +redirect($_);}page_header($da!=""?'Alter sequence'.": ".h($da):'Create sequence',$l);if(!$K)$K["name"]=$da;echo' +
    +

    + +';if($da!="")echo"".confirm(sprintf('Drop %s?',$da))."\n";echo +input_token(),'

    +';}elseif(isset($_GET["type"])){$ea=$_GET["type"];$K=$_POST;if($_POST&&!$l){$_=substr(ME,0,-1);if($_POST["drop"])query_redirect("DROP TYPE ".idf_escape($ea),$_,'Type has been dropped.');else +query_redirect("CREATE TYPE ".idf_escape(trim($K["name"]))." $K[as]",$_,'Type has been created.');}page_header($ea!=""?'Alter type'.": ".h($ea):'Create type',$l);if(!$K)$K["as"]="AS ";echo' +
    +

    +';if($ea!=""){$ij=driver()->types();$Cc=type_values($ij[$ea]);if($Cc)echo"ENUM (".h($Cc).")\n

    ";echo"".confirm(sprintf('Drop %s?',$ea))."\n";}else{echo'Name'.": \n",doc_link(array('pgsql'=>"datatype-enum.html",),"?");textarea("as",$K["as"]);echo"

    \n";}echo +input_token(),'

    +';}elseif(isset($_GET["check"])){$a=$_GET["check"];$B=$_GET["name"];$K=$_POST;if($K&&!$l){if(JUSH=="sqlite")$I=recreate_table($a,$a,array(),array(),array(),"",array(),"$B",($K["drop"]?"":$K["clause"]));else{$I=($B==""||queries("ALTER TABLE ".table($a)." DROP CONSTRAINT ".idf_escape($B)));if(!$K["drop"])$I=queries("ALTER TABLE ".table($a)." ADD".($K["name"]!=""?" CONSTRAINT ".idf_escape($K["name"]):"")." CHECK ($K[clause])");}queries_redirect(ME."table=".urlencode($a),($K["drop"]?'Check has been dropped.':($B!=""?'Check has been altered.':'Check has been created.')),$I);}page_header(($B!=""?'Alter check'.": ".h($B):'Create check'),$l,array("table"=>$a));if(!$K){$ab=driver()->checkConstraints($a);$K=array("name"=>$B,"clause"=>$ab[$B]);}echo' +
    +

    ';if(JUSH!="sqlite")echo'Name'.': ';echo +doc_link(array('sql'=>"create-table-check-constraints.html",'mariadb'=>"constraint/",'pgsql'=>"ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS",'mssql'=>"relational-databases/tables/create-check-constraints",'sqlite'=>"lang_createtable.html#check_constraints",),"?"),'

    ';textarea("clause",$K["clause"]);echo'

    +';if($B!="")echo'',confirm(sprintf('Drop %s?',$B));echo +input_token(),'

    +';}elseif(isset($_GET["trigger"])){$a=$_GET["trigger"];$B="$_GET[name]";$ej=trigger_options();$K=(array)trigger($B,$a)+array("Trigger"=>$a."_bi");if($_POST){if(!$l&&in_array($_POST["Timing"],$ej["Timing"])&&in_array($_POST["Event"],$ej["Event"])&&in_array($_POST["Type"],$ej["Type"])){$Of=" ON ".table($a);$ic="DROP TRIGGER ".idf_escape($B).(JUSH=="pgsql"?$Of:"");$Re=ME."table=".urlencode($a);if($_POST["drop"])query_redirect($ic,$Re,'Trigger has been dropped.');else{if($B!="")queries($ic);queries_redirect($Re,($B!=""?'Trigger has been altered.':'Trigger has been created.'),queries(create_trigger($Of,$_POST)));if($B!="")queries(create_trigger($Of,$K+array("Type"=>reset($ej["Type"]))));}}$K=$_POST;}page_header(($B!=""?'Alter trigger'.": ".h($B):'Create trigger'),$l,array("table"=>$a));echo' +
    + +
    Time',html_select("Timing",$ej["Timing"],$K["Timing"],"triggerChange(/^".preg_quote($a,"/")."_[ba][iud]$/, '".js_escape($a)."', this.form);"),'
    Event',html_select("Event",$ej["Event"],$K["Event"],"this.form['Timing'].onchange();"),(in_array("UPDATE OF",$ej["Event"])?" ":""),'
    Type',html_select("Type",$ej["Type"],$K["Type"]),'
    +

    Name: +',script("qs('#form')['Timing'].onchange();"),'

    ';textarea("Statement",$K["Statement"]);echo'

    + +';if($B!="")echo'',confirm(sprintf('Drop %s?',$B));echo +input_token(),'

    +';}elseif(isset($_GET["user"])){$fa=$_GET["user"];$Vg=array(""=>array("All privileges"=>""));foreach(get_rows("SHOW PRIVILEGES")as$K){foreach(explode(",",($K["Privilege"]=="Grant option"?"":$K["Context"]))as$_b)$Vg[$_b][$K["Privilege"]]=$K["Comment"];}$Vg["Server Admin"]+=$Vg["File access on server"];$Vg["Databases"]["Create routine"]=$Vg["Procedures"]["Create routine"];unset($Vg["Procedures"]["Create routine"]);$Vg["Columns"]=array();foreach(array("Select","Insert","Update","References")as$X)$Vg["Columns"][$X]=$Vg["Tables"][$X];unset($Vg["Server Admin"]["Usage"]);foreach($Vg["Tables"]as$x=>$X)unset($Vg["Databases"][$x]);$xf=array();if($_POST){foreach($_POST["objects"]as$x=>$X)$xf[$X]=(array)$xf[$X]+idx($_POST["grants"],$x,array());}$vd=array();$Mf="";if(isset($_GET["host"])&&($I=connection()->query("SHOW GRANTS FOR ".q($fa)."@".q($_GET["host"])))){while($K=$I->fetch_row()){if(preg_match('~GRANT (.*) ON (.*) TO ~',$K[0],$A)&&preg_match_all('~ *([^(,]*[^ ,(])( *\([^)]+\))?~',$A[1],$Ye,PREG_SET_ORDER)){foreach($Ye +as$X){if($X[1]!="USAGE")$vd["$A[2]$X[2]"][$X[1]]=true;if(preg_match('~ WITH GRANT OPTION~',$K[0]))$vd["$A[2]$X[2]"]["GRANT OPTION"]=true;}}if(preg_match("~ IDENTIFIED BY PASSWORD '([^']+)~",$K[0],$A))$Mf=$A[1];}}if($_POST&&!$l){$Nf=(isset($_GET["host"])?q($fa)."@".q($_GET["host"]):"''");if($_POST["drop"])query_redirect("DROP USER $Nf",ME."privileges=",'User has been dropped.');else{$zf=q($_POST["user"])."@".q($_POST["host"]);$Ag=$_POST["pass"];if($Ag!=''&&!$_POST["hashed"]&&!min_version(8)){$Ag=get_val("SELECT PASSWORD(".q($Ag).")");$l=!$Ag;}$Eb=false;if(!$l){if($Nf!=$zf){$Eb=queries((min_version(5)?"CREATE USER":"GRANT USAGE ON *.* TO")." $zf IDENTIFIED BY ".(min_version(8)?"":"PASSWORD ").q($Ag));$l=!$Eb;}elseif($Ag!=$Mf)queries("SET PASSWORD FOR $zf = ".q($Ag));}if(!$l){$th=array();foreach($xf +as$Gf=>$ud){if(isset($_GET["grant"]))$ud=array_filter($ud);$ud=array_keys($ud);if(isset($_GET["grant"]))$th=array_diff(array_keys(array_filter($xf[$Gf],'strlen')),$ud);elseif($Nf==$zf){$Kf=array_keys((array)$vd[$Gf]);$th=array_diff($Kf,$ud);$ud=array_diff($ud,$Kf);unset($vd[$Gf]);}if(preg_match('~^(.+)\s*(\(.*\))?$~U',$Gf,$A)&&(!grant("REVOKE",$th,$A[2]," ON $A[1] FROM $zf")||!grant("GRANT",$ud,$A[2]," ON $A[1] TO $zf"))){$l=true;break;}}}if(!$l&&isset($_GET["host"])){if($Nf!=$zf)queries("DROP USER $Nf");elseif(!isset($_GET["grant"])){foreach($vd +as$Gf=>$th){if(preg_match('~^(.+)(\(.*\))?$~U',$Gf,$A))grant("REVOKE",array_keys($th),$A[2]," ON $A[1] FROM $zf");}}}queries_redirect(ME."privileges=",(isset($_GET["host"])?'User has been altered.':'User has been created.'),!$l);if($Eb)connection()->query("DROP USER $zf");}}page_header((isset($_GET["host"])?'Username'.": ".h("$fa@$_GET[host]"):'Create user'),$l,array("privileges"=>array('','Privileges')));$K=$_POST;if($K)$vd=$xf;else{$K=$_GET+array("host"=>get_val("SELECT SUBSTRING_INDEX(CURRENT_USER, '@', -1)"));$K["pass"]=$Mf;if($Mf!="")$K["hashed"]=true;$vd[(DB==""||$vd?"":idf_escape(addcslashes(DB,"%_\\"))).".*"]=array();}echo'
    + +
    Server +
    Username +
    Password +',($K["hashed"]?"":script("typePassword(qs('#pass'));")),(min_version(8)?"":checkbox("hashed",1,$K["hashed"],'Hashed',"typePassword(this.form['pass'], this.checked);")),'
    + +',"\n","\n";foreach(array(""=>"","Server Admin"=>'Server',"Databases"=>'Database',"Tables"=>'Table',"Columns"=>'Column',"Procedures"=>'Routine',)as$_b=>$Yb){foreach((array)$Vg[$_b]as$Ug=>$ob){echo"$Yb'.h($Ug);$s=0;foreach($vd +as$Gf=>$ud){$B="'grants[$s][".h(strtoupper($Ug))."]'";$Y=$ud[strtoupper($Ug)];if($_b=="Server Admin"&&$Gf!=(isset($vd["*.*"])?"*.*":".*"))echo"
    ".'Privileges'.doc_link(array('sql'=>"grant.html#priv_level"));$s=0;foreach($vd +as$Gf=>$ud){echo''.($Gf!="*.*"?"":input_hidden("objects[$s]","*.*")."*.*");$s++;}echo"
    ";elseif(isset($_GET["grant"]))echo"";else +echo"";$s++;}}}echo"
    \n",'

    + +';if(isset($_GET["host"]))echo'',confirm(sprintf('Drop %s?',"$fa@$_GET[host]"));echo +input_token(),'

    +';}elseif(isset($_GET["processlist"])){if(support("kill")){if($_POST&&!$l){$De=0;foreach((array)$_POST["kill"]as$X){if(kill_process($X))$De++;}queries_redirect(ME."processlist=",lang_format(array('%d process has been killed.','%d processes have been killed.'),$De),$De||!$_POST["kill"]);}}page_header('Process list',$l);echo' +
    +
    + +',script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});");$s=-1;foreach(process_list()as$s=>$K){if(!$s){echo"".(support("kill")?"\n";}echo"".(support("kill")?"
    ":"");foreach($K +as$x=>$X)echo"$x".doc_link(array('sql'=>"show-processlist.html#processlist_".strtolower($x),'pgsql'=>"monitoring-stats.html#PG-STAT-ACTIVITY-VIEW",'oracle'=>"REFRN30223",));echo"
    ".checkbox("kill[]",$K[JUSH=="sql"?"Id":"pid"],0):"");foreach($K +as$x=>$X)echo"".((JUSH=="sql"&&$x=="Info"&&preg_match("~Query|Killed~",$K["Command"])&&$X!="")||(JUSH=="pgsql"&&$x=="current_query"&&$X!="")||(JUSH=="oracle"&&$x=="sql_text"&&$X!="")?"".shorten_utf8($X,100,"").' '.'Clone'.'':h($X));echo"\n";}echo'
    +
    +

    +';if(support("kill"))echo($s+1)."/".sprintf('%d in total',max_connections()),"

    \n";echo +input_token(),'

    +',script("tableCheck();");}elseif(isset($_GET["select"])){$a=$_GET["select"];$S=table_status1($a);$w=indexes($a);$n=fields($a);$ld=column_foreign_keys($a);$If=$S["Oid"];$na=get_settings("adminer_import");$uh=array();$e=array();$Gh=array();$ag=array();$Ji="";foreach($n +as$x=>$m){$B=adminer()->fieldName($m);$vf=html_entity_decode(strip_tags($B),ENT_QUOTES);if(isset($m["privileges"]["select"])&&$B!=""){$e[$x]=$vf;if(is_shortable($m))$Ji=adminer()->selectLengthProcess();}if(isset($m["privileges"]["where"])&&$B!="")$Gh[$x]=$vf;if(isset($m["privileges"]["order"])&&$B!="")$ag[$x]=$vf;$uh+=$m["privileges"];}list($M,$wd)=adminer()->selectColumnsProcess($e,$w);$M=array_unique($M);$wd=array_unique($wd);$te=count($wd)selectSearchProcess($n,$w);$Zf=adminer()->selectOrderProcess($n,$w);$z=adminer()->selectLimitProcess();if($_GET["val"]&&is_ajax()){header("Content-Type: text/plain; charset=utf-8");foreach($_GET["val"]as$nj=>$K){$wa=convert_field($n[key($K)]);$M=array($wa?:idf_escape(key($K)));$Z[]=where_check($nj,$n);$J=driver()->select($a,$M,$Z,$M);if($J)echo +first($J->fetch_row());}exit;}$G=$pj=array();foreach($w +as$v){if($v["type"]=="PRIMARY"){$G=array_flip($v["columns"]);$pj=($M?$G:array());foreach($pj +as$x=>$X){if(in_array(idf_escape($x),$M))unset($pj[$x]);}break;}}if($If&&!$G){$G=$pj=array($If=>0);$w[]=array("type"=>"PRIMARY","columns"=>array($If));}if($_POST&&!$l){$Mj=$Z;if(!$_POST["all"]&&is_array($_POST["check"])){$ab=array();foreach($_POST["check"]as$Wa)$ab[]=where_check($Wa,$n);$Mj[]="((".implode(") OR (",$ab)."))";}$Mj=($Mj?"\nWHERE ".implode(" AND ",$Mj):"");if($_POST["export"]){save_settings(array("output"=>$_POST["output"],"format"=>$_POST["format"]),"adminer_import");dump_headers($a);adminer()->dumpTable($a,"");$pd=($M?implode(", ",$M):"*").convert_fields($e,$n,$M)."\nFROM ".table($a);$yd=($wd&&$te?"\nGROUP BY ".implode(", ",$wd):"").($Zf?"\nORDER BY ".implode(", ",$Zf):"");$H="SELECT $pd$Mj$yd";if(is_array($_POST["check"])&&!$G){$lj=array();foreach($_POST["check"]as$X)$lj[]="(SELECT".limit($pd,"\nWHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($X,$n).$yd,1).")";$H=implode(" UNION ALL ",$lj);}adminer()->dumpData($a,"table",$H);adminer()->dumpFooter();exit;}if(!adminer()->selectEmailProcess($Z,$ld)){if($_POST["save"]||$_POST["delete"]){$I=true;$oa=0;$O=array();if(!$_POST["delete"]){foreach($_POST["fields"]as$B=>$X){$X=process_input($n[$B]);if($X!==null&&($_POST["clone"]||$X!==false))$O[idf_escape($B)]=($X!==false?$X:idf_escape($B));}}if($_POST["delete"]||$O){$H=($_POST["clone"]?"INTO ".table($a)." (".implode(", ",array_keys($O)).")\nSELECT ".implode(", ",$O)."\nFROM ".table($a):"");if($_POST["all"]||($G&&is_array($_POST["check"]))||$te){$I=($_POST["delete"]?driver()->delete($a,$Mj):($_POST["clone"]?queries("INSERT $H$Mj".driver()->insertReturning($a)):driver()->update($a,$O,$Mj)));$oa=connection()->affected_rows;if(is_object($I))$oa+=$I->num_rows;}else{foreach((array)$_POST["check"]as$X){$Lj="\nWHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($X,$n);$I=($_POST["delete"]?driver()->delete($a,$Lj,1):($_POST["clone"]?queries("INSERT".limit1($a,$H,$Lj)):driver()->update($a,$O,$Lj,1)));if(!$I)break;$oa+=connection()->affected_rows;}}}$if=lang_format(array('%d item has been affected.','%d items have been affected.'),$oa);if($_POST["clone"]&&$I&&$oa==1){$Ie=last_id($I);if($Ie)$if=sprintf('Item%s has been inserted.'," $Ie");}queries_redirect(remove_from_uri($_POST["all"]&&$_POST["delete"]?"page":""),$if,$I);if(!$_POST["delete"]){$Mg=(array)$_POST["fields"];edit_form($a,array_intersect_key($n,$Mg),$Mg,!$_POST["clone"],$l);page_footer();exit;}}elseif(!$_POST["import"]){if(!$_POST["val"])$l='Ctrl+click on a value to modify it.';else{$I=true;$oa=0;foreach($_POST["val"]as$nj=>$K){$O=array();foreach($K +as$x=>$X){$x=bracket_escape($x,true);$O[idf_escape($x)]=(preg_match('~char|text~',$n[$x]["type"])||$X!=""?adminer()->processInput($n[$x],$X):"NULL");}$I=driver()->update($a,$O," WHERE ".($Z?implode(" AND ",$Z)." AND ":"").where_check($nj,$n),($te||$G?0:1)," ");if(!$I)break;$oa+=connection()->affected_rows;}queries_redirect(remove_from_uri(),lang_format(array('%d item has been affected.','%d items have been affected.'),$oa),$I);}}elseif(!is_string($Zc=get_file("csv_file",true)))$l=upload_error($Zc);elseif(!preg_match('~~u',$Zc))$l='File must be in UTF-8 encoding.';else{save_settings(array("output"=>$na["output"],"format"=>$_POST["separator"]),"adminer_import");$I=true;$kb=array_keys($n);preg_match_all('~(?>"[^"]*"|[^"\r\n]+)+~',$Zc,$Ye);$oa=count($Ye[0]);driver()->begin();$Mh=($_POST["separator"]=="csv"?",":($_POST["separator"]=="tsv"?"\t":";"));$L=array();foreach($Ye[0]as$x=>$X){preg_match_all("~((?>\"[^\"]*\")+|[^$Mh]*)$Mh~",$X.$Mh,$Ze);if(!$x&&!array_diff($Ze[1],$kb)){$kb=$Ze[1];$oa--;}else{$O=array();foreach($Ze[1]as$s=>$hb)$O[idf_escape($kb[$s])]=($hb==""&&$n[$kb[$s]]["null"]?"NULL":q(preg_match('~^".*"$~s',$hb)?str_replace('""','"',substr($hb,1,-1)):$hb));$L[]=$O;}}$I=(!$L||driver()->insertUpdate($a,$L,$G));if($I)driver()->commit();queries_redirect(remove_from_uri("page"),lang_format(array('%d row has been imported.','%d rows have been imported.'),$oa),$I);driver()->rollback();}}}$vi=adminer()->tableName($S);if(is_ajax()){page_headers();ob_start();}else +page_header('Select'.": $vi",$l);$O=null;if(isset($uh["insert"])||!support("table")){$rg=array();foreach((array)$_GET["where"]as$X){if(isset($ld[$X["col"]])&&count($ld[$X["col"]])==1&&($X["op"]=="="||(!$X["op"]&&(is_array($X["val"])||!preg_match('~[_%]~',$X["val"])))))$rg["set"."[".bracket_escape($X["col"])."]"]=$X["val"];}$O=$rg?"&".http_build_query($rg):"";}adminer()->selectLinks($S,$O);if(!$e&&support("table"))echo"

    ".'Unable to select the table'.($n?".":": ".error())."\n";else{echo"

    \n","
    ";hidden_fields_get();echo(DB!=""?input_hidden("db",DB).(isset($_GET["ns"])?input_hidden("ns",$_GET["ns"]):""):""),input_hidden("select",$a),"
    \n";adminer()->selectColumnsPrint($M,$e);adminer()->selectSearchPrint($Z,$Gh,$w);adminer()->selectOrderPrint($Zf,$ag,$w);adminer()->selectLimitPrint($z);adminer()->selectLengthPrint($Ji);adminer()->selectActionPrint($w);echo"
    \n";$D=$_GET["page"];$od=null;if($D=="last"){$od=get_val(count_rows($a,$Z,$te,$wd));$D=floor(max(0,intval($od)-1)/$z);}$Hh=$M;$xd=$wd;if(!$Hh){$Hh[]="*";$Ab=convert_fields($e,$n,$M);if($Ab)$Hh[]=substr($Ab,2);}foreach($M +as$x=>$X){$m=$n[idf_unescape($X)];if($m&&($wa=convert_field($m)))$Hh[$x]="$wa AS $X";}if(!$te&&$pj){foreach($pj +as$x=>$X){$Hh[]=idf_escape($x);if($xd)$xd[]=idf_escape($x);}}$I=driver()->select($a,$Hh,$Z,$xd,$Zf,$z,$D,true);if(!$I)echo"

    ".error()."\n";else{if(JUSH=="mssql"&&$D)$I->seek($z*$D);$wc=array();echo"

    \n";$L=array();while($K=$I->fetch_assoc()){if($D&&JUSH=="oracle")unset($K["RNUM"]);$L[]=$K;}if($_GET["page"]!="last"&&$z&&$wd&&$te&&JUSH=="sql")$od=get_val(" SELECT FOUND_ROWS()");if(!$L)echo"

    ".'No rows.'."\n";else{$Ea=adminer()->backwardKeys($a,$vi);echo"

    ","",script("mixin(qs('#table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true), onkeydown: editingKeydown});"),"".(!$wd&&$M?"":"\n";if(is_ajax())ob_end_clean();foreach(adminer()->rowDescriptions($L,$ld)as$uf=>$K){$mj=unique_array($L[$uf],$w);if(!$mj){$mj=array();reset($M);foreach($L[$uf]as$x=>$X){if(!preg_match('~^(COUNT|AVG|GROUP_CONCAT|MAX|MIN|SUM)\(~',current($M)))$mj[$x]=$X;next($M);}}$nj="";foreach($mj +as$x=>$X){$m=(array)$n[$x];if((JUSH=="sql"||JUSH=="pgsql")&&preg_match('~char|text|enum|set~',$m["type"])&&strlen($X)>64){$x=(strpos($x,'(')?$x:idf_escape($x));$x="MD5(".(JUSH!='sql'||preg_match("~^utf8~",$m["collation"])?$x:"CONVERT($x USING ".charset(connection()).")").")";$X=md5($X);}$nj +.="&".($X!==null?urlencode("where[".bracket_escape($x)."]")."=".urlencode($X===false?"f":$X):"null%5B%5D=".urlencode($x));}echo"".(!$wd&&$M?"":"\n";}if(is_ajax())exit;echo"
    ".script("qs('#all-page').onclick = partial(formCheck, /check/);","")." ".'Modify'."");$wf=array();$rd=array();reset($M);$eh=1;foreach($L[0]as$x=>$X){if(!isset($pj[$x])){$X=idx($_GET["columns"],key($M))?:array();$m=$n[$M?($X?$X["col"]:current($M)):$x];$B=($m?adminer()->fieldName($m,$eh):($X["fun"]?"*":h($x)));if($B!=""){$eh++;$wf[$x]=$B;$d=idf_escape($x);$Nd=remove_from_uri('(order|desc)[^=]*|page').'&order%5B0%5D='.urlencode($x);$Yb="&desc%5B0%5D=1";echo"".script("mixin(qsl('th'), {onmouseover: partial(columnMouse), onmouseout: partial(columnMouse, ' hidden')});","");$qd=apply_sql_function($X["fun"],$B);$Zh=isset($m["privileges"]["order"])||$qd;echo($Zh?"$qd":$qd),"";}$rd[$x]=$X["fun"];next($M);}}$Ne=array();if($_GET["modify"]){foreach($L +as$K){foreach($K +as$x=>$X)$Ne[$x]=max($Ne[$x],min(40,strlen(utf8_decode($X))));}}echo($Ea?"".'Relations':"")."
    ".checkbox("check[]",substr($nj,1),in_array(substr($nj,1),(array)$_POST["check"])).($te||information_schema(DB)?"":" ".'edit'.""));reset($M);foreach($K +as$x=>$X){if(isset($wf[$x])){$d=current($M);$m=(array)$n[$x];$X=driver()->value($X,$m);if($X!=""&&(!isset($wc[$x])||$wc[$x]!=""))$wc[$x]=(is_mail($X)?$wf[$x]:"");$_="";if(preg_match('~blob|bytea|raw|file~',$m["type"])&&$X!="")$_=ME.'download='.urlencode($a).'&field='.urlencode($x).$nj;if(!$_&&$X!==null){foreach((array)$ld[$x]as$p){if(count($ld[$x])==1||end($p["source"])==$x){$_="";foreach($p["source"]as$s=>$ai)$_ +.=where_link($s,$p["target"][$s],$L[$uf][$ai]);$_=($p["db"]!=""?preg_replace('~([?&]db=)[^&]+~','\1'.urlencode($p["db"]),ME):ME).'select='.urlencode($p["table"]).$_;if($p["ns"])$_=preg_replace('~([?&]ns=)[^&]+~','\1'.urlencode($p["ns"]),$_);if(count($p["source"])==1)break;}}}if($d=="COUNT(*)"){$_=ME."select=".urlencode($a);$s=0;foreach((array)$_GET["where"]as$W){if(!array_key_exists($W["col"],$mj))$_ +.=where_link($s++,$W["col"],$W["val"],$W["op"]);}foreach($mj +as$_e=>$W)$_ +.=where_link($s++,$_e,$W);}$Od=select_value($X,$_,$m,$Ji);$t=h("val[$nj][".bracket_escape($x)."]");$Ng=idx(idx($_POST["val"],$nj),bracket_escape($x));$rc=!is_array($K[$x])&&is_utf8($Od)&&$L[$uf][$x]==$K[$x]&&!$rd[$x]&&!$m["generated"];$U=(preg_match('~^(AVG|MIN|MAX)\((.+)\)~',$d,$A)?$n[idf_unescape($A[2])]["type"]:$m["type"]);$Hi=preg_match('~text|json|lob~',$U);$ue=preg_match(number_type(),$U)||preg_match('~^(CHAR_LENGTH|ROUND|FLOOR|CEIL|TIME_TO_SEC|COUNT|SUM)\(~',$d);echo"".($Hi?"":"");}else{$Te=strpos($Od,"…");echo" data-text='".($Te?2:($Hi?1:0))."'".($rc?"":" data-warning='".h('Use edit link to modify this value.')."'").">$Od";}}next($M);}if($Ea)echo"";adminer()->backwardKeysPrint($Ea,$L[$uf]);echo"
    \n","
    \n";}if(!is_ajax()){if($L||$D){$Jc=true;if($_GET["page"]!="last"){if(!$z||(count($L)<$z&&($L||!$D)))$od=($D?$D*$z:0)+count($L);elseif(JUSH!="sql"||!$te){$od=($te?false:found_rows($S,$Z));if(intval($od)$z||$D));if($pg)echo(($od===false?count($L)+1:$od-$D*$z)>$z?'

    '.'Load more data'.''.script("qsl('a').onclick = partial(selectLoadMore, $z, '".'Loading'."…');",""):''),"\n";echo"

    \n";}if(adminer()->selectImportPrint())echo"

    ","".'Import'."",script("qsl('a').onclick = partial(toggle, 'import');",""),"";echo +input_token(),"

    \n",(!$wd&&$M?"":script("tableCheck();"));}}}if(is_ajax()){ob_end_clean();exit;}}elseif(isset($_GET["variables"])){$P=isset($_GET["status"]);page_header($P?'Status':'Variables');$Cj=($P?show_status():show_variables());if(!$Cj)echo"

    ".'No rows.'."\n";else{echo"\n";foreach($Cj +as$K){echo"";$x=array_shift($K);echo"
    ".h($x)."";foreach($K +as$X)echo"".nl_br(h($X));}echo"
    \n";}}elseif(isset($_GET["script"])){header("Content-Type: text/javascript; charset=utf-8");if($_GET["script"]=="db"){$ri=array("Data_length"=>0,"Index_length"=>0,"Data_free"=>0);foreach(table_status()as$B=>$S){json_row("Comment-$B",h($S["Comment"]));if(!is_view($S)){foreach(array("Engine","Collation")as$x)json_row("$x-$B",h($S[$x]));foreach($ri+array("Auto_increment"=>0,"Rows"=>0)as$x=>$X){if($S[$x]!=""){$X=format_number($S[$x]);if($X>=0)json_row("$x-$B",($x=="Rows"&&$X&&$S["Engine"]==(JUSH=="pgsql"?"table":"InnoDB")?"~ $X":$X));if(isset($ri[$x]))$ri[$x]+=($S["Engine"]!="InnoDB"||$x!="Data_free"?$S[$x]:0);}elseif(array_key_exists($x,$S))json_row("$x-$B","?");}}}foreach($ri +as$x=>$X)json_row("sum-$x",format_number($X));json_row("");}elseif($_GET["script"]=="kill")connection()->query("KILL ".number($_POST["kill"]));else{foreach(count_tables(adminer()->databases())as$j=>$X){json_row("tables-$j",$X);json_row("size-$j",db_size($j));}json_row("");}exit;}else{$Bi=array_merge((array)$_POST["tables"],(array)$_POST["views"]);if($Bi&&!$l&&!$_POST["search"]){$I=true;$if="";if(JUSH=="sql"&&$_POST["tables"]&&count($_POST["tables"])>1&&($_POST["drop"]||$_POST["truncate"]||$_POST["copy"]))queries("SET foreign_key_checks = 0");if($_POST["truncate"]){if($_POST["tables"])$I=truncate_tables($_POST["tables"]);$if='Tables have been truncated.';}elseif($_POST["move"]){$I=move_tables((array)$_POST["tables"],(array)$_POST["views"],$_POST["target"]);$if='Tables have been moved.';}elseif($_POST["copy"]){$I=copy_tables((array)$_POST["tables"],(array)$_POST["views"],$_POST["target"]);$if='Tables have been copied.';}elseif($_POST["drop"]){if($_POST["views"])$I=drop_views($_POST["views"]);if($I&&$_POST["tables"])$I=drop_tables($_POST["tables"]);$if='Tables have been dropped.';}elseif(JUSH=="sqlite"&&$_POST["check"]){foreach((array)$_POST["tables"]as$R){foreach(get_rows("PRAGMA integrity_check(".q($R).")")as$K)$if +.="".h($R).": ".h($K["integrity_check"])."
    ";}}elseif(JUSH!="sql"){$I=(JUSH=="sqlite"?queries("VACUUM"):apply_queries("VACUUM".($_POST["optimize"]?"":" ANALYZE"),$_POST["tables"]));$if='Tables have been optimized.';}elseif(!$_POST["tables"])$if='No tables.';elseif($I=queries(($_POST["optimize"]?"OPTIMIZE":($_POST["check"]?"CHECK":($_POST["repair"]?"REPAIR":"ANALYZE")))." TABLE ".implode(", ",array_map('Adminer\idf_escape',$_POST["tables"])))){while($K=$I->fetch_assoc())$if +.="".h($K["Table"]).": ".h($K["Msg_text"])."
    ";}queries_redirect(substr(ME,0,-1),$if,$I);}page_header(($_GET["ns"]==""?'Database'.": ".h(DB):'Schema'.": ".h($_GET["ns"])),$l,true);if(adminer()->homepage()){if($_GET["ns"]!==""){echo"

    ".'Tables and views'."

    \n";$Ai=tables_list();if(!$Ai)echo"

    ".'No tables.'."\n";else{echo"

    \n";if(support("table")){echo"
    ".'Search data in tables'."
    ","",script("qsl('input').onkeydown = partialArg(bodyKeydown, 'search');","")," \n","
    \n";if($_POST["search"]&&$_POST["query"]!=""){$_GET["where"][0]["op"]=driver()->convertOperator("LIKE %%");search_tables();}}echo"
    \n","\n",script("mixin(qsl('table'), {onclick: tableClick, ondblclick: partialArg(tableClick, true)});"),'','\n";$T=0;foreach($Ai +as$B=>$U){$Fj=($U!==null&&!preg_match('~table|sequence~i',$U));$t=h("Table-".$B);echo'
    '.script("qs('#check-all').onclick = partial(formCheck, /^(tables|views)\[/);",""),''.'Table',''.'Engine'.doc_link(array('sql'=>'storage-engines.html')),''.'Collation'.doc_link(array('sql'=>'charset-charsets.html','mariadb'=>'supported-character-sets-and-collations/')),''.'Data Length'.doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT','oracle'=>'REFRN20286')),''.'Index Length'.doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-admin.html#FUNCTIONS-ADMIN-DBOBJECT')),''.'Data Free'.doc_link(array('sql'=>'show-table-status.html')),''.'Auto Increment'.doc_link(array('sql'=>'example-auto-increment.html','mariadb'=>'auto_increment/')),''.'Rows'.doc_link(array('sql'=>'show-table-status.html','pgsql'=>'catalog-pg-class.html#CATALOG-PG-CLASS','oracle'=>'REFRN20286')),(support("comment")?''.'Comment'.doc_link(array('sql'=>'show-table-status.html','pgsql'=>'functions-info.html#FUNCTIONS-INFO-COMMENT-TABLE')):''),"
    '.checkbox(($Fj?"views[]":"tables[]"),$B,in_array("$B",$Bi,true),"","","",$t),''.(support("table")||support("indexes")?"".h($B).'':h($B));if($Fj)echo''.(preg_match('~materialized~i',$U)?'Materialized view':'View').'','?';else{foreach(array("Engine"=>array(),"Collation"=>array(),"Data_length"=>array("create",'Alter table'),"Index_length"=>array("indexes",'Alter indexes'),"Data_free"=>array("edit",'New item'),"Auto_increment"=>array("auto_increment=1&create",'Alter table'),"Rows"=>array("select",'Select data'),)as$x=>$_){$t=" id='$x-".h($B)."'";echo($_?"".(support("table")||$x=="Rows"||(support("indexes")&&$x!="Data_length")?"?":"?"):"");}$T++;}echo(support("comment")?"":""),"\n";}echo"
    ".sprintf('%d in total',count($Ai)),"".h(JUSH=="sql"?get_val("SELECT @@default_storage_engine"):""),"".h(db_collation(DB,collations()));foreach(array("Data_length","Index_length","Data_free")as$x)echo"";echo"\n","
    \n","
    \n";if(!information_schema(DB)){echo"\n";}echo"
    \n",script("tableCheck();");}echo"

    ".'Routines'."

    \n";$yh=routines();if($yh){echo"\n",'\n";foreach($yh +as$K){$B=($K["SPECIFIC_NAME"]==$K["ROUTINE_NAME"]?"":"&name=".urlencode($K["ROUTINE_NAME"]));echo'','
    '.'Name'.''.'Type'.''.'Return type'."
    '.h($K["ROUTINE_NAME"]).'',''.h($K["ROUTINE_TYPE"]),''.h($K["DTD_IDENTIFIER"]),''.'Alter'."";}echo"
    \n";}echo'

    ".'Sequences'."

    \n";$Ph=get_vals("SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema = current_schema() ORDER BY sequence_name");if($Ph){echo"\n","\n";foreach($Ph +as$X)echo"
    ".'Name'."
    ".h($X)."\n";echo"
    \n";}echo"

    ".'User types'."

    \n";$yj=types();if($yj){echo"\n","\n";foreach($yj +as$X)echo"
    ".'Name'."
    ".h($X)."\n";echo"
    \n";}echo"

    ".'Events'."

    \n";$L=get_rows("SHOW EVENTS");if($L){echo"\n","\n";foreach($L +as$K)echo"","
    ".'Name'."".'Schedule'."".'Start'."".'End'."
    ".h($K["Name"]),"".($K["Execute at"]?'At given time'."".$K["Execute at"]:'Every'." ".$K["Interval value"]." ".$K["Interval field"]."$K[Starts]"),"$K[Ends]",''.'Alter'.'';echo"
    \n";$Hc=get_val("SELECT @@event_scheduler");if($Hc&&$Hc!="ON")echo"

    event_scheduler: ".h($Hc)."\n";}echo'

    Test send mail

    +
    +

    + +

    +

    + +

    +

    + +

    +

    + + +

    +
    + +

    +

    +

    SMTP debug log

    ' . $smtpLog . '
    '; endif; ?> + + diff --git a/sodew-bash-main/assets/wordpress/__opcache.php b/sodew-bash-main/assets/wordpress/__opcache.php new file mode 100644 index 0000000..07564a4 --- /dev/null +++ b/sodew-bash-main/assets/wordpress/__opcache.php @@ -0,0 +1,8 @@ + 0, 'WARN' => 0, 'FAIL' => 0]; +$FINDINGS = []; + +function add_result(string $level, string $title, string $detail = ''): void { + global $SCORE, $FINDINGS; + if (!isset($SCORE[$level])) { + $level = 'WARN'; + } + $SCORE[$level]++; + $FINDINGS[] = [$level, $title, $detail]; +} + +function line(string $label, $value = null): void { + if ($value === null) { + echo $label . PHP_EOL; + return; + } + if (is_bool($value)) { + $value = $value ? 'YES' : 'NO'; + } elseif (is_array($value) || is_object($value)) { + $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + echo str_pad($label, 46) . ': ' . $value . PHP_EOL; +} + +function section(string $title): void { + echo PHP_EOL . "--- {$title} ---" . PHP_EOL; +} + +function bytes_from_ini(?string $val): ?int { + if ($val === null || $val === '') return null; + $val = trim($val); + if ($val === '-1') return -1; + $last = strtolower(substr($val, -1)); + $num = (float)$val; + return match($last) { + 'g' => (int)($num * 1024 * 1024 * 1024), + 'm' => (int)($num * 1024 * 1024), + 'k' => (int)($num * 1024), + default => (int)$num, + }; +} + +function with_error_capture(callable $fn): array { + $error = null; + set_error_handler(function($severity, $message) use (&$error) { + $error = $message; + return true; + }); + try { + $result = $fn(); + restore_error_handler(); + return ['result' => $result, 'error' => $error]; + } catch (Throwable $e) { + restore_error_handler(); + return ['result' => null, 'error' => $e->getMessage()]; + } +} + +function try_read_file(string $path): array { + return with_error_capture(function() use ($path) { + $data = @file_get_contents($path); + if ($data === false) return false; + return 'len=' . strlen($data); + }) + ['path' => $path]; +} + +function try_scandir_path(string $path): array { + return with_error_capture(function() use ($path) { + $data = @scandir($path); + if ($data === false) return false; + return array_slice($data, 0, 15); + }) + ['path' => $path]; +} + +function try_socket(string $target, int $port, float $timeout = 1.2): array { + $errno = 0; + $errstr = ''; + $fp = @fsockopen($target, $port, $errno, $errstr, $timeout); + $ok = is_resource($fp); + if ($ok) fclose($fp); + return [ + 'target' => $target, + 'port' => $port, + 'connected' => $ok, + 'errno' => $errno, + 'errstr' => $errstr, + ]; +} + +function try_unix_socket(string $path, float $timeout = 1.0): array { + $errno = 0; + $errstr = ''; + $fp = @stream_socket_client('unix://' . $path, $errno, $errstr, $timeout); + $ok = is_resource($fp); + if ($ok) fclose($fp); + return [ + 'path' => $path, + 'connected' => $ok, + 'errno' => $errno, + 'errstr' => $errstr, + ]; +} + +echo "=== SHARED HOSTING SAFE AUDIT v2.1 ===" . PHP_EOL; +echo "Time: " . date('c') . PHP_EOL; + +$docRoot = realpath($_SERVER['DOCUMENT_ROOT'] ?? getcwd()) ?: getcwd(); +$scriptFile = $_SERVER['SCRIPT_FILENAME'] ?? __FILE__; +$baseTmp = $docRoot . '/.audit_tmp_' . getmypid() . '_' . mt_rand(1000, 9999); +@mkdir($baseTmp, 0700, true); + +section('Runtime identity'); +line('PHP version', PHP_VERSION); +line('SAPI', PHP_SAPI); +line('OS', PHP_OS_FAMILY); +line('Document root', $docRoot); +line('Script filename', $scriptFile); +line('Current dir', getcwd()); +line('Loaded php.ini', php_ini_loaded_file() ?: 'none'); +line('Additional .ini files', php_ini_scanned_files() ?: 'none'); +line('Hostname', php_uname('n')); +line('Server software', $_SERVER['SERVER_SOFTWARE'] ?? 'n/a'); +line('Server addr', $_SERVER['SERVER_ADDR'] ?? 'n/a'); +line('Server port', $_SERVER['SERVER_PORT'] ?? 'n/a'); +line('Remote addr', $_SERVER['REMOTE_ADDR'] ?? 'n/a'); + +section('PHP limits and config'); +$iniKeys = [ + 'memory_limit', + 'max_execution_time', + 'max_input_time', + 'max_input_vars', + 'post_max_size', + 'upload_max_filesize', + 'open_basedir', + 'disable_functions', + 'disable_classes', + 'user_ini.filename', + 'user_ini.cache_ttl', + 'file_uploads', + 'allow_url_fopen', + 'allow_url_include', + 'session.save_path', + 'upload_tmp_dir', + 'sys_temp_dir', + 'display_errors', + 'log_errors', + 'expose_php', + 'mail.add_x_header', + 'mysqli.default_socket', + 'pdo_mysql.default_socket', +]; +foreach ($iniKeys as $k) { + line($k, ini_get($k)); +} + +section('Dangerous functions present'); +$dangerFns = [ + 'exec','shell_exec','system','passthru','proc_open','popen', + 'pcntl_exec','pcntl_fork','putenv','mail','symlink','link', + 'stream_socket_client','fsockopen' +]; +foreach ($dangerFns as $fn) { + line("function_exists($fn)", function_exists($fn)); +} + +section('Loaded extensions'); +$exts = ['mysqli','pdo_mysql','redis','ftp','curl','openssl','pcntl','posix','sockets','imap','intl']; +foreach ($exts as $ext) { + line("extension_loaded($ext)", extension_loaded($ext)); +} + +section('Filesystem isolation'); +$fsTests = [ + $docRoot, + $docRoot . '/../', + $docRoot . '/../../', + '/var/www', + '/var/www/vhosts', + '/etc/passwd', + '/etc/hosts', + '/proc/self/environ', + '/proc/meminfo', + '/tmp', + '/var/tmp', + '/run', + '/run/php', + '/run/mysqld', + '/dev/shm', + '/var/spool/postfix', +]; +foreach ($fsTests as $path) { + $isDir = @is_dir($path); + $result = $isDir ? try_scandir_path($path) : try_read_file($path); + line($path, $result); +} + +section('Path traversal / realpath checks'); +$traversalTests = [ + $docRoot . '/../www', + $docRoot . '/../tmp', + $docRoot . '/../session', + $docRoot . '/../../../../etc/passwd', + $docRoot . '/../other-vhost/www', +]; +foreach ($traversalTests as $path) { + line("realpath($path)", @realpath($path) ?: 'false'); + line("read($path)", try_read_file($path)); +} + +section('Allowed path write tests'); +$writeFile = $baseTmp . '/write-test.txt'; +$res = with_error_capture(function() use ($writeFile) { + return file_put_contents($writeFile, "audit\n"); +}); +line('Write file in docroot tmp', ['path' => $writeFile] + $res); +line('File exists after write', file_exists($writeFile)); +line('File readable after write', is_readable($writeFile)); +line('File writable after write', is_writable($writeFile)); + +$renameTo = $baseTmp . '/write-test-renamed.txt'; +$res = with_error_capture(function() use ($writeFile, $renameTo) { + return @rename($writeFile, $renameTo); +}); +line('Rename inside allowed path', ['from' => $writeFile, 'to' => $renameTo] + $res); + +$copyToSession = dirname($docRoot) . '/session/audit-copy.txt'; +$res = with_error_capture(function() use ($renameTo, $copyToSession) { + return @copy($renameTo, $copyToSession); +}); +line('Copy from docroot to session dir', ['to' => $copyToSession] + $res); + +section('Symlink / hardlink inside allowed path'); +$src = $baseTmp . '/src.txt'; +@file_put_contents($src, 'x'); +$symlinkTarget = $baseTmp . '/sym.txt'; +$hardlinkTarget = $baseTmp . '/hard.txt'; +$symlinkRes = with_error_capture(fn() => @symlink($src, $symlinkTarget)); +$hardlinkRes = with_error_capture(fn() => @link($src, $hardlinkTarget)); +line('Symlink allowed path', $symlinkRes); +line('Hardlink allowed path', $hardlinkRes); +line('Symlink exists', is_link($symlinkTarget)); +line('Hardlink exists', file_exists($hardlinkTarget)); + +section('Runtime override attempts'); +$overrideTests = [ + 'memory_limit' => '2048M', + 'max_execution_time' => '600', + 'upload_max_filesize' => '2048M', + 'post_max_size' => '2048M', + 'open_basedir' => '/', +]; +$overrideResults = []; +foreach ($overrideTests as $key => $value) { + $before = ini_get($key); + $ret = @ini_set($key, $value); + $after = ini_get($key); + $overrideResults[$key] = [ + 'before' => $before, + 'return' => $ret, + 'after' => $after, + 'changed' => ($before !== $after), + ]; + line("ini_set($key)", $overrideResults[$key]); +} + +section('Execution capability tests'); +$execResults = []; + +if (function_exists('exec')) { + $execResults['exec'] = with_error_capture(function() { + $out = []; + $rc = 0; + @exec('id 2>&1', $out, $rc); + return ['rc' => $rc, 'out' => implode("\n", array_slice($out, 0, 5))]; + }); + line('exec("id")', $execResults['exec']); +} + +if (function_exists('shell_exec')) { + $execResults['shell_exec'] = with_error_capture(function() { + $out = @shell_exec('whoami 2>&1'); + return $out === null ? null : trim($out); + }); + line('shell_exec("whoami")', $execResults['shell_exec']); +} + +if (function_exists('system')) { + $execResults['system'] = with_error_capture(function() { + ob_start(); + $rc = 0; + @system('pwd 2>&1', $rc); + $out = ob_get_clean(); + return ['rc' => $rc, 'out' => trim((string)$out)]; + }); + line('system("pwd")', $execResults['system']); +} + +if (function_exists('proc_open')) { + $execResults['proc_open'] = with_error_capture(function() { + $desc = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $proc = @proc_open('id', $desc, $pipes); + if (!is_resource($proc)) return 'proc_open failed'; + fclose($pipes[0]); + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $code = proc_close($proc); + return ['rc' => $code, 'stdout' => trim($stdout), 'stderr' => trim($stderr)]; + }); + line('proc_open("id")', $execResults['proc_open']); +} + +if (function_exists('popen')) { + $execResults['popen'] = with_error_capture(function() { + $h = @popen('id 2>&1', 'r'); + if (!is_resource($h)) return 'popen failed'; + $out = stream_get_contents($h); + $rc = pclose($h); + return ['rc' => $rc, 'out' => trim((string)$out)]; + }); + line('popen("id")', $execResults['popen']); +} + +section('Fork capability'); +line('extension_loaded(pcntl)', extension_loaded('pcntl')); +line('function_exists(pcntl_fork)', function_exists('pcntl_fork')); +line('Active fork test', 'SKIPPED in web SAPI for safety'); + +section('Controlled memory probe'); +$memoryLimit = bytes_from_ini(ini_get('memory_limit')); +$chunks = []; +$allocated = 0; +$chunkSize = 4 * 1024 * 1024; +$target = ($memoryLimit !== null && $memoryLimit > 0) ? (int)($memoryLimit * 0.70) : 64 * 1024 * 1024; +$oom = false; +$oomMsg = null; +try { + while ($allocated + $chunkSize <= $target) { + $chunks[] = str_repeat('A', $chunkSize); + $allocated += $chunkSize; + } +} catch (Throwable $e) { + $oom = true; + $oomMsg = $e->getMessage(); +} +line('memory_limit parsed', $memoryLimit); +line('allocated safely', $allocated); +line('oom caught', $oom); +line('oom message', $oomMsg ?: 'none'); +unset($chunks); + +section('Controlled CPU / timeout probe'); +$start = microtime(true); +$iterations = 0; +$limitSeconds = max(1, (int)ini_get('max_execution_time')); +$softBudget = min(3, max(1, $limitSeconds - 1)); +while ((microtime(true) - $start) < $softBudget) { + hash('sha256', random_bytes(256), false); + $iterations++; +} +line('elapsed', round(microtime(true) - $start, 3) . 's'); +line('iterations', $iterations); +line('soft budget', $softBudget . 's'); + +section('set_time_limit test'); +$setTimeLimitRes = with_error_capture(function() { + return @set_time_limit(600); +}); +line('set_time_limit(600)', $setTimeLimitRes); +line('max_execution_time after set_time_limit', ini_get('max_execution_time')); + +section('Network reachability'); +$netTargets = [ + ['127.0.0.1', 25], + ['127.0.0.1', 3306], + ['127.0.0.1', 6379], + ['127.0.0.1', 11211], + ['127.0.0.1', 7080], + ['127.0.0.1', 80], + ['127.0.0.1', 443], +]; +$netResults = []; +foreach ($netTargets as [$host, $port]) { + $netResults["$host:$port"] = try_socket($host, $port); + line("$host:$port", $netResults["$host:$port"]); +} + +section('Unix socket reachability'); +$unixCandidates = [ + '/run/mysqld/mysqld.sock', + '/var/run/mysqld/mysqld.sock', + '/tmp/mysql.sock', + '/run/redis/redis-server.sock', + '/var/run/redis/redis.sock', + '/tmp/redis.sock', + '/usr/local/lsws/admin/tmp/admin.sock', +]; +$unixResults = []; +foreach ($unixCandidates as $sock) { + $unixResults[$sock] = try_unix_socket($sock); + line($sock, $unixResults[$sock]); +} + +section('Stream wrappers'); +$wrappers = stream_get_wrappers(); +sort($wrappers); +line('wrappers', $wrappers); + +$wrapperReads = [ + 'php://memory', + 'php://temp', + 'data://text/plain;base64,SGVsbG8=', +]; +foreach ($wrapperReads as $wrapper) { + line("read $wrapper", with_error_capture(function() use ($wrapper) { + $d = @file_get_contents($wrapper); + if ($d === false) return false; + return 'len=' . strlen($d) . ' data=' . substr($d, 0, 40); + })); +} + +section('Include wrapper tests'); +$includeFile = $baseTmp . '/include-test.php'; +file_put_contents($includeFile, " true, 'time' => time()];"); +$includeLocal = with_error_capture(function() use ($includeFile) { + return include $includeFile; +}); +$includeData = with_error_capture(function() { + return @include 'data://text/plain;base64,PD9waHAgcmV0dXJuIFsiZGF0YSI9PnRydWVdOw=='; +}); +line('include local file', $includeLocal); +line('include data:// wrapper', $includeData); + +section('Environment leakage'); +$envKeys = ['HOME','USER','LOGNAME','PATH','TMPDIR','TEMP','HOSTNAME']; +$envOut = []; +foreach ($envKeys as $k) { + $envOut[$k] = getenv($k); +} +line('getenv selected', $envOut); +line('_ENV count', is_array($_ENV) ? count($_ENV) : 'n/a'); +line('_SERVER selected', [ + 'PATH' => $_SERVER['PATH'] ?? null, + 'USER' => $_SERVER['USER'] ?? null, + 'HOME' => $_SERVER['HOME'] ?? null, +]); + +section('Self-request capability'); +$selfUrl = null; +if (!empty($_SERVER['HTTP_HOST'])) { + $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; + $selfUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . ($_SERVER['REQUEST_URI'] ?? '/'); +} +line('self url', $selfUrl ?: 'n/a'); +if ($selfUrl && function_exists('file_get_contents')) { + line('self file_get_contents', with_error_capture(function() use ($selfUrl) { + $ctx = stream_context_create(['http' => ['timeout' => 2]]); + $data = @file_get_contents($selfUrl, false, $ctx); + if ($data === false) return false; + return 'len=' . strlen($data); + })); +} + +section('Session basics'); +$sessionRes = with_error_capture(function() { + if (session_status() !== PHP_SESSION_ACTIVE) { + @session_start(); + } + $_SESSION['audit_test'] = 'ok'; + return session_id(); +}); +line('session.save_path', ini_get('session.save_path')); +line('session_start()', $sessionRes); +line('session file expected', ini_get('session.save_path') . '/sess_' . session_id()); + +section('mail() basics'); +line('function_exists(mail)', function_exists('mail')); +line('sendmail_path', ini_get('sendmail_path')); +line('mail() active send test', 'SKIPPED by design'); + +section('.user.ini verification hint'); +$userIniFile = $docRoot . '/.user.ini.audit-test'; +$userIniContent = << .user.ini, wait cache_ttl, reload script, compare values.'); + +section('Assessment'); + +$openBasedir = (string)ini_get('open_basedir'); +$disableFunctions = (string)ini_get('disable_functions'); +$userIni = (string)ini_get('user_ini.filename'); +$allowUrlInclude = (string)ini_get('allow_url_include'); + +if ($openBasedir !== '') { + add_result('PASS', 'open_basedir is set', $openBasedir); +} else { + add_result('FAIL', 'open_basedir is empty'); +} + +if (!empty($overrideResults['memory_limit']['changed'])) { + add_result('FAIL', 'memory_limit can be changed via ini_set()', json_encode($overrideResults['memory_limit'])); +} else { + add_result('PASS', 'memory_limit is not changeable via ini_set()'); +} + +if (!empty($overrideResults['max_execution_time']['changed'])) { + add_result('FAIL', 'max_execution_time can be changed via ini_set()', json_encode($overrideResults['max_execution_time'])); +} else { + add_result('PASS', 'max_execution_time is not changeable via ini_set()'); +} + +if (!empty($overrideResults['upload_max_filesize']['changed'])) { + add_result('FAIL', 'upload_max_filesize can be changed via ini_set()', json_encode($overrideResults['upload_max_filesize'])); +} else { + add_result('PASS', 'upload_max_filesize resisted ini_set()'); +} + +if (!empty($overrideResults['post_max_size']['changed'])) { + add_result('FAIL', 'post_max_size can be changed via ini_set()', json_encode($overrideResults['post_max_size'])); +} else { + add_result('PASS', 'post_max_size resisted ini_set()'); +} + +if (!empty($overrideResults['open_basedir']['changed'])) { + add_result('FAIL', 'open_basedir can be changed via ini_set()', json_encode($overrideResults['open_basedir'])); +} else { + add_result('PASS', 'open_basedir resisted ini_set()'); +} + +$dangerousAvailable = []; +foreach (['exec','shell_exec','system','passthru','proc_open','popen'] as $fn) { + if (function_exists($fn) && !str_contains($disableFunctions, $fn)) { + $dangerousAvailable[] = $fn; + } +} +if ($dangerousAvailable) { + add_result('FAIL', 'Command execution functions are available', implode(', ', $dangerousAvailable)); +} else { + add_result('PASS', 'Command execution functions are blocked'); +} + +if (extension_loaded('pcntl')) { + add_result('FAIL', 'pcntl extension is loaded', 'Not recommended for shared hosting web SAPI'); +} else { + add_result('PASS', 'pcntl extension is not loaded'); +} + +if ($userIni === '' || strtolower($userIni) === 'none') { + add_result('PASS', '.user.ini appears disabled'); +} else { + add_result('WARN', '.user.ini appears enabled', $userIni); +} + +if ($allowUrlInclude === '' || $allowUrlInclude === '0') { + add_result('PASS', 'allow_url_include is off'); +} else { + add_result('FAIL', 'allow_url_include is on'); +} + +if (is_link($symlinkTarget)) { + add_result('WARN', 'Symlink creation works inside allowed path', $symlinkTarget); +} else { + add_result('PASS', 'Symlink creation did not work'); +} + +if (file_exists($hardlinkTarget)) { + add_result('WARN', 'Hardlink creation works inside allowed path', $hardlinkTarget); +} else { + add_result('PASS', 'Hardlink creation did not work'); +} + +$localhostSensitiveOpen = []; +foreach (['127.0.0.1:25','127.0.0.1:3306','127.0.0.1:6379','127.0.0.1:7080'] as $k) { + if (!empty($netResults[$k]['connected'])) { + $localhostSensitiveOpen[] = $k; + } +} +if ($localhostSensitiveOpen) { + add_result('WARN', 'Sensitive localhost TCP ports reachable', implode(', ', $localhostSensitiveOpen)); +} else { + add_result('PASS', 'Sensitive localhost TCP ports not reachable'); +} + +$reachableUnix = []; +foreach ($unixResults as $sock => $res) { + if (!empty($res['connected'])) { + $reachableUnix[] = $sock; + } +} +if ($reachableUnix) { + add_result('WARN', 'Sensitive UNIX sockets reachable', implode(', ', $reachableUnix)); +} else { + add_result('PASS', 'Sensitive UNIX sockets not reachable'); +} + +section('Score'); +line('PASS', $SCORE['PASS']); +line('WARN', $SCORE['WARN']); +line('FAIL', $SCORE['FAIL']); + +section('Findings'); +foreach ($FINDINGS as [$level, $title, $detail]) { + echo '[' . $level . '] ' . $title; + if ($detail !== '') { + echo ' :: ' . $detail; + } + echo PHP_EOL; +} + +section('Cleanup'); +$cleanupFiles = [ + $renameTo, + $copyToSession, + $src, + $symlinkTarget, + $hardlinkTarget, + $includeFile, + $userIniFile, +]; +foreach ($cleanupFiles as $f) { + if (is_link($f) || file_exists($f)) { + @unlink($f); + } +} +@rmdir($baseTmp); + +echo PHP_EOL . "Done." . PHP_EOL; diff --git a/sodew-bash-main/assets/wordpress/shared/00-hosting-loader.php b/sodew-bash-main/assets/wordpress/shared/00-hosting-loader.php new file mode 100644 index 0000000..e18b3d2 --- /dev/null +++ b/sodew-bash-main/assets/wordpress/shared/00-hosting-loader.php @@ -0,0 +1,5 @@ +'; + echo '

    MU TEST OK — shared MU plugin.

    '; + echo ''; +}); + +add_action('admin_bar_menu', function ($wp_admin_bar) { + if (!current_user_can('manage_options')) { + return; + } + + $wp_admin_bar->add_node([ + 'id' => 'mu-test-ok', + 'title' => 'MU TEST OK', + 'href' => admin_url('plugins.php?plugin_status=mustuse'), + 'meta' => [ + 'title' => 'MU plugin', + ], + ]); +}, 100); + +add_action('wp_footer', function () { + if (!current_user_can('manage_options')) { + return; + } + + echo '
    MU TEST OK
    '; +}); diff --git a/sodew-bash-main/assets/wordpress/shared/mu-plugin/load.php b/sodew-bash-main/assets/wordpress/shared/mu-plugin/load.php new file mode 100644 index 0000000..f932aab --- /dev/null +++ b/sodew-bash-main/assets/wordpress/shared/mu-plugin/load.php @@ -0,0 +1,7 @@ + + * @copyright SODEW, s.r.o. + * + * @wordpress-plugin + * Plugin Name: SODEW SMTP config + * Plugin URI: https://sodew.ai + * Description: SMTP config + * Author: SODEW + * Author URI: https://sodew.ai + * Version: 1.1 + */ + +defined('ABSPATH') || exit; + +add_action('phpmailer_init', function ($phpmailer) { + $domain = wp_parse_url(home_url(), PHP_URL_HOST); + $fromName = defined('SODEW_SMTP_FROM_NAME') ? SODEW_SMTP_FROM_NAME : 'WordPress'; + $replyTo = defined('SODEW_SMTP_REPLY_TO') ? SODEW_SMTP_REPLY_TO : "wordpress@$domain"; + $replyToName = defined('SODEW_SMTP_REPLY_TO_NAME') ? SODEW_SMTP_REPLY_TO_NAME : $fromName; + + $phpmailer->isSMTP(); + $phpmailer->XMailer = 'sodewMailer 1.1'; + $phpmailer->Host = 'postfix'; + $phpmailer->Port = 587; + $phpmailer->SMTPAuth = true; + $phpmailer->SMTPSecure = 'tls'; + $phpmailer->SMTPAutoTLS = true; + $phpmailer->SMTPOptions = [ + 'ssl' => [ + 'verify_peer' => true, + 'verify_peer_name' => true, + 'peer_name' => SODEW_SMTP_HOST, + 'allow_self_signed' => true, + ], + ]; + + $phpmailer->Username = SODEW_SMTP_USER; + $phpmailer->Password = SODEW_SMTP_PASS; + $phpmailer->setFrom(SODEW_SMTP_POSTMASTER, $fromName, false); + $phpmailer->Sender = SODEW_SMTP_POSTMASTER; + $phpmailer->clearReplyTos(); + $phpmailer->addReplyTo($replyTo, $replyToName); +}); diff --git a/sodew-bash-main/assets/worker/worker.py b/sodew-bash-main/assets/worker/worker.py new file mode 100644 index 0000000..e5dd1d0 --- /dev/null +++ b/sodew-bash-main/assets/worker/worker.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 + +import os +import time +import json +import threading +import http.server +from pathlib import Path +from urllib.parse import urlencode +from urllib.request import Request, urlopen +from urllib.error import URLError, HTTPError +from collections.abc import Mapping +from urllib.response import addinfourl +from typing import cast, Any +from datetime import datetime + +class H(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/healthz": + self.send_response(200) + self.end_headers() + self.wfile.write(b"ok") + else: + self.send_response(404) + self.end_headers() + def log_message(self, format, *args): + pass + +def int_env(name: str, default: int) -> int: + try: + return int(os.getenv(name, str(default))) + except Exception: + return default + +TASKS_PATH = Path("/app/tasks") +API_URL = os.getenv("API_URL", "https://api.sodew.ai/api/tasks").rstrip("/") +WORKER_UUID = os.getenv("WORKER_UUID", "worker-uuid") +WORKER_NAME = os.getenv("WORKER_NAME", "worker-name") +WORKER_POOL = os.getenv("WORKER_POOL", "") +WORKER_VERSION = "6.3.445" +GETTING_PAUSE = int_env("GETTING_PAUSE", 30) +SENDING_PAUSE = int_env("SENDING_PAUSE", 15) + +HTTP_TIMEOUT = 15 +DEFAULT_HEADERS = {"Accept": "application/json", "Content-Type": "application/json", "User-Agent": "kube-worker/1.1"} + +def start_health(): + t = threading.Thread(target=http.server.HTTPServer(('0.0.0.0', 8080), H).serve_forever, daemon=True) + t.start() + +def ensure_dir() -> None: + TASKS_PATH.mkdir(parents=True, exist_ok=True) + +def log_info(text: str) -> None: + print(datetime.now().strftime("[%Y.%m.%d %H:%M:%S]") + f" {text}") + +def format_error_body(body: Any) -> str: + if body is None: + return "" + + if isinstance(body, dict): + msg = body.get("message") + if isinstance(msg, str) and msg.strip(): + return msg + + try: + return json.dumps(body, ensure_ascii=False, sort_keys=True) + except Exception: + return repr(body) + + if isinstance(body, list): + try: + return json.dumps(body, ensure_ascii=False, sort_keys=True) + except Exception: + return repr(body) + + try: + return str(body) + except Exception: + return "" + +def task_read(path: Path) -> dict[str, str]: + result: dict[str, str] = {} + for line in path.read_text(encoding="utf-8", errors="ignore").splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, v = line.split("=", 1) + result[k.strip()] = v.strip() + return result + +def task_save(path: Path, data: Mapping[str, object]) -> None: + flat: dict[str, object] = dict(data) + lines: list[str] = [] + for key, value in flat.items(): + if not isinstance(key, str): + key = str(key) + + if value is None: + value_str = "" + elif isinstance(value, (dict, list)): + try: + value_str = json.dumps(value, ensure_ascii=False) + except Exception: + value_str = str(value) + else: + value_str = str(value) + + value_str = value_str.replace("\n", " ").replace("\r", " ") + lines.append(f"{key}={value_str}") + + content = "\n".join(lines) + "\n" + path.write_text(content, encoding="utf-8") + +def http_request_json( + method: str, + url: str, + *, + params: Mapping[str, str] | None = None, + json_body: Mapping[str, object] | None = None, + headers: Mapping[str, str] | None = None, + timeout: int = HTTP_TIMEOUT, +) -> tuple[int, object | None]: + if params: + qs = urlencode(params) + url = f"{url}?{qs}" + body_bytes = None + req_headers = dict(DEFAULT_HEADERS) + if headers: + req_headers.update(headers) + if json_body is not None: + body_bytes = json.dumps(json_body).encode("utf-8") + req = Request(url, data=body_bytes, headers=req_headers, method=method) + try: + with urlopen(req, timeout=timeout) as resp: + resp_typed = cast(addinfourl, resp) + code = resp_typed.getcode() or 0 + body = resp_typed.read() + + except HTTPError as e: + try: + err_bytes = e.read() + except Exception: + err_bytes = b"" + if not err_bytes: + return e.code, None + try: + return e.code, json.loads(err_bytes.decode("utf-8", errors="replace")) + except Exception: + return e.code, err_bytes.decode("utf-8", errors="replace") + except URLError as e: + return 0, {"error": "network", "reason": str(e)} + + if not body: + return code, None + try: + return code, json.loads(body.decode("utf-8", errors="replace")) + except Exception: + return code, None + +def task_receive() -> dict[str, Any] | None: + log_info(f"Receiving new tasks from API | Worker: {WORKER_NAME}") + url = f"{API_URL}/receive" + payload: dict[str, str] = { + "worker_name": WORKER_NAME, + "worker_uuid": WORKER_UUID, + "worker_version": WORKER_VERSION + } + if WORKER_POOL and WORKER_POOL.strip(): + payload["worker_pool"] = WORKER_POOL.strip() + + code, body = http_request_json("POST", url, json_body=payload) + + if code == 204: + log_info("Code 204 | No tasks from API") + return None + + if code == 200: + try: + log_info("Code: 200 | Raw data: " + (json.dumps(body, ensure_ascii=False, sort_keys=True) if isinstance(body, (dict, list)) else repr(body))) + except Exception: + log_info("Code: 200 | Raw data: ") + + if isinstance(body, dict): + d = cast(dict[str, object], body) + try: + log_info("Task: received | " + json.dumps(d, ensure_ascii=False, sort_keys=True)) + except Exception: + log_info("Task: received | ") + + if "uuid" in d and "action" in d: + return d + + log_info("Task: missing required fields 'uuid' or 'action'") + else: + log_info("Task: not recognized (body is not a dict)") + else: + message_error = format_error_body(body) + log_info(f"Code: {code} | Error: {message_error}") + + return None + +def main() -> None: + start_health() + ensure_dir() + + while True: + task_files = list(TASKS_PATH.glob("*.task")) + task_count = len(task_files) + log_info(f"Task files found: {task_count}") + + if not task_files: + task = task_receive() + if task: + uuid = str(task.get("uuid", "")).strip() + action = str(task.get("action", "")).strip() + task.pop("uuid", None) + task["status"] = "waiting" + if uuid and action: + log_info(f"Task from API: {uuid}") + path = TASKS_PATH / f"{uuid}.task" + if not path.exists(): + task_save(path=path, data=task) + else: + log_info(f"Task: {uuid} | Error: uuid or action is empty, skip") + time.sleep(GETTING_PAUSE) + else: + for path in task_files: + uuid = path.stem + try: + data = task_read(path) + log_info(f"Task: {uuid} | Parse task success") + except Exception: + log_info(f"Task: {uuid} | Task not recognized") + continue + action = (data.get("action") or "unknown").strip().lower() + if action == "pause": + continue + status = (data.get("status") or "unknown").strip().lower() + + payload: dict[str, str] = dict(data) + payload["worker_uuid"] = WORKER_UUID + payload["status"] = status + log_info(f"Task: {uuid} | Payload: {json.dumps(payload, ensure_ascii=False)}") + url = f"{API_URL}/{uuid}/status" + code, body = http_request_json("PATCH", url, json_body=payload) + if not (200 <= code < 300): + message_error = format_error_body(body) + log_info(f"Task: {uuid} | Code: {code} | Error: {message_error}") + continue + + if status not in {"new", "waiting", "execution"}: + log_info(f"Task: {uuid} | Remove...") + try: + path.unlink(missing_ok=True) + except Exception: + pass + + time.sleep(SENDING_PAUSE) + +if __name__ == "__main__": + print(f"[ Worker Agent {WORKER_VERSION} | {datetime.now():%Y-%m-%d %H-%M-%S} ______________ ]") + print(f"🔹Worker: {WORKER_NAME} | {WORKER_UUID}") + print(f"🔹Tasks path: {TASKS_PATH}") + print(f"🔹API URL: {API_URL}") + try: + main() + except KeyboardInterrupt: + print("🔥Interrupted by user.") diff --git a/sodew-bash-main/config.sh.default b/sodew-bash-main/config.sh.default new file mode 100644 index 0000000..aa6cd22 --- /dev/null +++ b/sodew-bash-main/config.sh.default @@ -0,0 +1,169 @@ +# App +appAssetsPath="$appPath/assets" +appDataPath="$appPath/data" + +hostName=$(hostname) +hostIp="127.0.0.1" +hostUuid=$(fileValueGetOrCreate "$appDataPath/$hostName.uuid" $(uuidgen)) +hostSalt=$(filePasswordGetOrCreate "$appDataPath/$hostName.salt") +basePath="/_data" +domainsList="$appAssetsPath/domains.list" +sftpAccessGroup="sftp-access" +dnsResolver="@1.1.1.1" +pigzThreads="4" +rocketChatUrl="" + +# Logs +logPath="$appDataPath/logs" +logFile="$logPath/$appDate.log" + +# CPU/Memory profiles 4c/8c/16c/32c 16g/32g/64g/128g +kubeCpuProfile="8c" +kubeMemoryProfile="16g" + +# k3s +k3sCmd="/usr/local/bin/k3s" +k3sNamespace="sodew-dev" +k3sReadyRetries=20 +k3sReadySleep=4 + +# Redis +redisKube="redis" +redisSentinelKube="$redisKube-sentinel" +redisYaml="$appDataPath/yaml/$redisKube.yaml" +redisPath="$basePath/$redisKube" +redisFileUsersAcl="users.acl" +redisRootPass=$(filePasswordGetOrCreate "$appDataPath/$hostName.$redisKube") + +# MariaDB +mariadbKube="mariadb" +mariadbMasterKube="$mariadbKube-master" +mariadbSlaveKube="$mariadbKube-slave" +mariadbYaml="$appDataPath/yaml/$mariadbKube.yaml" +mariadbMasterPath="$basePath/$mariadbKube/master" +mariadbSlavePath="$basePath/$mariadbKube/slave" +mariadbRootPass=$(filePasswordGetOrCreate "$appDataPath/$hostName.$mariadbKube") + +# OpenLiteSpeed +olsKube="openlitespeed" +olsYaml="$appDataPath/yaml/$olsKube.yaml" +olsPath="$basePath/$olsKube" +olsVhostsPath="$basePath/vhosts" +olsPrivatePath="$olsPath/vhosts-private" +olsPublicPath="$olsPath/vhosts-public" +olsLogsPath="$olsPath/logs" +olsAdminPath="$olsPath/admin" +olsPhpIniPath="$olsPath/php-ini" +olsConfigPath="$olsPath/config" +olsConfigFile="$olsConfigPath/httpd_config.conf" +olsVhostsConfigPath="$olsConfigPath/vhosts" +olsPodVhostsPath="/var/www/vhosts" +olsPodPrivatePath="/var/www/private" +olsPodPublicPath="/var/www/public" +olsAdminWhiteList=("0.0.0.0/0") +olsAdminPass=$(filePasswordGetOrCreate "$appDataPath/$hostName.$olsKube") +olsVhostMode="standalone" +olsVhostFile="virtual.host.conf" +olsVhostTemplate="v.template" +olsQuotaBlockLimit=10485760 +olsQuotaInodeLimit=100000 +olsPhpList=() # olsPhpList=(81 82 83 84 85) + +# Postfix +postfixKube="postfix" +postfixYaml="$appDataPath/yaml/$postfixKube.yaml" +postfixPath="$basePath/$postfixKube" +postfixConfigPath="$postfixPath/config" +postfixDkimPath="$postfixPath/dkim" +postfixTlsCrtFile="$appDataPath/$postfixKube/tls.crt" +postfixTlsKeyFile="$appDataPath/$postfixKube/tls.key" +postfixHost="mta.example.com" +postfixPostmaster="postmaster@$postfixHost" +postfixDefaultRealm="auth.mta" +postfixDkimSelector="dkim" +postfixDomainsFile="virtual_domains.maps" +postfixAliasesFile="virtual_aliases.maps" +postfixSendersFile="senders.maps" +postfixIp="$hostIp" + +# Worker +workerKube="worker" +workerYaml="$appDataPath/yaml/$workerKube.yaml" +workerPath="$basePath/$workerKube" +workerAgentPath="$workerPath/agent" +workerTasksPath="$workerPath/tasks" +workerFilesPath="$appDataPath/$workerKube" +workerName="$hostName" +workerPool="" +workerApiUrl="https://api.sodew.ai/api/tasks" +workerApiGettingPause=30 +workerApiSendingPause=15 + +# Metric +metricKube="metric" +metricYaml="$appDataPath/yaml/$metricKube.yaml" +metricApiUrl="https://metric.api.sodew.ai/api/v1/write" +metricPath="$basePath/$metricKube" +metricPromPath="$metricPath/prom" +metricVmagentPath="$metricPath/vmagent" + +# Diag +diagKube="diag" +diagDeep=0 +diagSince="4h" +diagOpcacheUrl="https://demo.133.vedos.cz/__opcache.php" +diagApiUrl="https://api.sodew.ai/api/diag" +diagFile="$appDataPath/$diagKube/${appDate}_$appTime.report" + +# Malware +malwareKube="malware" +malwarePath="$appDataPath/$malwareKube" +malwareQuarantinePath="$malwarePath/quarantine" + +# Backups +backupType="tar" +backupFirstDir="_first" +backupParallelJobs=1 +backupDailyPath="$appDataPath/backup-daily" +backupDailySuffix="" +backupDailyKeep=3 +backupArchivePath="$appDataPath/backup-archive" +backupArchiveSuffix=".archive" +backupArchiveInterval=30 + +# Storage +# Default path to remote storage root for rSync / FTP / SSH +storageType="rsync" +storagePath="/$hostName" +storageDailyKeep=10 +storageArchiveKeep=3 + +# Storage rSync +rsyncUri="username@wedos.net/path" +rsyncPassFile="$appDataPath/$hostName.rsync" +filePasswordGetOrCreate "$rsyncPassFile" >/dev/null +rsyncPath="$storagePath" + +# Storage FTP +ftpHost="wedos.net" +ftpPort="21" +ftpUser="user" +ftpPass=$(filePasswordGetOrCreate "$appDataPath/$hostName.ftp") +ftpPath="$storagePath" + +# Storage SSH +sshHost="wedos.net" +sshUser="user" +sshKeyFile="/home/user/.ssh/ssh_key" +sshPath="/_storage$storagePath" + +# List of tasks for CRON +cronJobs=( + "* * * * * /bin/bash $appPath/$appFile --script metric-kube" + "0 * * * * /bin/bash $appPath/$appFile --script metric-sites" + "*/5 * * * * /bin/bash $appPath/$appFile --script worker-observer" + "20,40 * * * * /bin/bash $appPath/$appFile --script diag-report-ai-short" + "5 * * * * /bin/bash $appPath/$appFile --script diag-report-ai-full" + "0 * * * * /bin/bash $appPath/$appFile --script backup" + "* * * * * /bin/bash $appPath/$appFile --script unigma" +) diff --git a/sodew-bash-main/docs/backup.md b/sodew-bash-main/docs/backup.md new file mode 100644 index 0000000..b04964b --- /dev/null +++ b/sodew-bash-main/docs/backup.md @@ -0,0 +1,151 @@ +[KUBE](../README.md)  /  Backup + +# Backup + +- [Creation](#creation) +- [Verification](#verification) +- [Cleanup](#cleanup) +- [LongTerm](#longterm) +- [Commands](#commands) +*** + +## Creation +Directory structure of backups on the host machine: `//`. +- `` – set by the `backupPath` parameter. +- `` – backup creation date in the format `YYYY-MM-DD`. +- `` – a marker indicating the backup creation time in the format `HH-MM-SS`, with the first backup of the day named ``. +When the backup process starts, a directory `//` is created (if it does not exist), where the backup files will be stored. + +First, a backup of files is created depending on the `backupType` parameter: +- `archive` – all subdirectories from `vhostsPath` are packed individually into separate archives named `.tar.gz`. +- `rsync` – backup of the `vhostsPath` directory contents using the `rSync` utility. + +Then, in each subdirectory of `vhostsPath`, the file `www/wp-config.php` is searched for, and database connection parameters are read from it. After that, the database is exported to a file and packed into an archive named `.sql.tar.gz`. + +Next, the backup is copied to an external storage using the `rSync` utility. Two types of external storage (`storageType`) are supported: +- `rsync` – for full functionality, connection parameters `rRync` and `FTP` must be set. +- `ssh` – for full functionality, connection parameters `SSH` must be set. + +Backup directory structure on external storage: `/`. Set by the following parameters: +- `storagePath` – general path parameter for external storage +- `rsyncPath` – path parameter for `rSync` +- `ftpPath` – path parameter for `FTP` +- `sshPath` – path parameter for `SSH` + +Example: +```bash +storagePath="/$hostname" +rsyncPath="$storagePath" +ftpPath="$storagePath" +sshPath="/_storage$storagePath" +``` +Inside `/`, the directory structure on external storage fully mirrors the structure of `` on the host machine. +*** + +## Verification +There are two types of backup verification available: verifying the latest backup and verifying all backups created on the current day. + +When verifying the latest backup, the system searches for the latest backup execution log (directory `logs/backup` in the script folder). The file name is used to check the elapsed time since the last backup creation (parameter `backupElapsedMax`). The log contains the backup creation directory. Then, a non-recursive scan of subdirectories and files in the backup directory `//` is performed. All directories and `*.tar.gz` files are considered website file backups, while `*.sql.tar.gz` files are considered database backups. A comparison is made between website file backups and database backups, and any discrepancies are noted. Next, the archive file sizes are checked (the minimum allowable size is set by the parameter `backupFileSizeMin`). + +The next step is verifying the backup on external storage. The presence of the same subdirectories and files (non-recursively) is checked: +```bash +///* --> ////* +``` +A non-recursive file size comparison is performed, and any discrepancies are noted. + +When verifying backups created on the current day, the day's directories are first compared: +```bash +//* --> ///* +``` +Then, each subdirectory is compared as described above. +*** + +## Cleanup +Backups cleanup occurs once every 24 hours. It is configured using two parameters: +- `backupDays` – the number of past days (excluding the current day) for storing backups on the host machine. +- `storageDays` – the number of past days (excluding the current day) for storing backups on external storage. + +Example: +```bash +/2025-05-20/ +/2025-05-10/ +/2025-05-05/ +/2025-05-01/ +``` + +When `backupDays=2` and run `2025-05-20` will remain: +```bash +/2025-05-20/ +/2025-05-10/ +/2025-05-05/ +``` + +If `backupDays=2` and run after `2025-05-20` will remain: +```bash +/2025-05-20/ +/2025-05-10/ +``` +*** + +## LongTerm +The `backupLongTermDays` parameter is responsible for the number of days for a long-term backup. +When the corresponding command, the following happens +1. The contents of the long-term backups directory (`/_longterm`) are checked. +2. All copies older than `backupLongTermDays` except the earliest one are deleted. +3. If there are no backups younger than `backupLongTermDays`, the first backup for the last day available is moved: +```bash +// --> /_longterm// +``` +*** + +## Commands + +### `-b, --backup [option]` +Backup files and database of one site or all sites. Options: +- `archive` - backup all domains to archives +- `rsync` - backup all domains using rSync +- `` - backup domain to archives (e.g., `example.com`) + +> [!NOTE] +> The default value (`archive` or `rsync`) is set by the `backupType` variable in the configuration file `config.sh`. + +Example: +```bash +sudo kube.sh -b +sudo kube.sh -b archive +sudo kube.sh -b example.com +``` + +Directory structure of backups: `//` +- `` - Date format `YYYY-MM-DD` (e.g., `2025-05-20`) +- `` - First backup for day: `` (`_first`), all next in time format `HH-MM-SS` (e.g., `09-30-55`) + +Backup options: +- `archive` - creates 2 archives and copies the `.conf` file for all sites + copies file `map.conf`: + - `///.tar.gz` + - `///.sql.tar.gz` + - `///.conf` + - `///map.conf` +- `rsync` - copies the `vhostsPath` virtual hosts folders, creates database archives and copied and database archives are created and copies `.conf` file for all sites + copies file `map.conf`: + - `///` + - `///.sql.tar.gz` + - `///.conf` + - `///map.conf` +- `domain` - creates 2 archives and copies the `.conf` file for the site: + - `///.tar.gz` + - `///.sql.tar.gz` + - `///.conf` + +Example: +```bash +/backup/2025-05-21/08-00-00/example.com +/backup/2025-05-21/08-00-00/example.com.conf +/backup/2025-05-21/08-00-00/example.com.sql.tar.gz +/backup/2025-05-21/_first/example.com +/backup/2025-05-21/_first/example.com.conf +/backup/2025-05-21/_first/example.com.sql.tar.gz +/backup/2025-05-21/_first/map.conf +/backup/2025-05-21/12-12-12/example.com.conf +/backup/2025-05-20/12-12-12/example.com.tar.gz +/backup/2025-05-20/12-12-12/example.com.sql.tar.gz +``` diff --git a/sodew-bash-main/docs/cron.md b/sodew-bash-main/docs/cron.md new file mode 100644 index 0000000..cfd4f89 --- /dev/null +++ b/sodew-bash-main/docs/cron.md @@ -0,0 +1,22 @@ +[KUBE](../README.md)  /  CRON + +# CRON + +> Task management in CRON for scripts + +## Commands + +### `--cron ` + +Working with CRON: +- `add` - Adding jobs to CRON +- `remove` - Removing jobs from CRON +- `clean` - Clearing jobs of this script from CRON +- `list` - Get list tasks for ROOT (default) + +Example: +```bash +sudo kube.sh --cron +sudo kube.sh --cron add +sudo kube.sh --cron clean +``` diff --git a/sodew-bash-main/docs/file_scructure.md b/sodew-bash-main/docs/file_scructure.md new file mode 100644 index 0000000..7767386 --- /dev/null +++ b/sodew-bash-main/docs/file_scructure.md @@ -0,0 +1,21 @@ +[KUBE](../README.md)  /  File structure + +# File structure + +- `assets/` - Supporting materials for infrastructure deployment +- `assets/vhost.default/` - Template for vhosts without Wordpress (If there is) +- `assets/vhost.wordpress/` - Template for vhosts with Wordpress (If there is) +- `assets/yaml/*` - YAML templates for k3s +- `assets/domains.list` - List of domains for Wordpress deployment +- `assets/php.ini` - PHP settings file for OpenLiteSpeed +- `assets/vhost.default.tar.gz` - Default packaged image of the site +- `assets/vhost.wordpress.tar.gz` - Packaged Wordpress image +- `assets/*.template` - Templates of files +- `backups/` - Backup directory (default) +- `data/` - Directory with service data for script +- `docs/` - Directory with documents +- `libs/` - Directory with function libraries +- `logs/` - Directory with journals +- `config.sh.default` - Settings script (example) +- `kube.sh` - Executable script +- `README.md` - Reference Guide diff --git a/sodew-bash-main/docs/install.md b/sodew-bash-main/docs/install.md new file mode 100644 index 0000000..fdfe0d1 --- /dev/null +++ b/sodew-bash-main/docs/install.md @@ -0,0 +1,14 @@ +[KUBE](../README.md)  /  Install + +# Install + +> Scenarios for fully deploying the infrastructure for full operation. Install APK, update ipset/iptables, install kubernetes, create namespaces, apply yaml-files ... + +## Commands + +### `--install` + +Example: +```bash +sudo kube.sh --install +``` diff --git a/sodew-bash-main/docs/k3s.md b/sodew-bash-main/docs/k3s.md new file mode 100644 index 0000000..54a12f1 --- /dev/null +++ b/sodew-bash-main/docs/k3s.md @@ -0,0 +1,44 @@ +[KUBE](../README.md)  /  k3s + +# k3s + +## Resources + +- [MariaDB](resources/mariadb.md) +- [Redis](resources/redis.md) +- [OpenLiteSpeed](resources/openlitespeed.md) +- [Postfix](resources/postfix.md) +- [Transfer](resources/transfer.md) + +## Commands + +### `-k, --k3s [option]` +Options: +- `create` - Create all resources +- `clean` - Remove all resources +- `status` - Status about a k3s resources +- `info` - Detail about a k3s resources +- `version` - Version info +- `pod` - Get resources types of Pod +- `pv` - Get resources types of PersistentVolume (PV) +- `pvc` - Get resources types of PersistentVolumeClaim (PVC) +- `service` - Get resources types of Service +- `ing` - Get resources types of Ingress +- `rs` - Get resources types of ReplicaSet +- `sts` - Get resources types of StatefulSet +- `deploy` - Get resources types of Deployment +- `secret` - Get resources types of Secret +- `cm` - Get resources types of ConfigMap +- `ds` - Get resources types of DaemonSet +- `job` - Get resources types of Job +- `cj` - Get resources types of CronJob +- `ep` - Get resources types of Endpoint +- `nodes` - Get resources types of Node +- `cs` - Get resources types of ComponentStatuses + +Example: +```bash +sudo kube.sh -k status +sudo kube.sh -k pod +sudo kube.sh -k +``` diff --git a/sodew-bash-main/docs/log.md b/sodew-bash-main/docs/log.md new file mode 100644 index 0000000..ccc25fe --- /dev/null +++ b/sodew-bash-main/docs/log.md @@ -0,0 +1,38 @@ +[KUBE](../README.md)  /  Log + +# Log + +> Opening the logs in the pods. + +## Commands + +### `--log` + +> [!NOTE] +> Standard kubectl parameters for logs can be added, for example: +> `-f`: logs should be streamed
    +> `--previous`: print the logs for the previous instance of the container in a pod if it exists. + +Example: +```bash +sudo kube.sh --log +sudo kube.sh --log -f --tail=30 +``` + +Examples of responses: +```bash +🔹Log + 1: mariadb-master-0 [Running] + 2: mariadb-slave-0 [Running] + 3: openlitespeed-0 [Running] + 4: openlitespeed-1 [Running] + 5: postfix-78c47b95f6-42jcq [Running] + 6: redis-0 [Running] + 7: redis-1 [Running] + 8: redis-2 [Running] + 9: redis-sentinel-0 [Running] +10: redis-sentinel-1 [Running] +11: redis-sentinel-2 [Running] +12: worker-6cd4bdb6b8-c6f5d [Running] +Specify the pod number [0]: +``` diff --git a/sodew-bash-main/docs/mta.md b/sodew-bash-main/docs/mta.md new file mode 100644 index 0000000..eea2ea1 --- /dev/null +++ b/sodew-bash-main/docs/mta.md @@ -0,0 +1,286 @@ +[KUBE](../README.md)  /  Mail server + +# Mail server + +## Template of zone +```zone +$TTL 300 +@ IN SOA ns1.mydns.example. dnsadmin.mydomain.com. ( + 2025091001 ; serial + 3600 ; refresh + 900 ; retry + 1209600 ; expire + 300 ; minimum +) + IN NS ns1.mydns.example. + IN NS ns2.mydns.example. + +; ===== A/AAAA ===== +mta IN A {{PUBLIC_IPV4}} +; mta IN AAAA {{PUBLIC_IPV6}} ; if available + +; if desired, client sites can resolve to the same IP +{{US1}} IN A {{PUBLIC_IPV4}} +{{US2}} IN A {{PUBLIC_IPV4}} +{{US3}} IN A {{PUBLIC_IPV4}} + +; ===== MX ===== +; @ IN MX 10 mta.{{ROOT_DOMAIN}}. ; the root domain also accepts mail, if needed +{{US1}} IN MX 10 mta.{{ROOT_DOMAIN}}. +{{US2}} IN MX 10 mta.{{ROOT_DOMAIN}}. +{{US3}} IN MX 10 mta.{{ROOT_DOMAIN}}. + +; ===== SPF ===== +; @ IN TXT "v=spf1 mx ~all" ; if available +mta IN TXT "v=spf1 a -all" +{{US1}} IN TXT "v=spf1 mx ~all" +{{US2}} IN TXT "v=spf1 mx ~all" +{{US3}} IN TXT "v=spf1 mx ~all" + +; ===== DKIM ===== +; For each customer domain, publish its own public key. +; The selector can be shared (e.g., selector1); keys are different. +selector1._domainkey.{{US1}} IN TXT "v=DKIM1; k=rsa; p={{PUBKEY_US1}}" +selector1._domainkey.{{US2}} IN TXT "v=DKIM1; k=rsa; p={{PUBKEY_US2}}" +selector1._domainkey.{{US3}} IN TXT "v=DKIM1; k=rsa; p={{PUBKEY_US3}}" + +; ===== DMARC ===== +; Initially p=none to collect reports without impacting delivery. +_dmarc.{{US1}} IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:{{DMARC_AGG}}; ruf=mailto:{{DMARC_FOR}}" +_dmarc.{{US2}} IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:{{DMARC_AGG}}; ruf=mailto:{{DMARC_FOR}}" +_dmarc.{{US3}} IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:{{DMARC_AGG}}; ruf=mailto:{{DMARC_FOR}}" +; It is recommended to set DMARC on the root domain to cover ALL subdomains with a single policy. +; _dmarc IN TXT "v=DMARC1; p=quarantine; sp=quarantine; adkim=r; aspf=r; rua=mailto:{{DMARC_AGG}}; ruf=mailto:{{DMARC_FOR}}" +; (if test mode first — replace p=none, sp=none) + +; ===== Additionally (optional but useful) ===== +; MTA-STS (if to implement) +;_mta-sts IN TXT "v=STSv1; id=2025-09-10" +;_smtp._tls IN TXT "v=TLSRPTv1; rua=mailto:tlsrpt@{{ROOT_DOMAIN}}" +;autoconfig IN CNAME autoconfig.mailhost.example. ; for client autoconfiguration +;autodiscover IN CNAME autodiscover.mailhost.example. +``` + +```zone +; ===== PTR ===== +{{PUBLIC_IPV4}} → mta.{{ROOT_DOMAIN}} +``` + +Check: Postfix HELO/EHLO = mta.`{{ROOT_DOMAIN}}` + +## Example +`{{ROOT_DOMAIN}}` → krumax.cz \ +`{{PUBLIC_IPV4}}` → 31.31.73.67 \ +`{{US1}}`/`{{US2}}`/`{{US3}}` → us1 / us2 / us3 \ +`{{PUBKEY_US1}}`/`{{PUBKEY_US2}}`/`{{PUBKEY_US3}}` → DKIM public keys (only the content after `p=`, in a single line) \ +`{{DMARC_AGG}}`/`{{DMARC_FOR}}` → Addresses for DMARC reports (for example, dmarc@krumax.cz) + +```zone +$TTL 300 + +; ===== A ===== +mta IN A 31.31.73.67 +us1 IN A 31.31.73.67 +us2 IN A 31.31.73.67 +us3 IN A 31.31.73.67 + +; ===== MX ===== +us1 IN MX 10 mta.krumax.cz. +us2 IN MX 10 mta.krumax.cz. +us3 IN MX 10 mta.krumax.cz. + +; ===== SPF ===== +mta IN TXT "v=spf1 a -all" +us1 IN TXT "v=spf1 mx ~all" +us2 IN TXT "v=spf1 mx ~all" +us3 IN TXT "v=spf1 mx ~all" + +; ===== DKIM ===== +selector1._domainkey.us1 IN TXT "v=DKIM1; k=rsa; p=MIIBI...(us1)" +selector1._domainkey.us2 IN TXT "v=DKIM1; k=rsa; p=MIIBI...(us2)" +selector1._domainkey.us3 IN TXT "v=DKIM1; k=rsa; p=MIIBI...(us3)" + +; ===== DMARC ===== +_dmarc.us1 IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:dmarc@krumax.cz; ruf=mailto:dmarc@krumax.cz" +_dmarc.us2 IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:dmarc@krumax.cz; ruf=mailto:dmarc@krumax.cz" +_dmarc.us3 IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:dmarc@krumax.cz; ruf=mailto:dmarc@krumax.cz" +``` + +```zone +; ===== PTR ===== +31.31.73.67 → mta.krumax.cz +``` + + +## Example of adding a new domain in Postfix +1. Adding the domain and generating DKIM +```bash +sudo kube.sh postfix add us2.krumax.cz +``` +2. Retrieving DKIM +```bash +sudo kube.sh postfix dkim us2.krumax.cz +``` +3. Adding DNS records: +```zone +us2 IN A 31.31.73.67 +us2 IN MX 10 mta.krumax.cz. +selector1._domainkey.us2 IN TXT "v=DKIM1; k=rsa; p=MIIBI...(from step 2)" +_dmarc.us2 IN TXT "v=DMARC1; p=none; adkim=r; aspf=r; rua=mailto:dmarc@krumax.cz; ruf=mailto:dmarc@krumax.cz" +``` + + +## Send mail in PHP. +Create file for test send mail `send-mail.php` +```php +", + "Reply-To: $from", + "Return-Path: $return", + "Date: " . date('r'), + "Message-ID: <" . time() . "." . bin2hex(random_bytes(6)) . "@" . $hostname . ">", + "MIME-Version: 1.0", + "Content-Type: text/plain; charset=UTF-8", + "Content-Transfer-Encoding: quoted-printable", + "List-Unsubscribe: , ", +]; +$headersStr = implode("\r\n", $headers); + +$status = mail("$toName <$to>", $subject, $bodyQP, $headersStr, "-f$return"); +echo $status ? "Success\n" : "Failed\n"; +``` + + +## Send mail in Wordpress. +1. Add Wordpress plugin `/wp-content/mu-plugins/smtp.php` +```php +isSMTP(); + $phpmailer->XMailer = 'krumaxMailer 1.0'; + $phpmailer->Host = 'mta.krumax.cz'; + $phpmailer->Port = 25; + $phpmailer->SMTPAuth = false; + $phpmailer->SMTPSecure = ''; + $phpmailer->SMTPAutoTLS = false; + $phpmailer->From = 'no-reply@us1.krumax.cz'; + // $phpmailer->FromName = 'Support team US1'; + // $phpmailer->Hostname = 'us1.krumax.cz'; + // $phpmailer->Encoding = 'quoted-printable'; + // $phpmailer->Sender = 'bounce@us1.krumax.cz'; + // $phpmailer->Timeout = 15; +}); +``` +```php +isSMTP(); + $phpmailer->XMailer = 'krumaxMailer 1.0'; + $phpmailer->Host = 'mta.krumax.cz'; + $phpmailer->Port = 587; + $phpmailer->Username = 'postmaster@us1.krumax.cz'; + $phpmailer->Password = 'qwerty'; + $phpmailer->SMTPAuth = true; + $phpmailer->SMTPSecure = 'tls'; + $phpmailer->SMTPAutoTLS = true; + $phpmailer->SMTPOptions = [ + 'ssl' => [ + 'allow_self_signed' => true, + ], + ]; + $phpmailer->From = 'no-reply@us1.krumax.cz'; + // $phpmailer->FromName = 'Support team US1'; + // $phpmailer->Hostname = 'us1.krumax.cz'; + // $phpmailer->Encoding = 'quoted-printable'; + // $phpmailer->Sender = 'bounce@us1.krumax.cz'; + // $phpmailer->Timeout = 15; +}); +``` +2. Create file for test send mail `/send-mail.php` +```php +, " +]; + +if (wp_mail("$toName <{$to}>", $subject, $message, $headers)) { + echo "Success"; +} else { + echo "Failed"; +} +``` +3. Open URL http://us1.krumax.cz/send-mail.php + + +## Send mail from pod in k3s (use Postfix). +1. Install postfix: `apt-get install -y postfix` +2. Set config: +```bash +postconf -e 'myhostname = mta.krumax.cz' +postconf -e 'mydomain = us1.krumax.cz' +postconf -e 'myorigin = $mydomain' +``` +3. Create file `test.mail`: +``` +From: Support team US1 +To: Maksym Krugol +Subject: Test delivery (postfix) +Date: Wed, 17 Sep 2025 10:53:13 +0200 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi! Test with /usr/sbin/sendmail. +``` +4. Send mail: `sendmail -v -oi -t -f 'no-reply@us1.krumax.cz' < test.mail` + +## Send mail from pod in k3s (use msmtp). +1. Install msmtp: `apt-get install -y msmtp msmtp-mta ca-certificates` +2. Set config `/etc/msmtprc`: +``` +defaults +auth on +tls on +tls_starttls on +tls_trust_file /etc/ssl/certs/ca-certificates.crt +logfile /var/log/msmtp.log + +account default +host mta.krumax.cz +port 587 +from no-reply@us1.krumax.cz +user postmaster@us1.krumax.cz +password qwerty +``` +3. Create file `test.mail`: +``` +From: Support team US1 +To: Maksym Krugol +Subject: Test delivery (msmtp) +Date: Wed, 17 Sep 2025 10:53:13 +0200 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + +Hi! Test with msmtp/sendmail. +``` +4. Send mail: `sendmail -v -oi -t -f 'no-reply@us1.krumax.cz' < test.mail` diff --git a/sodew-bash-main/docs/old.md b/sodew-bash-main/docs/old.md new file mode 100644 index 0000000..000918e --- /dev/null +++ b/sodew-bash-main/docs/old.md @@ -0,0 +1,47 @@ + + + +- [Getting started](#getting-started) +- [Site Creation](#site-creation) +- [Wordpress configuration](#wordpress-configuration) +- [Transfer](docs/transfer.md) + +> [!NOTE] +> For `rSync` to work, the password must be in a file, with permissions `600` and owner `root`. + + +*** +### `--info [resource]` + +Extensive information on the status of various resources is provided: +- `[all]` - Info from all resources +- `k3s` - Info from all resources `k3s` +- `mariadb` - Info from `MariaDB` (master-slave replica) +- `redis` - Info from `Redis` and `RedisSentinel` +- `openlitespeed` - Info from `OpenLiteSpeed` + +Example: +```bash +sudo kube.sh --info +sudo kube.sh --info all +sudo kube.sh --info mariadb +``` + + + +--- +*** +### `--version` + +Collecting information about version system components: + +- Kubernetes (k3s) +- MariaDB +- Redis +- OpenLiteSpeed + +Example: +```bash +sudo kube.sh --version +``` +--- diff --git a/sodew-bash-main/docs/options.md b/sodew-bash-main/docs/options.md new file mode 100644 index 0000000..494c474 --- /dev/null +++ b/sodew-bash-main/docs/options.md @@ -0,0 +1,129 @@ +[KUBE](../README.md)  /  Options + +# Options + +>Options in script `config.sh` + +- `assetsPath` - Directory assets (for script) +- `dataPath` - Directory data (for script) +- `namespace` - Default kubernetes namespace +- `hostname` - Hostname server +- `basePath` - Base path for all resources +- `olsVhostsPath` - Directory for vhosts +- `domainsList` - File with domains list +*** + +- `k3sCmd` - Path to k3s on host +- `k3sReadyRetries` - Number of attempts to check k3s readiness +- `k3sReadySleep` - Pause between attempts +*** + +- `redisKube` - Redis identifier in k3s +- `redisSentinelKube` - Redis Sentinel identifier in k3s +- `redisYaml` - Path to file template YAML for Redis +- `redisPath` - Directory for Redis configuration +- `redisFileUsersAcl` - Filename for users in Redis +- `redisRootPass` - Redis password +*** + +- `mariadbKube` - MariaDB identifier in k3s +- `mariadbMasterKube` - MariaDB Master node identifier in k3s +- `mariadbSlaveKube` - MariaDB Slave node identifier in k3s +- `mariadbYaml` - Path to file template YAML for MariaDB +- `mariadbMasterPath` - Directory for MariaDB Master node (replica) +- `mariadbSlavePath` - Directory for MariaDB Slave node (replica) +- `mariadbRootPass` - MariaDB root password +*** + +- `olsKube` - Openlitespeed identifier in k3s +- `olsYaml` - Path to file template YAML for OpenLiteSpeed +- `olsPath` - Directory for OpenLiteSpeed configuration +- `olsAdminPath` - Directory for OpenLiteSpeed Admin configuration +- `olsConfigFile` - OpenLiteSpeed configuration file +- `olsVhostsConfigPath` - Directory for vhosts configuration files +- `olsAdminPass` - OpenLiteSpeed admin panel password +*** + +- `postfixKube` - Postfix identifier in k3s +- `postfixYaml` - Path to file template YAML for Postfix +- `postfixPath` - Directory for Postfix +- `postfixConfigPath` - Directory for Postfix configuration +- `postfixDkimPath` - Directory for Postfix DKIM +- `postfixTlsCrtFile` - File TLS certificate (if not specified, a self-signed one will be generated) +- `postfixTlsKey` - Fil TLS key (if not specified, a self-signed one will be generated) +- `postfixHost` - Host for MTA +- `postfixPostmaster` - Postmaster (email) for MTA +- `postfixDefaultRealm` - Default realm for MTA +- `postfixDkimSelector` - Selector DKIM +- `postfixDomainsFile` - File of Domains list +- `postfixAliasesFile` - File of Aliases list +- `postfixSendersFile` - File of Senders list +- `postfixIp` - IP address for MTA +*** + +- `workerKube` - Worker identifier in k3s +- `workerYaml` - Path to file template YAML for Worker +- `workerPath` - Directory for Worker +- `workerAgentPath` - Path to directory with agent script +- `workerTasksPath` - Path to directory with tasks-files +- `workerFilesPath` - Path to directory with loading files +- `workerName` - Worker name +- `workerApiUrl` - URL to API +- `workerApiGettingPause` - Pause between requests for a new task +- `workerApiSendingPause` - Pause between sending task statuses +*** + +- `logPath` - Directory journals (for script) +- `logFile` - Log file name format +*** + +- `backupPath` - Directory for backups on current host +- `backupLogPath` - Directory for backups logs +- `backupType` - Backup Type (rsync, archive) +- `backupFirst` - Directory name for the first backup of the day +- `backupDays` - Number of last days of backup storage (excluding current day backups) +- `backupMask` - A mask for selecting backup storage day directories (must match `scriptDate`) +- `backupLongTermPath` - Directory for long-term backups +- `backupLongTermDays` - Minimum number of days for a long-term backup. +- `backupExcludeFile` - List of files and directories that were excluded during backup +- `backupExcludeList` - List of files and directories that should be excluded during backup +- `backupFileSizeMin` - Minimum archive size during verification (bytes) +- `backupElapsedMax` - Maximum time elapsed since the last backup was created (min) +*** + +- `storagePath` - Directory for backups on external storage +- `storageType` - Directory external storage (rsync, ssh) +- `storageDays` - Number of last days of backup storage on external storage (excluding current day backups) +*** + +- `rsyncUri` - URI (format: `user@server[:port][/path]`) (for rSync) +- `rsyncPassFile` - File with password (for rSync) +- `rsyncPath` - Directory for backups on external storage (for rSync) +*** + +- `ftpHost` - Hostname (for FTP) +- `ftpPort` - Port (for FTP) +- `ftpUser` - Username (for FTP) +- `ftpPass` - Password (for FTP) +- `ftpPath` - Directory for backups on external storage (for FTP) +*** + +- `sshHost` - Hostname (for SSH) +- `sshUser` - Username (for SSH) +- `sshKeyFile` - Public key file (for SSH) +- `sshPath` - Directory for backups on external storage (for SSH) +*** + +- `reportFile` - Filename for reports +- `reportMask` - Report filename mask +*** + +- `mailTo` - Email for sending reports (to) +- `mailFrom` - Email for sending reports (from) +*** + +- `cronJobs` - Jobs for adding to CRON +*** + +- `diskUsedSpaceLimit` - Limiting the disk space used (%) +*** diff --git a/sodew-bash-main/docs/resources/mariadb.md b/sodew-bash-main/docs/resources/mariadb.md new file mode 100644 index 0000000..0b2ca4f --- /dev/null +++ b/sodew-bash-main/docs/resources/mariadb.md @@ -0,0 +1,167 @@ +[KUBE](../../README.md)  /  [k3s](../k3s.md)  /  MariaDB + +# Resource MariaDB + +> [!NOTE] +> A replica of two pods `mariadb-master` and `mariadb-slave` is created. Each of the pods has a separate storage. When configuring the connection, the host is specified: `mariadb-master`. + +- [Install](#install) +- [Remove](#remove) +- [Status](#status) +- [Info](#info) +- [Version](#version) +- [Database list](#database-list) +- [User list](#user-list) +- [Dump](#dump) +*** + +## Install + +### Processing +1. Preparation. + - Recreate the secret with the passwords `root-password` and `replication-password`. + +2. Creation. + - Prepare the import file. + - Import the YAML. + +3. Post-processing. + - Initialize the replica. + +Command: +```bash +sudo kube.sh --mariadb install +``` + +Example of log: +```bash +🔹[K3S] Resource add | mariadb +✅[MariaDB] | Delete old Secret: OK +✅[MariaDB] | Create new Secret: OK +🔹[MariaDB] Preparing /app/kube/assets/yaml/mariadb.yaml +🔹[K3S] Applying YAML /app/kube/data/yaml/mariadb.yaml +💡[K3S] Waiting for pods to be ready... | 2 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +✅[MariaDB] Applied /app/kube/data/yaml/mariadb.yaml +🔹[MariaDB] Replica initialization... +✅[MariaDB] Master node | Create replication user: OK +✅[MariaDB] Master node | Status: OK +✅[MariaDB] Slave node | Change master: OK +✅[MariaDB] Slave node | Status: OK | Delay 0s +✅[MariaDB] Replica initialization completed successfully +``` +*** + +## Remove + +Command: +```bash +sudo kube.sh --mariadb remove +``` +*** + +## Status + +Command: +```bash +sudo kube.sh --mariadb status +``` + +Example of log: +```bash +🔹[MariaDB] Status +✅[MariaDB] Databases: 10 | Users: 10 | Size: 1.234 Gb +✅[MariaDB] Master node | Running +✅[MariaDB] Slave node | Running +``` +*** + +## Info + +Command: +```bash +sudo kube.sh --mariadb info +``` + +Example of log: +```bash +🔹[MariaDB] Info +🔹[MariaDB] Master node +mysql-bin.000025 10341 +🔹[MariaDB] Slave node +*************************** 1. row *************************** + Slave_IO_State: Waiting for master to send event + Master_Host: mariadb-master + Master_User: replication_user + Master_Port: 3306 +... +``` +*** + +## Version + +Command: +```bash +sudo kube.sh --mariadb version +``` + +Example of log: +```bash +🔹[MariaDB] Version +✅[MariaDB] Master node | mariadb from 11.7.2-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper +✅[MariaDB] Slave node | mariadb from 11.7.2-MariaDB, client 15.2 for debian-linux-gnu (x86_64) using EditLine wrapper +``` +*** + +## Database list + +> [!NOTE] +> List of databases (except for service databases: `information_schema`, `performance_schema`, `mysql`, `sys`) + +Command: +```bash +sudo kube.sh --mariadb database +``` + +Example of log: +```bash +🔹[MariaDB] Database list +us1_example_com +us2_example_com +us3_example_com +``` +*** + +## User list + +> [!NOTE] +> List of users (except for service users `root`, `replication_user`) + +Command: +```bash +sudo kube.sh --mariadb user +``` + +Example of log: +```bash +🔹[MariaDB] User list +us1_example_com +us2_example_com +us3_example_com +``` +*** + +## Dump + +Command: +```bash +sudo kube.sh --mariadb dump +sudo kube.sh --mariadb dump dump.sql +sudo kube.sh --mariadb dump dump.sql us1_example_com +``` + +Example of log: +```bash +🔹[MariaDB] Dump +✅[MariaDB] Dump: /app/kube/data/mariadb/2025-10-27_10-10-58.sql.tar.gz +``` diff --git a/sodew-bash-main/docs/resources/openlitespeed.md b/sodew-bash-main/docs/resources/openlitespeed.md new file mode 100644 index 0000000..38f4c1e --- /dev/null +++ b/sodew-bash-main/docs/resources/openlitespeed.md @@ -0,0 +1,179 @@ +[KUBE](../../README.md)  /  [k3s](../k3s.md)  /  OpenLiteSpeed + +# Resource OpenLiteSpeed + +> [!NOTE] +> Two (you can specify a different number) pods are created. They work simultaneously. If one pod fails, the second pod starts processing all requests until a replacement for the failed pod is brought up. When changes are made to the virtual host configuration, OpenLiteSpeed is restarted sequentially in all pods. +> +> The administration panel OpenLiteSpeed is available at an address: +> - `:31080` (local and remote) +> - `:31080` (remote) +> - `:7080` (local, `` can be found upon request: `kube.sh --info | grep 7080`) + +- [Install](#install) +- [Remove](#remove) +- [Status](#status) +- [Info](#info) +- [Version](#version) +- [Restart](#restart) +- [Site list](#site-list) +- [Site unlock](#site-unlock) +- [Site lock](#site-lock) +- [Config](#config-test) +*** + +## Install + +### Processing +1. Creation. + - Prepare the import file. + - Import the YAML. + +2. Post-processing. + - Initialization. + +Command: +```bash +sudo kube.sh --ols install +``` + +Example of log: +```bash +🔹[OpenLiteSpeed] Install +🔹[OpenLiteSpeed] Preparing /app/kube/assets/yaml/openlitespeed.yaml +🔹[K3S] Applying YAML /app/kube/data/yaml/openlitespeed.yaml +💡[K3S] Waiting for pods to be ready... | 1 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +✅[OpenLiteSpeed] Applied /app/kube/data/yaml/openlitespeed.yaml +🔹[OpenLiteSpeed] Initialization... +🔹[OpenLiteSpeed] Authorization parameters updated +service/traefik patched +🔹[OpenLiteSpeed] Pod: openlitespeed-0 | Restart... +🔹[OpenLiteSpeed] Pod: openlitespeed-1 | Restart... +✅[OpenLiteSpeed] Initialization completed successfully +``` +*** + +## Remove + +Command: +```bash +sudo kube.sh --ols remove +``` +*** + +## Status + +Command: +```bash +sudo kube.sh --ols status +``` + +Example of log: +```bash +🔹[OpenLiteSpeed] Status +✅[OpenLiteSpeed] openlitespeed-0 | Running | PID 251 +✅[OpenLiteSpeed] openlitespeed-1 | Running | PID 216 +``` +*** + +## Info + +Command: +```bash +sudo kube.sh --ols info +``` + +Example of log: +```bash +🔹[OpenLiteSpeed] Info +🔹[OpenLiteSpeed] openlitespeed-0 +litespeed is running with PID 251. +🔹[OpenLiteSpeed] openlitespeed-1 +litespeed is running with PID 216. +``` +*** + +## Version + +Command: +```bash +sudo kube.sh --ols version +``` + +Example of log: +```bash +🔹[OpenLiteSpeed] Version +✅[OpenLiteSpeed] openlitespeed-0 | LiteSpeed/1.8.3 Open (BUILD built: Fri Feb 28 16:33:02 UTC 2025) +✅[OpenLiteSpeed] openlitespeed-0 | PHP: 8.3.17 +✅[OpenLiteSpeed] openlitespeed-1 | LiteSpeed/1.8.3 Open (BUILD built: Fri Feb 28 16:33:02 UTC 2025) +✅[OpenLiteSpeed] openlitespeed-1 | PHP: 8.3.17 +``` +*** + +## Restart + +Command: +```bash +sudo kube.sh --ols restart +``` + +Example of log: +```bash +🔹[OpenLiteSpeed] Restart +🔹[OpenLiteSpeed] Pod: openlitespeed-0 | Restart... +🔹[OpenLiteSpeed] Pod: openlitespeed-1 | Restart... +``` +*** + +## Site list + +Command: +```bash +sudo kube.sh --ols list [option] +``` + +Options: +- all - All sites (colored up/down) (default) +- all - All sites +- up - Unlocked sites +- down - Locked sites + +Example of log: +```bash +example1.com +example2.com +example3.com +``` +*** + +## Site unlock + +> [!NOTE] +> Unlock domain (Resume site operation in OpenLiteSpeed). + +Command: +```bash +sudo kube.sh --ols up +``` +*** + +## Site lock + +> [!NOTE] +> Lock domain (Pause the site in OpenLiteSpeed). +> +> The OpenLiteSpeed page will be displayed with a 403 error when the domain is accessed. + +Command: +```bash +sudo kube.sh --ols down +``` +*** + +## Config test + +Command: +```bash +sudo kube.sh --ols config +``` diff --git a/sodew-bash-main/docs/resources/postfix.md b/sodew-bash-main/docs/resources/postfix.md new file mode 100644 index 0000000..cae7165 --- /dev/null +++ b/sodew-bash-main/docs/resources/postfix.md @@ -0,0 +1,121 @@ +[KUBE](../../README.md)  /  [k3s](../k3s.md)  /  Postfix + +# Resource Postfix + +- [Install](#install) +- [Remove](#remove) +- [Status](#status) +- [Add domain](#add-domain-) +- [Get DKIM key](#get-dkim-key-for-dns-record-domain-or-dkim-record-lists) +- [Check DNS records](#check-dns-records-for--or-mta) +- [Get template of DNS records](#get-template-of-dns-records) +*** + +## Install + +Command: +```bash +sudo kube.sh --postfix install +``` + +Example of log: +```bash +🔹[Postfix] Install +🔹[Postfix] Preparing /app/kube/assets/yaml/postfix.yaml +🔹[K3S] Applying YAML /app/kube/data/yaml/postfix.yaml +💡[K3S] Waiting for pods to be ready... | 1 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +✅[Postfix] Applied /app/kube/data/yaml/postfix.yaml +``` +*** + +## Remove + +Command: +```bash +sudo kube.sh --postfix remove +``` +*** + +## Status + +> [!NOTE] +> In progress... + +Command: +```bash +sudo kube.sh --postfix status +``` + +Example of log: +```bash +??? +``` +*** + +## Add domain + +> [!NOTE] +> In progress... + +Command: +```bash +sudo kube.sh --postfix add +``` +*** + +## Get DKIM key for DNS record domain or DKIM record lists + +Command: +```bash +sudo ube.sh --postfix dkim [domain] +``` + +Example of log: +```bash +[Postfix] DKIM key for DNS +selector1._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; s=email; " + "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6rCyqEvQ1...FQIDAQAB" ) ; ----- DKIM key selector1 for krumax.cz +``` +*** + +## Check DNS records for or MTA + +Command: +```bash +sudo kube.sh --postfix dns [domain] +``` + +Example of log: +```bash +🔹[Postfix] Check DNS records +✅[Postfix] A record: mta.krumax.cz | 31.31.73.67 +✅[Postfix] SPF record: mta.krumax.cz | "v=spf1 a -all" +✅[Postfix] PTR record: mta.krumax.cz | mta.krumax.cz. +``` +*** + +## Get template of DNS records + +Command: +``` +sudo kube.sh --postfix template +``` + +Example of log: +```bash +🔹[Postfix] Template +🔹[Postfix] For MTA ============== +🔹[Postfix] IN A 31.31.73.67 +🔹[Postfix] IN TXT v=spf1 a -all +🔹[Postfix] PTR -> mta.krumax.cz + +🔹[Postfix] For site ============= +🔹[Postfix] IN A 31.31.73.67 +🔹[Postfix] IN MX 10 mta.krumax.cz. +🔹[Postfix] IN TXT v=spf1 mx ~all +🔹[Postfix] IN TXT v=DKIM1; ... +🔹[Postfix] IN TXT v=DMARC1; ... +``` diff --git a/sodew-bash-main/docs/resources/redis.md b/sodew-bash-main/docs/resources/redis.md new file mode 100644 index 0000000..adfba7c --- /dev/null +++ b/sodew-bash-main/docs/resources/redis.md @@ -0,0 +1,163 @@ +[KUBE](../../README.md)  /  [k3s](../k3s.md)  /  Redis + + +# Resource Redis + +> [!NOTE] +> A replica of three pods is created: 1 master (`redis-0`) + 2 slaves (`redis-1`, `redis-2`). A cluster of three RedisSentinel is created to manage the replica. When configuring the connection, the host is specified: redis-0.redis. + +> [!WARNING] +> In the current configuration, when changing the RedisSentinel master node of the replica, there is no automatic change of connection to the new master node. Solution: adding HAProxy to automatically redirect requests to the current Redis master node. + +- [Install](#install) +- [Remove](#remove) +- [Status](#status) +- [Info](#info) +- [Version](#version) +- [User list](#user-list) +*** + +## Install + +### Processing +1. Preparation. + - Recreate the secret with the passwords `root-password`. + +2. Creation. + - Prepare the import file. + - Import the YAML. + +Command: +```bash +sudo kube.sh --redis install +``` + +Example of log: +```bash +🔹[Redis] Install +✅[Redis] Delete old Secret: OK +✅[Redis] Create new Secret: OK +🔹[Redis] Preparing /app/kube/assets/yaml/redis.yaml +🔹[K3S] Applying YAML /app/kube/data/yaml/redis.yaml +💡[K3S] Waiting for pods to be ready... | 2 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +✅[Redis] Applied /app/kube/data/yaml/redis.yaml +``` +*** + +## Remove + +Command: +```bash +sudo kube.sh --redis remove +``` +*** + +## Status + +Command: +```bash +sudo kube.sh --redis status +``` + +Example of log: +```bash +🔹[Redis] Status +✅[Redis] redis-0 | Role: Master | Slaves: 2 +✅[Redis] redis-1 | Role: Slave | Master: redis-0.redis +✅[Redis] redis-2 | Role: Slave | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-0 | Master: mymaster [redis-0.redis:6379] | Slaves: 2 | Other sentinels: 2 | Quorum: 2 +✅[RedisSentinel] redis-sentinel-0 | Slave: 10.42.0.16:6379 [10.42.0.16:6379] | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-0 | Slave: 10.42.0.14:6379 [10.42.0.14:6379] | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-1 | Master: mymaster [redis-0.redis:6379] | Slaves: 2 | Other sentinels: 2 | Quorum: 2 +✅[RedisSentinel] redis-sentinel-1 | Slave: 10.42.0.16:6379 [10.42.0.16:6379] | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-1 | Slave: 10.42.0.14:6379 [10.42.0.14:6379] | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-2 | Master: mymaster [redis-0.redis:6379] | Slaves: 2 | Other sentinels: 2 | Quorum: 2 +✅[RedisSentinel] redis-sentinel-2 | Slave: 10.42.0.16:6379 [10.42.0.16:6379] | Master: redis-0.redis +✅[RedisSentinel] redis-sentinel-2 | Slave: 10.42.0.14:6379 [10.42.0.14:6379] | Master: redis-0.redis +``` +*** + +## Info + +Command: +```bash +sudo kube.sh --redis info +``` + +Example of log: +```bash +🔹[Redis] Info +🔹[Redis] redis-0 +# Replication +role:master +connected_slaves:2 +slave0:ip=10.42.0.14,port=6379,state=online,offset=1092850,lag=0 +slave1:ip=10.42.0.16,port=6379,state=online,offset=1092714,lag=1 +... +🔹[Redis] redis-1 +# Replication +role:slave +master_host:redis-0.redis +master_port:6379 +master_link_status:up +... +🔹[Redis] redis-2 +# Replication +role:slave +master_host:redis-0.redis +master_port:6379 +master_link_status:up +... +🔹[RedisSentinel] redis-sentinel-0 +# Sentinel +sentinel_masters:1 +... +master0:name=mymaster,status=ok,address=redis-0.redis:6379,slaves=2,sentinels=3 +🔹[RedisSentinel] redis-sentinel-1 +# Sentinel +sentinel_masters:1 +... +master0:name=mymaster,status=ok,address=redis-0.redis:6379,slaves=2,sentinels=3 +🔹[RedisSentinel] redis-sentinel-2 +# Sentinel +sentinel_masters:1 +... +master0:name=mymaster,status=ok,address=redis-0.redis:6379,slaves=2,sentinels=3 +``` +*** + +## Version + +Command: +```bash +sudo kube.sh --redis version +``` + +Example of log: +```bash +🔹[Redis] Version +✅[Redis] redis-0 | 7.4.2 +✅[Redis] redis-1 | 7.4.2 +✅[Redis] redis-2 | 7.4.2 +✅[RedisSentinel] redis-sentinel-0 | 7.4.2 +✅[RedisSentinel] redis-sentinel-1 | 7.4.2 +✅[RedisSentinel] redis-sentinel-2 | 7.4.2 +``` +*** + +## User list + +Command: +```bash +sudo kube.sh --redis user +``` + +Example of log: +```bash +🔹[Redis] User list +default +us1_example_com +us2_example_com +us3_example_com +``` diff --git a/sodew-bash-main/docs/resources/worker.md b/sodew-bash-main/docs/resources/worker.md new file mode 100644 index 0000000..b042446 --- /dev/null +++ b/sodew-bash-main/docs/resources/worker.md @@ -0,0 +1,121 @@ +[KUBE](../../README.md)  /  [k3s](../k3s.md)  /  Worker + +# Resource Transfer + +- [Install](#install) +- [Remove](#remove) +- [Transfer site](#transfer-site) +- [Pause for Agent to receive new tasks from the API](#pause-for-agent-to-receive-new-tasks-from-the-api) +- [Removing the pause for Agent to receive new tasks from the API](#removing-the-pause-for-agent-to-receive-new-tasks-from-the-api) +- [Tasks list](#tasks-list) +- [Update ENV](#update-env) +*** + +## Install + +Command: +```bash +sudo kube.sh --worker install +``` + +Example of log: +```bash +🔹[Worker] Install +🔹[Worker] Preparing /app/kube/assets/yaml/worker.yaml +🔹[K3S] Applying YAML /app/kube/data/yaml/worker.yaml +💡[K3S] Waiting for pods to be ready... | 1 not ready +💡[K3S] Waiting for pods to be ready... | 1 not ready +``` +*** + +## Remove + +Command: +```bash +sudo kube.sh --Worker remove +``` +*** + +## Execute task + +Command: +```bash +sudo kube.sh --worker task [domain] +``` + +Example of log: +```bash +🔹[Worker] Task: /_data/worker/tasks/6d5b3169-a15f-4007-8cc7-ebfc8d75e3b2.task +🔹[Worker] Action: test +🔹[Worker] Database URL: https://wp.krumax.cz/transfer_36b91e68-53d3-49ac-922a-0031e664125b/0bf26901-c12d-4015-9bbe-cefc5c1a92d6.sql.zip +🔹[Worker] Files URL: https://wp.krumax.cz/transfer_36b91e68-53d3-49ac-922a-0031e664125b/0bf26901-c12d-4015-9bbe-cefc5c1a92d6.zip +🔹[Worker] Download archive database --> /app/transfers/us00.krumax.cz/us00.krumax.cz.sql.zip +🔹[Worker] Download archive files --> /app/transfers/us00.krumax.cz/us00.krumax.cz.zip +🔹[Worker] Create site: us00.krumax.cz +🔹Files source: /app/transfers/us00.krumax.cz/us00.krumax.cz.zip +🔹Database source: /app/transfers/us00.krumax.cz/us00.krumax.cz.sql.zip +🔹[OpenLiteSpeed] Pod: openlitespeed-0 | Restart... +🔹[OpenLiteSpeed] Pod: openlitespeed-1 | Restart... +✅[Worker] Action: test | Success +``` +*** + +## Tasks list + +Command: +```bash +sudo kube.sh --worker list +``` + +Example of log: +```bash +🔹[Worker] Task list +# | Task | Action | Status | Time, sec +--------------------------------------------------------------------------- +1 | d580040f-b3b8-43e1-af7a-8e35c80a688c | test | new | ? +2 | pause | pause | execution | 691175 +``` +*** + +## Pause for Agent to receive new tasks from the API (pause) + +Command: +```bash +sudo kube.sh --worker down +``` + +Example of log: +```bash +🔹[Worker] Put on pause... +``` +*** + +## Removing the pause for Agent to receive new tasks from the API (unpause) + +Command: +```bash +sudo kube.sh --worker up +``` + +Example of log: +```bash +🔹[Worker] Resuming from pause... +``` +*** + +## Reload + +Command: +```bash +sudo kube.sh --worker reload +``` + +Example of log: +```bash +🔹[Worker] Reload... +🔹[Worker] Updated ENV: +API_URL: http://api.sodew.ai/api/tasks +WORKER_ID: worker-dev +GETTING_PAUSE: 30 +SENDING_PAUSE: 15 +``` diff --git a/sodew-bash-main/docs/restore.md b/sodew-bash-main/docs/restore.md new file mode 100644 index 0000000..fb9e6ea --- /dev/null +++ b/sodew-bash-main/docs/restore.md @@ -0,0 +1,73 @@ +[KUBE](../README.md)  /  Restore + +# Restore + +The restore process is divided into three stages: +1. Restoring website files from the `` directory or the `.tar.gz` archive. +2. Restoring the `.conf` file. +3. Restoring the database from the `.sql` file or the `.sql.tar.gz` archive. + +> [!WARNING] +> If an error occurs at any stage, the process does not stop. + +> [!WARNING] +> Files, databases, and other resources at each stage are either pre-cleaned or immediately overwritten without confirmation. + + +## Commands + +### `-r, --restore [action]` +Restore site files and database from backup for a `domain`. + +Example: +```bash +sudo kube.sh -r example.com +``` + +The source of resources for recovery is the directories defined in `backupPath` and `backupLongTermPath`.\ +In the backup folder is searched for 3 types of resources: +- `///` - directory with site files (`file:d`) +- `///.tar.gz` - site file archive (`file:a`) +- `///.sql` - site database SQL-file (`db:f`) +- `///.sql.tar.gz` - site database archive (`db:a`) +- `///.conf` - site database archive (`conf`) + +The search results in a list of format: `: []`. Then you should specify the number of the selected marker for restore. + +> [!NOTE] +> If one resource is selected (`file:d`/`file:a`/`db:f`/`db:a`/`conf`), only one resource will be restored. The other will remain unchanged. + +> [!NOTE] +> If there's a `file:d` and a `file:a`. Priority is given to `file:d`. If there's a `db:f` and a `db:a`. Priority is given to `db:f`. + +> [!NOTE] +> Once database are restored, the Redis keys for the domain are deleted. + +Example: +```bash +1: 2025-04-29 14-22-22 [file:d file:a db:f conf] +2: 2025-04-29 14-20-20 [file:d db:a] +3: 2025-04-29 12-00-00 [file:a conf] +4: 2025-04-29 _first [db:a] +``` + +You can specify an additional parameter after the domain: +- `list` - will display a list of available markers for restore +- `last` - automatic site restore from the `last backup` +- `full` - automatic site restore from the last `FULL backup` +- `auto` - automatic site restore from the last `FULL backup` or the `last backup` +- `N` - automatic site restore from the `last backup #N` (N: 1+) + +`FULL backup` is a backup that contains a copy of the site files, a copy of the database, and a copy of the .conf file\ +`last backup #N` marker number (starting from 1) in the list sorted by creation time (can be seen with the list command) + +Example: +```bash +sudo kube.sh -r example.com list +sudo kube.sh -r example.com last +sudo kube.sh -r example.com auto +sudo kube.sh -r example.com 3 +``` + +> [!WARNING] +> Restoring from the last full copy will be done without question. diff --git a/sodew-bash-main/docs/script.md b/sodew-bash-main/docs/script.md new file mode 100644 index 0000000..3bd7757 --- /dev/null +++ b/sodew-bash-main/docs/script.md @@ -0,0 +1,24 @@ +[KUBE](../README.md)  /  Script + +# Script + +> A set of scripts to execute. + +## Commands + +### `--script