mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
880b635827 | ||
|
|
67c14cfa89 | ||
|
|
428a87ae84 | ||
|
|
1f1e22e69c | ||
|
|
c325d42b89 | ||
|
|
04a19cead1 | ||
|
|
648c315312 | ||
|
|
3a205c2f1d | ||
|
|
49ebb01ffd | ||
|
|
e957fb67dd | ||
|
|
7cda83122a | ||
|
|
11195d9078 | ||
|
|
64d7a12f9f | ||
|
|
9853ac425a | ||
|
|
6ad38228ce | ||
|
|
7d576b50a9 | ||
|
|
db3bd98425 | ||
|
|
7d8d0846cb | ||
|
|
05540a8d8d | ||
|
|
8250db9ce5 | ||
|
|
1e8cc46672 |
@@ -32,5 +32,5 @@ keywords:
|
||||
- mongodb
|
||||
- mariadb
|
||||
license: Apache-2.0
|
||||
version: 2.17.0
|
||||
date-released: "2025-12-27"
|
||||
version: 2.18.4
|
||||
date-released: "2025-12-30"
|
||||
|
||||
38
README.md
38
README.md
@@ -114,7 +114,7 @@ You have four ways to install Databasus:
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
You have three ways to install Databasus: automated script (recommended), simple Docker run, or Docker Compose setup.
|
||||
You have four ways to install Databasus: automated script (recommended), simple Docker run, or Docker Compose setup.
|
||||
|
||||
### Option 1: Automated installation script (recommended, Linux only)
|
||||
|
||||
@@ -245,6 +245,8 @@ This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENS
|
||||
|
||||
Contributions are welcome! Read the <a href="https://databasus.com/contribute">contributing guide</a> for more details, priorities and rules. If you want to contribute but don't know where to start, message me on Telegram [@rostislav_dugin](https://t.me/rostislav_dugin)
|
||||
|
||||
Also you can join our large community of developers, DBAs and DevOps engineers on Telegram [@databasus_community](https://t.me/databasus_community).
|
||||
|
||||
--
|
||||
|
||||
## 📖 Migration guide
|
||||
@@ -271,6 +273,8 @@ Then manually move databases from Postgresus to Databasus.
|
||||
|
||||
### Why was Postgresus renamed to Databasus?
|
||||
|
||||
Databasus has been developed since 2023. It was internal tool to backup production and home projects databases. In start of 2025 it was released as open source project on GitHub. By the end of 2025 it became popular and the time for renaming has come in December 2025.
|
||||
|
||||
It was an important step for the project to grow. Actually, there are a couple of reasons:
|
||||
|
||||
1. Postgresus is no longer a little tool that just adds UI for pg_dump for little projects. It became a tool both for individual users, DevOps, DBAs, teams, companies and even large enterprises. Tens of thousands of users use Postgresus every day. Postgresus grew into a reliable backup management tool. Initial positioning is no longer suitable: the project is not just a UI wrapper, it's a solid backup management system now (despite it's still easy to use).
|
||||
@@ -278,3 +282,35 @@ It was an important step for the project to grow. Actually, there are a couple o
|
||||
2. New databases are supported: although the primary focus is PostgreSQL (with 100% support in the most efficient way) and always will be, Databasus added support for MySQL, MariaDB and MongoDB. Later more databases will be supported.
|
||||
|
||||
3. Trademark issue: "postgres" is a trademark of PostgreSQL Inc. and cannot be used in the project name. So for safety and legal reasons, we had to rename the project.
|
||||
|
||||
## AI disclaimer
|
||||
|
||||
There have been questions about AI usage in project development in issues and discussions. As the project focuses on security, reliability and production usage, it's important to explain how AI is used in the development process.
|
||||
|
||||
AI is used as a helper for:
|
||||
|
||||
- verification of code quality and searching for vulnerabilities
|
||||
- cleaning up and improving documentation, comments and code
|
||||
- assistance during development
|
||||
- double-checking PRs and commits after human review
|
||||
|
||||
AI is not used for:
|
||||
|
||||
- writing entire code
|
||||
- "vibe code" approach
|
||||
- code without line-by-line verification by a human
|
||||
- code without tests
|
||||
|
||||
The project has:
|
||||
|
||||
- solid test coverage (both unit and integration tests)
|
||||
- CI/CD pipeline automation with tests and linting to ensure code quality
|
||||
- verification by experienced developers with experience in large and secure projects
|
||||
|
||||
So AI is just an assistant and a tool for developers to increase productivity and ensure code quality. The work is done by developers.
|
||||
|
||||
Moreover, it's important to note that we do not differentiate between bad human code and AI vibe code. There are strict requirements for any code to be merged to keep the codebase maintainable.
|
||||
|
||||
Even if code is written manually by a human, it's not guaranteed to be merged. Vibe code is not allowed at all and all such PRs are rejected by default (see [contributing guide](https://databasus.com/contribute)).
|
||||
|
||||
We also draw attention to fast issue resolution and security [vulnerability reporting](https://github.com/databasus/databasus?tab=security-ov-file#readme).
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 767 KiB After Width: | Height: | Size: 766 KiB |
@@ -193,8 +193,8 @@ func (c *BackupController) GetFile(ctx *gin.Context) {
|
||||
}
|
||||
}()
|
||||
|
||||
extension := ".dump.zst"
|
||||
if dbType == databases.DatabaseTypeMysql {
|
||||
extension := ".dump"
|
||||
if dbType == databases.DatabaseTypeMysql || dbType == databases.DatabaseTypeMariadb {
|
||||
extension = ".sql.zst"
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ package s3_storage
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"databasus-backend/internal/util/encryption"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -101,15 +103,21 @@ func (s *S3Storage) SaveFile(
|
||||
return fmt.Errorf("read error: %w", readErr)
|
||||
}
|
||||
|
||||
partData := buf[:n]
|
||||
hash := md5.Sum(partData)
|
||||
md5Base64 := base64.StdEncoding.EncodeToString(hash[:])
|
||||
|
||||
part, err := coreClient.PutObjectPart(
|
||||
ctx,
|
||||
s.S3Bucket,
|
||||
objectKey,
|
||||
uploadID,
|
||||
partNumber,
|
||||
bytes.NewReader(buf[:n]),
|
||||
bytes.NewReader(partData),
|
||||
int64(n),
|
||||
minio.PutObjectPartOptions{},
|
||||
minio.PutObjectPartOptions{
|
||||
Md5Base64: md5Base64,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
_ = coreClient.AbortMultipartUpload(ctx, s.S3Bucket, objectKey, uploadID)
|
||||
@@ -147,7 +155,9 @@ func (s *S3Storage) SaveFile(
|
||||
objectKey,
|
||||
bytes.NewReader([]byte{}),
|
||||
0,
|
||||
minio.PutObjectOptions{},
|
||||
minio.PutObjectOptions{
|
||||
SendContentMd5: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload empty file: %w", err)
|
||||
@@ -283,7 +293,9 @@ func (s *S3Storage) TestConnection(encryptor encryption.FieldEncryptor) error {
|
||||
testObjectKey,
|
||||
testReader,
|
||||
int64(len(testData)),
|
||||
minio.PutObjectOptions{},
|
||||
minio.PutObjectOptions{
|
||||
SendContentMd5: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upload test file to S3: %w", err)
|
||||
|
||||
@@ -74,7 +74,10 @@ export const BackupsComponent = ({ database, isCanManageDBs, scrollContainerRef
|
||||
// Find the backup to get a meaningful filename
|
||||
const backup = backups.find((b) => b.id === backupId);
|
||||
const createdAt = backup ? dayjs(backup.createdAt).format('YYYY-MM-DD_HH-mm-ss') : 'backup';
|
||||
const extension = database.type === DatabaseType.MYSQL ? '.sql.zst' : '.dump.zst';
|
||||
const extension =
|
||||
database.type === DatabaseType.MYSQL || database.type === DatabaseType.MARIADB
|
||||
? '.sql.zst'
|
||||
: '.dump';
|
||||
link.download = `${database.name}_backup_${createdAt}${extension}`;
|
||||
|
||||
// Trigger download
|
||||
|
||||
@@ -23,7 +23,17 @@ export const CreateReadOnlyComponent = ({
|
||||
|
||||
const isPostgres = database.type === DatabaseType.POSTGRES;
|
||||
const isMysql = database.type === DatabaseType.MYSQL;
|
||||
const databaseTypeName = isPostgres ? 'PostgreSQL' : isMysql ? 'MySQL' : 'database';
|
||||
const isMariadb = database.type === DatabaseType.MARIADB;
|
||||
const isMongodb = database.type === DatabaseType.MONGODB;
|
||||
const databaseTypeName = isPostgres
|
||||
? 'PostgreSQL'
|
||||
: isMysql
|
||||
? 'MySQL'
|
||||
: isMariadb
|
||||
? 'MariaDB'
|
||||
: isMongodb
|
||||
? 'MongoDB'
|
||||
: 'database';
|
||||
|
||||
const checkReadOnlyUser = async (): Promise<boolean> => {
|
||||
try {
|
||||
@@ -47,6 +57,12 @@ export const CreateReadOnlyComponent = ({
|
||||
} else if (isMysql && database.mysql) {
|
||||
database.mysql.username = response.username;
|
||||
database.mysql.password = response.password;
|
||||
} else if (isMariadb && database.mariadb) {
|
||||
database.mariadb.username = response.username;
|
||||
database.mariadb.password = response.password;
|
||||
} else if (isMongodb && database.mongodb) {
|
||||
database.mongodb.username = response.username;
|
||||
database.mongodb.password = response.password;
|
||||
}
|
||||
|
||||
onReadOnlyUserUpdated(database);
|
||||
|
||||
@@ -14,7 +14,8 @@ export function EditSFTPStorageComponent({ storage, setStorage, setUnsaved }: Pr
|
||||
const hasAdvancedValues = !!storage?.sftpStorage?.skipHostKeyVerify;
|
||||
const [showAdvanced, setShowAdvanced] = useState(hasAdvancedValues);
|
||||
|
||||
const authMethod = storage?.sftpStorage?.privateKey ? 'privateKey' : 'password';
|
||||
const initialAuthMethod = storage?.sftpStorage?.privateKey ? 'privateKey' : 'password';
|
||||
const [authMethod, setAuthMethod] = useState<'password' | 'privateKey'>(initialAuthMethod);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -93,7 +94,10 @@ export function EditSFTPStorageComponent({ storage, setStorage, setUnsaved }: Pr
|
||||
onChange={(e) => {
|
||||
if (!storage?.sftpStorage) return;
|
||||
|
||||
if (e.target.value === 'password') {
|
||||
const newMethod = e.target.value as 'password' | 'privateKey';
|
||||
setAuthMethod(newMethod);
|
||||
|
||||
if (newMethod === 'password') {
|
||||
setStorage({
|
||||
...storage,
|
||||
sftpStorage: {
|
||||
|
||||
Reference in New Issue
Block a user