471cfdd161
- 1 - Add: Gitea now creates timestamped database backup bundles under `[backup].PATH`, exposes the backup schedule in the installer, and surfaces the `database_backup` cron task in admin monitoring. - 2 - Add: installed instances now use `.gitea-installed` and `.gitea-recovery.ini` to enter email-gated recovery instead of falling back to public install mode when configuration or database access is broken. - 3 - Mod: the installer recovery flow now covers backup-bundle restore, bundled or manual `app.ini` reuse, uploaded SQL/GZ database restores, and repository-filesystem recovery with source-specific validation, confirmations, and preserved launcher state. - 4 - Fix: recovery now restores bundled `app.ini` snapshots when needed, discovers backup bundles from both the active backup path and persisted `.gitea-recovery.ini` path, and preserves SMTP and other rebuilt settings correctly when `app.ini` is missing or incomplete. - 5 - Fix: recovery validation and restore handling now accept either a selected backup bundle or an uploaded SQL/GZ dump, keep sensitive secrets and existing `LFS_JWT_SECRET` when appropriate, clear SQLite restore targets before import, and complete the post-install handoff without redirect loops. - 6 - Mod: fresh installs now default recovery email authorization to enabled with first-admin fallback, and the install/recovery UI, styling, and EN/RO wording were refined to match the final launcher behavior. Co-Authored-By: petru @ codex (GPT-5) <codex@openai.com> (cherry picked from commit 9879caf2292691b0cb521d12e6fee924b066bae2)
162 lines
3.9 KiB
Go
162 lines
3.9 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package setting
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func Test_parsePostgreSQLHostPort(t *testing.T) {
|
|
tests := map[string]struct {
|
|
HostPort string
|
|
Host string
|
|
Port string
|
|
}{
|
|
"host-port": {
|
|
HostPort: "127.0.0.1:1234",
|
|
Host: "127.0.0.1",
|
|
Port: "1234",
|
|
},
|
|
"no-port": {
|
|
HostPort: "127.0.0.1",
|
|
Host: "127.0.0.1",
|
|
Port: "5432",
|
|
},
|
|
"ipv6-port": {
|
|
HostPort: "[::1]:1234",
|
|
Host: "::1",
|
|
Port: "1234",
|
|
},
|
|
"ipv6-no-port": {
|
|
HostPort: "[::1]",
|
|
Host: "::1",
|
|
Port: "5432",
|
|
},
|
|
"unix-socket": {
|
|
HostPort: "/tmp/pg.sock:1234",
|
|
Host: "/tmp/pg.sock",
|
|
Port: "1234",
|
|
},
|
|
"unix-socket-no-port": {
|
|
HostPort: "/tmp/pg.sock",
|
|
Host: "/tmp/pg.sock",
|
|
Port: "5432",
|
|
},
|
|
}
|
|
for k, test := range tests {
|
|
t.Run(k, func(t *testing.T) {
|
|
t.Log(test.HostPort)
|
|
host, port := parsePostgreSQLHostPort(test.HostPort)
|
|
assert.Equal(t, test.Host, host)
|
|
assert.Equal(t, test.Port, port)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_getPostgreSQLConnectionString(t *testing.T) {
|
|
tests := []struct {
|
|
Host string
|
|
User string
|
|
Passwd string
|
|
Name string
|
|
SSLMode string
|
|
Output string
|
|
}{
|
|
{
|
|
Host: "", // empty means default
|
|
Output: "postgres://:@127.0.0.1:5432?sslmode=",
|
|
},
|
|
{
|
|
Host: "/tmp/pg.sock",
|
|
User: "testuser",
|
|
Passwd: "space space !#$%^^%^```-=?=",
|
|
Name: "gitea",
|
|
SSLMode: "false",
|
|
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
|
},
|
|
{
|
|
Host: "/tmp/pg.sock:6432",
|
|
User: "testuser",
|
|
Passwd: "pass",
|
|
Name: "gitea",
|
|
SSLMode: "false",
|
|
Output: "postgres://testuser:pass@:6432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
|
},
|
|
{
|
|
Host: "localhost",
|
|
User: "pgsqlusername",
|
|
Passwd: "I love Gitea!",
|
|
Name: "gitea",
|
|
SSLMode: "true",
|
|
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
|
|
},
|
|
{
|
|
Host: "localhost:1234",
|
|
User: "user",
|
|
Passwd: "pass",
|
|
Name: "gitea?param=1",
|
|
Output: "postgres://user:pass@localhost:1234/gitea?param=1&sslmode=",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.SSLMode)
|
|
assert.Equal(t, test.Output, connStr)
|
|
}
|
|
}
|
|
|
|
// start edit/add - by petru @ codex
|
|
func TestDBConnStrSQLiteMode(t *testing.T) {
|
|
oldDatabase := Database
|
|
oldInstallLock := InstallLock
|
|
oldEnableSQLite3 := EnableSQLite3
|
|
oldAllowSQLite3CreateForInstall := AllowSQLite3CreateForInstall
|
|
oldCustomConf := CustomConf
|
|
defer func() {
|
|
Database = oldDatabase
|
|
InstallLock = oldInstallLock
|
|
EnableSQLite3 = oldEnableSQLite3
|
|
AllowSQLite3CreateForInstall = oldAllowSQLite3CreateForInstall
|
|
CustomConf = oldCustomConf
|
|
}()
|
|
|
|
tmpDir := t.TempDir()
|
|
CustomConf = filepath.Join(tmpDir, "custom", "conf", "app.ini")
|
|
Database.Type = "sqlite3"
|
|
Database.Path = filepath.Join(tmpDir, "data", "gitea.db")
|
|
Database.Timeout = 500
|
|
EnableSQLite3 = true
|
|
|
|
connStr, err := DBConnStr()
|
|
require.NoError(t, err)
|
|
assert.Contains(t, connStr, "mode=rwc")
|
|
|
|
InstallLock = true
|
|
connStr, err = DBConnStr()
|
|
require.NoError(t, err)
|
|
assert.Contains(t, connStr, "mode=rw")
|
|
assert.NotContains(t, connStr, "mode=rwc")
|
|
|
|
InstallLock = false
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(InstallSentinelPath()), 0o755))
|
|
require.NoError(t, os.WriteFile(InstallSentinelPath(), []byte("installed\n"), 0o600))
|
|
connStr, err = DBConnStr()
|
|
require.NoError(t, err)
|
|
assert.Contains(t, connStr, "mode=rw")
|
|
assert.NotContains(t, connStr, "mode=rwc")
|
|
|
|
AllowSQLite3CreateForInstall = true
|
|
connStr, err = DBConnStr()
|
|
require.NoError(t, err)
|
|
assert.Contains(t, connStr, "mode=rwc")
|
|
assert.NotContains(t, connStr, "mode=rw&")
|
|
}
|
|
|
|
// end edit/add - by petru @ codex
|