chore: Adds "day-one"/v2.0.1 bug fixes.
All checks were successful
Release code / build (push) Successful in 11m41s

This fixes database error reporting, as well as majorly fixes users
not being able to authenticate to the API if you used the automated
migration setup, as the password would remain in hex encoding.

We now decode the hexadecimal and then change it to the far more
compact base64 encoding before adding it to the database. This should
fix login, and not cause 500 Interal Server Errors anymore.

Sorry folks!
This commit is contained in:
Tera << 8 2024-12-27 00:10:13 -05:00
parent d334878599
commit 538c5b6c51
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
13 changed files with 34 additions and 14 deletions

View file

@ -3,6 +3,7 @@ package main
import ( import (
"compress/gzip" "compress/gzip"
"encoding/base64" "encoding/base64"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -222,11 +223,18 @@ func backupRestoreEntrypoint(cCtx *cli.Context) error {
username = *user.Username username = *user.Username
} }
bcryptPassword, err := hex.DecodeString(user.Password)
if err != nil {
log.Errorf("Failed to decode hex encoded password: %s", err.Error())
continue
}
userDatabase := &dbcore.User{ userDatabase := &dbcore.User{
Email: user.Email, Email: user.Email,
Username: username, Username: username,
Name: user.Name, Name: user.Name,
Password: user.Password, Password: base64.StdEncoding.EncodeToString(bcryptPassword),
IsBot: user.IsBot, IsBot: user.IsBot,
Tokens: tokens, Tokens: tokens,
@ -235,6 +243,7 @@ func backupRestoreEntrypoint(cCtx *cli.Context) error {
if err := dbcore.DB.Create(userDatabase).Error; err != nil { if err := dbcore.DB.Create(userDatabase).Error; err != nil {
log.Errorf("Failed to create user: %s", err.Error()) log.Errorf("Failed to create user: %s", err.Error())
continue
} }
if uint(bestEffortOwnerUIDFromBackup) == user.ID { if uint(bestEffortOwnerUIDFromBackup) == user.ID {
@ -255,6 +264,7 @@ func backupRestoreEntrypoint(cCtx *cli.Context) error {
if err := dbcore.DB.Create(backendDatabase).Error; err != nil { if err := dbcore.DB.Create(backendDatabase).Error; err != nil {
log.Errorf("Failed to create backend: %s", err.Error()) log.Errorf("Failed to create backend: %s", err.Error())
continue
} }
log.Infof("Migrating proxies for backend ID '%d'", backend.ID) log.Infof("Migrating proxies for backend ID '%d'", backend.ID)

View file

@ -69,7 +69,7 @@ func RemoveBackend(c *gin.Context) {
backendRequest := dbcore.DB.Where("id = ?", req.BackendID).Find(&backend) backendRequest := dbcore.DB.Where("id = ?", req.BackendID).Find(&backend)
if backendRequest.Error != nil { if backendRequest.Error != nil {
log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error) log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if backend exists", "error": "Failed to find if backend exists",

View file

@ -87,7 +87,7 @@ func GetConnections(c *gin.Context) {
proxyRequest := dbcore.DB.Where("id = ?", req.Id).First(&proxy) proxyRequest := dbcore.DB.Where("id = ?", req.Id).First(&proxy)
if proxyRequest.Error != nil { if proxyRequest.Error != nil {
log.Warnf("failed to find proxy: %s", proxyRequest.Error) log.Warnf("failed to find proxy: %s", proxyRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find forward entry", "error": "Failed to find forward entry",

View file

@ -84,7 +84,7 @@ func CreateProxy(c *gin.Context) {
backendRequest := dbcore.DB.Where("id = ?", req.ProviderID).First(&backend) backendRequest := dbcore.DB.Where("id = ?", req.ProviderID).First(&backend)
if backendRequest.Error != nil { if backendRequest.Error != nil {
log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error) log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if backend exists", "error": "Failed to find if backend exists",

View file

@ -69,7 +69,7 @@ func RemoveProxy(c *gin.Context) {
proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy) proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy)
if proxyRequest.Error != nil { if proxyRequest.Error != nil {
log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error) log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if forward rule exists", "error": "Failed to find if forward rule exists",

View file

@ -69,7 +69,7 @@ func StartProxy(c *gin.Context) {
proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy) proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy)
if proxyRequest.Error != nil { if proxyRequest.Error != nil {
log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error) log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if forward rule exists", "error": "Failed to find if forward rule exists",

View file

@ -69,7 +69,7 @@ func StopProxy(c *gin.Context) {
proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy) proxyRequest := dbcore.DB.Where("id = ?", req.ID).Find(&proxy)
if proxyRequest.Error != nil { if proxyRequest.Error != nil {
log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error) log.Warnf("failed to find if proxy exists or not: %s", proxyRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if forward rule exists", "error": "Failed to find if forward rule exists",

View file

@ -59,7 +59,7 @@ func CreateUser(c *gin.Context) {
userRequest := dbcore.DB.Where("email = ? OR username = ?", req.Email, req.Username).Find(&user) userRequest := dbcore.DB.Where("email = ? OR username = ?", req.Email, req.Username).Find(&user)
if userRequest.Error != nil { if userRequest.Error != nil {
log.Warnf("failed to find if user exists or not: %s", userRequest.Error) log.Warnf("failed to find if user exists or not: %s", userRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if user exists", "error": "Failed to find if user exists",

View file

@ -65,7 +65,7 @@ func LoginUser(c *gin.Context) {
userRequest := dbcore.DB.Where(userFindRequest, userFindRequestArguments...).Find(&user) userRequest := dbcore.DB.Where(userFindRequest, userFindRequestArguments...).Find(&user)
if userRequest.Error != nil { if userRequest.Error != nil {
log.Warnf("failed to find if user exists or not: %s", userRequest.Error) log.Warnf("failed to find if user exists or not: %s", userRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if user exists", "error": "Failed to find if user exists",
@ -88,7 +88,7 @@ func LoginUser(c *gin.Context) {
_, err := base64.StdEncoding.Decode(decodedPassword, []byte(user.Password)) _, err := base64.StdEncoding.Decode(decodedPassword, []byte(user.Password))
if err != nil { if err != nil {
log.Warnf("failed to decode password in database: %s", userRequest.Error) log.Warnf("failed to decode password in database: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to parse database result for password", "error": "Failed to parse database result for password",

View file

@ -39,7 +39,7 @@ func RefreshUserToken(c *gin.Context) {
tokenRequest := dbcore.DB.Where("token = ?", req.Token).Find(&tokenInDatabase) tokenRequest := dbcore.DB.Where("token = ?", req.Token).Find(&tokenInDatabase)
if tokenRequest.Error != nil { if tokenRequest.Error != nil {
log.Warnf("failed to find if token exists or not: %s", tokenRequest.Error) log.Warnf("failed to find if token exists or not: %s", tokenRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if token exists", "error": "Failed to find if token exists",
@ -79,7 +79,7 @@ func RefreshUserToken(c *gin.Context) {
userRequest := dbcore.DB.Where("id = ?", tokenInDatabase.UserID).Find(&user) userRequest := dbcore.DB.Where("id = ?", tokenInDatabase.UserID).Find(&user)
if tokenRequest.Error != nil { if tokenRequest.Error != nil {
log.Warnf("failed to find if token user or not: %s", userRequest.Error) log.Warnf("failed to find if token user or not: %s", userRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find user", "error": "Failed to find user",

View file

@ -77,7 +77,7 @@ func RemoveUser(c *gin.Context) {
userRequest := dbcore.DB.Where("id = ?", uid).Find(customUser) userRequest := dbcore.DB.Where("id = ?", uid).Find(customUser)
if userRequest.Error != nil { if userRequest.Error != nil {
log.Warnf("failed to find if user exists or not: %s", userRequest.Error) log.Warnf("failed to find if user exists or not: %s", userRequest.Error.Error())
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to find if user exists", "error": "Failed to find if user exists",

View file

@ -77,7 +77,7 @@ func GetUserFromJWT(token string) (*dbcore.User, error) {
userRequest := dbcore.DB.Preload("Permissions").Where("id = ?", uint(uid)).Find(&user) userRequest := dbcore.DB.Preload("Permissions").Where("id = ?", uint(uid)).Find(&user)
if userRequest.Error != nil { if userRequest.Error != nil {
return user, fmt.Errorf("failed to find if user exists or not: %s", userRequest.Error) return user, fmt.Errorf("failed to find if user exists or not: %s", userRequest.Error.Error())
} }
userExists := userRequest.RowsAffected > 0 userExists := userRequest.RowsAffected > 0

View file

@ -31,3 +31,13 @@ Below are new environment variables that may need to be set up:
9. If successful, remove the environment variable `HERMES_MIGRATE_POSTGRES_DATABASE`. 9. If successful, remove the environment variable `HERMES_MIGRATE_POSTGRES_DATABASE`.
10. Switch the API docker image from `ghcr.io/imterah/hermes-backend-migration:latest` to `ghcr.io/imterah/hermes:latest`. 10. Switch the API docker image from `ghcr.io/imterah/hermes-backend-migration:latest` to `ghcr.io/imterah/hermes:latest`.
11. Start the backend. 11. Start the backend.
## Failed Migration / Manual Restoration Steps
1. Get to step 4 in the ordinary migration setps.
2. Add the `entrypoint` option in the API compose section, and set it to `/bin/bash`
3. Add the `command` option in the API compose section, and set it to `"-c 'sleep 10000'"`
4. Get a shell in the container (likely named `nextnet-api`): `docker exec -it nextnet-api /bin/bash`
5. Copy the base64 section (excluding the `BEGIN` and `END` portions) of the backup, and run the following command to begin the transfer: `cat >> /tmp/db.json.gz.b64 << EOF`
6. Paste in the base64 data, and then press enter, type `EOF`, and then press enter again. This should return you to the shell prompt.
7. Decode the base64 backup: `cat /tmp/db.json.gz.b64 | base64 -d > /tmp/db.json.gz`
8. Run the migration script: `./entrypoint.sh`
9. When done, remove the `entrypoint` and `command` sections, and then jump to step 9 in the ordinary migration steps.