├── console
├── service
│ ├── VERSION
│ ├── pkg
│ │ ├── tracer
│ │ │ └── cid.go
│ │ └── patroni
│ │ │ └── models.go
│ ├── internal
│ │ ├── controllers
│ │ │ ├── errors.go
│ │ │ ├── utils.go
│ │ │ ├── secret
│ │ │ │ └── delete_secret.go
│ │ │ └── cluster
│ │ │ │ └── delete_cluster.go
│ │ ├── xdocker
│ │ │ ├── images.go
│ │ │ └── imanager.go
│ │ ├── watcher
│ │ │ ├── consts.go
│ │ │ └── models.go
│ │ ├── storage
│ │ │ ├── cluster_flags_test.go
│ │ │ └── cluster_flags.go
│ │ └── convert
│ │ │ ├── postgres_versions.go
│ │ │ ├── database_extensions.go
│ │ │ ├── settings.go
│ │ │ └── projects.go
│ ├── env.sh
│ ├── migrations
│ │ ├── migrate.go
│ │ └── goose_logger.go
│ ├── Dockerfile
│ ├── middleware
│ │ └── cid.go
│ └── Makefile
├── .env.example
├── ui
│ ├── src
│ │ ├── entities
│ │ │ ├── cluster
│ │ │ │ ├── storage-block
│ │ │ │ │ ├── lib
│ │ │ │ │ │ └── functions.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── const.ts
│ │ │ │ ├── cluster-info
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── connection-info
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── ui
│ │ │ │ │ │ └── ConnectionInfoRowConteiner.tsx
│ │ │ │ │ └── assets
│ │ │ │ │ │ └── eyeIcon.svg
│ │ │ │ ├── ssh-key-block
│ │ │ │ │ ├── model
│ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── vip-address-block
│ │ │ │ │ └── index.ts
│ │ │ │ ├── expert-mode
│ │ │ │ │ ├── network-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── validation.ts
│ │ │ │ │ ├── data-directory-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── kernel-parameters-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── validation.ts
│ │ │ │ │ ├── extensions-block
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ ├── citus.png
│ │ │ │ │ │ │ ├── pgaudit.png
│ │ │ │ │ │ │ ├── postgis.png
│ │ │ │ │ │ │ ├── pgrouting.png
│ │ │ │ │ │ │ └── timescaledb.png
│ │ │ │ │ │ ├── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ │ ├── ui
│ │ │ │ │ │ │ └── styles.css
│ │ │ │ │ │ └── lib
│ │ │ │ │ │ │ └── functions.ts
│ │ │ │ │ ├── postgres-parameters-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── validation.ts
│ │ │ │ │ ├── additional-settings-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ │ └── validation.ts
│ │ │ │ │ ├── backups-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── databases-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ │ ├── validation.ts
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ ├── dcs-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ │ └── types.ts
│ │ │ │ │ └── connection-pools-block
│ │ │ │ │ │ └── model
│ │ │ │ │ │ ├── types.ts
│ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ └── validation.ts
│ │ │ │ ├── cloud-region-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ └── lib
│ │ │ │ │ │ └── hooks.tsx
│ │ │ │ ├── instances-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── load-balancers-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── database-servers-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ ├── const.ts
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── description-block
│ │ │ │ │ └── index.ts
│ │ │ │ ├── instances-amount-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── const.ts
│ │ │ │ ├── postgres-version-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── providers-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ │ └── ui
│ │ │ │ │ │ └── ClusterFormCloudProviderBox.tsx
│ │ │ │ ├── environment-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ ├── cluster-name-block
│ │ │ │ │ └── index.ts
│ │ │ │ ├── cluster-instance-config-box
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ │ └── types.ts
│ │ │ │ └── cluster-name-description-block
│ │ │ │ │ └── index.ts
│ │ │ ├── sidebar-item
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── breadcumb-item
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── secret-form-block
│ │ │ │ └── index.ts
│ │ │ ├── settings
│ │ │ │ └── proxy-block
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── model
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ └── types.ts
│ │ │ └── authentification-method-form-block
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ └── constants.ts
│ │ ├── features
│ │ │ ├── cluster-secret-modal
│ │ │ │ ├── model
│ │ │ │ │ └── validation.ts
│ │ │ │ └── index.ts
│ │ │ ├── theme-toggle
│ │ │ │ └── index.ts
│ │ │ ├── settings-table-row-actions
│ │ │ │ ├── model
│ │ │ │ │ └── constants.ts
│ │ │ │ └── index.ts
│ │ │ ├── add-project
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── validation.ts
│ │ │ ├── bradcrumbs
│ │ │ │ ├── index.ts
│ │ │ │ └── hooks
│ │ │ │ │ └── useBreadcrumbs.tsx
│ │ │ ├── clusters-table-buttons
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── index.ts
│ │ │ ├── logout-button
│ │ │ │ └── index.ts
│ │ │ ├── add-secret
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ └── types.ts
│ │ │ ├── add-environment
│ │ │ │ └── index.ts
│ │ │ ├── settings-table-buttons
│ │ │ │ ├── index.ts
│ │ │ │ ├── lib
│ │ │ │ │ └── functions.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── operations-table-buttons
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── constants.ts
│ │ │ ├── clusters-table-row-actions
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── pojects-table-row-actions
│ │ │ │ └── index.ts
│ │ │ ├── operations-table-row-actions
│ │ │ │ └── index.ts
│ │ │ └── clusters-overview-table-row-actions
│ │ │ │ └── index.ts
│ │ ├── app
│ │ │ ├── layout
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── vite-env.d.ts
│ │ │ ├── redux
│ │ │ │ ├── slices
│ │ │ │ │ ├── projectSlice
│ │ │ │ │ │ ├── projectSelectors.ts
│ │ │ │ │ │ └── projectSlice.ts
│ │ │ │ │ └── themeSlice
│ │ │ │ │ │ └── themeSelectors.ts
│ │ │ │ └── store
│ │ │ │ │ └── hooks.ts
│ │ │ ├── router
│ │ │ │ ├── routerPathsConfig
│ │ │ │ │ ├── routerOperationsPathsConfig.ts
│ │ │ │ │ ├── routerClustersPathsConfig.ts
│ │ │ │ │ ├── routerSettingsPathsConfig.ts
│ │ │ │ │ └── index.ts
│ │ │ │ └── routerConfig
│ │ │ │ │ └── OperationsRoutes.tsx
│ │ │ └── main.tsx
│ │ ├── pages
│ │ │ ├── 404
│ │ │ │ └── index.ts
│ │ │ ├── login
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ ├── constants.ts
│ │ │ │ │ └── types.ts
│ │ │ ├── clusters
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── settings
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── constants.ts
│ │ │ ├── operations
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── add-cluster
│ │ │ │ └── index.ts
│ │ │ ├── operation-log
│ │ │ │ └── index.ts
│ │ │ └── overview-cluster
│ │ │ │ └── index.ts
│ │ ├── widgets
│ │ │ ├── main
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── sidebar
│ │ │ │ └── index.ts
│ │ │ ├── header
│ │ │ │ └── index.ts
│ │ │ ├── cluster-form
│ │ │ │ ├── index.ts
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── ui
│ │ │ │ │ └── ClusterFormLocalMachineFormPart.tsx
│ │ │ ├── secrets-table
│ │ │ │ ├── index.ts
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── lib
│ │ │ │ │ └── hooks.tsx
│ │ │ ├── settings-form
│ │ │ │ └── index.ts
│ │ │ ├── clusters-table
│ │ │ │ ├── index.ts
│ │ │ │ ├── assets
│ │ │ │ │ ├── warningIcon.svg
│ │ │ │ │ ├── errorIcon.svg
│ │ │ │ │ └── correctIcon.svg
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── projects-table
│ │ │ │ ├── index.tsx
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ ├── ui
│ │ │ │ │ └── ProjectsTableButtons.tsx
│ │ │ │ └── lib
│ │ │ │ │ └── hooks.tsx
│ │ │ ├── cluster-summary
│ │ │ │ ├── index.ts
│ │ │ │ └── assets
│ │ │ │ │ ├── hetznerIcon2.svg
│ │ │ │ │ └── digitaloceanIcon.svg
│ │ │ ├── operations-table
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── environments-table
│ │ │ │ ├── index.ts
│ │ │ │ ├── ui
│ │ │ │ │ └── EnvironmentsTableButtons.tsx
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── lib
│ │ │ │ │ └── hooks.tsx
│ │ │ ├── cluster-overview-table
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ └── yaml-editor-form
│ │ │ │ ├── model
│ │ │ │ ├── types.ts
│ │ │ │ └── const.ts
│ │ │ │ └── lib
│ │ │ │ └── functions.ts
│ │ └── shared
│ │ │ ├── ui
│ │ │ ├── error-box
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── copy-icon
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── assets
│ │ │ │ │ └── copyIcon.svg
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── spinner
│ │ │ │ ├── index.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── default-table
│ │ │ │ └── index.ts
│ │ │ ├── info-card-body
│ │ │ │ ├── index.ts
│ │ │ │ ├── model
│ │ │ │ │ └── types.ts
│ │ │ │ └── ui
│ │ │ │ │ └── index.tsx
│ │ │ ├── slider-box
│ │ │ │ ├── index.ts
│ │ │ │ ├── lib
│ │ │ │ │ └── functions.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── selectable-box
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ ├── default-form-buttons
│ │ │ │ ├── index.ts
│ │ │ │ └── model
│ │ │ │ │ └── types.ts
│ │ │ └── settings-add-entity
│ │ │ │ └── model
│ │ │ │ ├── constants.ts
│ │ │ │ ├── validation.ts
│ │ │ │ └── types.ts
│ │ │ ├── assets
│ │ │ ├── checkIcon.svg
│ │ │ ├── flagIcon.svg
│ │ │ ├── HomeOutlinedIcon.svg
│ │ │ ├── collapseIcon.svg
│ │ │ ├── lanIcon.svg
│ │ │ ├── settingsIcon.svg
│ │ │ ├── supportIcon.svg
│ │ │ ├── storageIcon.svg
│ │ │ ├── ramIcon.svg
│ │ │ ├── calendarClockICon.svg
│ │ │ ├── docsIcon.svg
│ │ │ ├── cpuIcon.svg
│ │ │ ├── githubIcon.svg
│ │ │ └── instanceIcon.svg
│ │ │ ├── model
│ │ │ ├── types.ts
│ │ │ ├── validation.ts
│ │ │ └── constants.ts
│ │ │ ├── api
│ │ │ ├── enhancedSecretsApi.ts
│ │ │ └── baseApi.ts
│ │ │ └── i18n
│ │ │ └── locales
│ │ │ └── en
│ │ │ ├── validation.json
│ │ │ └── operations.json
│ ├── .prettierignore
│ ├── .env
│ ├── .prettierrc
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── .env.production
│ ├── index.html
│ └── .eslintrc.cjs
└── db
│ ├── pg_hba.conf
│ └── migrations
│ ├── 20250323121343_2.2.0.sql
│ └── 20250927122311_2.4.0.sql
├── automation
├── .dockerignore
├── playbooks
│ └── files
│ │ └── .gitkeep
├── changelog.yaml
├── roles
│ ├── consul
│ │ ├── version.txt
│ │ ├── vars
│ │ │ ├── VMware Photon OS.yml
│ │ │ ├── FreeBSD.yml
│ │ │ ├── Archlinux.yml
│ │ │ ├── Flatcar.yml
│ │ │ ├── Solaris.yml
│ │ │ ├── Debian.yml
│ │ │ ├── Amazon.yml
│ │ │ ├── RedHat.yml
│ │ │ ├── Darwin.yml
│ │ │ └── Windows.yml
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ ├── consul_profile.sh.j2
│ │ │ ├── rsyslogd_00-consul.conf.j2
│ │ │ ├── consul_resolved.conf.j2
│ │ │ ├── configd_50custom.json.j2
│ │ │ ├── syslogng_consul.conf.j2
│ │ │ └── consul_systemd_service.override.j2
│ │ ├── requirements.txt
│ │ ├── files
│ │ │ └── README.md
│ │ ├── handlers
│ │ │ ├── restart_consul_mac.yml
│ │ │ ├── restart_syslogng.yml
│ │ │ ├── restart_rsyslog.yml
│ │ │ ├── start_snapshot.yml
│ │ │ ├── reload_consul_conf.yml
│ │ │ └── start_consul.yml
│ │ └── tasks
│ │ │ ├── systemd_resolved.yml
│ │ │ └── user_group.yml
│ ├── firewall
│ │ ├── .gitignore
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── .yamllint
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── firewall.unit.j2
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── .travis.yml
│ ├── confd
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── templates
│ │ │ ├── confd.service.j2
│ │ │ └── haproxy.toml.j2
│ ├── copy
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── cron
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── etcd
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── templates
│ │ │ └── etcd.service.j2
│ ├── haproxy
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── handlers
│ │ │ └── main.yml
│ ├── locales
│ │ └── meta
│ │ │ └── main.yml
│ ├── mount
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── netdata
│ │ └── meta
│ │ │ └── main.yml
│ ├── ntp
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── handlers
│ │ │ └── main.yml
│ ├── patroni
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ ├── pg_hba.yml
│ │ │ └── patroni.yml
│ │ └── handlers
│ │ │ └── main.yml
│ ├── pgpass
│ │ └── meta
│ │ │ └── main.yml
│ ├── sudo
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── README.md
│ ├── swap
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── README.md
│ ├── sysctl
│ │ └── meta
│ │ │ └── main.yml
│ ├── update
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── upgrade
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ ├── pgbouncer_resume.yml
│ │ │ └── dcs_remove_cluster.yml
│ ├── wal_g
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── walg.json.j2
│ │ └── defaults
│ │ │ └── main.yml
│ ├── etc_hosts
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── hostname
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── README.md
│ ├── io_scheduler
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── templates
│ │ │ └── io-scheduler.service.j2
│ ├── keepalived
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── packages
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── defaults
│ │ │ └── main.yml
│ ├── pam_limits
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── README.md
│ ├── pg_probackup
│ │ └── meta
│ │ │ └── main.yml
│ ├── pgbackrest
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ ├── pgbackrest.server.conf.j2
│ │ │ ├── pgbackrest.server.stanza.conf.j2
│ │ │ └── pgbackrest_bootstrap.sh.j2
│ │ └── tasks
│ │ │ └── bootstrap_script.yml
│ ├── pgbouncer
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── templates
│ │ │ ├── userlist.txt.j2
│ │ │ └── pgbouncer.service.j2
│ ├── pre_checks
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── system.yml
│ ├── resolv_conf
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── README.md
│ │ └── tasks
│ │ │ └── main.yml
│ ├── ssh_keys
│ │ └── meta
│ │ │ └── main.yml
│ ├── timezone
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── README.md
│ │ └── tasks
│ │ │ └── main.yml
│ ├── vip_manager
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── disable.yml
│ │ ├── templates
│ │ │ └── vip-manager.service.j2
│ │ └── handlers
│ │ │ └── main.yml
│ ├── add_repository
│ │ └── meta
│ │ │ └── main.yml
│ ├── authorized_keys
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── defaults
│ │ │ └── main.yml
│ │ └── tasks
│ │ │ └── main.yml
│ ├── cloud_resources
│ │ └── meta
│ │ │ └── main.yml
│ ├── deploy_finish
│ │ ├── meta
│ │ │ └── main.yml
│ │ └── README.md
│ ├── postgresql_privs
│ │ └── meta
│ │ │ └── main.yml
│ ├── postgresql_users
│ │ └── meta
│ │ │ └── main.yml
│ ├── tls_certificate
│ │ └── meta
│ │ │ └── main.yml
│ ├── postgresql_databases
│ │ └── meta
│ │ │ └── main.yml
│ ├── postgresql_extensions
│ │ └── meta
│ │ │ └── main.yml
│ ├── postgresql_schemas
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── README.md
│ ├── transparent_huge_pages
│ │ ├── meta
│ │ │ └── main.yml
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── README.md
│ └── bind_address
│ │ └── README.md
├── requirements.txt
├── molecule
│ ├── default
│ │ ├── cleanup.yml
│ │ └── prepare.yml
│ ├── tests
│ │ ├── postgres
│ │ │ ├── replication.yml
│ │ │ └── postgres.yml
│ │ ├── roles
│ │ │ ├── confd
│ │ │ │ └── main.yml
│ │ │ ├── patroni
│ │ │ │ └── main.yml
│ │ │ ├── swap
│ │ │ │ └── main.yml
│ │ │ ├── pre-checks
│ │ │ │ └── main.yml
│ │ │ └── haproxy
│ │ │ │ └── main.yml
│ │ └── variables
│ │ │ └── main.yml
│ └── pg_upgrade
│ │ └── prepare.yml
└── requirements.yml
├── .config
├── python_version.config
├── python
│ └── dev
│ │ └── requirements.txt
├── .flake8
├── .yamllint
├── make
│ ├── help.mak
│ └── formatting.mak
└── ansible-lint.yml
├── .prettierignore
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── config.yml
└── workflows
│ ├── flake8.yml
│ ├── yamllint.yml
│ ├── ansible-lint.yml
│ ├── schedule_pg_debian12.yml
│ ├── schedule_pg_debian13.yml
│ ├── schedule_pg_ubuntu2204.yml
│ ├── schedule_pg_ubuntu2404.yml
│ ├── schedule_pg_rockylinux10.yml
│ ├── schedule_pg_rockylinux9.yml
│ ├── schedule_pg_almalinux9.yml
│ ├── schedule_pg_almalinux10.yml
│ └── schedule_pg_centosstream9.yml
├── images
├── TypeA.png
├── TypeB.png
├── TypeC.png
├── load_balancing.jpg
├── github-autobase.png
├── pg_cluster_scheme.png
├── autobase_create_cluster_demo.gif
└── pg_cluster_scheme.dark_mode.png
├── .gitpod.yml
├── .editorconfig-checker.json
├── .gitignore
├── .prettierrc.json
├── .sql-formatter.json
└── .editorconfig
/console/service/VERSION:
--------------------------------------------------------------------------------
1 | 2.5.2
2 |
--------------------------------------------------------------------------------
/automation/.dockerignore:
--------------------------------------------------------------------------------
1 | .venv/
2 |
--------------------------------------------------------------------------------
/automation/playbooks/files/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.config/python_version.config:
--------------------------------------------------------------------------------
1 | PYTHON_VERSION=3.12
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | console/service
2 | console/ui
3 | *.sql
4 |
--------------------------------------------------------------------------------
/console/.env.example:
--------------------------------------------------------------------------------
1 | DOMAIN=
2 | EMAIL=
3 | AUTH_TOKEN=
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/storage-block/lib/functions.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/features/cluster-secret-modal/model/validation.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ---
2 | github: vitabaks
3 | patreon: vitabaks
4 |
--------------------------------------------------------------------------------
/automation/changelog.yaml:
--------------------------------------------------------------------------------
1 | # changelog.yaml
2 | ---
3 | releases: {}
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/version.txt:
--------------------------------------------------------------------------------
1 | commit: 6251974 on Nov 15, 2022
2 |
--------------------------------------------------------------------------------
/automation/roles/firewall/.gitignore:
--------------------------------------------------------------------------------
1 | *.retry
2 | */__pycache__
3 | *.pyc
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/theme-toggle/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from './ui';
--------------------------------------------------------------------------------
/console/service/pkg/tracer/cid.go:
--------------------------------------------------------------------------------
1 | package tracer
2 |
3 | type CtxCidKey struct{}
4 |
--------------------------------------------------------------------------------
/images/TypeA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/TypeA.png
--------------------------------------------------------------------------------
/images/TypeB.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/TypeB.png
--------------------------------------------------------------------------------
/images/TypeC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/TypeC.png
--------------------------------------------------------------------------------
/automation/roles/consul/vars/VMware Photon OS.yml:
--------------------------------------------------------------------------------
1 | ---
2 | consul_os_packages:
3 | - unzip
4 |
--------------------------------------------------------------------------------
/console/ui/src/app/layout/index.ts:
--------------------------------------------------------------------------------
1 | import Layout from './ui';
2 |
3 | export default Layout;
4 |
--------------------------------------------------------------------------------
/automation/roles/confd/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/copy/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/cron/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/etcd/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/haproxy/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/locales/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/mount/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/netdata/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/ntp/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/patroni/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pgpass/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/sudo/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/swap/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/sysctl/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/update/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/upgrade/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/wal_g/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/images/load_balancing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/load_balancing.jpg
--------------------------------------------------------------------------------
/automation/roles/etc_hosts/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/firewall/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/hostname/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/io_scheduler/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/keepalived/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/packages/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pam_limits/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pg_probackup/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pgbouncer/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/pre_checks/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/resolv_conf/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/ssh_keys/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/timezone/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/vip_manager/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/console/ui/.prettierignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | node_modules/
4 | public/
5 | package.json
6 | yarn.lock
7 |
--------------------------------------------------------------------------------
/console/ui/src/pages/404/index.ts:
--------------------------------------------------------------------------------
1 | import Page404 from '@pages/404/ui';
2 |
3 | export default Page404;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/login/index.ts:
--------------------------------------------------------------------------------
1 | import Login from '@pages/login/ui';
2 |
3 | export default Login;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/main/index.ts:
--------------------------------------------------------------------------------
1 | import Main from '@widgets/main/ui';
2 |
3 | export default Main;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/sidebar/index.ts:
--------------------------------------------------------------------------------
1 | import Sidebar from './ui';
2 |
3 | export default Sidebar;
4 |
--------------------------------------------------------------------------------
/images/github-autobase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/github-autobase.png
--------------------------------------------------------------------------------
/automation/roles/add_repository/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/authorized_keys/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/cloud_resources/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/deploy_finish/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_privs/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_users/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/tls_certificate/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/error-box/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface ErrorBoxProps {
2 | text?: string;
3 | }
4 |
--------------------------------------------------------------------------------
/images/pg_cluster_scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/pg_cluster_scheme.png
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | ---
2 | image:
3 | file: .config/gitpod/Dockerfile
4 |
5 | tasks:
6 | - init: make bootstrap-dev
7 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_databases/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_extensions/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_schemas/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/automation/roles/transparent_huge_pages/meta/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dependencies:
3 | - role: vitabaks.autobase.common
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/sidebar-item/index.ts:
--------------------------------------------------------------------------------
1 | import SidebarItem from './ui';
2 |
3 | export default SidebarItem;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/header/index.ts:
--------------------------------------------------------------------------------
1 | import Header from '@widgets/header/ui';
2 |
3 | export default Header;
4 |
--------------------------------------------------------------------------------
/console/db/pg_hba.conf:
--------------------------------------------------------------------------------
1 | local all all trust
2 | host all all 127.0.0.1/32 trust
3 | host all all 0.0.0.0/0 scram-sha-256
4 |
--------------------------------------------------------------------------------
/console/service/internal/controllers/errors.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | const (
4 | BaseError = int64(100)
5 | )
6 |
--------------------------------------------------------------------------------
/console/ui/src/pages/clusters/index.ts:
--------------------------------------------------------------------------------
1 | import Clusters from '@pages/clusters/ui';
2 |
3 | export default Clusters;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/settings/index.ts:
--------------------------------------------------------------------------------
1 | import Settings from '@pages/settings/ui';
2 |
3 | export default Settings;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/copy-icon/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface CopyIconProps {
2 | valueToCopy?: string;
3 | }
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/spinner/index.ts:
--------------------------------------------------------------------------------
1 | import Spinner from '@shared/ui/spinner/ui';
2 |
3 | export default Spinner;
4 |
--------------------------------------------------------------------------------
/automation/requirements.txt:
--------------------------------------------------------------------------------
1 | ansible==12.2.0
2 | boto3==1.41.5
3 | dopy==0.3.7
4 | google-auth==2.43.0
5 | hcloud==2.11.1
6 |
--------------------------------------------------------------------------------
/automation/roles/pgbouncer/templates/userlist.txt.j2:
--------------------------------------------------------------------------------
1 | "{{ patroni_superuser_username }}" "{{ patroni_superuser_password }}"
2 |
--------------------------------------------------------------------------------
/console/ui/src/app/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/console/ui/src/features/settings-table-row-actions/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const SECRET_TOAST_DISPLAY_CLUSTERS_LIMIT = 10;
2 |
--------------------------------------------------------------------------------
/console/ui/src/pages/operations/index.ts:
--------------------------------------------------------------------------------
1 | import Operations from '@pages/operations/ui';
2 |
3 | export default Operations;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/add-cluster/index.ts:
--------------------------------------------------------------------------------
1 | import AddCluster from '@pages/add-cluster/ui';
2 |
3 | export default AddCluster;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/login/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const LOGIN_FORM_FIELD_NAMES = Object.freeze({
2 | TOKEN: 'token',
3 | });
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/copy-icon/index.ts:
--------------------------------------------------------------------------------
1 | import CopyIcon from '@shared/ui/copy-icon/ui';
2 |
3 | export default CopyIcon;
4 |
--------------------------------------------------------------------------------
/images/autobase_create_cluster_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/autobase_create_cluster_demo.gif
--------------------------------------------------------------------------------
/images/pg_cluster_scheme.dark_mode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/images/pg_cluster_scheme.dark_mode.png
--------------------------------------------------------------------------------
/automation/roles/firewall/.yamllint:
--------------------------------------------------------------------------------
1 | ---
2 | extends: default
3 | rules:
4 | line-length:
5 | max: 120
6 | level: warning
7 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-project/index.ts:
--------------------------------------------------------------------------------
1 | import AddProject from '@features/add-project/ui';
2 |
3 | export default AddProject;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/bradcrumbs/index.ts:
--------------------------------------------------------------------------------
1 | import Breadcrumbs from '@/features/bradcrumbs/ui';
2 |
3 | export default Breadcrumbs;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/operation-log/index.ts:
--------------------------------------------------------------------------------
1 | import OperationLog from '@pages/operation-log/ui';
2 |
3 | export default OperationLog;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-form/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterForm from '@widgets/cluster-form/ui';
2 |
3 | export default ClusterForm;
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/consul_profile.sh.j2:
--------------------------------------------------------------------------------
1 | export CONSUL_CACERT={{ consul_tls_dir }}/ca.crt
2 | export CONSUL_HTTP_SSL=true
3 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/rsyslogd_00-consul.conf.j2:
--------------------------------------------------------------------------------
1 | {{ consul_syslog_facility }}.* {{ consul_log_path }}/{{ consul_log_file }}
2 |
--------------------------------------------------------------------------------
/automation/roles/packages/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pgdg_architecture_map:
3 | amd64: x86_64
4 | x86_64: x86_64
5 | aarch64: aarch64
6 |
--------------------------------------------------------------------------------
/console/ui/src/features/clusters-table-buttons/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface ClustersTableButtonsProps {
2 | refetch: () => void;
3 | }
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/logout-button/index.ts:
--------------------------------------------------------------------------------
1 | import LogoutButton from '@features/logout-button/ui';
2 |
3 | export default LogoutButton;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/secrets-table/index.ts:
--------------------------------------------------------------------------------
1 | import SecretsTable from '@widgets/secrets-table/ui';
2 |
3 | export default SecretsTable;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/settings-form/index.ts:
--------------------------------------------------------------------------------
1 | import SettingsForm from '@widgets/settings-form/ui';
2 |
3 | export default SettingsForm;
4 |
--------------------------------------------------------------------------------
/.editorconfig-checker.json:
--------------------------------------------------------------------------------
1 | {
2 | "Exclude": [".env", ".j2", "console/", "settings.json", ".sql", "go.mod", "go.sum", "patroni/library/"]
3 | }
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/requirements.txt:
--------------------------------------------------------------------------------
1 | rich>=14.2.0,<14.3.0
2 | molecule===2.22
3 | docker
4 | netaddr
5 | testinfra
6 | flake8
7 | yamllint
8 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | pgdg_architecture_map:
3 | amd64: x86_64
4 | x86_64: x86_64
5 | aarch64: aarch64
6 |
--------------------------------------------------------------------------------
/console/ui/src/entities/breadcumb-item/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface BreadcrumbsItemProps {
2 | label: string;
3 | path: string;
4 | }
5 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-secret/index.ts:
--------------------------------------------------------------------------------
1 | import SettingsAddSecret from '@features/add-secret/ui';
2 |
3 | export default SettingsAddSecret;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/default-table/index.ts:
--------------------------------------------------------------------------------
1 | import DefaultTable from '@shared/ui/default-table/ui';
2 |
3 | export default DefaultTable;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/info-card-body/index.ts:
--------------------------------------------------------------------------------
1 | import InfoCardBody from '@shared/ui/info-card-body/ui';
2 |
3 | export default InfoCardBody;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/slider-box/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterSliderBox from '@shared/ui/slider-box/ui';
2 |
3 | export default ClusterSliderBox;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/clusters-table/index.ts:
--------------------------------------------------------------------------------
1 | import ClustersTable from '@widgets/clusters-table/ui';
2 |
3 | export default ClustersTable;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/projects-table/index.tsx:
--------------------------------------------------------------------------------
1 | import ProjectsTable from '@widgets/projects-table/ui';
2 |
3 | export default ProjectsTable;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/breadcumb-item/index.ts:
--------------------------------------------------------------------------------
1 | import BreadcrumbsItem from '@entities/breadcumb-item/ui';
2 |
3 | export default BreadcrumbsItem;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-environment/index.ts:
--------------------------------------------------------------------------------
1 | import AddEnvironment from '@features/add-environment/ui';
2 |
3 | export default AddEnvironment;
4 |
--------------------------------------------------------------------------------
/console/ui/src/pages/overview-cluster/index.ts:
--------------------------------------------------------------------------------
1 | import OverviewCluster from '@pages/overview-cluster/ui';
2 |
3 | export default OverviewCluster;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/selectable-box/index.ts:
--------------------------------------------------------------------------------
1 | import SelectableBox from '@shared/ui/selectable-box/ui';
2 |
3 | export default SelectableBox;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-summary/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterSummary from '@widgets/cluster-summary/ui';
2 |
3 | export default ClusterSummary;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/operations-table/index.ts:
--------------------------------------------------------------------------------
1 | import OperationsTable from '@widgets/operations-table/ui';
2 |
3 | export default OperationsTable;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-info/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterInfo from '@entities/cluster/cluster-info/ui';
2 |
3 | export default ClusterInfo;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/secret-form-block/index.ts:
--------------------------------------------------------------------------------
1 | import SecretFormBlock from '@entities/secret-form-block/ui';
2 |
3 | export default SecretFormBlock;
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | .venv
4 | .vscode/settings.json
5 | __pycache__/
6 | *.log
7 | molecule/**/tmp
8 | .ansible
9 | .ansible-lint-env
10 |
--------------------------------------------------------------------------------
/automation/roles/firewall/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart firewall
3 | ansible.builtin.service:
4 | name: firewall
5 | state: restarted
6 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/storage-block/index.ts:
--------------------------------------------------------------------------------
1 | import StorageBlock from '@entities/cluster/storage-block/ui';
2 |
3 | export default StorageBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/environments-table/index.ts:
--------------------------------------------------------------------------------
1 | import EnvironmentsTable from '@widgets/environments-table/ui';
2 |
3 | export default EnvironmentsTable;
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/files/README.md:
--------------------------------------------------------------------------------
1 | # files
2 |
3 | This directory is used for holding temporary files and should be present
4 | in the role even when empty.
5 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/connection-info/index.ts:
--------------------------------------------------------------------------------
1 | import ConnectionInfo from '@entities/cluster/connection-info/ui';
2 |
3 | export default ConnectionInfo;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/ssh-key-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const SSH_KEY_BLOCK_FIELD_NAMES = Object.freeze({
2 | SSH_PUBLIC_KEY: 'sshPublicKey',
3 | });
4 |
--------------------------------------------------------------------------------
/automation/roles/authorized_keys/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # ssh_public_keys: [] # Defined in roles/common/defaults/main.yml. Commented out here to prevent conflicts.
3 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/vip-address-block/index.ts:
--------------------------------------------------------------------------------
1 | import VipAddressBlock from '@entities/cluster/vip-address-block/ui';
2 |
3 | export default VipAddressBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/settings/proxy-block/index.ts:
--------------------------------------------------------------------------------
1 | import SettingsProxyBlock from '@entities/settings/proxy-block/ui';
2 |
3 | export default SettingsProxyBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-project/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const PROJECT_FORM_NAMES = Object.freeze({
2 | NAME: 'name',
3 | DESCRIPTION: 'description',
4 | });
5 |
--------------------------------------------------------------------------------
/console/ui/src/features/cluster-secret-modal/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterSecretModal from '@features/cluster-secret-modal/ui';
2 |
3 | export default ClusterSecretModal;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/default-form-buttons/index.ts:
--------------------------------------------------------------------------------
1 | import DefaultFormButtons from '@shared/ui/default-form-buttons/ui';
2 |
3 | export default DefaultFormButtons;
4 |
--------------------------------------------------------------------------------
/automation/roles/confd/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | confd_architecture_map:
3 | amd64: amd64
4 | x86_64: amd64
5 | aarch64: arm64
6 | arm64: arm64
7 | 64-bit: amd64
8 |
--------------------------------------------------------------------------------
/console/ui/src/features/clusters-table-buttons/index.ts:
--------------------------------------------------------------------------------
1 | import ClustersTableButtons from '@features/clusters-table-buttons/ui';
2 |
3 | export default ClustersTableButtons;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/settings-table-buttons/index.ts:
--------------------------------------------------------------------------------
1 | import SettingsTableButtons from '@features/settings-table-buttons/ui';
2 |
3 | export default SettingsTableButtons;
4 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-overview-table/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterOverviewTable from '@widgets/cluster-overview-table/ui';
2 |
3 | export default ClusterOverviewTable;
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/FreeBSD.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: FreeBSD.yml - FreeBSD OS variables for Consul
3 |
4 | consul_os_packages:
5 | - unzip
6 |
7 | dnsmasq_package: dnsmasq
8 |
--------------------------------------------------------------------------------
/console/service/internal/xdocker/images.go:
--------------------------------------------------------------------------------
1 | package xdocker
2 |
3 | const (
4 | playbookCreateCluster = "deploy_pgcluster.yml"
5 |
6 | entryPoint = "ansible-playbook"
7 | )
8 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/network-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const NETWORK_BLOCK_FIELD_NAMES = Object.freeze({
2 | SERVER_NETWORK: 'serverNetwork',
3 | });
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/ssh-key-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterFormSshKeyBlock from '@entities/cluster/ssh-key-block/ui';
2 |
3 | export default ClusterFormSshKeyBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/settings-add-entity/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const ADD_ENTITY_FORM_NAMES = Object.freeze({
2 | NAME: 'name',
3 | DESCRIPTION: 'description',
4 | });
5 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/restart_consul_mac.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - ansible.builtin.import_tasks: "stop_consul_mac.yml"
3 |
4 | - ansible.builtin.import_tasks: "start_consul_mac.yml"
5 |
--------------------------------------------------------------------------------
/automation/roles/cron/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # defaults file for cron
3 |
4 | # cron_jobs: [] # Defined in roles/common/defaults/main.yml. Commented out here to prevent conflicts.
5 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cloud-region-block/index.ts:
--------------------------------------------------------------------------------
1 | import CloudFormRegionBlock from '@entities/cluster/cloud-region-block/ui';
2 |
3 | export default CloudFormRegionBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/data-directory-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const DATA_DIRECTORY_FIELD_NAMES = Object.freeze({
2 | DATA_DIRECTORY: 'dataDirectory',
3 | });
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/instances-block/index.ts:
--------------------------------------------------------------------------------
1 | import CloudFormInstancesBlock from '@entities/cluster/instances-block/ui';
2 |
3 | export default CloudFormInstancesBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/load-balancers-block/index.ts:
--------------------------------------------------------------------------------
1 | import LoadBalancersBlock from '@entities/cluster/load-balancers-block/ui';
2 |
3 | export default LoadBalancersBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/operations-table-buttons/index.ts:
--------------------------------------------------------------------------------
1 | import OperationsTableButtons from '@features/operations-table-buttons/ui';
2 |
3 | export default OperationsTableButtons;
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Archlinux.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Archlinux.yml - Archlinux variables for Consul
3 |
4 | consul_os_packages:
5 | - unzip
6 |
7 | consul_syslog_enable: false
8 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Flatcar.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Flatcar.yml - Flatcar variables for Consul
3 |
4 | consul_os_packages: []
5 |
6 | consul_systemd_unit_path: "/etc/systemd/system"
7 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/database-servers-block/index.ts:
--------------------------------------------------------------------------------
1 | import DatabaseServersBlock from '@entities/cluster/database-servers-block/ui';
2 |
3 | export default DatabaseServersBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/description-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterDescriptionBlock from '@entities/cluster/description-block/ui';
2 |
3 | export default ClusterDescriptionBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/instances-amount-block/index.ts:
--------------------------------------------------------------------------------
1 | import InstancesAmountBlock from '@entities/cluster/instances-amount-block/ui';
2 |
3 | export default InstancesAmountBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/postgres-version-block/index.ts:
--------------------------------------------------------------------------------
1 | import PostgresVersionBox from '@entities/cluster/postgres-version-block/ui';
2 |
3 | export default PostgresVersionBox;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/providers-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterFormProvidersBlock from '@entities/cluster/providers-block/ui';
2 |
3 | export default ClusterFormProvidersBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/clusters-table-row-actions/index.ts:
--------------------------------------------------------------------------------
1 | import ClustersTableRowActions from '@features/clusters-table-row-actions/ui';
2 |
3 | export default ClustersTableRowActions;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/pojects-table-row-actions/index.ts:
--------------------------------------------------------------------------------
1 | import ProjectsTableRowActions from '@features/pojects-table-row-actions/ui';
2 |
3 | export default ProjectsTableRowActions;
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/settings-table-buttons/lib/functions.ts:
--------------------------------------------------------------------------------
1 | export const handleDelete = () => {};
2 | export const handleEdit = () => {};
3 | export const handleAddSecret = () => {};
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/settings-table-row-actions/index.ts:
--------------------------------------------------------------------------------
1 | import SettingsTableRowActions from '@features/settings-table-row-actions/ui';
2 |
3 | export default SettingsTableRowActions;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/environment-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterFormEnvironmentBlock from '@entities/cluster/environment-block/ui';
2 |
3 | export default ClusterFormEnvironmentBlock;
4 |
--------------------------------------------------------------------------------
/automation/roles/wal_g/templates/walg.json.j2:
--------------------------------------------------------------------------------
1 | {
2 | {% for wal_g in wal_g_json %}
3 | "{{ wal_g.option }}": "{{ wal_g.value }}"{% if not loop.last %},
4 | {% endif %}
5 | {% endfor %}
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-name-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterFormClusterNameBlock from '@entities/cluster/cluster-name-block/ui';
2 |
3 | export default ClusterFormClusterNameBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/kernel-parameters-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const KERNEL_PARAMETERS_FIELD_NAMES = Object.freeze({
2 | KERNEL_PARAMETERS: 'kernelParameters',
3 | });
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/instances-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const INSTANCES_BLOCK_FIELD_NAMES = Object.freeze({
2 | INSTANCE_TYPE: 'instanceType',
3 | SERVER_TYPE: 'serverType',
4 | });
5 |
--------------------------------------------------------------------------------
/console/ui/src/features/operations-table-row-actions/index.ts:
--------------------------------------------------------------------------------
1 | import OperationsTableRowActions from '@features/operations-table-row-actions/ui';
2 |
3 | export default OperationsTableRowActions;
4 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/restart_syslogng.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart syslog-ng
3 | ansible.builtin.service:
4 | name: syslog-ng
5 | state: restarted
6 | listen: "restart syslog-ng"
7 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/citus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/citus.png
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/postgres-parameters-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const POSTGRES_PARAMETERS_FIELD_NAMES = Object.freeze({
2 | POSTGRES_PARAMETERS: 'postgresParameters',
3 | });
4 |
--------------------------------------------------------------------------------
/console/ui/src/features/clusters-table-row-actions/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface ClustersTableRemoveButtonProps {
2 | clusterId: number;
3 | clusterName: string;
4 | closeMenu: () => void;
5 | }
6 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "trailingComma": "all",
4 | "singleQuote": false,
5 | "printWidth": 160,
6 | "tabWidth": 2,
7 | "endOfLine": "lf",
8 | "useTabs": false
9 | }
10 |
--------------------------------------------------------------------------------
/automation/roles/vip_manager/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | vip_manager_architecture_map:
3 | amd64: x86_64
4 | x86_64: x86_64
5 | aarch64: arm64
6 | arm64: arm64
7 | 32-bit: "i386"
8 | 64-bit: x86_64
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/pgaudit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/pgaudit.png
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/postgis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/postgis.png
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const EXTENSION_BLOCK_FIELD_NAMES = Object.freeze({
2 | EXTENSIONS: 'extensions',
3 | IS_ENABLED: 'isEnabled',
4 | });
5 |
--------------------------------------------------------------------------------
/console/ui/src/features/operations-table-buttons/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface OperationsTableButtonsProps {
2 | refetch: () => void;
3 | startDate: Date;
4 | setStartDate: (date: Date) => void;
5 | }
6 |
--------------------------------------------------------------------------------
/.config/python/dev/requirements.txt:
--------------------------------------------------------------------------------
1 | ansible==12.2.0
2 | ansible-lint==25.11.1
3 | ansible-compat==25.11.0
4 | yamllint==1.37.1
5 | molecule==25.11.1
6 | molecule-plugins==25.8.12
7 | docker==7.1.0
8 | flake8==7.3.0
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/pgrouting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/pgrouting.png
--------------------------------------------------------------------------------
/console/ui/src/pages/login/model/types.ts:
--------------------------------------------------------------------------------
1 | import { LOGIN_FORM_FIELD_NAMES } from '@pages/login/model/constants.ts';
2 |
3 | export interface LoginFormValues {
4 | [LOGIN_FORM_FIELD_NAMES.TOKEN]: string;
5 | }
6 |
--------------------------------------------------------------------------------
/.config/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = E501, W503, E402
3 | exclude =
4 | .git,
5 | __pycache__,
6 | docs/source/conf.py,
7 | old,
8 | build,
9 | dist,
10 | .venv,
11 | .ansible
12 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/consul_resolved.conf.j2:
--------------------------------------------------------------------------------
1 | {{ ansible_managed | comment }}
2 |
3 | [Resolve]
4 | DNS={{ consul_addresses.dns }}:{{ consul_ports.dns }}
5 | Domains=
6 | Cache=no
7 | DNSStubListener=yes
8 |
--------------------------------------------------------------------------------
/console/ui/src/app/redux/slices/projectSlice/projectSelectors.ts:
--------------------------------------------------------------------------------
1 | import { RootState } from '@app/redux/store/store.ts';
2 |
3 | export const selectCurrentProject = (state: RootState) => state.project.currentProject;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/authentification-method-form-block/index.ts:
--------------------------------------------------------------------------------
1 | import AuthenticationMethodFormBlock from '@entities/authentification-method-form-block/ui';
2 |
3 | export default AuthenticationMethodFormBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-instance-config-box/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterFromInstanceConfigBox from '@entities/cluster/cluster-instance-config-box/ui';
2 |
3 | export default ClusterFromInstanceConfigBox;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-name-description-block/index.ts:
--------------------------------------------------------------------------------
1 | import ClusterNameDescriptionBlock from '@entities/cluster/cluster-name-description-block/ui';
2 |
3 | export default ClusterNameDescriptionBlock;
4 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/timescaledb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vitabaks/autobase/HEAD/console/ui/src/entities/cluster/expert-mode/extensions-block/assets/timescaledb.png
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/checkIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/vip_manager/tasks/disable.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Disabe vip-manager service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: vip-manager
6 | state: stopped
7 | enabled: false
8 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/ui/styles.css:
--------------------------------------------------------------------------------
1 | .swiper {
2 | width: 100%;
3 | margin: 0;
4 | }
5 |
6 | .swiper-pagination {
7 | position: relative;
8 | margin-top: 24px;
9 | }
10 |
--------------------------------------------------------------------------------
/console/ui/src/features/clusters-overview-table-row-actions/index.ts:
--------------------------------------------------------------------------------
1 | import ClustersOverviewTableRowActions from '@features/clusters-overview-table-row-actions/ui';
2 |
3 | export default ClustersOverviewTableRowActions;
4 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/info-card-body/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | export interface InfoCardBodyProps {
4 | config: {
5 | title: string;
6 | children: ReactNode;
7 | }[];
8 | }
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/instances-amount-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const INSTANCES_AMOUNT_BLOCK_VALUES = Object.freeze({
2 | INSTANCES_AMOUNT: 'instancesAmount',
3 | IS_SPOT_INSTANCES: 'isSpotInstances',
4 | });
5 |
--------------------------------------------------------------------------------
/automation/roles/confd/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart confd service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: confd
6 | enabled: true
7 | state: restarted
8 | listen: "restart confd"
9 |
--------------------------------------------------------------------------------
/console/db/migrations/20250323121343_2.2.0.sql:
--------------------------------------------------------------------------------
1 | -- +goose Up
2 | -- Extensions
3 | update
4 | public.extensions
5 | set
6 | postgres_max_version = '17'
7 | where
8 | extension_name = 'citus';
9 |
10 | -- +goose Down
11 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/environment-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ResponseEnvironment } from '@shared/api/api/environments.ts';
2 |
3 | export interface EnvironmentBlockProps {
4 | environments: ResponseEnvironment[];
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cloud-region-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentInfoCloudRegion } from '@shared/api/api/other.ts';
2 |
3 | export interface CloudFormRegionBlockProps {
4 | regions: DeploymentInfoCloudRegion[];
5 | }
6 |
--------------------------------------------------------------------------------
/automation/roles/etcd/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | etcd_architecture_map:
3 | amd64: amd64
4 | x86_64: amd64
5 | armv6l: armhfv6
6 | armv7l: armhfv6
7 | aarch64: arm64
8 | arm64: arm64
9 | 32-bit: "386"
10 | 64-bit: amd64
11 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-info/model/types.ts:
--------------------------------------------------------------------------------
1 | export interface ClusterInfoProps {
2 | postgresVersion?: number;
3 | clusterName?: string;
4 | description?: string;
5 | environment?: string;
6 | location?: string;
7 | }
8 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/configd_50custom.json.j2:
--------------------------------------------------------------------------------
1 | {# consul_config_custom variables are free-style, passed through a hash -#}
2 | {% if consul_config_custom -%}
3 | {{ consul_config_custom | to_nice_json }}
4 | {% else %}
5 | {}
6 | {% endif %}
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/postgres-version-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ResponsePostgresVersion } from '@shared/api/api/other.ts';
2 |
3 | export interface PostgresVersionBlockProps {
4 | postgresVersions: ResponsePostgresVersion[];
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/yaml-editor-form/model/types.ts:
--------------------------------------------------------------------------------
1 | import { YAML_EDITOR_FORM_FIELD_NAMES } from '@widgets/yaml-editor-form/model/const.ts';
2 |
3 | export interface YamlEditorFormValues {
4 | [YAML_EDITOR_FORM_FIELD_NAMES.EDITOR]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/automation/roles/io_scheduler/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Start io-scheduler service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: io-scheduler
6 | state: restarted
7 | enabled: true
8 | listen: "restart io-scheduler"
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/ssh-key-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { SSH_KEY_BLOCK_FIELD_NAMES } from '@entities/cluster/ssh-key-block/model/const.ts';
2 |
3 | export interface SshKeyBlockValues {
4 | [SSH_KEY_BLOCK_FIELD_NAMES.SSH_PUBLIC_KEY]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-project/model/types.ts:
--------------------------------------------------------------------------------
1 | import { PROJECT_FORM_NAMES } from '@features/add-project/model/constants.ts';
2 |
3 | export interface ProjectFormValues {
4 | [PROJECT_FORM_NAMES.NAME]: string;
5 | [PROJECT_FORM_NAMES.NAME]: string;
6 | }
7 |
--------------------------------------------------------------------------------
/console/ui/src/shared/model/types.ts:
--------------------------------------------------------------------------------
1 | import { MRT_Row, MRT_RowData } from 'material-react-table';
2 |
3 | export interface TableRowActionsProps {
4 | closeMenu: () => void;
5 | row: MRT_Row;
6 | }
7 |
8 | export type valueOf = T[keyof T];
9 |
--------------------------------------------------------------------------------
/console/ui/.env:
--------------------------------------------------------------------------------
1 | VITE_API_URL=http://localhost:8080/api/v1/
2 | VITE_AUTH_TOKEN=auth_token
3 | VITE_CLUSTERS_POLLING_INTERVAL=60000
4 | VITE_CLUSTER_OVERVIEW_POLLING_INTERVAL=60000
5 | VITE_OPERATIONS_POLLING_INTERVAL=60000
6 | VITE_OPERATION_LOGS_POLLING_INTERVAL=10000
7 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/syslogng_consul.conf.j2:
--------------------------------------------------------------------------------
1 | destination d_consul { file("{{ consul_log_path }}/{{ consul_log_file }}"); };
2 | filter f_consul { facility({{ consul_syslog_facility }}); };
3 | log { source(s_sys); filter(f_consul); destination(d_consul); };
4 |
--------------------------------------------------------------------------------
/console/ui/src/app/redux/slices/themeSlice/themeSelectors.ts:
--------------------------------------------------------------------------------
1 | import { RootState } from '@app/redux/store/store';
2 |
3 | export const selectThemeMode = (state: RootState) => state.theme.mode;
4 | export const selectActualTheme = (state: RootState) => state.theme.actualTheme;
--------------------------------------------------------------------------------
/console/ui/src/widgets/yaml-editor-form/model/const.ts:
--------------------------------------------------------------------------------
1 | export const YAML_EDITOR_FORM_FIELD_NAMES = Object.freeze({
2 | EDITOR: 'yamlEditor',
3 | });
4 |
5 | export const YAML_EDITOR_FORM_DEFAULT_VALUES = Object.freeze({
6 | [YAML_EDITOR_FORM_FIELD_NAMES.EDITOR]: '',
7 | });
8 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Solaris.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Solaris.yml - Solaris OS variables for Consul
3 |
4 | consul_os_packages:
5 | - unzip
6 |
7 | consul_pkg: "consul_{{ consul_version }}_solaris_amd64.zip"
8 | consul_smf_manifest: "/opt/local/lib/svc/manifest/consul.xml"
9 |
--------------------------------------------------------------------------------
/console/db/migrations/20250927122311_2.4.0.sql:
--------------------------------------------------------------------------------
1 | -- +goose Up
2 | insert into public.postgres_versions (major_version, release_date, end_of_life)
3 | values (18, '2025-09-25', '2030-11-14');
4 |
5 | -- +goose Down
6 | delete from public.postgres_versions
7 | where major_version = 18;
8 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/network-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { NETWORK_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/network-block/model/const.ts';
2 |
3 | export interface NetworkBlockValues {
4 | [NETWORK_BLOCK_FIELD_NAMES.SERVER_NETWORK]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/flagIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/entities/settings/proxy-block/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const SETTINGS_FORM_FIELDS_NAMES = Object.freeze({
2 | HTTP_PROXY: 'http_proxy',
3 | HTTPS_PROXY: 'https_proxy',
4 | IS_EXPERT_MODE_ENABLED: 'is_expert_mode_enabled',
5 | IS_YAML_ENABLED: 'is_yaml_enabled',
6 | });
7 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/projects-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { PROJECTS_TABLE_COLUMN_NAMES } from '@widgets/projects-table/model/constants.ts';
2 |
3 | export interface ProjectsTableValues {
4 | [PROJECTS_TABLE_COLUMN_NAMES.ID]: string;
5 | [PROJECTS_TABLE_COLUMN_NAMES.NAME]: string;
6 | }
7 |
--------------------------------------------------------------------------------
/automation/molecule/default/cleanup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Molecule.default.cleanup
3 | hosts: localhost
4 | gather_facts: false
5 |
6 | tasks:
7 | - name: Delete dcs_type.yml file
8 | ansible.builtin.file:
9 | path: "../../dcs_type.yml"
10 | state: absent
11 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/restart_rsyslog.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: restart rsyslog
3 | ansible.builtin.service:
4 | name: rsyslog
5 | state: restarted
6 | when:
7 | - ansible_os_family != "Darwin"
8 | - ansible_os_family != "Windows"
9 | listen: "restart rsyslog"
10 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Debian.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Debian.yml - Debian OS variables for Consul
3 |
4 | consul_os_packages:
5 | - unzip
6 |
7 | dnsmasq_package: dnsmasq
8 |
9 | consul_repo_prerequisites:
10 | - gpg
11 |
12 | consul_repo_url: "https://apt.releases.hashicorp.com"
13 |
--------------------------------------------------------------------------------
/console/ui/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": true,
7 | "trailingComma": "all",
8 | "bracketSameLine": true,
9 | "arrowParens": "always",
10 | "endOfLine": "auto",
11 | "bracketSpacing": true
12 | }
13 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/data-directory-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { DATA_DIRECTORY_FIELD_NAMES } from '@entities/cluster/expert-mode/data-directory-block/model/const.ts';
2 |
3 | export interface DataDirectoryFormValues {
4 | [DATA_DIRECTORY_FIELD_NAMES.DATA_DIRECTORY]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/automation/roles/firewall/templates/firewall.unit.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Firewall
3 | After=syslog.target network.target
4 |
5 | [Service]
6 | Type=oneshot
7 | ExecStart=/etc/firewall.bash
8 | ExecStop=/sbin/iptables -F
9 | RemainAfterExit=yes
10 |
11 | [Install]
12 | WantedBy=multi-user.target
13 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-secret/model/constants.ts:
--------------------------------------------------------------------------------
1 | import { SECRET_MODAL_CONTENT_FORM_FIELD_NAMES } from '@entities/secret-form-block/model/constants.ts';
2 |
3 | export const ADD_SECRET_FORM_FIELD_NAMES = Object.freeze({
4 | SECRET_NAME: 'secretName',
5 | ...SECRET_MODAL_CONTENT_FORM_FIELD_NAMES,
6 | });
7 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/selectable-box/model/types.ts:
--------------------------------------------------------------------------------
1 | import { SxProps } from '@mui/material';
2 | import { ReactNode } from 'react';
3 |
4 | export interface ClusterFormSelectableBoxProps {
5 | children?: ReactNode;
6 | isActive?: boolean;
7 | sx?: SxProps;
8 | [key: string]: unknown;
9 | }
10 |
--------------------------------------------------------------------------------
/console/ui/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/templates/pgbackrest.server.conf.j2:
--------------------------------------------------------------------------------
1 | [global]
2 | {% for global in pgbackrest_server_conf.global %}
3 | {{ global.option }}={{ global.value }}
4 | {% endfor %}
5 |
6 | # Include stanzas configuration files
7 | repo1-host-config-include-path = {{ pgbackrest_conf_file | dirname }}/conf.d
8 |
9 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/HomeOutlinedIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/wal_g/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | wal_g_architecture_map:
3 | amd64: amd64
4 | x86_64: amd64
5 | aarch64: aarch64
6 | arm64: aarch64
7 |
8 | # if 'wal_g_installation_method' is 'src'
9 | go_architecture_map:
10 | amd64: amd64
11 | x86_64: amd64
12 | aarch64: arm64
13 | arm64: arm64
14 |
--------------------------------------------------------------------------------
/console/ui/src/features/operations-table-buttons/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const DATE_RANGE_VALUES = Object.freeze({
2 | LAST_DAY: 'lastDay',
3 | LAST_WEEK: 'lastWeek',
4 | LAST_MONTH: 'lastMonth',
5 | LAST_THREE_MONTHS: 'lastThreeMonths',
6 | LAST_SIX_MONTHS: 'lastSixMonths',
7 | LAST_YEAR: 'lastYear',
8 | });
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cluster-instance-config-box/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ClusterFormSelectableBoxProps } from '@shared/ui/selectable-box/model/types.ts';
2 |
3 | export interface ClusterFromInstanceConfigBoxProps extends ClusterFormSelectableBoxProps {
4 | name: string;
5 | cpu: string;
6 | ram: string;
7 | }
8 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/kernel-parameters-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { KERNEL_PARAMETERS_FIELD_NAMES } from '@entities/cluster/expert-mode/kernel-parameters-block/model/const.ts';
2 |
3 | export interface KernelParametersBlockValues {
4 | [KERNEL_PARAMETERS_FIELD_NAMES.KERNEL_PARAMETERS]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/entities/sidebar-item/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ComponentType, SVGProps } from 'react';
2 |
3 | export interface SidebarItemProps {
4 | path: string;
5 | label: string;
6 | icon?: ComponentType>;
7 | isActive?: boolean;
8 | isCollapsed?: boolean;
9 | target?: string;
10 | }
11 |
--------------------------------------------------------------------------------
/automation/roles/confd/templates/confd.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Сonfiguration management tool - confd
3 | After=network.target
4 | After=etcd.service
5 |
6 | [Service]
7 | Type=simple
8 | ExecStart=/usr/local/bin/confd
9 | TimeoutSec=30
10 | Restart=on-failure
11 |
12 | [Install]
13 | WantedBy=multi-user.target
14 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/start_snapshot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: start consul snapshot on unix
3 | ansible.builtin.service:
4 | name: consul_snapshot
5 | state: started
6 | enabled: true
7 | when:
8 | - ansible_os_family != "Darwin"
9 | - ansible_os_family != "Windows"
10 | listen: "start snapshot"
11 |
--------------------------------------------------------------------------------
/console/ui/src/app/router/routerPathsConfig/routerOperationsPathsConfig.ts:
--------------------------------------------------------------------------------
1 | const routerOperationsPathsConfig = {
2 | absolutePath: '/operations',
3 | log: {
4 | absolutePath: '/operations/:operationId/log',
5 | relativePath: ':operationId/log',
6 | },
7 | };
8 |
9 | export default routerOperationsPathsConfig;
10 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/collapseIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Amazon.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Amazon.yml - Amazonlinux variables for Consul
3 | consul_os_packages:
4 | - git
5 | - unzip
6 |
7 | consul_syslog_enable: false
8 |
9 | consul_repo_prerequisites:
10 | - yum-utils
11 |
12 | consul_repo_url: https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
13 |
--------------------------------------------------------------------------------
/console/service/internal/controllers/utils.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import "postgresql-cluster-console/models"
4 |
5 | func MakeErrorPayload(err error, code int64) *models.ResponseError {
6 | return &models.ResponseError{
7 | Code: code,
8 | Description: err.Error(),
9 | Title: err.Error(),
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/console/service/internal/watcher/consts.go:
--------------------------------------------------------------------------------
1 | package watcher
2 |
3 | const (
4 | ContainerStatusExited = "exited"
5 | ContainerStatusRemoving = "removing"
6 | ContainerStatusDead = "dead"
7 |
8 | LogFieldSystemInfo = "System info"
9 | LogFieldConnectionInfo = "vitabaks.autobase.deploy_finish : Connection info"
10 | )
11 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/postgres-parameters-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { POSTGRES_PARAMETERS_FIELD_NAMES } from '@entities/cluster/expert-mode/postgres-parameters-block/model/const.ts';
2 |
3 | export interface PostgresParametersBlockValues {
4 | [POSTGRES_PARAMETERS_FIELD_NAMES.POSTGRES_PARAMETERS]?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/console/ui/src/pages/clusters/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { Box } from '@mui/material';
3 | import ClustersTable from '@widgets/clusters-table';
4 |
5 | const Clusters: FC = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default Clusters;
14 |
--------------------------------------------------------------------------------
/automation/roles/transparent_huge_pages/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Start disable-transparent-huge-pages service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: disable-transparent-huge-pages
6 | state: restarted
7 | enabled: true
8 | listen: "restart disable-thp"
9 | when: not ansible_check_mode
10 |
--------------------------------------------------------------------------------
/console/ui/src/pages/operations/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import OperationsTable from '@widgets/operations-table';
3 | import { Box } from '@mui/material';
4 |
5 | const Operations: FC = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default Operations;
14 |
--------------------------------------------------------------------------------
/console/service/env.sh:
--------------------------------------------------------------------------------
1 | export PG_CONSOLE_DB_MIGRATIONDIR='./db/migrations'
2 | export PG_CONSOLE_LOGGER_LEVEL=TRACE
3 | export PG_CONSOLE_DB_DBNAME=db_console
4 | export PG_CONSOLE_DOCKER_LOGDIR='/home/nikolay.gurban/log_dir'
5 | export PG_CONSOLE_DB_PASSWORD=postgres
6 | export PG_CONSOLE_LOGWATCHER_RUNEVERY=10m
7 | export PG_CONSOLE_CLUSTERWATCHER_RUNEVERY=10m
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/lib/functions.ts:
--------------------------------------------------------------------------------
1 | import { ResponseDatabaseExtension } from '@shared/api/api/other.ts';
2 |
3 | export const filterValues = (searchValue: string, extensions: ResponseDatabaseExtension[]) =>
4 | searchValue ? extensions?.filter((extension) => extension?.name?.includes(searchValue)) : extensions;
5 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/default-form-buttons/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export interface DefaultFormButtonsProps {
4 | isDisabled?: boolean;
5 | isSubmitting?: boolean;
6 | submitButtonLabel?: string;
7 | cancelButtonLabel?: string;
8 | cancelHandler: () => void;
9 | children?: ReactElement;
10 | }
11 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/instances-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { DeploymentInstanceType } from '@/shared/api/api/deployments';
2 |
3 | export interface CloudFormInstancesBlockProps {
4 | instances: {
5 | small?: DeploymentInstanceType[];
6 | medium?: DeploymentInstanceType[];
7 | large?: DeploymentInstanceType[];
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-secret/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ADD_SECRET_FORM_FIELD_NAMES } from '@features/add-secret/model/constants.ts';
2 |
3 | import { SecretFormValues } from '@entities/secret-form-block/model/types.ts';
4 |
5 | export interface AddSecretFormValues extends SecretFormValues {
6 | [ADD_SECRET_FORM_FIELD_NAMES.SECRET_NAME]: string;
7 | }
8 |
--------------------------------------------------------------------------------
/automation/roles/upgrade/tasks/pgbouncer_resume.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Perform RESUME pgbouncers server
3 | - name: RESUME PgBouncer pools
4 | become: true
5 | become_user: postgres
6 | ansible.builtin.shell: kill -SIGUSR2 $(pidof pgbouncer)
7 | args:
8 | executable: /bin/bash
9 | ignore_errors: true # if there is an error, show the message and continue
10 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/lanIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/app/redux/store/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from 'react-redux';
2 | import type { AppDispatch, RootState } from './store';
3 |
4 | // Use throughout your app instead of plain `useDispatch` and `useSelector`
5 | export const useAppDispatch = useDispatch.withTypes();
6 | export const useAppSelector = useSelector.withTypes();
7 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/providers-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 |
3 | export interface ProvidersBlockProps {
4 | providers: { code?: string; description?: string }[];
5 | }
6 |
7 | export interface ClusterFormCloudProviderBoxProps {
8 | children?: ReactElement;
9 | isActive?: boolean;
10 | [key: string]: unknown;
11 | }
12 |
--------------------------------------------------------------------------------
/automation/roles/confd/templates/haproxy.toml.j2:
--------------------------------------------------------------------------------
1 | [template]
2 | prefix = "/{{ patroni_etcd_namespace | default('service') }}/{{ patroni_cluster_name }}"
3 | src = "haproxy.tmpl"
4 | dest = "/etc/haproxy/haproxy.cfg"
5 | check_cmd = "/usr/sbin/haproxy -c -f {% raw %}{{ .src }}{% endraw %}"
6 | reload_cmd = "/bin/systemctl reload haproxy"
7 |
8 | keys = [
9 | "/members/",
10 | ]
11 |
--------------------------------------------------------------------------------
/console/ui/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/spinner/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { CircularProgress, Stack } from '@mui/material';
3 |
4 | const Spinner: FC = () => {
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default Spinner;
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | blank_issues_enabled: false
3 | contact_links:
4 | - name: "Question"
5 | url: https://github.com/vitabaks/autobase/discussions/new?category=q-a
6 | about: Ask a question about Autobase
7 | - name: "Commercial support"
8 | url: https://autobase.tech/docs/support
9 | about: Find out more about the available Autobase packages
10 |
--------------------------------------------------------------------------------
/automation/roles/etc_hosts/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add entries into /etc/hosts file
3 | ansible.builtin.lineinfile:
4 | path: /etc/hosts
5 | regexp: "^{{ item }}"
6 | line: "{{ item }}"
7 | unsafe_writes: true # to prevent failures in CI
8 | loop: "{{ etc_hosts }}"
9 | when:
10 | - etc_hosts is defined
11 | - etc_hosts | length > 0
12 | tags: etc_hosts
13 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/templates/pgbackrest.server.stanza.conf.j2:
--------------------------------------------------------------------------------
1 | [{{ pgbackrest_stanza }}]
2 | {% for host in groups['postgres_cluster'] %}
3 | pg{{ loop.index }}-host={{ hostvars[host].bind_address }}
4 | pg{{ loop.index }}-port={{ postgresql_port }}
5 | pg{{ loop.index }}-socket-path={{ postgresql_unix_socket_dir }}
6 | pg{{ loop.index }}-path={{ postgresql_data_dir }}
7 | {% endfor %}
8 |
9 |
--------------------------------------------------------------------------------
/.sql-formatter.json:
--------------------------------------------------------------------------------
1 | {
2 | "language": "postgresql",
3 | "keywordCase": "lower",
4 | "dataTypeCase": "lower",
5 | "functionCase": "lower",
6 | "identifierCase": "lower",
7 | "tabWidth": 2,
8 | "useTabs": false,
9 | "linesBetweenQueries": 1,
10 | "logicalOperatorNewline": true,
11 | "expressionWidth": 160,
12 | "denseOperators": false,
13 | "newlineBeforeSemicolon": false
14 | }
15 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/network-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import * as yup from 'yup';
3 | import { NETWORK_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/network-block/model/const.ts';
4 |
5 | export const NetworkBlockFormSchema = (t: TFunction) =>
6 | yup.object({
7 | [NETWORK_BLOCK_FIELD_NAMES.SERVER_NETWORK]: yup.string(),
8 | });
9 |
--------------------------------------------------------------------------------
/.config/.yamllint:
--------------------------------------------------------------------------------
1 | ---
2 | extends: default
3 |
4 | rules:
5 | line-length:
6 | max: 160
7 | comments-indentation: disable
8 | comments:
9 | min-spaces-from-content: 1
10 | braces:
11 | min-spaces-inside: 0
12 | max-spaces-inside: 1
13 | new-lines:
14 | type: unix
15 | empty-lines:
16 | max: 1
17 | indentation: false
18 |
19 | ignore: |
20 | .github/
21 | .venv
22 |
--------------------------------------------------------------------------------
/automation/roles/consul/templates/consul_systemd_service.override.j2:
--------------------------------------------------------------------------------
1 | # WARNING!!! Ansible managed.
2 |
3 | [Unit]
4 | ConditionFileNotEmpty=
5 | ConditionFileNotEmpty={{ consul_config_path }}/config.{{ consul_config_type }}
6 |
7 | [Service]
8 | ExecStart=
9 | ExecStart=/usr/bin/consul agent -config-file={{ consul_config_path }}/config.{{ consul_config_type }} -config-dir={{ consul_configd_path }}
10 |
11 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/additional-settings-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES = Object.freeze({
2 | SYNC_STANDBY_NODES: 'synchronousStandbyModes',
3 | IS_SYNC_MODE_STRICT: 'isSynchronousModeStrict',
4 | IS_DB_PUBLIC_ACCESS: 'isDbPublicAccess',
5 | IS_CLOUD_LOAD_BALANCER: 'isCloudLoadBalancer',
6 | IS_NETDATA_MONITORING: 'isNetdataMonitoring',
7 | });
8 |
--------------------------------------------------------------------------------
/.config/make/help.mak:
--------------------------------------------------------------------------------
1 | ## —— Help ———————————————————————————————————————————————————————————————————————————————————————
2 | .PHONY: help
3 | help: ## Help command
4 | echo -e "$$HEADER"
5 | grep -E '(^[a-zA-Z0-9_-]+:.*?## .*$$)|(^## )' $(MAKEFILE_LIST) | sed 's/^[^:]*://g' | awk 'BEGIN {FS = ":.*?## | #"} ; {printf "${cyan}%-30s${reset} ${white}%s${reset} ${green}%s${reset}\n", $$1, $$2, $$3}' | sed -e 's/\[36m##/\n[32m##/'
6 |
--------------------------------------------------------------------------------
/console/ui/src/app/router/routerPathsConfig/routerClustersPathsConfig.ts:
--------------------------------------------------------------------------------
1 | const routerClustersPathsConfig = {
2 | absolutePath: '/clusters',
3 | add: {
4 | absolutePath: '/clusters/add',
5 | relativePath: 'add',
6 | },
7 | overview: {
8 | absolutePath: '/clusters/:clusterId/overview',
9 | relativePath: ':clusterId/overview',
10 | },
11 | };
12 |
13 | export default routerClustersPathsConfig;
14 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/projects-table/ui/ProjectsTableButtons.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import AddProject from '@features/add-project';
3 | import { Stack } from '@mui/material';
4 |
5 | const ProjectsTableButtons: FC = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default ProjectsTableButtons;
14 |
--------------------------------------------------------------------------------
/console/ui/src/shared/api/enhancedSecretsApi.ts:
--------------------------------------------------------------------------------
1 | import { secretsApi } from '@shared/api/api/secrets.ts';
2 |
3 | const enhancedSecretsApi = secretsApi.enhanceEndpoints({
4 | addTagTypes: ['Secrets'],
5 | endpoints: {
6 | getSecrets: {
7 | providesTags: ['Secrets'],
8 | },
9 | postSecrets: {
10 | invalidatesTags: ['Secrets'],
11 | },
12 | },
13 | });
14 |
15 | export default enhancedSecretsApi;
16 |
--------------------------------------------------------------------------------
/automation/roles/mount/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Example for --extra-vars
3 | # '{"mount": [{"path": "/pgdata", "src": "UUID=83304ebb-d942-4093-975b-8253be2aabe1", "fstype": "ext4", "opts": "defaults,noatime", "state": "mounted"}]}'
4 |
5 | # Defined in roles/common/defaults/main.yml. Commented out here to prevent conflicts.
6 | # mount:
7 | # - path: ""
8 | # src: ""
9 | # fstype: ""
10 | # opts: ""
11 | # state: ""
12 |
--------------------------------------------------------------------------------
/console/ui/.env.production:
--------------------------------------------------------------------------------
1 | VITE_API_URL=REPLACE_ME_WITH_API_URL
2 | VITE_AUTH_TOKEN=REPLACE_ME_WITH_AUTH_TOKEN
3 | VITE_CLUSTERS_POLLING_INTERVAL=REPLACE_ME_WITH_CLUSTERS_POLLING_INTERVAL
4 | VITE_CLUSTER_OVERVIEW_POLLING_INTERVAL=REPLACE_ME_WITH_CLUSTER_OVERVIEW_POLLING_INTERVAL
5 | VITE_OPERATIONS_POLLING_INTERVAL=REPLACE_ME_WITH_OPERATIONS_POLLING_INTERVAL
6 | VITE_OPERATION_LOGS_POLLING_INTERVAL=REPLACE_ME_WITH_OPERATION_LOGS_POLLING_INTERVAL
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/connection-info/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 |
3 | export interface ConnectionInfoProps {
4 | connectionInfo?: {
5 | address?: string | Record;
6 | port?: string | Record;
7 | superuser?: string;
8 | password?: string;
9 | };
10 | }
11 |
12 | export interface ConnectionInfoRowContainerProps {
13 | children: ReactNode;
14 | }
15 |
--------------------------------------------------------------------------------
/console/ui/src/entities/settings/proxy-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { SETTINGS_FORM_FIELDS_NAMES } from '@entities/settings/proxy-block/model/constants.ts';
2 |
3 | export interface SettingsFormValues {
4 | [SETTINGS_FORM_FIELDS_NAMES.HTTP_PROXY]: string;
5 | [SETTINGS_FORM_FIELDS_NAMES.HTTPS_PROXY]: string;
6 | [SETTINGS_FORM_FIELDS_NAMES.IS_EXPERT_MODE_ENABLED]: boolean;
7 | [SETTINGS_FORM_FIELDS_NAMES.IS_YAML_ENABLED]: boolean;
8 | }
9 |
--------------------------------------------------------------------------------
/console/ui/src/shared/i18n/locales/en/validation.json:
--------------------------------------------------------------------------------
1 | {
2 | "requiredField": "Required field",
3 | "onlyNumbers": "Values should be only numbers",
4 | "clusterShouldHaveProperNaming": "Cluster name should have only letters, numbers, hyphens and have length equal or less than 24",
5 | "shouldBeACorrectV4Ip": "The value should be a valid IPv4 address",
6 | "configFormat": "Value should have \"key:value\" or \"key=value\" format"
7 | }
8 |
--------------------------------------------------------------------------------
/automation/roles/patroni/tasks/pg_hba.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Update pg_hba.conf
3 | ansible.builtin.template:
4 | src: templates/pg_hba.conf.j2
5 | dest: "{{ postgresql_conf_dir }}/pg_hba.conf"
6 | owner: postgres
7 | group: postgres
8 | mode: "0640"
9 | notify: "reload postgres"
10 | tags: pg_hba, pg_hba_generate
11 |
12 | - name: Make sure handlers are flushed immediately
13 | ansible.builtin.meta: flush_handlers
14 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/templates/pgbackrest_bootstrap.sh.j2:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | {% raw %}
3 | while getopts ":-:" optchar; do
4 | [[ "${optchar}" == "-" ]] || continue
5 | case "${OPTARG}" in
6 | datadir=* )
7 | DATA_DIR=${OPTARG#*=}
8 | ;;
9 | scope=* )
10 | SCOPE=${OPTARG#*=}
11 | ;;
12 | esac
13 | done
14 | {% endraw %}
15 |
16 | {{ pgbackrest_patroni_cluster_restore_command }}
17 |
18 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/database-servers-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const DATABASE_SERVERS_FIELD_NAMES = Object.freeze({
2 | IS_CLUSTER_EXISTS: 'databaseServerExistingCluster',
3 | DATABASE_SERVERS: 'databaseServers',
4 | DATABASE_HOSTNAME: 'databaseServerHostname',
5 | DATABASE_IP_ADDRESS: 'databaseServerIpAddress',
6 | DATABASE_LOCATION: 'databaseServerLocation',
7 | IS_POSTGRESQL_EXISTS: 'databaseServerIsPostgreSQLExist',
8 | });
9 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/storage-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const STORAGE_BLOCK_FIELDS = Object.freeze({
2 | STORAGE_AMOUNT: 'storageAmount',
3 | FILE_SYSTEM_TYPE: 'fileSystemType',
4 | VOLUME_TYPE: 'volumeType',
5 | });
6 |
7 | export const fileSystemTypeOptions = Object.freeze([
8 | { label: 'ext4', value: 'ext4' },
9 | {
10 | label: 'xfs',
11 | value: 'xfs',
12 | },
13 | { label: 'zfs', value: 'zfs' },
14 | ]);
15 |
--------------------------------------------------------------------------------
/console/ui/src/app/layout/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import Sidebar from '@widgets/sidebar';
3 | import Header from '@widgets/header';
4 | import Main from '@widgets/main';
5 |
6 | const Layout: FC = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default Layout;
17 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/settingsIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/service/internal/storage/cluster_flags_test.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import (
4 | "gotest.tools/v3/assert"
5 | "testing"
6 | )
7 |
8 | func TestClusterFlags(t *testing.T) {
9 | assert.Equal(t, uint32(1), *SetPatroniConnectStatus(0, 1))
10 | assert.Equal(t, uint32(1), *SetPatroniConnectStatus(1, 1))
11 | assert.Equal(t, uint32(0x11), *SetPatroniConnectStatus(0x10, 1))
12 | assert.Equal(t, uint32(0), *SetPatroniConnectStatus(1, 0))
13 | }
14 |
--------------------------------------------------------------------------------
/console/ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Autobase for PostgreSQL®
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/console/ui/src/features/add-project/model/validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { TFunction } from 'i18next';
3 | import { PROJECT_FORM_NAMES } from '@features/add-project/model/constants.ts';
4 |
5 | export const AddProjectFormSchema = (t: TFunction) =>
6 | yup.object({
7 | [PROJECT_FORM_NAMES.NAME]: yup.string().required(t('requiredField', { ns: 'validation' })),
8 | [PROJECT_FORM_NAMES.DESCRIPTION]: yup.string(),
9 | });
10 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/environments-table/ui/EnvironmentsTableButtons.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { Stack } from '@mui/material';
3 | import AddEnvironment from '@features/add-environment';
4 |
5 | const EnvironmentsTableButtons: FC = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default EnvironmentsTableButtons;
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | tab_width = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.go]
13 | indent_style = tab
14 |
15 | [*.mak]
16 | indent_style = tab
17 |
18 | [Makefile]
19 | indent_style = tab
20 |
21 | [*.conf]
22 | indent_style = tab
23 |
24 | [*.md]
25 | trim_trailing_whitespace = false
26 | indent_size = 1
27 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/RedHat.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: RedHat.yml - Red Hat OS variables for Consul
3 |
4 | consul_os_packages:
5 | - python3-libselinux
6 | - unzip
7 |
8 | consul_repo_prerequisites:
9 | - yum-utils
10 |
11 | consul_repo_url: "{{ 'https://rpm.releases.hashicorp.com/fedora/hashicorp.repo' if ansible_distribution == 'Fedora' else
12 | 'https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo' }}"
13 |
14 | dnsmasq_package: dnsmasq
15 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-summary/assets/hetznerIcon2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/io_scheduler/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Create systemd unit file io-scheduler.service
3 | ansible.builtin.template:
4 | src: templates/io-scheduler.service.j2
5 | dest: /etc/systemd/system/io-scheduler.service
6 | notify: "restart io-scheduler"
7 | when: set_scheduler is defined and set_scheduler|bool
8 | tags: scheduler, io_scheduler
9 |
10 | - name: Make sure handlers are flushed immediately
11 | ansible.builtin.meta: flush_handlers
12 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/settings-add-entity/model/validation.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import * as yup from 'yup';
3 | import { ADD_ENTITY_FORM_NAMES } from '@shared/ui/settings-add-entity/model/constants.ts';
4 |
5 | export const AddEntityFormSchema = (t: TFunction) =>
6 | yup.object({
7 | [ADD_ENTITY_FORM_NAMES.NAME]: yup.string().required(t('requiredField', { ns: 'validation' })),
8 | [ADD_ENTITY_FORM_NAMES.DESCRIPTION]: yup.string(),
9 | });
10 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/clusters-table/assets/warningIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/environments-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { PROJECTS_TABLE_COLUMN_NAMES } from '@widgets/projects-table/model/constants.ts';
2 |
3 | export interface EnvironmentTableValues {
4 | [PROJECTS_TABLE_COLUMN_NAMES.ID]: string;
5 | [PROJECTS_TABLE_COLUMN_NAMES.NAME]: string;
6 | [PROJECTS_TABLE_COLUMN_NAMES.DESCRIPTION]: 'description';
7 | [PROJECTS_TABLE_COLUMN_NAMES.CREATED]: 'created_at';
8 | [PROJECTS_TABLE_COLUMN_NAMES.UPDATED]: 'updated_at';
9 | }
10 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/secrets-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { SECRETS_TABLE_COLUMN_NAMES } from '@widgets/secrets-table/model/constants.ts';
2 |
3 | export interface SecretsTableValues {
4 | [SECRETS_TABLE_COLUMN_NAMES.NAME]: string;
5 | [SECRETS_TABLE_COLUMN_NAMES.TYPE]: string;
6 | [SECRETS_TABLE_COLUMN_NAMES.CREATED]: string;
7 | [SECRETS_TABLE_COLUMN_NAMES.UPDATED]: string;
8 | [SECRETS_TABLE_COLUMN_NAMES.USED]: string;
9 | [SECRETS_TABLE_COLUMN_NAMES.ID]: number;
10 | }
11 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/reload_consul_conf.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Use SIGHUP to reload most configurations as per https://www.consul.io/docs/agent/options.html
3 | # Cannot use `consul reload` because it requires the HTTP API to be bound to a non-loopback interface
4 |
5 | - name: reload consul configuration on unix
6 | ansible.builtin.command: "pkill --pidfile '{{ consul_run_path }}/consul.pid' --signal SIGHUP"
7 | when: ansible_os_family != "Windows"
8 | listen: "reload consul configuration"
9 |
--------------------------------------------------------------------------------
/automation/roles/keepalived/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | keepalived_instances:
3 | - name: VI_1
4 | state: BACKUP
5 | interface: "{{ vip_interface }}"
6 | virtual_router_id: "{{ keepalived_virtual_router_id | default(123) }}"
7 | priority: 100
8 | advert_int: 2
9 | check_status_command: /usr/libexec/keepalived/haproxy_check.sh
10 | authentication:
11 | auth_type: PASS
12 | auth_pass: "1ce24b6e"
13 | virtual_ipaddresses:
14 | - "{{ cluster_vip }}"
15 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/settings-add-entity/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ADD_ENTITY_FORM_NAMES } from '@shared/ui/settings-add-entity/model/constants.ts';
2 |
3 | export interface AddEntityFormValues {
4 | [ADD_ENTITY_FORM_NAMES.NAME]: string;
5 | [ADD_ENTITY_FORM_NAMES.NAME]: string;
6 | }
7 |
8 | export interface SettingsAddEntityProps {
9 | buttonLabel: string;
10 | submitButtonLabel: string;
11 | isLoading?: boolean;
12 | submitTrigger: (values: AddEntityFormValues) => void;
13 | }
14 |
--------------------------------------------------------------------------------
/console/service/migrations/migrate.go:
--------------------------------------------------------------------------------
1 | package migrations
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/rs/zerolog/log"
7 |
8 | "github.com/jackc/pgx/v5/pgxpool"
9 | "github.com/jackc/pgx/v5/stdlib"
10 | "github.com/pressly/goose/v3"
11 | )
12 |
13 | func Migrate(dbPool *pgxpool.Pool, migrationDir string) error {
14 | db := stdlib.OpenDBFromPool(dbPool)
15 | goose.SetLogger(NewZeroLogAdapter(log.Logger))
16 |
17 | return goose.RunContext(context.Background(), "up", db, migrationDir)
18 | }
19 |
--------------------------------------------------------------------------------
/console/ui/src/shared/i18n/locales/en/operations.json:
--------------------------------------------------------------------------------
1 | {
2 | "operations": "Operations",
3 | "operation": "Operation",
4 | "started": "Started",
5 | "finished": "Finished",
6 | "creationTime": "Creation time",
7 | "type": "Type",
8 | "showDetails": "Show details",
9 | "lastDay": "Last day",
10 | "lastWeek": "Last week",
11 | "lastMonth": "Last month",
12 | "lastThreeMonths": "Last three months",
13 | "lastSixMonths": "Last six months",
14 | "lastYear": "Last year",
15 | "log": "Log"
16 | }
--------------------------------------------------------------------------------
/console/ui/src/widgets/clusters-table/assets/errorIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/consul/tasks/systemd_resolved.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Ensure resolved.conf.d exists
3 | ansible.builtin.file:
4 | path: /etc/systemd/resolved.conf.d/
5 | state: directory
6 |
7 | - name: Configure systemd-resolved DNS
8 | ansible.builtin.template:
9 | src: consul_resolved.conf.j2
10 | dest: /etc/systemd/resolved.conf.d/consul.conf
11 | mode: "0644"
12 |
13 | - name: Reload service systemd-resolved
14 | ansible.builtin.systemd:
15 | name: systemd-resolved.service
16 | state: restarted
17 |
--------------------------------------------------------------------------------
/console/ui/src/shared/model/validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { TFunction } from 'i18next';
3 |
4 | export const configValidationSchema = (t: TFunction) =>
5 | yup
6 | .string()
7 | .test(
8 | 'should have correct format',
9 | t('configFormat', { ns: 'validation' }),
10 | (value) =>
11 | /^[^:=\n\r]+:[^:=\n\r]+([\n\r][^:=\n\r]+:[^:=\n\r]+)*$/i.test(value) ||
12 | /^[^:=\n\r]+=[^:=\n\r]+([\n\r][^:=\n\r]+=[^:=\n\r]+)*$/i.test(value) ||
13 | value === '',
14 | );
15 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-summary/assets/digitaloceanIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/service/internal/storage/cluster_flags.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | import "go.openly.dev/pointy"
4 |
5 | const (
6 | patroniConnectStatusMaskSet = uint32(0x1)
7 | patroniConnectStatusMaskRemove = uint32(0xfffffff6)
8 | )
9 |
10 | func SetPatroniConnectStatus(oldMask uint32, status uint32) *uint32 {
11 | return pointy.Uint32((oldMask & patroniConnectStatusMaskRemove) | (status & patroniConnectStatusMaskSet))
12 | }
13 |
14 | func GetPatroniConnectStatus(mask uint32) uint32 {
15 | return mask & patroniConnectStatusMaskSet
16 | }
17 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/backups-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const BACKUPS_BLOCK_FIELD_NAMES = Object.freeze({
2 | IS_BACKUPS_ENABLED: 'isBackupsEnabled',
3 | BACKUP_METHOD: 'backupMethod',
4 | BACKUP_START_TIME: 'backupStartTime',
5 | BACKUP_RETENTION: 'backupRetention',
6 | CONFIG: 'backupConfig',
7 | ACCESS_KEY: 'backupAccessKey',
8 | SECRET_KEY: 'backupSecretKey',
9 | });
10 |
11 | export const BACKUP_METHODS = Object.freeze({
12 | PG_BACK_REST: 'pgbackrest_install',
13 | WAL_G: 'wal_g_install',
14 | });
15 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/kernel-parameters-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import * as yup from 'yup';
3 | import { configValidationSchema } from '@shared/model/validation.ts';
4 | import { KERNEL_PARAMETERS_FIELD_NAMES } from '@entities/cluster/expert-mode/kernel-parameters-block/model/const.ts';
5 |
6 | export const KernelParametersBlockFormSchema = (t: TFunction) =>
7 | yup.object({
8 | [KERNEL_PARAMETERS_FIELD_NAMES.KERNEL_PARAMETERS]: configValidationSchema(t).optional(),
9 | });
10 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/clusters-table/assets/correctIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/postgres-parameters-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import * as yup from 'yup';
3 | import { POSTGRES_PARAMETERS_FIELD_NAMES } from '@entities/cluster/expert-mode/postgres-parameters-block/model/const.ts';
4 | import { configValidationSchema } from '@shared/model/validation.ts';
5 |
6 | export const PostgresParametersBlockFormSchema = (t: TFunction) =>
7 | yup.object({
8 | [POSTGRES_PARAMETERS_FIELD_NAMES.POSTGRES_PARAMETERS]: configValidationSchema(t).optional(),
9 | });
10 |
--------------------------------------------------------------------------------
/console/ui/src/features/settings-table-buttons/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useTranslation } from 'react-i18next';
3 | import { Stack } from '@mui/material';
4 | import SettingsAddSecret from '@features/add-secret';
5 |
6 | const SettingsTableButtons: React.FC = () => {
7 | const { t } = useTranslation(['shared', 'settings']);
8 |
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | export default SettingsTableButtons;
17 |
--------------------------------------------------------------------------------
/automation/molecule/tests/postgres/replication.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Check PostgreSQL replication status
3 | postgresql_query:
4 | query: "SELECT pg_is_in_recovery(), count(*) FROM pg_stat_wal_receiver;"
5 | login_unix_socket: "{{ postgresql_unix_socket_dir }}"
6 | login_port: "{{ postgresql_port }}"
7 | login_user: "{{ patroni_superuser_username }}"
8 | login_db: template1
9 | register: pg_replication_status
10 | failed_when: "pg_replication_status.query_result[0].pg_is_in_recovery and pg_replication_status.query_result[0].count == 0"
11 |
--------------------------------------------------------------------------------
/automation/roles/pam_limits/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Linux PAM limits | add or modify nofile limits
3 | community.general.pam_limits:
4 | domain: "{{ limits_user }}"
5 | limit_type: "{{ item.limit_type }}"
6 | limit_item: "{{ item.limit_item }}"
7 | value: "{{ item.value }}"
8 | loop:
9 | - { limit_type: "soft", limit_item: "nofile", value: "{{ soft_nofile }}" }
10 | - { limit_type: "hard", limit_item: "nofile", value: "{{ hard_nofile }}" }
11 | when: set_limits is defined and set_limits|bool
12 | tags: limits, pam_limits
13 |
--------------------------------------------------------------------------------
/automation/roles/upgrade/tasks/dcs_remove_cluster.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Remove existing cluster "{{ patroni_cluster_name | default('') }}" from DCS
3 | ansible.builtin.expect:
4 | command: "patronictl -c {{ patroni_config_file }} remove {{ patroni_cluster_name }}"
5 | responses:
6 | (.*)Please confirm the cluster name to remove: "{{ patroni_cluster_name }}"
7 | (.*)"Yes I am aware": "Yes I am aware"
8 | environment:
9 | PATH: "{{ ansible_env.PATH }}:/usr/bin:/usr/local/bin"
10 | when:
11 | - inventory_hostname in groups['primary']
12 |
--------------------------------------------------------------------------------
/console/ui/src/app/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App.tsx';
4 | import 'normalize.css/normalize.css';
5 | import '@shared/i18n/i18n.ts';
6 | import '@fontsource/roboto/300.css';
7 | import '@fontsource/roboto/400.css';
8 | import '@fontsource/roboto/500.css';
9 | import '@fontsource/roboto/700.css';
10 | import 'react-toastify/ReactToastify.css';
11 |
12 | ReactDOM.createRoot(document.getElementById('root')!).render(
13 |
14 |
15 | ,
16 | );
17 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/connection-info/ui/ConnectionInfoRowConteiner.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { Stack } from '@mui/material';
3 | import { ConnectionInfoRowContainerProps } from '@entities/cluster/connection-info/model/types.ts';
4 |
5 | const ConnectionInfoRowContainer: FC = ({ children }) => {
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | };
12 |
13 | export default ConnectionInfoRowContainer;
14 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Darwin.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: MacOSX.yml - Mac OS X variables for Consul
3 |
4 | consul_config_path: "/Users/{{ consul_user }}/Library/Preferences/io.consul"
5 |
6 | consul_configd_path: "{{ consul_config_path }}/consul.d"
7 |
8 | consul_launchctl_ident: "io.consul"
9 |
10 | consul_launchctl_plist: "/Library/LaunchAgents/{{ consul_launchctl_ident }}.plist"
11 |
12 | consul_log_path: "/Users/{{ consul_user }}/Library/Logs/consul"
13 |
14 | consul_os_packages: []
15 |
16 | consul_run_path: "/Users/{{ consul_user }}/Library/Caches/io.consul"
17 |
--------------------------------------------------------------------------------
/console/ui/src/entities/breadcumb-item/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { BreadcrumbsItemProps } from '@entities/breadcumb-item/model/types.ts';
3 | import { Link } from 'react-router-dom';
4 | import { useTheme } from '@mui/material';
5 |
6 | const BreadcrumbsItem: FC = ({ label, path }) => {
7 | const theme = useTheme();
8 |
9 | return (
10 |
11 | {label}
12 |
13 | );
14 | };
15 |
16 | export default BreadcrumbsItem;
17 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/clusters-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { CLUSTER_TABLE_COLUMN_NAMES } from '@widgets/clusters-table/model/constants.ts';
2 |
3 | export interface ClustersTableValues {
4 | [CLUSTER_TABLE_COLUMN_NAMES.NAME]: string;
5 | [CLUSTER_TABLE_COLUMN_NAMES.STATUS]: Element;
6 | [CLUSTER_TABLE_COLUMN_NAMES.CREATION_TIME]: string;
7 | [CLUSTER_TABLE_COLUMN_NAMES.ENVIRONMENT]: string;
8 | [CLUSTER_TABLE_COLUMN_NAMES.SERVERS]: number;
9 | [CLUSTER_TABLE_COLUMN_NAMES.POSTGRES_VERSION]: number;
10 | [CLUSTER_TABLE_COLUMN_NAMES.LOCATION]: string;
11 | }
12 |
--------------------------------------------------------------------------------
/automation/roles/vip_manager/templates/vip-manager.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Manages Virtual IP for Patroni
3 | After=network-online.target
4 | Before=patroni.service
5 |
6 | [Service]
7 | Type=simple
8 |
9 | ExecStart=/usr/bin/vip-manager --config={{ vip_manager_conf }}
10 |
11 | # VIP not released when service stopped https://github.com/cybertec-postgresql/vip-manager/issues/19
12 | ExecStopPost=/sbin/ip addr del {{ vip_manager_ip }}/{{ vip_manager_mask }} dev {{ vip_manager_iface }}
13 |
14 | Restart=on-failure
15 |
16 | [Install]
17 | WantedBy=multi-user.target
18 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/operations-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { OPERATIONS_TABLE_COLUMN_NAMES } from '@widgets/operations-table/model/constants.ts';
2 |
3 | export interface OperationsTableValues {
4 | [OPERATIONS_TABLE_COLUMN_NAMES.ID]: number;
5 | [OPERATIONS_TABLE_COLUMN_NAMES.STARTED]: string;
6 | [OPERATIONS_TABLE_COLUMN_NAMES.FINISHED]: string;
7 | [OPERATIONS_TABLE_COLUMN_NAMES.TYPE]: string;
8 | [OPERATIONS_TABLE_COLUMN_NAMES.STATUS]: string;
9 | [OPERATIONS_TABLE_COLUMN_NAMES.CLUSTER]: string;
10 | [OPERATIONS_TABLE_COLUMN_NAMES.ENVIRONMENT]: string;
11 | }
12 |
--------------------------------------------------------------------------------
/automation/roles/haproxy/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart haproxy service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: haproxy
6 | enabled: true
7 | state: restarted
8 | listen: "restart haproxy"
9 |
10 | - name: Check HAProxy is started and accepting connections
11 | ansible.builtin.wait_for:
12 | port: "{{ haproxy_listen_port.stats }}"
13 | host: "{{ haproxy_bind_address | default(bind_address, true) }}"
14 | state: started
15 | timeout: 120
16 | delay: 10
17 | ignore_errors: false
18 | listen: "restart haproxy"
19 |
--------------------------------------------------------------------------------
/console/service/internal/watcher/models.go:
--------------------------------------------------------------------------------
1 | package watcher
2 |
3 | type LogEntity struct {
4 | Task string `json:"task"`
5 | Failed bool `json:"failed"`
6 | Msg interface{} `json:"msg"`
7 | Summary interface{} `json:"summary,omitempty"`
8 | Status string `json:"status"`
9 | }
10 |
11 | type SystemInfo struct {
12 | ServerLocation *string `json:"server_location,omitempty" mapstructure:"server_location"`
13 | ServerName string `json:"server_name" mapstructure:"server_name"`
14 | IpAddress string `json:"ip_address" mapstructure:"ip_address"`
15 | }
16 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/databases-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const DATABASES_BLOCK_FIELD_NAMES = Object.freeze({
2 | DATABASES: 'databasesBlock',
3 | NAMES: 'databasesBlockNames',
4 | DATABASE_NAME: 'databasesBlockDatabaseName',
5 | USER_NAME: 'databasesBlockUsername',
6 | USER_PASSWORD: 'databasesBlockUserPassword',
7 | ENCODING: 'databasesBlockEncoding',
8 | LOCALE: 'databasesBlockLocale',
9 | BLOCK_ID: 'databasesBlockId', // blockId is required to match database name and presence for extensions. Should not be passed to API call or YAML editor
10 | });
11 |
--------------------------------------------------------------------------------
/automation/roles/io_scheduler/templates/io-scheduler.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=I/O Scheduler Setter
3 | DefaultDependencies=no
4 | After=sysinit.target local-fs.target
5 | Before=basic.target
6 |
7 | [Service]
8 | Type=oneshot
9 | {% if set_scheduler|bool %}
10 | {% for set in scheduler %}
11 | ExecStart=/bin/bash -c 'echo {{ set.sched }} > /sys/block/{{ set.device }}/queue/scheduler && echo {{ set.nr_requests }} > /sys/block/{{ set.device }}/queue/nr_requests'
12 | {% endfor %}
13 | {% endif %}
14 |
15 | TimeoutSec=0
16 | RemainAfterExit=yes
17 |
18 | [Install]
19 | WantedBy=basic.target
20 |
--------------------------------------------------------------------------------
/automation/roles/patroni/tasks/patroni.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Generate {{ patroni_config_file | default('/etc/patroni/patroni.yml') }}"
3 | ansible.builtin.template:
4 | src: templates/patroni.yml.j2
5 | dest: "{{ patroni_config_file | default('/etc/patroni/patroni.yml') }}"
6 | validate: patroni --ignore-listen-port --validate-config %s
7 | owner: postgres
8 | group: postgres
9 | mode: "0640"
10 | vars:
11 | etcd_hosts: "{{ groups['etcd_cluster'] | default([]) }}"
12 | notify: "reload patroni"
13 |
14 | - name: Flush handlers
15 | ansible.builtin.meta: flush_handlers
16 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-form/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ResponseDeploymentInfo } from '@shared/api/api/deployments.ts';
2 | import { ResponseEnvironment } from '@shared/api/api/environments.ts';
3 | import { ResponsePostgresVersion } from '@shared/api/api/other.ts';
4 |
5 | export interface ClusterFormProps {
6 | deploymentsData?: ResponseDeploymentInfo[];
7 | environmentsData?: ResponseEnvironment[];
8 | postgresVersionsData?: ResponsePostgresVersion[];
9 | }
10 |
11 | export interface ClusterFormRegionConfigBoxProps {
12 | name: string;
13 | place: string;
14 | isActive: boolean;
15 | }
16 |
--------------------------------------------------------------------------------
/automation/roles/hostname/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - block:
3 | - name: Change hostname
4 | ansible.builtin.hostname:
5 | name: "{{ hostname }}"
6 |
7 | - name: Change hostname in /etc/hosts
8 | ansible.builtin.lineinfile:
9 | dest: /etc/hosts
10 | regexp: "{{ item.regexp }}"
11 | line: "{{ item.line }}"
12 | state: present
13 | no_log: true
14 | loop:
15 | - { regexp: '^127\.0\.0\.1[ \t]+localhost', line: "127.0.0.1 localhost {{ ansible_hostname }}" }
16 | when: hostname is defined and hostname | length > 0
17 | tags: hostname
18 |
--------------------------------------------------------------------------------
/console/service/migrations/goose_logger.go:
--------------------------------------------------------------------------------
1 | package migrations
2 |
3 | import (
4 | "github.com/pressly/goose/v3"
5 | "github.com/rs/zerolog"
6 | )
7 |
8 | type zeroLogAdapter struct {
9 | log zerolog.Logger
10 | }
11 |
12 | func NewZeroLogAdapter(log zerolog.Logger) goose.Logger {
13 | return zeroLogAdapter{log: log.With().Str("module", "goouse").Logger()}
14 | }
15 |
16 | func (l zeroLogAdapter) Fatalf(format string, v ...interface{}) {
17 | l.log.Error().Msgf(format, v...)
18 | }
19 |
20 | func (l zeroLogAdapter) Printf(format string, v ...interface{}) {
21 | l.log.Info().Msgf(format, v...)
22 | }
23 |
--------------------------------------------------------------------------------
/console/ui/src/pages/settings/model/constants.ts:
--------------------------------------------------------------------------------
1 | import RouterPaths from '@app/router/routerPathsConfig';
2 |
3 | export const settingsTabsContent = [
4 | {
5 | translateKey: 'generalSettings',
6 | path: RouterPaths.settings.general.absolutePath,
7 | },
8 | {
9 | translateKey: 'secrets',
10 | path: RouterPaths.settings.secrets.absolutePath,
11 | },
12 | {
13 | translateKey: 'projects',
14 | path: RouterPaths.settings.projects.absolutePath,
15 | },
16 | {
17 | translateKey: 'environments',
18 | path: RouterPaths.settings.environments.absolutePath,
19 | },
20 | ];
21 |
--------------------------------------------------------------------------------
/automation/roles/ntp/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart ntp service
3 | ansible.builtin.systemd:
4 | name: ntp
5 | enabled: true
6 | state: restarted
7 | listen: "restart ntp"
8 |
9 | - name: Restart chrony service
10 | ansible.builtin.systemd:
11 | name: chrony
12 | enabled: true
13 | state: restarted
14 | listen: "restart chrony"
15 |
16 | - name: Restart chronyd service
17 | ansible.builtin.systemd:
18 | name: chronyd
19 | enabled: true
20 | state: restarted
21 | ignore_errors: "{{ chronyd_ignore_errors | default(false) }}"
22 | listen: "restart chronyd"
23 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/supportIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/error-box/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { Box, Typography } from '@mui/material';
3 | import { useTranslation } from 'react-i18next';
4 | import { ErrorBoxProps } from '@shared/ui/error-box/model/types.ts';
5 |
6 | const ErrorBox: FC = ({ text }) => {
7 | const { t } = useTranslation('shared');
8 |
9 | return (
10 |
11 | {text ?? t('somethingWentWrongWhileRendering')}
12 |
13 | );
14 | };
15 |
16 | export default ErrorBox;
17 |
--------------------------------------------------------------------------------
/automation/roles/consul/tasks/user_group.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: user_group.yml - User and group settings
3 |
4 | # Add group
5 | - name: Add Consul group
6 | ansible.builtin.group:
7 | name: "{{ consul_group }}"
8 | state: present
9 | when:
10 | - consul_manage_group | bool
11 | - not consul_install_from_repo | bool
12 |
13 | # Add user
14 | - name: Add Consul user
15 | ansible.builtin.user:
16 | name: "{{ consul_user }}"
17 | comment: "Consul user"
18 | group: "{{ consul_group }}"
19 | system: true
20 | when:
21 | - consul_manage_user | bool
22 | - not consul_install_from_repo | bool
23 |
--------------------------------------------------------------------------------
/console/ui/src/app/router/routerPathsConfig/routerSettingsPathsConfig.ts:
--------------------------------------------------------------------------------
1 | const routerSettingsPathsConfig = {
2 | absolutePath: '/settings',
3 | general: {
4 | absolutePath: '/settings/general',
5 | relativePath: 'general',
6 | },
7 | secrets: {
8 | absolutePath: '/settings/secrets',
9 | relativePath: 'secrets',
10 | },
11 | projects: {
12 | absolutePath: '/settings/projects',
13 | relativePath: 'projects',
14 | },
15 | environments: {
16 | absolutePath: '/settings/environments',
17 | relativePath: 'environments',
18 | },
19 | };
20 |
21 | export default routerSettingsPathsConfig;
22 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/backups-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { BACKUPS_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/backups-block/model/const.ts';
2 |
3 | export interface BackupsBlockValues {
4 | [BACKUPS_BLOCK_FIELD_NAMES.IS_BACKUPS_ENABLED]: boolean;
5 | [BACKUPS_BLOCK_FIELD_NAMES.BACKUP_METHOD]?: string;
6 | [BACKUPS_BLOCK_FIELD_NAMES.BACKUP_START_TIME]?: number;
7 | [BACKUPS_BLOCK_FIELD_NAMES.BACKUP_RETENTION]?: number;
8 | [BACKUPS_BLOCK_FIELD_NAMES.CONFIG]?: string;
9 | [BACKUPS_BLOCK_FIELD_NAMES.ACCESS_KEY]?: string;
10 | [BACKUPS_BLOCK_FIELD_NAMES.SECRET_KEY]?: string;
11 | }
12 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/connection-info/assets/eyeIcon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/console/service/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.25-bookworm as builder
2 | WORKDIR /go/src/pg-console
3 |
4 | COPY console/service/ .
5 |
6 | RUN make build_in_docker
7 |
8 | FROM debian:bookworm-slim
9 | LABEL maintainer="Vitaliy Kukharik vitabaks@gmail.com"
10 |
11 | COPY --from=builder /go/src/pg-console/pg-console /usr/local/bin/
12 | COPY console/db/migrations /etc/db/migrations
13 |
14 | RUN apt-get clean && rm -rf /var/lib/apt/lists/partial \
15 | && apt-get update -o Acquire::CompressionTypes::Order::=gz \
16 | && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl
17 |
18 | CMD ["/usr/local/bin/pg-console"]
19 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/additional-settings-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/additional-settings-block/model/const.ts';
2 |
3 | export interface AdditionalSettingsBlockValues {
4 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.SYNC_STANDBY_NODES]: number;
5 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_SYNC_MODE_STRICT]?: string;
6 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_DB_PUBLIC_ACCESS]?: number;
7 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_CLOUD_LOAD_BALANCER]?: number;
8 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_NETDATA_MONITORING]?: string;
9 | }
10 |
--------------------------------------------------------------------------------
/automation/roles/pam_limits/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: pam_limits
2 |
3 | Configures per-user file descriptor limits (nofile) via community.general.pam_limits
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |---|---|---|
9 | | set_limits | true | Enable applying PAM limits. When false, the role does nothing. |
10 | | limits_user | "postgres" | System user for which limits are applied. |
11 | | soft_nofile | 65536 | Soft nofile limit. |
12 | | hard_nofile | 200000 | Hard nofile limit. |
13 |
14 | ## Dependencies
15 |
16 | This role depends on:
17 | - `vitabaks.autobase.common` - Provides common variables and configurations
18 |
--------------------------------------------------------------------------------
/console/ui/src/entities/authentification-method-form-block/model/constants.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import { AUTHENTICATION_METHODS } from '@shared/model/constants.ts';
3 |
4 | export const authenticationMethods = (t: TFunction) =>
5 | Object.freeze([
6 | {
7 | id: AUTHENTICATION_METHODS.SSH,
8 | name: t('sshKey', { ns: 'clusters' }),
9 | description: t('sshKeyAuthDescription', { ns: 'clusters' }),
10 | },
11 | {
12 | id: AUTHENTICATION_METHODS.PASSWORD,
13 | name: t('password', { ns: 'shared' }),
14 | description: t('passwordAuthDescription', { ns: 'clusters' }),
15 | },
16 | ]);
17 |
--------------------------------------------------------------------------------
/automation/molecule/tests/postgres/postgres.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Check if PostgreSQL process is running
3 | command: pgrep -u postgres
4 | register: result
5 | failed_when: result.rc != 0
6 |
7 | - name: Check if PostgreSQL is listening on the default port
8 | ansible.builtin.wait_for:
9 | port: 5432
10 | timeout: 5
11 | register: is_listening
12 | failed_when: not is_listening
13 |
14 | - name: Try to connect to PostgreSQL
15 | postgresql_ping:
16 | login_unix_socket: "{{ postgresql_unix_socket_dir }}"
17 | login_port: "{{ postgresql_port }}"
18 | login_user: "{{ patroni_superuser_username }}"
19 | login_db: template1
20 |
--------------------------------------------------------------------------------
/automation/roles/consul/handlers/start_consul.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: start consul on unix
3 | ansible.builtin.service:
4 | name: consul
5 | state: started
6 | when:
7 | - ansible_os_family != "Darwin"
8 | - ansible_os_family != "Windows"
9 | listen: "start consul"
10 |
11 | - name: start consul on Mac
12 | ansible.builtin.include_tasks: "{{ role_path }}/handlers/start_consul_mac.yml"
13 | when: ansible_os_family == "Darwin"
14 | listen: "start consul"
15 |
16 | - name: start consul on windows
17 | ansible.windows.win_service:
18 | name: consul
19 | state: started
20 | when: ansible_os_family == "Windows"
21 | listen: "start consul"
22 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/slider-box/lib/functions.ts:
--------------------------------------------------------------------------------
1 | import { GenerateMarkType, GenerateSliderMarksType } from '@shared/ui/slider-box/model/types.ts';
2 |
3 | const generateMark: GenerateMarkType = (value, marksAdditionalLabel) => ({
4 | value,
5 | label: `${value} ${marksAdditionalLabel}`,
6 | });
7 |
8 | export const generateSliderMarks: GenerateSliderMarksType = (min, max, amount, marksAdditionalLabel) => {
9 | const step = (max - min) / (amount - 1);
10 | const marksArray = [];
11 |
12 | for (let i = min; i < max + step; i += step) {
13 | marksArray.push(generateMark(Math.trunc(i), marksAdditionalLabel));
14 | }
15 |
16 | return marksArray;
17 | };
18 |
--------------------------------------------------------------------------------
/automation/roles/hostname/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: hostname
2 |
3 | Sets the system hostname and ensures it is reflected in /etc/hosts for local resolution on PostgreSQL cluster nodes.
4 |
5 | ## Variables
6 |
7 | | Variable | Default | Description |
8 | |----------|---------|-------------|
9 | | hostname | "" | Desired system hostname (e.g., pgnode01). |
10 |
11 | ## Behavior
12 | - Uses the Ansible hostname module to set the hostname.
13 | - Adds/updates the localhost line in /etc/hosts to include the current ansible_hostname.
14 |
15 | ## Dependencies
16 |
17 | This role depends on:
18 | - `vitabaks.autobase.common` - Provides common variables and configurations
19 |
20 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/load-balancers-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const LOAD_BALANCERS_FIELD_NAMES = Object.freeze({
2 | IS_HAPROXY_ENABLED: 'isHaproxyEnabled',
3 | IS_DEPLOY_TO_DATABASE_SERVERS: 'isDeployToDatabaseServers',
4 | LOAD_BALANCER_DATABASES: 'loadBalancerDatabases',
5 | LOAD_BALANCER_DATABASES_HOSTNAME: 'loadBalancerDatabasesHostname',
6 | LOAD_BALANCER_DATABASES_IP_ADDRESS: 'loadBalancerDatabasesIpAddress',
7 | });
8 |
9 | export const LOAD_BALANCERS_DATABASES_DEFAULT_VALUES = Object.freeze({
10 | [LOAD_BALANCERS_FIELD_NAMES.LOAD_BALANCER_DATABASES_HOSTNAME]: '',
11 | [LOAD_BALANCERS_FIELD_NAMES.LOAD_BALANCER_DATABASES_IP_ADDRESS]: '',
12 | });
13 |
--------------------------------------------------------------------------------
/automation/requirements.yml:
--------------------------------------------------------------------------------
1 | ---
2 | collections:
3 | - name: amazon.aws
4 | version: ">=10.1.2"
5 | - name: community.aws
6 | version: ">=10.0.0"
7 | - name: google.cloud
8 | version: ">=1.10.2"
9 | - name: azure.azcollection
10 | version: ">=3.10.1"
11 | - name: community.digitalocean
12 | version: ">=1.27.0"
13 | - name: hetzner.hcloud
14 | version: ">=5.4.0"
15 | - name: community.postgresql
16 | version: ">=4.1.0"
17 | - name: community.docker
18 | version: ">=4.8.2"
19 | - name: community.general
20 | version: ">=11.4.1"
21 | - name: ansible.posix
22 | version: ">=2.1.0"
23 | - name: ansible.utils
24 | version: ">=6.0.0"
25 |
--------------------------------------------------------------------------------
/automation/roles/timezone/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: timezone
2 |
3 | Sets the system timezone on target hosts. Installs `tzdata` when using package-based installations and applies the timezone via `community.general.timezone` module.
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |---------------------|--------------|-------------|
9 | | timezone | "" | IANA timezone string (e.g., "Etc/UTC", "Europe/Berlin"). |
10 |
11 | Note: The role runs only when timezone variable is non-empty.
12 |
13 | ## Dependencies
14 |
15 | This role depends on:
16 | - `vitabaks.autobase.common` - Provides common variables and configurations
17 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/cloud-region-block/lib/hooks.tsx:
--------------------------------------------------------------------------------
1 | import AWSIcon from '../assets/aws.svg';
2 | import GCPIcon from '../assets/gcp.svg';
3 | import AzureIcon from '../assets/azure.svg';
4 | import DigitalOceanIcon from '../assets/digitalocean.svg';
5 | import HetznerIcon from '../assets/hetzner.svg';
6 | import { PROVIDERS } from '@shared/config/constants.ts';
7 |
8 | export const useNameIconProvidersMap = () => ({
9 | // TODO: refactor into moving from hooks to constant
10 | [PROVIDERS.AWS]: AWSIcon,
11 | [PROVIDERS.GCP]: GCPIcon,
12 | [PROVIDERS.AZURE]: AzureIcon,
13 | [PROVIDERS.DIGITAL_OCEAN]: DigitalOceanIcon,
14 | [PROVIDERS.HETZNER]: HetznerIcon,
15 | });
16 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/storageIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/consul/vars/Windows.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # File: Windows.yml - Windows OS variables for Consul
3 |
4 | # paths
5 | consul_windows_path: /ProgramData/consul
6 | consul_bin_path: "{{ consul_windows_path }}/bin"
7 | consul_config_path: "{{ consul_windows_path }}/config"
8 | consul_configd_path: "{{ consul_config_path }}.d/"
9 | consul_bootstrap_state: "{{ consul_windows_path }}/.consul_bootstrapped"
10 | consul_data_path: "{{ consul_windows_path }}/data"
11 | consul_log_path: "{{ consul_windows_path }}/log"
12 | consul_run_path: "{{ consul_windows_path }}"
13 | consul_binary: "{{ consul_windows_path }}/bin/consul.exe"
14 | consul_syslog_enable: false
15 |
16 | # users
17 | consul_user: LocalSystem
18 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/ramIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/copy-icon/assets/copyIcon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/console/service/pkg/patroni/models.go:
--------------------------------------------------------------------------------
1 | package patroni
2 |
3 | type MonitoringInfo struct {
4 | State string `json:"state"`
5 | Role string `json:"role"`
6 | ServerVersion int `json:"server_version"`
7 | }
8 |
9 | type ClusterInfo struct {
10 | Members []struct {
11 | Name string `json:"name"`
12 | Role string `json:"role"`
13 | State string `json:"state"`
14 | Host string `json:"host"`
15 | Timeline int64 `json:"timeline"`
16 | Lag interface{} `json:"lag"`
17 | Tags interface{} `json:"tags"`
18 | PendingRestart bool `json:"pending_restart"`
19 | } `json:"members"`
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/flake8.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Flake8"
3 |
4 | on:
5 | push:
6 | branches:
7 | - master
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Set TERM environment variable
18 | run: echo "TERM=xterm" >> $GITHUB_ENV
19 |
20 | - name: Checkout
21 | uses: actions/checkout@v6
22 |
23 | - name: Set up Python
24 | uses: actions/setup-python@v6
25 | with:
26 | python-version: "3.14"
27 |
28 | - name: Install dependencies
29 | run: make bootstrap-dev
30 |
31 | - name: Run Flake8
32 | run: make linter-flake8
33 |
--------------------------------------------------------------------------------
/automation/roles/vip_manager/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Restart vip-manager service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: vip-manager
6 | state: restarted
7 | enabled: true
8 | listen: "restart vip-manager"
9 |
10 | - name: Wait for the cluster ip address (VIP) "{{ cluster_vip | default('') }}" is running
11 | ansible.builtin.wait_for:
12 | host: "{{ cluster_vip }}"
13 | port: "{{ pgbouncer_listen_port if pgbouncer_install | bool else postgresql_port }}"
14 | state: started
15 | timeout: 15 # max wait time: 30 seconds
16 | delay: 2
17 | ignore_errors: true # show the error and continue the playbook execution
18 | listen: "restart vip-manager"
19 |
--------------------------------------------------------------------------------
/console/service/internal/xdocker/imanager.go:
--------------------------------------------------------------------------------
1 | package xdocker
2 |
3 | import "context"
4 |
5 | type InstanceID string
6 | type ManageClusterConfig struct {
7 | Envs []string
8 | ExtraVars string // JSON string
9 | Mounts []Mount
10 | }
11 |
12 | type Mount struct {
13 | DockerPath string
14 | HostPath string
15 | }
16 |
17 | type IManager interface {
18 | ManageCluster(ctx context.Context, req *ManageClusterConfig) (InstanceID, error)
19 | GetStatus(ctx context.Context, id InstanceID) (string, error)
20 | StoreContainerLogs(ctx context.Context, id InstanceID, store func(logMessage string))
21 | PreloadImage(ctx context.Context)
22 | RemoveContainer(ctx context.Context, id InstanceID) error
23 | }
24 |
--------------------------------------------------------------------------------
/console/ui/src/shared/api/baseApi.ts:
--------------------------------------------------------------------------------
1 | import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
2 | import { API_URL } from '@shared/config/constants.ts';
3 | import i18n from 'i18next';
4 |
5 | export const baseApi = createApi({
6 | baseQuery: fetchBaseQuery({
7 | baseUrl: API_URL as string,
8 | prepareHeaders: (headers, { endpoint }) => {
9 | headers.set('Accept-Language', i18n.language);
10 | if (endpoint !== 'login') headers.set('Authorization', `Bearer ${String(localStorage.getItem('token'))}`);
11 | return headers;
12 | },
13 | }),
14 | tagTypes: ['Clusters', 'Operations', 'Secrets', 'Projects', 'Environments', 'Settings'],
15 | endpoints: () => ({}),
16 | });
17 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/calendarClockICon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/yamllint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Yamllint"
3 |
4 | on:
5 | push:
6 | branches:
7 | - master
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Set TERM environment variable
18 | run: echo "TERM=xterm" >> $GITHUB_ENV
19 |
20 | - name: Checkout
21 | uses: actions/checkout@v6
22 |
23 | - name: Set up Python
24 | uses: actions/setup-python@v6
25 | with:
26 | python-version: "3.14"
27 |
28 | - name: Install dependencies
29 | run: make bootstrap-dev
30 |
31 | - name: Run Yamllint
32 | run: make linter-yamllint
33 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/copy-icon/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { CopyIconProps } from '@shared/ui/copy-icon/model/types.ts';
3 | import { useCopyToClipboard } from '@shared/lib/hooks.tsx';
4 | import CopyValueIcon from '@mui/icons-material/ContentCopyOutlined';
5 | import { Box, SxProps, Theme } from '@mui/material';
6 |
7 | const CopyIcon: FC }> = ({ valueToCopy, sx }) => {
8 | const [_, copyFunction] = useCopyToClipboard();
9 |
10 | return (
11 | copyFunction(valueToCopy)} sx={{ cursor: 'pointer', ...sx }}>
12 |
13 |
14 | );
15 | };
16 |
17 | export default CopyIcon;
18 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/docsIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/load-balancers-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { UseFieldArrayRemove } from 'react-hook-form';
2 | import { LOAD_BALANCERS_FIELD_NAMES } from './const';
3 |
4 | export interface LoadBalancersDatabaseBoxProps {
5 | index: number;
6 | remove?: UseFieldArrayRemove;
7 | }
8 |
9 | export interface LoadBalancersBlockValues {
10 | [LOAD_BALANCERS_FIELD_NAMES.IS_HAPROXY_ENABLED]: boolean;
11 | [LOAD_BALANCERS_FIELD_NAMES.IS_DEPLOY_TO_DATABASE_SERVERS]?: boolean;
12 | [LOAD_BALANCERS_FIELD_NAMES.LOAD_BALANCER_DATABASES]: {
13 | [LOAD_BALANCERS_FIELD_NAMES.LOAD_BALANCER_DATABASES_HOSTNAME]?: string;
14 | [LOAD_BALANCERS_FIELD_NAMES.LOAD_BALANCER_DATABASES_IP_ADDRESS]?: string;
15 | }[];
16 | }
17 |
--------------------------------------------------------------------------------
/.github/workflows/ansible-lint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Ansible-lint"
3 |
4 | on:
5 | push:
6 | branches:
7 | - master
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Set TERM environment variable
18 | run: echo "TERM=xterm" >> $GITHUB_ENV
19 |
20 | - name: Checkout
21 | uses: actions/checkout@v6
22 |
23 | - name: Set up Python
24 | uses: actions/setup-python@v6
25 | with:
26 | python-version: "3.14"
27 |
28 | - name: Install dependencies
29 | run: make bootstrap-dev
30 |
31 | - name: Run Ansible-lint
32 | run: make linter-ansible-lint
33 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/cpuIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/service/middleware/cid.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "postgresql-cluster-console/pkg/tracer"
7 |
8 | "github.com/google/uuid"
9 | )
10 |
11 | func SetCorrelationId(next http.Handler) http.Handler {
12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13 | cid := getCid(r)
14 | if r.Header.Get(XCorrID) == "" {
15 | r.Header.Set(XCorrID, cid)
16 | }
17 |
18 | ctx := context.WithValue(r.Context(), tracer.CtxCidKey{}, cid)
19 | next.ServeHTTP(w, r.WithContext(ctx))
20 | })
21 | }
22 |
23 | func getCid(r *http.Request) string {
24 | cid := r.Header.Get(XCorrID)
25 | if cid != "" {
26 | return cid
27 | }
28 |
29 | return uuid.New().String()
30 | }
31 |
--------------------------------------------------------------------------------
/automation/roles/deploy_finish/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: deploy_finish
2 |
3 | Auxiliary role that prints PostgreSQL cluster information and connection details at the end of a playbook run.
4 |
5 | ## What it prints
6 | - PostgreSQL users list
7 | - PostgreSQL databases list
8 | - Patroni cluster status (patronictl list)
9 | - Connection endpoints depending on environment:
10 | - etcd + cluster_vip (with/without HAProxy)
11 | - etcd without VIP (HAProxy or direct PostgreSQL, with optional PgBouncer)
12 | - Consul-based DNS names
13 | - Cloud load balancers (AWS CLB/NLB, GCP, Azure, DigitalOcean, Hetzner)
14 |
15 | ## Dependencies
16 |
17 | This role depends on:
18 | - `vitabaks.autobase.common` - Provides common variables and configurations
19 |
--------------------------------------------------------------------------------
/console/ui/src/app/router/routerPathsConfig/index.ts:
--------------------------------------------------------------------------------
1 | import routerClustersPathsConfig from '@app/router/routerPathsConfig/routerClustersPathsConfig.ts';
2 | import routerOperationsPathsConfig from '@app/router/routerPathsConfig/routerOperationsPathsConfig.ts';
3 | import routerSettingsPathsConfig from '@app/router/routerPathsConfig/routerSettingsPathsConfig.ts';
4 |
5 | /*
6 | Combines route paths into one config
7 | */
8 | const RouterPaths = {
9 | login: {
10 | absolutePath: 'login',
11 | },
12 | notFound: {
13 | absolutePath: 'notFound',
14 | },
15 | clusters: routerClustersPathsConfig,
16 | operations: routerOperationsPathsConfig,
17 | settings: routerSettingsPathsConfig,
18 | } as const;
19 |
20 | export default RouterPaths;
21 |
--------------------------------------------------------------------------------
/automation/roles/patroni/handlers/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Reload patroni service
3 | ansible.builtin.systemd:
4 | daemon_reload: true
5 | name: patroni
6 | enabled: true
7 | state: reloaded
8 | register: patroni_reload_result
9 | failed_when: false # ignore 'Could not find the requested service' for initial deployment.
10 | listen: "reload patroni"
11 |
12 | - name: Reload postgres
13 | become: true
14 | become_user: postgres
15 | ansible.builtin.command: "{{ postgresql_bin_dir }}/pg_ctl reload -D {{ postgresql_data_dir }}"
16 | register: pg_ctl_reload_result
17 | changed_when: pg_ctl_reload_result.rc == 0
18 | failed_when: false # exec 'reload' on all running postgres (to re-run with --tag pg_hba).
19 | listen: "reload postgres"
20 |
--------------------------------------------------------------------------------
/automation/roles/resolv_conf/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: resolv_conf
2 |
3 | A role to manage system DNS resolvers by ensuring /etc/resolv.conf exists and contains the provided nameserver entries.
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |--------------|---------|-------------|
9 | | nameservers | not set (role does nothing if empty) | List of DNS servers to add to /etc/resolv.conf (e.g., ["1.1.1.1", "8.8.8.8"]). If inherited from the common role, the default is Cloudflare + Google. |
10 |
11 | Note: Existing lines are not removed or reordered; duplicates are avoided per entry via regexp.
12 |
13 | ## Dependencies
14 |
15 | This role depends on:
16 | - `vitabaks.autobase.common` - Provides common variables and configurations
17 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/projects-table/lib/hooks.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { ResponseProject } from '@shared/api/api/projects.ts';
3 | import { PROJECTS_TABLE_COLUMN_NAMES } from '@widgets/projects-table/model/constants.ts';
4 |
5 | export const useGetProjectsTableData = (data: ResponseProject[]) =>
6 | useMemo(
7 | () =>
8 | data?.map((secret) => ({
9 | [PROJECTS_TABLE_COLUMN_NAMES.ID]: secret.id,
10 | [PROJECTS_TABLE_COLUMN_NAMES.NAME]: secret.name,
11 | [PROJECTS_TABLE_COLUMN_NAMES.CREATED]: secret.created_at,
12 | [PROJECTS_TABLE_COLUMN_NAMES.UPDATED]: secret.updated_at,
13 | [PROJECTS_TABLE_COLUMN_NAMES.DESCRIPTION]: secret.description ?? '-',
14 | })) ?? [],
15 | [data],
16 | );
17 |
--------------------------------------------------------------------------------
/automation/roles/firewall/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Note: Some variables defined in roles/common/defaults/main.yml. Commented out here to prevent conflicts.
3 |
4 | firewall_state: started
5 | # firewall_enabled_at_boot: true
6 |
7 | firewall_flush_rules_and_chains: true
8 |
9 | firewall_allowed_tcp_ports:
10 | - "22"
11 | - "25"
12 | - "80"
13 | - "443"
14 | firewall_allowed_udp_ports: []
15 | firewall_forwarded_tcp_ports: []
16 | firewall_forwarded_udp_ports: []
17 | firewall_additional_rules: []
18 | firewall_enable_ipv6: true
19 | firewall_ip6_additional_rules: []
20 | firewall_log_dropped_packets: true
21 | # Set to true to ensure other firewall management software is disabled.
22 | # firewall_disable_firewalld: false
23 | # firewall_disable_ufw: false
24 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/databases-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { DATABASES_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/databases-block/model/const.ts';
3 |
4 | export const DatabasesBlockSchema = yup.object({
5 | [DATABASES_BLOCK_FIELD_NAMES.DATABASES]: yup.array(
6 | yup.object({
7 | [DATABASES_BLOCK_FIELD_NAMES.DATABASE_NAME]: yup.string(),
8 | [DATABASES_BLOCK_FIELD_NAMES.USER_NAME]: yup.string(),
9 | [DATABASES_BLOCK_FIELD_NAMES.USER_PASSWORD]: yup.string(),
10 | [DATABASES_BLOCK_FIELD_NAMES.ENCODING]: yup.string(),
11 | [DATABASES_BLOCK_FIELD_NAMES.LOCALE]: yup.string(),
12 | [DATABASES_BLOCK_FIELD_NAMES.BLOCK_ID]: yup.string(),
13 | }),
14 | ),
15 | });
16 |
--------------------------------------------------------------------------------
/console/ui/src/app/redux/slices/projectSlice/projectSlice.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2 |
3 | interface ProjectSliceState {
4 | currentProject: string | null;
5 | }
6 |
7 | const initialState: ProjectSliceState = {
8 | currentProject: localStorage.getItem('currentProject') ?? '',
9 | };
10 |
11 | export const projectSlice = createSlice({
12 | name: 'project',
13 | initialState,
14 | reducers: {
15 | setProject: (state: ProjectSliceState, action: PayloadAction) => {
16 | state.currentProject = action.payload;
17 | localStorage.setItem('currentProject', action.payload);
18 | },
19 | },
20 | });
21 |
22 | export const { setProject } = projectSlice.actions;
23 |
24 | export default projectSlice.reducer;
25 |
--------------------------------------------------------------------------------
/console/ui/src/features/bradcrumbs/hooks/useBreadcrumbs.tsx:
--------------------------------------------------------------------------------
1 | import { useMatches } from 'react-router-dom';
2 | import { useTranslation } from 'react-i18next';
3 |
4 | const useBreadcrumbs = (): { label: string; path: string }[] => {
5 | const { t } = useTranslation();
6 | const matches = useMatches();
7 |
8 | return matches
9 | .filter((match: any) => Boolean(match?.handle?.breadcrumb))
10 | .map((match) => ({
11 | label:
12 | typeof match.handle.breadcrumb.label === 'function'
13 | ? match.handle.breadcrumb.label({ ...match.params })
14 | : t(match.handle.breadcrumb.label, { ns: match.handle.breadcrumb.ns }),
15 | path: match.handle.breadcrumb?.path ?? match.pathname,
16 | }));
17 | };
18 |
19 | export default useBreadcrumbs;
20 |
--------------------------------------------------------------------------------
/console/service/internal/convert/postgres_versions.go:
--------------------------------------------------------------------------------
1 | package convert
2 |
3 | import (
4 | "postgresql-cluster-console/internal/storage"
5 | "postgresql-cluster-console/models"
6 |
7 | "github.com/go-openapi/strfmt"
8 | )
9 |
10 | func PostgresVersion(pv *storage.PostgresVersion) *models.ResponsePostgresVersion {
11 | return &models.ResponsePostgresVersion{
12 | EndOfLife: strfmt.Date(pv.EndOfLife),
13 | MajorVersion: pv.MajorVersion,
14 | ReleaseDate: strfmt.Date(pv.ReleaseDate),
15 | }
16 | }
17 |
18 | func PostgresVersions(pvs []storage.PostgresVersion) []*models.ResponsePostgresVersion {
19 | resp := make([]*models.ResponsePostgresVersion, 0, len(pvs))
20 | for _, pv := range pvs {
21 | resp = append(resp, PostgresVersion(&pv))
22 | }
23 |
24 | return resp
25 | }
26 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/main/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC, Suspense } from 'react';
2 | import { Divider, Stack, Toolbar, useTheme } from '@mui/material';
3 | import { Outlet } from 'react-router-dom';
4 | import Breadcrumbs from '@features/bradcrumbs';
5 | import Spinner from '@shared/ui/spinner';
6 |
7 | const Main: FC = () => {
8 | const theme = useTheme();
9 | return (
10 |
11 |
12 |
13 |
14 |
15 | }>
16 |
17 |
18 |
19 |
20 | )};
21 |
22 | export default Main;
23 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/dcs-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { UseFieldArrayRemove } from 'react-hook-form';
2 | import { DCS_BLOCK_FIELD_NAMES } from './const';
3 |
4 | export interface DcsDatabaseBoxProps {
5 | index: number;
6 | remove?: UseFieldArrayRemove;
7 | fields: Record[];
8 | }
9 |
10 | export interface DcsBlockFormValues {
11 | [DCS_BLOCK_FIELD_NAMES.TYPE]: string;
12 | [DCS_BLOCK_FIELD_NAMES.IS_DEPLOY_NEW_CLUSTER]: boolean;
13 | [DCS_BLOCK_FIELD_NAMES.IS_DEPLOY_TO_DB_SERVERS]: boolean;
14 | [DCS_BLOCK_FIELD_NAMES.DCS_DATABASES]?: {
15 | [DCS_BLOCK_FIELD_NAMES.DCS_DATABASE_HOSTNAME]?: string;
16 | [DCS_BLOCK_FIELD_NAMES.DCS_DATABASE_IP_ADDRESS]?: string;
17 | [DCS_BLOCK_FIELD_NAMES.DCS_DATABASE_PORT]?: string;
18 | }[];
19 | }
20 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_schemas/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Make sure the PostgreSQL schemas are present
3 | become: true
4 | become_user: postgres
5 | community.postgresql.postgresql_schema:
6 | name: "{{ item.schema }}"
7 | owner: "{{ item.owner }}"
8 | login_db: "{{ item.db }}"
9 | login_host: "127.0.0.1"
10 | login_port: "{{ postgresql_port }}"
11 | login_user: "{{ patroni_superuser_username }}"
12 | login_password: "{{ patroni_superuser_password }}"
13 | state: present
14 | ignore_errors: true
15 | loop: "{{ postgresql_schemas | flatten(1) }}"
16 | when:
17 | - postgresql_schemas | default('') | length > 0
18 | - patroni_standby_cluster.host | default('') | length < 1 # do not perform on the Standby Cluster
19 | tags: postgresql_schemas
20 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/connection-pools-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { UseFieldArrayRemove } from 'react-hook-form';
2 | import { CONNECTION_POOLS_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/connection-pools-block/model/const.ts';
3 |
4 | export interface ConnectionPoolBlockProps {
5 | index: number;
6 | remove?: UseFieldArrayRemove;
7 | }
8 |
9 | export interface ConnectionPoolBlockValues {
10 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.IS_CONNECTION_POOLER_ENABLED]?: boolean;
11 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOLS]: [
12 | {
13 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_NAME]?: string;
14 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_SIZE]?: number;
15 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_MODE]?: string;
16 | },
17 | ];
18 | }
19 |
--------------------------------------------------------------------------------
/automation/roles/firewall/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: python
3 | services: docker
4 |
5 | env:
6 | global:
7 | - ROLE_NAME: firewall
8 | matrix:
9 | - MOLECULE_DISTRO: centos7
10 | - MOLECULE_DISTRO: centos6
11 | - MOLECULE_DISTRO: ubuntu1804
12 | - MOLECULE_DISTRO: ubuntu1604
13 | - MOLECULE_DISTRO: debian9
14 |
15 | install:
16 | # Install test dependencies.
17 | - pip install molecule yamllint ansible-lint docker
18 |
19 | before_script:
20 | # Use actual Ansible Galaxy role name for the project directory.
21 | - cd ../
22 | - mv ansible-role-$ROLE_NAME geerlingguy.$ROLE_NAME
23 | - cd geerlingguy.$ROLE_NAME
24 |
25 | script:
26 | # Run tests.
27 | - molecule test
28 |
29 | notifications:
30 | webhooks: https://galaxy.ansible.com/api/v1/notifications/
31 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/database-servers-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { UseFieldArrayRemove } from 'react-hook-form';
2 | import { DATABASE_SERVERS_FIELD_NAMES } from '@entities/cluster/database-servers-block/model/const.ts';
3 |
4 | export interface DatabaseServerBlockProps {
5 | index: number;
6 | remove?: UseFieldArrayRemove;
7 | }
8 |
9 | export interface DatabaseServerBlockValues {
10 | [DATABASE_SERVERS_FIELD_NAMES.IS_CLUSTER_EXISTS]?: boolean;
11 | [DATABASE_SERVERS_FIELD_NAMES.DATABASE_SERVERS]: {
12 | [DATABASE_SERVERS_FIELD_NAMES.DATABASE_HOSTNAME]: string;
13 | [DATABASE_SERVERS_FIELD_NAMES.DATABASE_IP_ADDRESS]: string;
14 | [DATABASE_SERVERS_FIELD_NAMES.DATABASE_LOCATION]: string;
15 | [DATABASE_SERVERS_FIELD_NAMES.IS_POSTGRESQL_EXISTS]?: boolean;
16 | }[];
17 | }
18 |
--------------------------------------------------------------------------------
/automation/molecule/tests/roles/confd/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 This task is designed to include variable tests for the main role in the Confd molecule tests
3 | # 🎯 The goal is to ensure that all variable tests are executed in a systematic and organised manner
4 |
5 | # 🔄 Including variable tests for the main role in the Confd molecule tests
6 | # We use a loop to include all YAML files in the 'variables' directory
7 | # Each file is included as a task, ensuring that all variable tests are executed
8 | - name: Molecule.tests.roles.confd.main | Include Variable Tests
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_roles_confd_main_file }}"
11 | loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_roles_confd_main_file
14 |
--------------------------------------------------------------------------------
/automation/molecule/tests/variables/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 This task aims to include all assert tasks for the main variables in Molecule tests
3 | # 🎯 The objective is to ensure that all assert tasks are executed for comprehensive testing
4 |
5 | # 🔄 Including all assert tasks found in the 'asserts' directory
6 | # For each .yml file in the 'asserts' directory, we include the tasks defined in the file
7 | # This allows us to modularize our tests and keep our codebase organized
8 | - name: Molecule.tests.variables.main | Include All Assert Tasks for Comprehensive Testing
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_variables_main_file }}"
11 | loop: "{{ lookup('fileglob', 'asserts/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_variables_main_file
14 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/connection-pools-block/model/const.ts:
--------------------------------------------------------------------------------
1 | export const CONNECTION_POOLS_BLOCK_FIELD_NAMES = Object.freeze({
2 | IS_CONNECTION_POOLER_ENABLED: 'isConnectionPoolerEnabled',
3 | POOLS: 'pools',
4 | POOL_NAME: 'poolName',
5 | POOL_SIZE: 'poolSize',
6 | POOL_MODE: 'poolMode',
7 | });
8 |
9 | export const POOL_MODES = Object.freeze([
10 | {
11 | option: 'transaction',
12 | tooltip: 'Server is released back to pool after transaction finishes.',
13 | },
14 | { option: 'session', tooltip: 'Server is released back to pool after client disconnects.' },
15 | {
16 | option: 'statement',
17 | tooltip:
18 | 'Server is released back to pool after query finishes. Transactions spanning multiple statements are disallowed in this mode.',
19 | },
20 | ]);
21 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/environments-table/lib/hooks.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { ENVIRONMENTS_TABLE_COLUMN_NAMES } from '@widgets/environments-table/model/constants.ts';
3 | import { ResponseEnvironment } from '@shared/api/api/environments.ts';
4 |
5 | export const useGetEnvironmentsTableData = (data: ResponseEnvironment[]) =>
6 | useMemo(
7 | () =>
8 | data?.map((secret) => ({
9 | [ENVIRONMENTS_TABLE_COLUMN_NAMES.ID]: secret.id,
10 | [ENVIRONMENTS_TABLE_COLUMN_NAMES.NAME]: secret.name,
11 | [ENVIRONMENTS_TABLE_COLUMN_NAMES.CREATED]: secret.created_at,
12 | [ENVIRONMENTS_TABLE_COLUMN_NAMES.UPDATED]: secret.updated_at,
13 | [ENVIRONMENTS_TABLE_COLUMN_NAMES.DESCRIPTION]: secret.description ?? '-',
14 | })) ?? [],
15 | [data],
16 | );
17 |
--------------------------------------------------------------------------------
/automation/molecule/tests/roles/patroni/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 The objective of this task is to include and execute variable tests for the main Patroni role
3 | # 🎯 This ensures that all variable tests are run, providing comprehensive coverage and validation of the role's variables
4 |
5 | # 🔄 Including and executing variable tests for the main Patroni role
6 | # We use a loop to iterate over all .yml files in the 'variables' directory
7 | # Each file is included and its tasks are executed
8 | - name: Molecule.tests.roles.patroni.main | Include and Execute Variable Tests
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_roles_patroni_main_file }}"
11 | loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_roles_patroni_main_file
14 |
--------------------------------------------------------------------------------
/automation/roles/etcd/templates/etcd.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Etcd Server
3 | After=network.target
4 | After=network-online.target
5 | Wants=network-online.target
6 |
7 | [Service]
8 | Type=notify
9 | WorkingDirectory={{ etcd_data_dir | default('/var/lib/etcd') }}
10 | EnvironmentFile=-{{ etcd_conf_dir | default('/etc/etcd') }}/etcd.conf
11 | User=etcd
12 | # set GOMAXPROCS to number of processors
13 | ExecStart=/bin/bash -c "GOMAXPROCS=$(nproc) /usr/local/bin/etcd"
14 | Restart=on-failure
15 | RestartSec=10
16 | StartLimitInterval=0
17 | TimeoutStartSec=180
18 | LimitNOFILE=65536
19 |
20 | {% if ansible_virtualization_type not in ['container', 'docker', 'lxc', 'podman'] %}
21 | IOSchedulingClass=realtime
22 | IOSchedulingPriority=0
23 | Nice=-20
24 | {% endif %}
25 |
26 | [Install]
27 | WantedBy=multi-user.target
28 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/providers-block/ui/ClusterFormCloudProviderBox.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import SelectableBox from '@shared/ui/selectable-box';
3 | import { ClusterFormCloudProviderBoxProps } from '@entities/cluster/providers-block/model/types.ts';
4 |
5 | const ClusterFormCloudProviderBox: FC = ({ children, isActive, ...props }) => {
6 | return (
7 |
18 | {children}
19 |
20 | );
21 | };
22 |
23 | export default ClusterFormCloudProviderBox;
24 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/extensions-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { EXTENSION_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/extensions-block/model/const.ts';
2 | import { ResponseDatabaseExtension } from '@shared/api/api/other.ts';
3 |
4 | export interface ExtensionSelectorProps {
5 | extension: ResponseDatabaseExtension;
6 | }
7 |
8 | export interface ExtensionBoxProps extends ExtensionSelectorProps {
9 | extensionIcons: Record;
10 | }
11 |
12 | export interface ExtensionsSwiperProps extends Pick {
13 | isPending: boolean;
14 | filteredExtensions: ResponseDatabaseExtension[];
15 | }
16 |
17 | export interface ExtensionsBlockValues {
18 | [EXTENSION_BLOCK_FIELD_NAMES.EXTENSIONS]?: Record;
19 | }
20 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/connection-pools-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import * as yup from 'yup';
2 | import { TFunction } from 'i18next';
3 | import { CONNECTION_POOLS_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/connection-pools-block/model/const.ts';
4 |
5 | export const ConnectionPoolsBlockSchema = (t: TFunction) =>
6 | yup.object({
7 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.IS_CONNECTION_POOLER_ENABLED]: yup.boolean(),
8 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOLS]: yup.array(
9 | yup.object({
10 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_NAME]: yup.string(),
11 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_SIZE]: yup.number().typeError(t('onlyNumbers', { ns: 'validation' })),
12 | [CONNECTION_POOLS_BLOCK_FIELD_NAMES.POOL_MODE]: yup.string(),
13 | }),
14 | ),
15 | });
16 |
--------------------------------------------------------------------------------
/automation/roles/bind_address/README.md:
--------------------------------------------------------------------------------
1 | ## Ansible Role: bind_address
2 |
3 | This role automatically detects and sets the available private IPv4 address for each host as the variable `bind_address`, unless it is already defined in inventory or group_vars.
4 |
5 | #### How it works
6 |
7 | - Finds the first available private IPv4 address on the host (excluding the docker0 interface, if present).
8 | - Sets this address as the Ansible fact `bind_address` using `set_fact`.
9 | - If `bind_address` is already defined in inventory or variables, it will not be executed.
10 | - If no suitable private IP is found, the role fails with a clear message.
11 |
12 | #### Recommendation
13 |
14 | If a host has multiple IP addresses (e.g., multiple interfaces/VLANs), explicitly set `bind_address` in inventory for each host instead of relying on auto-detection.
15 |
--------------------------------------------------------------------------------
/automation/roles/pgbackrest/tasks/bootstrap_script.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - block: # patroni cluster bootstrap script
3 | - name: Make sure the pgbackrest bootstrap script directory exist
4 | ansible.builtin.file:
5 | dest: /etc/patroni
6 | state: directory
7 | owner: postgres
8 | group: postgres
9 |
10 | - name: Create /etc/patroni/pgbackrest_bootstrap.sh script
11 | ansible.builtin.template:
12 | src: templates/pgbackrest_bootstrap.sh.j2
13 | dest: /etc/patroni/pgbackrest_bootstrap.sh
14 | owner: postgres
15 | group: postgres
16 | mode: "0775"
17 | when:
18 | - patroni_cluster_bootstrap_method is defined
19 | - patroni_cluster_bootstrap_method == "pgbackrest"
20 | - "'postgres_cluster' in group_names"
21 | tags: pgbackrest, pgbackrest_bootstrap_script
22 |
--------------------------------------------------------------------------------
/automation/roles/transparent_huge_pages/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: transparent_huge_pages
2 |
3 | Disables Linux Transparent Huge Pages (THP) for better PostgreSQL performance. The role creates and enables a systemd service that sets THP and defrag to "never".
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |-------------|---------|-------------|
9 | | disable_thp | true | When true, installs/enables a systemd service to disable THP and THP defrag. When false, the role does nothing. |
10 |
11 | ## Notes
12 | - Idempotent; updates the unit at /etc/systemd/system/disable-transparent-huge-pages.service
13 | - Not executed on virtualization types: container, docker, lxc, podman.
14 |
15 | ## Dependencies
16 |
17 | This role depends on:
18 | - `vitabaks.autobase.common` - Provides common variables and configurations
19 |
--------------------------------------------------------------------------------
/automation/roles/swap/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: swap
2 |
3 | Manages system swap via a swap file. Creates, resizes, or removes the swap file when needed, adds it to /etc/fstab, and enables it with swapon.
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |---------------------|------------|-------------|
9 | | swap_file_create | true | Master toggle. If false, the role does nothing. |
10 | | swap_file_path | /swapfile | Path to the swap file. |
11 | | swap_file_size_mb | "4096" | Desired swap file size in megabytes. If different from current, the role recreates the swap. |
12 |
13 | Notes:
14 | - Skips on virtualization types: container, docker, lxc, podman.
15 |
16 | ## Dependencies
17 |
18 | This role depends on:
19 | - `vitabaks.autobase.common` - Provides common variables and configurations
20 |
--------------------------------------------------------------------------------
/automation/molecule/default/prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Update docker network(s)"
3 | hosts: localhost
4 | gather_facts: false
5 | become: false
6 | tasks:
7 | - name: "Create docker network: test_docker_network"
8 | community.docker.docker_network:
9 | name: test_docker_network
10 | driver: bridge
11 | driver_options:
12 | com.docker.network.driver.mtu: 1440
13 | enable_ipv6: false
14 | internal: false
15 | ipam_config:
16 | - subnet: 10.172.0.0/24
17 | gateway: 10.172.0.1
18 | force: true
19 | state: present
20 | labels:
21 | owner: molecule
22 |
23 | - name: "Install netaddr dependency on control host"
24 | ansible.builtin.pip:
25 | name: netaddr
26 | environment:
27 | PIP_BREAK_SYSTEM_PACKAGES: "1"
28 |
--------------------------------------------------------------------------------
/automation/molecule/tests/roles/swap/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 This task aims to include variable tests for the main swap role in Molecule
3 | # 🎯 The objective is to ensure all conditions are tested by looping through each YAML file in the 'conditions' directory
4 |
5 | # 🔄 Including variable tests for the main swap role in Molecule
6 | # For each YAML file in the 'conditions' directory, we include its tasks in the current playbook
7 | # If a YAML file is not found or cannot be read, the playbook execution will fail at this point
8 | - name: Molecule.tests.roles.swap.main | Include Variable Tests from Conditions Directory
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_roles_swap_main_file }}"
11 | loop: "{{ lookup('fileglob', 'conditions/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_roles_swap_main_file
14 |
--------------------------------------------------------------------------------
/automation/molecule/tests/roles/pre-checks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 This task is designed to include variable tests in the main pre-checks for Molecule tests
3 | # 🎯 The objective is to ensure that all variable tests are properly included and executed
4 |
5 | # 🔄 Including variable tests in the main pre-checks for Molecule tests
6 | # For each YAML file in the 'variables' directory, we include its tasks in the main pre-checks
7 | # If a file does not exist or cannot be read, the task will fail and an error message will be displayed
8 | - name: Molecule.tests.roles.pre_checks.main | Include Variable Tests in Main Pre-checks
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_roles_pre_checks_main_file }}"
11 | loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_roles_pre_checks_main_file
14 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/additional-settings-block/model/validation.ts:
--------------------------------------------------------------------------------
1 | import { TFunction } from 'i18next';
2 | import * as yup from 'yup';
3 | import { ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/additional-settings-block/model/const.ts';
4 |
5 | export const AdditionalSettingsBlockFormSchema = (t: TFunction) =>
6 | yup.object({
7 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.SYNC_STANDBY_NODES]: yup
8 | .number()
9 | .typeError(t('onlyNumbers', { ns: 'validation' })),
10 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_SYNC_MODE_STRICT]: yup.boolean(),
11 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_DB_PUBLIC_ACCESS]: yup.boolean(),
12 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_CLOUD_LOAD_BALANCER]: yup.boolean(),
13 | [ADDITIONAL_SETTINGS_BLOCK_FIELD_NAMES.IS_NETDATA_MONITORING]: yup.boolean(),
14 | });
15 |
--------------------------------------------------------------------------------
/console/ui/src/shared/model/constants.ts:
--------------------------------------------------------------------------------
1 | export const AUTHENTICATION_METHODS = Object.freeze({
2 | // changing names might break secrets POST request
3 | SSH: 'ssh_key',
4 | PASSWORD: 'password',
5 | });
6 |
7 | export const LOCAL_STORAGE_ITEMS = Object.freeze({
8 | IS_EXPERT_MODE: 'isExpertMode',
9 | IS_YAML_ENABLED: 'isYamlEnabled',
10 | });
11 |
12 | export let IS_EXPERT_MODE = localStorage.getItem(LOCAL_STORAGE_ITEMS.IS_EXPERT_MODE)?.toString() === 'true';
13 | export let IS_YAML_ENABLED = localStorage.getItem(LOCAL_STORAGE_ITEMS.IS_YAML_ENABLED)?.toString() === 'true';
14 |
15 | window.addEventListener('storage', () => {
16 | IS_EXPERT_MODE = localStorage.getItem(LOCAL_STORAGE_ITEMS.IS_EXPERT_MODE)?.toString() === 'true'; // TODO: refactor
17 | IS_YAML_ENABLED = localStorage.getItem(LOCAL_STORAGE_ITEMS.IS_YAML_ENABLED)?.toString() === 'true'; // TODO: refactor
18 | });
19 |
--------------------------------------------------------------------------------
/.config/ansible-lint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | skip_list:
3 | - command-instead-of-module
4 | - command-instead-of-shell
5 | - experimental
6 | - ignore-errors
7 | - no-changed-when
8 | - no-handler
9 | - no-relative-paths
10 | - package-latest
11 | - key-order[task]
12 | - var-naming[no-role-prefix]
13 | - yaml[indentation]
14 | - yaml[line-length]
15 | - name[missing]
16 | - name[unique]
17 | - name[template]
18 | - name[casing] # TODO: All names should start with an uppercase letter.
19 | - risky-file-permissions # TODO: File permissions unset or incorrect.
20 | - role-name # TODO: Avoid using paths when importing roles. Role name XXX does not match ``^*$`` pattern.
21 | - schema[playbook] # TODO: Use FQCN for `become_method`.
22 | - schema[tasks] # TODO: Use FQCN for `become_method`.
23 | - galaxy[no-changelog]
24 |
25 | exclude_paths:
26 | - automation/molecule/
27 |
--------------------------------------------------------------------------------
/automation/roles/timezone/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - block:
3 | - name: Gather package facts
4 | ansible.builtin.package_facts:
5 | manager: auto
6 | when: ansible_facts.packages is not defined
7 | check_mode: false
8 |
9 | - name: Make sure that the tzdata package is installed
10 | become: true
11 | become_method: sudo
12 | ansible.builtin.package:
13 | name: tzdata
14 | state: present
15 | environment: "{{ proxy_env | default({}) }}"
16 | when:
17 | - installation_method == "packages"
18 | - "'tzdata' not in ansible_facts.packages"
19 |
20 | - name: Set timezone to "{{ timezone | default('') }}"
21 | become: true
22 | become_method: sudo
23 | community.general.timezone:
24 | name: "{{ timezone }}"
25 | when: timezone is defined and timezone | length > 0
26 | tags: timezone
27 |
--------------------------------------------------------------------------------
/.config/make/formatting.mak:
--------------------------------------------------------------------------------
1 | ## —— Formatting ——————————————————————————————————————————————————————————————————————————-------
2 |
3 | .PHONY: prettier
4 | prettier: ## Run Prettier formatting
5 | npx prettier --write .
6 |
7 | .PHONY: prettier-check
8 | prettier-check: ## Check formatting with Prettier (without modifying files)
9 | npx prettier --check .
10 |
11 | .PHONY: sql-format
12 | sql-format: ## Format all SQL files using sql-formatter
13 | find . -name "*.sql" -print0 | xargs -0 -n1 sql-formatter --fix
14 |
15 | # https://hub.docker.com/r/backplane/pgformatter
16 | .PHONY: pg-format
17 | pg-format: ## Format all SQL files using pgFormatter (PostgreSQL SQL queries and PL/PGSQL code beautifier)
18 | find . -name "*.sql" -print0 | xargs -0 -I{} \
19 | docker run --rm -v "$(shell pwd):/work" -u $(shell id -u):$(shell id -g) \
20 | backplane/pgformatter -u 1 -U 1 -f 1 -s 2 -W 0 -w 160 -i "{}"
21 |
--------------------------------------------------------------------------------
/automation/molecule/pg_upgrade/prepare.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Update docker network(s)"
3 | hosts: localhost
4 | gather_facts: false
5 | become: false
6 | tasks:
7 | - name: "Create docker network: upgrade_test_docker_network"
8 | community.docker.docker_network:
9 | name: upgrade_test_docker_network
10 | driver: bridge
11 | driver_options:
12 | com.docker.network.driver.mtu: 1440
13 | enable_ipv6: false
14 | internal: false
15 | ipam_config:
16 | - subnet: 10.172.2.0/24
17 | gateway: 10.172.2.1
18 | force: true
19 | state: present
20 | labels:
21 | owner: molecule
22 |
23 | - name: "Install netaddr dependency on control host"
24 | ansible.builtin.pip:
25 | name: netaddr
26 | become: false
27 | environment:
28 | PIP_BREAK_SYSTEM_PACKAGES: "1"
29 |
--------------------------------------------------------------------------------
/automation/roles/authorized_keys/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - block:
3 | - name: Get system username
4 | become: false
5 | ansible.builtin.command: whoami
6 | register: system_user
7 | changed_when: false
8 |
9 | - name: "Add public keys to ~{{ system_user.stdout | default('') }}/.ssh/authorized_keys"
10 | ansible.posix.authorized_key:
11 | user: "{{ system_user.stdout }}"
12 | key: "{{ item | replace(\"'\", '') | replace('\"', '') | trim }}"
13 | state: present
14 | loop: >-
15 | {{
16 | (ssh_public_keys
17 | | replace('\n', ',')
18 | | split(',')
19 | | reject('equalto', '')
20 | | list)
21 | if ssh_public_keys is string else ssh_public_keys
22 | }}
23 | when:
24 | - ssh_public_keys is defined
25 | - ssh_public_keys | length > 0
26 | tags: ssh_public_keys
27 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-form/ui/ClusterFormLocalMachineFormPart.tsx:
--------------------------------------------------------------------------------
1 | import { FC, lazy } from 'react';
2 | import DatabaseServersBlock from '@entities/cluster/database-servers-block';
3 | import AuthenticationMethodFormBlock from '@entities/authentification-method-form-block';
4 | import VipAddressBlock from '@entities/cluster/vip-address-block';
5 | import LoadBalancersBlock from '@entities/cluster/load-balancers-block';
6 | import { IS_EXPERT_MODE } from '@shared/model/constants.ts';
7 |
8 | const DcsBlock = lazy(() => import('@entities/cluster/expert-mode/dcs-block/ui'));
9 |
10 | const ClusterFormLocalMachineFormPart: FC = () => (
11 | <>
12 |
13 | {IS_EXPERT_MODE ? : null}
14 |
15 |
16 |
17 | >
18 | );
19 |
20 | export default ClusterFormLocalMachineFormPart;
21 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/githubIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/slider-box/model/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react';
2 | import { Mark } from '@mui/material/Slider/useSlider.types';
3 |
4 | export interface SliderBoxProps {
5 | amount: number;
6 | changeAmount: (...event: any[]) => void;
7 | icon?: ReactElement;
8 | unit?: string;
9 | min: number;
10 | max: number;
11 | marks?: { label: unknown; value: unknown }[];
12 | marksAmount?: number;
13 | marksAdditionalLabel?: string;
14 | step?: number | null;
15 | error?: object;
16 | limitMin?: boolean;
17 | limitMax?: boolean;
18 | topRightElements?: ReactElement | null;
19 | }
20 |
21 | export type GenerateMarkType = (value: number, marksAdditionalLabel: string) => { label: string; value: number };
22 |
23 | export type GenerateSliderMarksType = (
24 | min: number,
25 | max: number,
26 | amount: number,
27 | marksAdditionalLabel: string,
28 | ) => Mark[];
29 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_debian12.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (Debian 12)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 0 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: debian12
34 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_debian13.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (Debian 13)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 0 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: debian13
34 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_ubuntu2204.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (Ubuntu 22.04)
3 |
4 | on:
5 | schedule:
6 | - cron: "30 0 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: ubuntu2204
34 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_ubuntu2404.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (Ubuntu 24.04)
3 |
4 | on:
5 | schedule:
6 | - cron: "30 0 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: ubuntu2404
34 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_rockylinux10.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (RockyLinux 10)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 1 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: rockylinux10
34 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_rockylinux9.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (RockyLinux 9)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 1 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: rockylinux9
34 |
--------------------------------------------------------------------------------
/console/service/internal/convert/database_extensions.go:
--------------------------------------------------------------------------------
1 | package convert
2 |
3 | import (
4 | "postgresql-cluster-console/internal/storage"
5 | "postgresql-cluster-console/models"
6 | )
7 |
8 | func DbExtensionToSwagger(ext *storage.Extension) *models.ResponseDatabaseExtension {
9 | return &models.ResponseDatabaseExtension{
10 | Contrib: ext.Contrib,
11 | Description: ext.Description,
12 | Image: ext.Image,
13 | Name: ext.Name,
14 | PostgresMaxVersion: ext.PostgresMaxVersion,
15 | PostgresMinVersion: ext.PostgresMinVersion,
16 | URL: ext.Url,
17 | }
18 | }
19 |
20 | func DbExtensionsToSwagger(exts []storage.Extension) []*models.ResponseDatabaseExtension {
21 | resp := make([]*models.ResponseDatabaseExtension, 0, len(exts))
22 | for _, ext := range exts {
23 | resp = append(resp, DbExtensionToSwagger(&ext))
24 | }
25 |
26 | return resp
27 | }
28 |
--------------------------------------------------------------------------------
/console/service/internal/convert/settings.go:
--------------------------------------------------------------------------------
1 | package convert
2 |
3 | import (
4 | "postgresql-cluster-console/internal/storage"
5 | "postgresql-cluster-console/models"
6 |
7 | "github.com/go-openapi/strfmt"
8 | )
9 |
10 | func SettingToSwagger(s *storage.Setting) *models.ResponseSetting {
11 | return &models.ResponseSetting{
12 | CreatedAt: strfmt.DateTime(s.CreatedAt),
13 | ID: s.ID,
14 | Name: s.Name,
15 | UpdatedAt: func() *strfmt.DateTime {
16 | if s.UpdatedAt == nil {
17 | return nil
18 | }
19 | updated := strfmt.DateTime(*s.UpdatedAt)
20 |
21 | return &updated
22 | }(),
23 | Value: s.Value,
24 | }
25 | }
26 |
27 | func SettingsToSwagger(settings []storage.Setting) []*models.ResponseSetting {
28 | resp := make([]*models.ResponseSetting, 0, len(settings))
29 | for _, s := range settings {
30 | resp = append(resp, SettingToSwagger(&s))
31 | }
32 |
33 | return resp
34 | }
35 |
--------------------------------------------------------------------------------
/console/ui/src/shared/ui/info-card-body/ui/index.tsx:
--------------------------------------------------------------------------------
1 | import { FC } from 'react';
2 | import { Divider, Stack, Typography } from '@mui/material';
3 | import { InfoCardBodyProps } from '@shared/ui/info-card-body/model/types.ts';
4 |
5 | /**
6 | * Component renders body of a different summary and overview cards.
7 | * Recommended to use inside all similar looking card bodies.
8 | * @param config - Config with data to render.
9 | * @constructor
10 | */
11 | const InfoCardBody: FC = ({ config }) => (
12 |
13 | {config.map(({ title, children }, index) => (
14 |
15 |
16 | {title}
17 |
18 | {children}
19 | {index < config.length - 1 ? : null}
20 |
21 | ))}
22 |
23 | );
24 |
25 | export default InfoCardBody;
26 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/yaml-editor-form/lib/functions.ts:
--------------------------------------------------------------------------------
1 | import { ClusterFormValues } from '@features/cluster-secret-modal/model/types.ts';
2 | import {
3 | getBaseClusterExtraVars,
4 | getCloudProviderExtraVars,
5 | getLocalMachineExtraVars,
6 | } from '@shared/lib/clusterValuesTransformFunctions.ts';
7 | import { CLUSTER_FORM_FIELD_NAMES } from '@widgets/cluster-form/model/constants.ts';
8 | import { PROVIDERS } from '@shared/config/constants.ts';
9 |
10 | /**
11 | * Function converts passed form values into correct YAML "key:value" format with mapped keys.
12 | * @param values - Filled form values.
13 | */
14 | export const mapFormValuesToYamlEditor = (values: ClusterFormValues) => ({
15 | ...getBaseClusterExtraVars(values),
16 | ...(values[CLUSTER_FORM_FIELD_NAMES.PROVIDER]?.code !== PROVIDERS.LOCAL
17 | ? { ...getCloudProviderExtraVars(values) }
18 | : { ...getLocalMachineExtraVars(values) }),
19 | });
20 |
--------------------------------------------------------------------------------
/console/service/internal/controllers/secret/delete_secret.go:
--------------------------------------------------------------------------------
1 | package secret
2 |
3 | import (
4 | "postgresql-cluster-console/internal/controllers"
5 | "postgresql-cluster-console/internal/storage"
6 | "postgresql-cluster-console/restapi/operations/secret"
7 |
8 | "github.com/go-openapi/runtime/middleware"
9 | )
10 |
11 | type deleteSecretHandler struct {
12 | db storage.IStorage
13 | }
14 |
15 | func NewDeleteSecretHandler(db storage.IStorage) secret.DeleteSecretsIDHandler {
16 | return &deleteSecretHandler{
17 | db: db,
18 | }
19 | }
20 |
21 | func (h *deleteSecretHandler) Handle(param secret.DeleteSecretsIDParams) middleware.Responder {
22 | err := h.db.DeleteSecret(param.HTTPRequest.Context(), param.ID)
23 | if err != nil {
24 | return secret.NewDeleteSecretsIDBadRequest().WithPayload(controllers.MakeErrorPayload(err, controllers.BaseError))
25 | }
26 |
27 | return secret.NewDeleteSecretsIDNoContent()
28 | }
29 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/cluster-overview-table/model/types.ts:
--------------------------------------------------------------------------------
1 | import { CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES } from '@widgets/cluster-overview-table/model/constants.ts';
2 | import { ClusterInfoInstance } from '@shared/api/api/clusters.ts';
3 |
4 | export interface ClusterOverviewTableProps {
5 | clusterName?: string;
6 | items?: ClusterInfoInstance[];
7 | isLoading?: boolean;
8 | }
9 |
10 | export interface ClusterOverviewTableValues {
11 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.NAME]: string;
12 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.HOST]: string;
13 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.ROLE]: string;
14 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.STATE]: string;
15 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.TIMELINE]: number;
16 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.LAG_IN_MB]: number;
17 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.PENDING_RESTART]: string;
18 | [CLUSTER_OVERVIEW_TABLE_COLUMN_NAMES.TAGS]: string;
19 | }
20 |
--------------------------------------------------------------------------------
/automation/roles/pgbouncer/templates/pgbouncer.service.j2:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=pgBouncer connection pooling for PostgreSQL
3 | After=syslog.target network.target
4 |
5 | [Service]
6 | Type=forking
7 |
8 | User=postgres
9 | Group=postgres
10 |
11 | RuntimeDirectory=pgbouncer{{ '-%d' % (idx + 1) if idx > 0 else '' }}
12 | RuntimeDirectoryMode=0755
13 |
14 | {% if ansible_os_family == "Debian" %}
15 | ExecStart=/usr/sbin/pgbouncer -d {{ pgbouncer_conf_dir }}/pgbouncer{{ '-%d' % (idx + 1) if idx > 0 else '' }}.ini
16 | {% endif %}
17 | {% if ansible_os_family == "RedHat" %}
18 | ExecStart=/usr/bin/pgbouncer -d {{ pgbouncer_conf_dir }}/pgbouncer{{ '-%d' % (idx + 1) if idx > 0 else '' }}.ini
19 | {% endif %}
20 | ExecReload=/bin/kill -SIGHUP $MAINPID
21 | PIDFile=/run/pgbouncer{{ '-%d' % (idx + 1) if idx > 0 else '' }}/pgbouncer.pid
22 | Restart=on-failure
23 |
24 | LimitNOFILE=100000
25 |
26 | [Install]
27 | WantedBy=multi-user.target
28 |
--------------------------------------------------------------------------------
/automation/roles/resolv_conf/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - block:
3 | - name: Make sure /etc/resolv.conf exists
4 | ansible.builtin.stat:
5 | path: /etc/resolv.conf
6 | register: resolv_conf
7 |
8 | - name: Create /etc/resolv.conf
9 | ansible.builtin.file:
10 | path: /etc/resolv.conf
11 | state: touch
12 | owner: root
13 | group: root
14 | mode: u=rw,g=r,o=r
15 | when: not resolv_conf.stat.exists
16 |
17 | - name: Add DNS server(s) into /etc/resolv.conf
18 | ansible.builtin.lineinfile:
19 | path: /etc/resolv.conf
20 | regexp: "^nameserver {{ item }}"
21 | insertbefore: "^options"
22 | line: "nameserver {{ item }}"
23 | unsafe_writes: true # to prevent failures in CI
24 | loop: "{{ nameservers }}"
25 | when:
26 | - nameservers is defined
27 | - nameservers | length > 0
28 | tags: dns, nameservers
29 |
--------------------------------------------------------------------------------
/console/ui/src/widgets/secrets-table/lib/hooks.tsx:
--------------------------------------------------------------------------------
1 | import { ResponseSecretInfo } from '@shared/api/api/secrets.ts';
2 | import { useMemo } from 'react';
3 | import { SECRETS_TABLE_COLUMN_NAMES } from '@widgets/secrets-table/model/constants.ts';
4 |
5 | export const useGetSecretsTableData = (data: ResponseSecretInfo[]) =>
6 | useMemo(
7 | () =>
8 | data?.map((secret) => ({
9 | [SECRETS_TABLE_COLUMN_NAMES.NAME]: secret.name!,
10 | [SECRETS_TABLE_COLUMN_NAMES.TYPE]: secret.type!,
11 | [SECRETS_TABLE_COLUMN_NAMES.CREATED]: secret.created_at,
12 | [SECRETS_TABLE_COLUMN_NAMES.UPDATED]: secret.updated_at,
13 | [SECRETS_TABLE_COLUMN_NAMES.USED]: String(!!secret.is_used),
14 | [SECRETS_TABLE_COLUMN_NAMES.ID]: secret.id!,
15 | [SECRETS_TABLE_COLUMN_NAMES.USED_BY]: secret.used_by_clusters, // not displayed, required only for logic purposed
16 | })) ?? [],
17 | [data],
18 | );
19 |
--------------------------------------------------------------------------------
/automation/roles/pre_checks/tasks/system.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # https://www.postgresql.org/docs/current/kernel-resources.html#SYSTEMD-REMOVEIPC
3 | #
4 | # Configure systemd RemoveIPC=no to prevent errors like:
5 | # WARNING: could not remove shared memory segment "/PostgreSQL.1450751626": No such file or directory
6 | # FATAL: could not open shared memory segment "/PostgreSQL.3317458760": No such file or directory
7 |
8 | - name: Ensure RemoveIPC is disabled (RemoveIPC=no in logind.conf)
9 | community.general.ini_file:
10 | path: /etc/systemd/logind.conf
11 | section: Login
12 | option: RemoveIPC
13 | value: "no"
14 | create: true
15 | backup: true
16 | register: removeipc_result
17 |
18 | - name: Restart systemd-logind service
19 | ansible.builtin.systemd:
20 | name: systemd-logind
21 | masked: false
22 | state: restarted
23 | when:
24 | - removeipc_result.changed
25 | - ansible_service_mgr == "systemd"
26 |
--------------------------------------------------------------------------------
/console/ui/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended-type-checked',
7 | 'plugin:react-hooks/recommended',
8 | 'plugin:@typescript-eslint/stylistic-type-checked',
9 | 'plugin:react/recommended',
10 | 'plugin:react/jsx-runtime',
11 | ],
12 | ignorePatterns: ['dist', '.eslintrc.cjs'],
13 | parser: '@typescript-eslint/parser',
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | sourceType: 'module',
17 | project: ['./tsconfig.json', './tsconfig.node.json'],
18 | tsconfigRootDir: __dirname,
19 | },
20 | plugins: ['react-refresh'],
21 | rules: {
22 | 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
23 | '@typescript-eslint/no-misused-promises': 'off',
24 | '@typescript-eslint/no-unsafe-argument': 'off',
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/console/ui/src/entities/cluster/expert-mode/databases-block/model/types.ts:
--------------------------------------------------------------------------------
1 | import { UseFieldArrayRemove } from 'react-hook-form';
2 | import { DATABASES_BLOCK_FIELD_NAMES } from '@entities/cluster/expert-mode/databases-block/model/const.ts';
3 |
4 | export interface DatabasesBlockProps {
5 | index: number;
6 | remove?: UseFieldArrayRemove;
7 | }
8 |
9 | export interface DatabasesBlockSingleValue {
10 | [DATABASES_BLOCK_FIELD_NAMES.DATABASE_NAME]?: string;
11 | [DATABASES_BLOCK_FIELD_NAMES.USER_NAME]?: string;
12 | [DATABASES_BLOCK_FIELD_NAMES.USER_PASSWORD]?: string;
13 | [DATABASES_BLOCK_FIELD_NAMES.ENCODING]?: string;
14 | [DATABASES_BLOCK_FIELD_NAMES.LOCALE]?: string;
15 | [DATABASES_BLOCK_FIELD_NAMES.BLOCK_ID]: string;
16 | }
17 |
18 | export interface DatabasesBlockValues {
19 | [DATABASES_BLOCK_FIELD_NAMES.DATABASES]?: DatabasesBlockSingleValue[];
20 | [DATABASES_BLOCK_FIELD_NAMES.NAMES]?: Record;
21 | }
22 |
--------------------------------------------------------------------------------
/automation/roles/update/defaults/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | target: postgres # Defines the target for the update. Available values: 'postgres', 'patroni', 'system'
3 |
4 | update_extensions: true # Attempt will be made to automatically update all PostgreSQL extensions in all databases.
5 |
6 | # if target=system
7 | reboot_host_after_update: true # Restart the server if it is required after the update.
8 | reboot_host_timeout: 1800 # Maximum seconds to wait for machine to reboot and respond to a test command.
9 | reboot_host_post_delay: 5 # The waiting time (in minutes) for the caches to warm up after restarting the server before updating the next server.
10 |
11 | # pre-checks vars
12 | max_replication_lag_bytes: 10485760 # (10 MiB) Determines the size of the replication lag above which the update will not be performed.
13 | max_transaction_sec: 15 # (seconds) Determines the maximum transaction time, in the presence of which the update will not be performed.
14 |
--------------------------------------------------------------------------------
/console/service/internal/controllers/cluster/delete_cluster.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "postgresql-cluster-console/internal/controllers"
5 | "postgresql-cluster-console/internal/storage"
6 | "postgresql-cluster-console/restapi/operations/cluster"
7 |
8 | "github.com/go-openapi/runtime/middleware"
9 | )
10 |
11 | type deleteClusterHandler struct {
12 | db storage.IStorage
13 | }
14 |
15 | func NewDeleteClusterHandler(db storage.IStorage) cluster.DeleteClustersIDHandler {
16 | return &deleteClusterHandler{
17 | db: db,
18 | }
19 | }
20 |
21 | func (h *deleteClusterHandler) Handle(param cluster.DeleteClustersIDParams) middleware.Responder {
22 | err := h.db.DeleteClusterSoft(param.HTTPRequest.Context(), param.ID)
23 | if err != nil {
24 | return cluster.NewDeleteClustersIDBadRequest().WithPayload(controllers.MakeErrorPayload(err, controllers.BaseError))
25 | }
26 |
27 | return cluster.NewDeleteClustersIDNoContent()
28 | }
29 |
--------------------------------------------------------------------------------
/console/ui/src/app/router/routerConfig/OperationsRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react';
2 | import { Route } from 'react-router-dom';
3 | import RouterPaths from '@app/router/routerPathsConfig';
4 | import OperationLog from '@pages/operation-log';
5 |
6 | const Operations = lazy(() => import('@pages/operations'));
7 |
8 | const OperationsRoutes = () => (
9 |
10 |
15 | } />
16 | `${data.operationId}`,
21 | },
22 | }}
23 | element={}
24 | />
25 |
26 |
27 | );
28 |
29 | export default OperationsRoutes;
30 |
--------------------------------------------------------------------------------
/console/ui/src/shared/assets/instanceIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/automation/roles/copy/tasks/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Fetch files from the master
3 | become: true
4 | become_user: root
5 | run_once: true
6 | ansible.builtin.fetch:
7 | src: "{{ item.src }}"
8 | dest: "{{ item.dest }}"
9 | flat: true
10 | validate_checksum: true
11 | loop: "{{ fetch_files_from_master }}"
12 | delegate_to: "{{ groups.master[0] }}"
13 | when:
14 | - fetch_files_from_master is defined
15 | - fetch_files_from_master | length > 0
16 | tags: fetch_files
17 |
18 | - name: Copy files to all servers
19 | become: true
20 | become_user: root
21 | ansible.builtin.copy:
22 | src: "{{ item.src }}"
23 | dest: "{{ item.dest }}"
24 | owner: "{{ item.owner }}"
25 | group: "{{ item.group }}"
26 | mode: "{{ item.mode }}"
27 | loop: "{{ copy_files_to_all_server }}"
28 | when:
29 | - copy_files_to_all_server is defined
30 | - copy_files_to_all_server | length > 0
31 | tags: copy_files
32 |
--------------------------------------------------------------------------------
/automation/roles/postgresql_schemas/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: postgresql_schemas
2 |
3 | Creates and manages PostgreSQL schemas in specified databases. Ensures schemas exist with the desired owner, using [community.postgresql.postgresql_schema](https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_schema_module.html) module.
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |----------|---------|-------------|
9 | | `postgresql_schemas` | `[]` | List of schema definitions to apply. Each item typically includes: schema (name), db (database), owner (role). |
10 |
11 | ### Example:
12 |
13 | ```yaml
14 | postgresql_schemas:
15 | - { schema: "app_schema1", db: "app_db", owner: "app_user" }
16 | - { schema: "app_schema2", db: "app_db", owner: "app_user" }
17 | ```
18 |
19 | ## Dependencies
20 |
21 | This role depends on:
22 | - `vitabaks.autobase.common` - Provides common variables and configurations
23 |
--------------------------------------------------------------------------------
/automation/roles/sudo/README.md:
--------------------------------------------------------------------------------
1 | # Ansible Role: sudo
2 |
3 | Manages sudo privileges via drop-in files under /etc/sudoers.d for defined users. Supports password-less sudo (NOPASSWD) and command whitelisting.
4 |
5 | ## Role Variables
6 |
7 | | Variable | Default | Description |
8 | |------------|---------|-------------|
9 | | sudo_users | see below | List of user entries with sudo rules. Each item supports: name (string, required), nopasswd ("yes"/"no"), commands ("ALL" or comma-separated absolute paths). |
10 |
11 | Default:
12 | ```yaml
13 | sudo_users:
14 | - name: "postgres"
15 | nopasswd: "yes" # or "no" to require a password
16 | commands: "ALL"
17 | # - name: "joe" # other user (example)
18 | # nopasswd: "no"
19 | # commands: "/usr/bin/find, /usr/bin/less, /usr/bin/tail, /bin/kill"
20 | ```
21 |
22 | ## Dependencies
23 |
24 | This role depends on:
25 | - `vitabaks.autobase.common` - Provides common variables and configurations
26 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_almalinux9.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (AlmaLinux 9)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 1 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: almalinux9
34 | IMAGE_NAMESPACE: glillico
35 |
--------------------------------------------------------------------------------
/console/service/Makefile:
--------------------------------------------------------------------------------
1 | ifndef GO_BIN
2 | override GO_BIN = "pg-console"
3 | endif
4 |
5 | APP = main.go
6 |
7 | swagger_install:
8 | { \
9 | export my_dir=$$(pwd) ;\
10 | export dir=$$(mktemp -d) ;\
11 | retry_count=0 ;\
12 | max_retries=5 ;\
13 | until [ "$$retry_count" -ge "$$max_retries" ]; do \
14 | git clone https://github.com/go-swagger/go-swagger "$$dir" && break ;\
15 | retry_count=$$((retry_count+1)) ;\
16 | echo "Retry $$retry_count/$$max_retries" ;\
17 | sleep 1 ;\
18 | done ;\
19 | cd "$$dir" ;\
20 | go install ./cmd/swagger ;\
21 | cd "$$my_dir" ;\
22 | swagger version ;\
23 | }
24 |
25 | ensure_deps: ## Ensure Go module dependencies are tidy
26 | @go mod tidy
27 |
28 | build: ## Build app
29 | @go build -o $(GO_BIN) $(APP)
30 |
31 | swagger:
32 | @swagger generate server --name PgConsole --spec api/swagger.yaml --principal interface{} --exclude-main
33 |
34 | build_in_docker: swagger_install swagger ensure_deps build
35 |
--------------------------------------------------------------------------------
/console/service/internal/convert/projects.go:
--------------------------------------------------------------------------------
1 | package convert
2 |
3 | import (
4 | "postgresql-cluster-console/internal/storage"
5 | "postgresql-cluster-console/models"
6 |
7 | "github.com/go-openapi/strfmt"
8 | )
9 |
10 | func ProjectToSwagger(prj *storage.Project) *models.ResponseProject {
11 | return &models.ResponseProject{
12 | CreatedAt: strfmt.DateTime(prj.CreatedAt),
13 | Description: prj.Description,
14 | ID: prj.ID,
15 | Name: prj.Name,
16 | UpdatedAt: func() *strfmt.DateTime {
17 | if prj.UpdatedAt == nil {
18 | return nil
19 | }
20 | updated := strfmt.DateTime(*prj.UpdatedAt)
21 |
22 | return &updated
23 | }(),
24 | }
25 | }
26 |
27 | func ProjectsToSwagger(projects []storage.Project) []*models.ResponseProject {
28 | resp := make([]*models.ResponseProject, 0, len(projects))
29 | for _, prj := range projects {
30 | resp = append(resp, ProjectToSwagger(&prj))
31 | }
32 |
33 | return resp
34 | }
35 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_almalinux10.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (AlmaLinux 10)
3 |
4 | on:
5 | schedule:
6 | - cron: "15 1 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: almalinux10
34 | IMAGE_NAMESPACE: glillico
35 |
--------------------------------------------------------------------------------
/automation/molecule/tests/roles/haproxy/main.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # 🚀 The purpose of this task is to incorporate a series of variable tests for HAProxy, a reliable, high performance TCP/HTTP load balancer
3 | # 🎯 The objective is to ensure that the variables used in the HAProxy configuration are correctly defined and functional
4 |
5 | # 🔄 Including variable tests for HAProxy
6 | # This task iterates over all the YAML files in the 'variables' directory, and includes each file's tasks in the current playbook
7 | # If a variable test fails, it will be immediately apparent, aiding in debugging and ensuring the robustness of the HAProxy configuration
8 | - name: Molecule.tests.roles.haproxy.main | Incorporate Variable Tests
9 | run_once: true
10 | ansible.builtin.include_tasks: "{{ molecule_tests_roles_haproxy_main_file }}"
11 | loop: "{{ lookup('fileglob', 'variables/*.yml', wantlist=True) }}"
12 | loop_control:
13 | loop_var: molecule_tests_roles_haproxy_main_file
14 |
--------------------------------------------------------------------------------
/.github/workflows/schedule_pg_centosstream9.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: scheduled PostgreSQL (CentOS Stream 9)
3 |
4 | on:
5 | schedule:
6 | - cron: "0 0 * * *"
7 |
8 | jobs:
9 | test:
10 | runs-on: ubuntu-latest
11 | if: ${{ github.repository_owner == 'vitabaks' || github.event_name == 'workflow_dispatch' }}
12 |
13 | steps:
14 | - name: Set TERM environment variable
15 | run: echo "TERM=xterm" >> $GITHUB_ENV
16 |
17 | - name: Checkout
18 | uses: actions/checkout@v6
19 |
20 | - name: Set up Python
21 | uses: actions/setup-python@v6
22 | with:
23 | python-version: "3.14"
24 |
25 | - name: Install dependencies
26 | run: make bootstrap-dev
27 |
28 | - name: Run Molecule tests
29 | run: make molecule-test
30 | env:
31 | PY_COLORS: "1"
32 | ANSIBLE_FORCE_COLOR: "1"
33 | IMAGE_DISTRO: centosstream9
34 | IMAGE_NAMESPACE: glillico
35 |
--------------------------------------------------------------------------------