Merge branch 'main' into f-droid

This commit is contained in:
vishnukvmd 2024-03-13 21:57:19 +05:30
commit 1e1633bb45
22 changed files with 145 additions and 35 deletions

View file

@ -3,7 +3,7 @@ name: "Lint (mobile)"
on: on:
# Run on every push to a branch other than main that changes mobile/ # Run on every push to a branch other than main that changes mobile/
push: push:
branches-ignore: [main] branches-ignore: [main, f-droid]
paths: paths:
- "mobile/**" - "mobile/**"
- ".github/workflows/mobile-lint.yml" - ".github/workflows/mobile-lint.yml"

View file

@ -0,0 +1 @@
{}

View file

@ -144,6 +144,7 @@
"enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.", "enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.",
"lostDeviceTitle": "Gerät verloren?", "lostDeviceTitle": "Gerät verloren?",
"twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung", "twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung",
"passkeyAuthTitle": "Passkey Authentifizierung",
"recoverAccount": "Konto wiederherstellen", "recoverAccount": "Konto wiederherstellen",
"enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein", "enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein",
"recover": "Wiederherstellen", "recover": "Wiederherstellen",
@ -404,5 +405,15 @@
"signOutOtherDevices": "Andere Geräte abmelden", "signOutOtherDevices": "Andere Geräte abmelden",
"doNotSignOut": "Nicht abmelden", "doNotSignOut": "Nicht abmelden",
"hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)", "hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)",
"hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!" "hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!",
"waitingForBrowserRequest": "Warten auf Browseranfrage...",
"launchPasskeyUrlAgain": "Passwort-URL erneut starten",
"passkey": "Passkey",
"developerSettingsWarning": "Sind Sie sicher, dass Sie die Entwicklereinstellungen ändern möchten?",
"developerSettings": "Entwicklereinstellungen",
"serverEndpoint": "Server Endpunkt",
"invalidEndpoint": "Ungültiger Endpunkt",
"invalidEndpointMessage": "Der eingegebene Endpunkt ist ungültig. Bitte geben Sie einen gültigen Endpunkt ein und versuchen Sie es erneut.",
"endpointUpdatedMessage": "Endpunkt erfolgreich aktualisiert",
"customEndpoint": "Mit {endpoint} verbunden"
} }

View file

@ -144,6 +144,7 @@
"enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください", "enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください",
"lostDeviceTitle": "デバイスを紛失しましたか?", "lostDeviceTitle": "デバイスを紛失しましたか?",
"twoFactorAuthTitle": "2 要素認証", "twoFactorAuthTitle": "2 要素認証",
"passkeyAuthTitle": "パスキー認証",
"recoverAccount": "アカウントを回復", "recoverAccount": "アカウントを回復",
"enterRecoveryKeyHint": "回復キーを入力", "enterRecoveryKeyHint": "回復キーを入力",
"recover": "回復", "recover": "回復",
@ -404,5 +405,15 @@
"signOutOtherDevices": "他のデバイスからサインアウトする", "signOutOtherDevices": "他のデバイスからサインアウトする",
"doNotSignOut": "サインアウトしない", "doNotSignOut": "サインアウトしない",
"hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)", "hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)",
"hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!" "hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!",
"waitingForBrowserRequest": "ブラウザのリクエストを待っています...",
"launchPasskeyUrlAgain": "パスキーのURLを再度起動する",
"passkey": "パスキー",
"developerSettingsWarning": "開発者向け設定を変更してもよろしいですか?",
"developerSettings": "開発者向け設定",
"serverEndpoint": "サーバーエンドポイント",
"invalidEndpoint": "無効なエンドポイントです",
"invalidEndpointMessage": "入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。",
"endpointUpdatedMessage": "エンドポイントの更新に成功しました",
"customEndpoint": "{endpoint} に接続しました"
} }

View file

@ -144,6 +144,7 @@
"enterCodeHint": "Digite o código de 6 dígitos de\nseu aplicativo autenticador", "enterCodeHint": "Digite o código de 6 dígitos de\nseu aplicativo autenticador",
"lostDeviceTitle": "Perdeu seu dispositivo?", "lostDeviceTitle": "Perdeu seu dispositivo?",
"twoFactorAuthTitle": "Autenticação de dois fatores", "twoFactorAuthTitle": "Autenticação de dois fatores",
"passkeyAuthTitle": "Autenticação via Chave de acesso",
"recoverAccount": "Recuperar conta", "recoverAccount": "Recuperar conta",
"enterRecoveryKeyHint": "Digite sua chave de recuperação", "enterRecoveryKeyHint": "Digite sua chave de recuperação",
"recover": "Recuperar", "recover": "Recuperar",
@ -404,5 +405,15 @@
"signOutOtherDevices": "Terminar sessão em outros dispositivos", "signOutOtherDevices": "Terminar sessão em outros dispositivos",
"doNotSignOut": "Não encerrar sessão", "doNotSignOut": "Não encerrar sessão",
"hearUsWhereTitle": "Como você ouviu sobre o Ente? (opcional)", "hearUsWhereTitle": "Como você ouviu sobre o Ente? (opcional)",
"hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!" "hearUsExplanation": "Não rastreamos instalações do aplicativo. Seria útil se você nos contasse onde nos encontrou!",
"waitingForBrowserRequest": "Aguardando solicitação do navegador...",
"launchPasskeyUrlAgain": "Iniciar a URL de chave de acesso novamente",
"passkey": "Chave de acesso",
"developerSettingsWarning": "Tem certeza de que deseja modificar as configurações de Desenvolvedor?",
"developerSettings": "Configurações de desenvolvedor",
"serverEndpoint": "Endpoint do servidor",
"invalidEndpoint": "Endpoint inválido",
"invalidEndpointMessage": "Desculpe, o endpoint que você inseriu é inválido. Por favor, insira um endpoint válido e tente novamente.",
"endpointUpdatedMessage": "Endpoint atualizado com sucesso",
"customEndpoint": "Conectado a {endpoint}"
} }

View file

@ -131,6 +131,16 @@
"about": "Om", "about": "Om",
"terms": "Villkor", "terms": "Villkor",
"warning": "Varning", "warning": "Varning",
"importSuccessDesc": "Du har importerat {count} koder!",
"@importSuccessDesc": {
"placeholders": {
"count": {
"description": "The number of codes imported",
"type": "int",
"example": "1"
}
}
},
"pendingSyncs": "Varning", "pendingSyncs": "Varning",
"activeSessions": "Aktiva sessioner", "activeSessions": "Aktiva sessioner",
"enterPassword": "Ange lösenord", "enterPassword": "Ange lösenord",
@ -143,5 +153,7 @@
"iOSOkButton": "OK", "iOSOkButton": "OK",
"@iOSOkButton": { "@iOSOkButton": {
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
} },
"noInternetConnection": "Ingen internetanslutning",
"pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen."
} }

View file

@ -408,5 +408,12 @@
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!", "hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
"waitingForBrowserRequest": "正在等待浏览器请求...", "waitingForBrowserRequest": "正在等待浏览器请求...",
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL", "launchPasskeyUrlAgain": "再次启动 通行密钥 URL",
"passkey": "通行密钥" "passkey": "通行密钥",
"developerSettingsWarning": "您确定要修改开发者设置吗?",
"developerSettings": "开发者设置",
"serverEndpoint": "服务器端点",
"invalidEndpoint": "端点无效",
"invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。",
"endpointUpdatedMessage": "端点更新成功",
"customEndpoint": "已连接至 {endpoint}"
} }

View file

@ -1,6 +1,6 @@
ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos.
Se você está procurando uma alternativa ao Google Photos com foco em privacidade, veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los. Se você está procurando uma alternativa ao Google Fotos com foco em privacidade, você veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los.
Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee).

View file

@ -1,12 +1,12 @@
ente 是一个简单的应用程序来备份和分享您的照片和视频。 ente 是一个简单的应用程序来备份和分享您的照片和视频。
如果你一直在寻找一个隐私友好的Google Photos替代品那么你就来对地方了。 使用 Ente它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 如果你一直在寻找一个隐私友好的Google Photos替代品那么你就来对地方了。 使用 Ente它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。 使用 Ente它们以端到端加密 (e2ee) 的方式存储。 这意味着只有您可以查看它们。
我们在Android、iOS、web 和桌面上有开源应用, 和您的照片将以端到端加密方式 (e2ee) 无缝同步。 我们在Android、iOS、web 和桌面上有开源应用, 和您的照片将以端到端加密方式 (e2ee) 无缝同步。
ente也使分享相册给自己的爱人、亲人变得轻而易举即使他们可能并不使用ente。 您可以分享可公开查看的链接使他们可以查看您的相册并通过添加照片来协作而不需要注册账户或下载app。 ente也使分享相册给自己的爱人、亲人变得轻而易举即使他们可能并不使用ente。 您可以分享可公开查看的链接使他们可以查看您的相册并通过添加照片来协作而不需要注册账户或下载app。 权限
您的加密数据已复制到三个不同的地点,包括巴黎的一个安全屋。 我们认真对待子孙后代,并确保您的回忆比您长寿。 您的加密数据已复制到三个不同的地点,包括巴黎的一个安全屋。 我们认真对待子孙后代,并确保您的回忆比您长寿。 我们认真对待子孙后代,并确保您的回忆比您长寿。
我们来这里是为了打造有史以来最安全的照片应用,来和我们一起前行! 我们来这里是为了打造有史以来最安全的照片应用,来和我们一起前行!
@ -30,7 +30,7 @@ ente也使分享相册给自己的爱人、亲人变得轻而易举即使他
ente需要特定权限以执行作为图像存储提供商的职责相关内容可以在此链接查阅https://github.com/ente-io/photos-app/blob/f-droid/android/permissions.md ente需要特定权限以执行作为图像存储提供商的职责相关内容可以在此链接查阅https://github.com/ente-io/photos-app/blob/f-droid/android/permissions.md
价格 价格
我们不会提供永久免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 我们不会提供永久免费计划,因为我们必须保持可持续性,经受住时间的考验。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。 相反,我们向您提供了价格实惠、可自由分享的订阅计划。 您可以在 ente.io 找到更多信息。
支持 支持
我们对提供真人支持感到自豪。 如果您是我们的付费客户,您可以联系 team@ente.io 并在24小时内收到来自我们团队的回复。 我们对提供真人支持感到自豪。 我们对提供真人支持感到自豪。 如果您是我们的付费客户,您可以联系 team@ente.io 并在24小时内收到来自我们团队的回复。

View file

@ -1,6 +1,6 @@
Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos.
Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com ente, eles são armazenados com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-los. Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com Ente, elas são armazenadas com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-las.
Temos aplicativos de código aberto em Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). Temos aplicativos de código aberto em Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee).

View file

@ -1,6 +1,6 @@
Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos. Ente é um aplicativo simples para fazer backup e compartilhar suas fotos e vídeos.
Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com ente, eles são armazenados com criptografados de ponta a ponta (e2ee). Isso significa que só você pode vê-los. Se você esteve procurando uma alternativa amigável à privacidade para preservar suas memórias, você veio ao lugar certo. Com Ente, elas são armazenadas com criptografia de ponta a ponta (e2ee). Isso significa que só você pode vê-las.
Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee). Temos aplicativos de código aberto em todas as plataformas, Android, iOS, web e desktop, e suas fotos irão sincronizar perfeitamente entre todas elas de forma criptografada (e2ee).

View file

@ -406,6 +406,15 @@
}, },
"photoGridSize": "Fotorastergröße", "photoGridSize": "Fotorastergröße",
"manageDeviceStorage": "Gerätespeicher verwalten", "manageDeviceStorage": "Gerätespeicher verwalten",
"machineLearning": "Maschinelles Lernen",
"magicSearch": "Magische Suche",
"magicSearchDescription": "Bitte beachten Sie, dass dies mehr Bandbreite nutzt und zu einem höheren Akkuverbrauch führt, bis alle Elemente indiziert sind.",
"loadingModel": "Lade Modelle herunter...",
"waitingForWifi": "Warte auf WLAN...",
"status": "Status",
"indexedItems": "Indizierte Elemente",
"pendingItems": "Ausstehende Elemente",
"clearIndexes": "Indexe löschen",
"selectFoldersForBackup": "Ordner für Sicherung auswählen", "selectFoldersForBackup": "Ordner für Sicherung auswählen",
"selectedFoldersWillBeEncryptedAndBackedUp": "Ausgewählte Ordner werden verschlüsselt und gesichert", "selectedFoldersWillBeEncryptedAndBackedUp": "Ausgewählte Ordner werden verschlüsselt und gesichert",
"unselectAll": "Alle demarkieren", "unselectAll": "Alle demarkieren",
@ -1178,7 +1187,17 @@
"changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?", "changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?",
"editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein", "editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein",
"cleanUncategorized": "Unkategorisiert leeren", "cleanUncategorized": "Unkategorisiert leeren",
"joinDiscord": "Join Discord", "cleanUncategorizedDescription": "Entferne alle Dateien von \"Unkategorisiert\" die in anderen Alben vorhanden sind",
"locations": "Locations", "waitingForVerification": "Warte auf Bestätigung...",
"descriptions": "Descriptions" "passkey": "Passkey",
"passkeyAuthTitle": "Passkey-Verifizierung",
"verifyPasskey": "Passkey verifizieren",
"playOnTv": "Album auf dem Fernseher wiedergeben",
"pair": "Koppeln",
"deviceNotFound": "Gerät nicht gefunden",
"castInstruction": "Besuche cast.ente.io auf dem Gerät, das du verbinden möchtest.\n\nGib den unten angegebenen Code ein, um das Album auf deinem Fernseher abzuspielen.",
"deviceCodeHint": "Code eingeben",
"joinDiscord": "Discord beitreten",
"locations": "Orte",
"descriptions": "Beschreibungen"
} }

View file

@ -1187,16 +1187,17 @@
"changeLocationOfSelectedItems": "Alterar o local dos itens selecionados?", "changeLocationOfSelectedItems": "Alterar o local dos itens selecionados?",
"editsToLocationWillOnlyBeSeenWithinEnte": "Edições para local só serão vistas dentro do Ente", "editsToLocationWillOnlyBeSeenWithinEnte": "Edições para local só serão vistas dentro do Ente",
"cleanUncategorized": "Limpar Sem Categoria", "cleanUncategorized": "Limpar Sem Categoria",
"waitingForBrowserRequest": "Aguardando solicitação do navegador...", "cleanUncategorizedDescription": "Remover todos os arquivos de Não Categorizados que estão presentes em outros álbuns",
"launchPasskeyUrlAgain": "Iniciar a URL de chave de acesso novamente", "waitingForVerification": "Esperando por verificação...",
"passkey": "Chave de acesso", "passkey": "Chave de acesso",
"passkeyAuthTitle": "Autenticação via Chave de acesso", "passkeyAuthTitle": "Autenticação via Chave de acesso",
"verifyPasskey": "Verificar chave de acesso",
"playOnTv": "Reproduzir álbum na TV", "playOnTv": "Reproduzir álbum na TV",
"pair": "Parear", "pair": "Parear",
"deviceNotFound": "Dispositivo não encontrado", "deviceNotFound": "Dispositivo não encontrado",
"castInstruction": "Visite cast.ente.io no dispositivo que você deseja parear.\n\ndigite o código abaixo para reproduzir o álbum em sua TV.", "castInstruction": "Visite cast.ente.io no dispositivo que você deseja parear.\n\ndigite o código abaixo para reproduzir o álbum em sua TV.",
"deviceCodeHint": "Insira o código", "deviceCodeHint": "Insira o código",
"joinDiscord": "Junte-se ao Discord", "joinDiscord": "Junte-se ao Discord",
"locations": "Locations", "locations": "Locais",
"descriptions": "Descriptions" "descriptions": "Descrições"
} }

View file

@ -1187,16 +1187,17 @@
"changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?", "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?",
"editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到", "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到",
"cleanUncategorized": "清除未分类的", "cleanUncategorized": "清除未分类的",
"waitingForBrowserRequest": "正在等待浏览器请求...", "cleanUncategorizedDescription": "从“未分类”中删除其他相册中存在的所有文件",
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL", "waitingForVerification": "等待验证...",
"passkey": "通行密钥", "passkey": "通行密钥",
"passkeyAuthTitle": "通行密钥认证", "passkeyAuthTitle": "通行密钥认证",
"verifyPasskey": "验证通行密钥",
"playOnTv": "在电视上播放相册", "playOnTv": "在电视上播放相册",
"pair": "配对", "pair": "配对",
"deviceNotFound": "未发现设备", "deviceNotFound": "未发现设备",
"castInstruction": "在您要配对的设备上访问 cast.ente.io。\n输入下面的代码即可在电视上播放相册。", "castInstruction": "在您要配对的设备上访问 cast.ente.io。\n输入下面的代码即可在电视上播放相册。",
"deviceCodeHint": "输入代码", "deviceCodeHint": "输入代码",
"joinDiscord": "加入 Discord", "joinDiscord": "加入 Discord",
"locations": "Locations", "locations": "位置",
"descriptions": "Descriptions" "descriptions": "描述"
} }

View file

@ -68,6 +68,7 @@ class GalleryFileWidget extends StatelessWidget {
: _onLongPressNoSelectionLimit(context, file); : _onLongPressNoSelectionLimit(context, file);
}, },
child: Stack( child: Stack(
clipBehavior: Clip.none,
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(1), borderRadius: BorderRadius.circular(1),

View file

@ -12,7 +12,7 @@ description: ente photos application
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.8.70+590 version: 0.8.71+591
publish_to: none publish_to: none
environment: environment:

View file

@ -620,6 +620,7 @@ func main() {
adminAPI.POST("/user/disable-2fa", adminHandler.DisableTwoFactor) adminAPI.POST("/user/disable-2fa", adminHandler.DisableTwoFactor)
adminAPI.POST("/user/disable-passkeys", adminHandler.RemovePasskeys) adminAPI.POST("/user/disable-passkeys", adminHandler.RemovePasskeys)
adminAPI.POST("/user/close-family", adminHandler.CloseFamily) adminAPI.POST("/user/close-family", adminHandler.CloseFamily)
adminAPI.PUT("/user/change-email", adminHandler.ChangeEmail)
adminAPI.DELETE("/user/delete", adminHandler.DeleteUser) adminAPI.DELETE("/user/delete", adminHandler.DeleteUser)
adminAPI.POST("/user/recover", adminHandler.RecoverAccount) adminAPI.POST("/user/recover", adminHandler.RecoverAccount)
adminAPI.GET("/email-hash", adminHandler.GetEmailHash) adminAPI.GET("/email-hash", adminHandler.GetEmailHash)

View file

@ -46,6 +46,11 @@ type UpdateSubscriptionRequest struct {
Attributes SubscriptionAttributes `json:"attributes"` Attributes SubscriptionAttributes `json:"attributes"`
} }
type ChangeEmailRequest struct {
UserID int64 `json:"userID" binding:"required"`
Email string `json:"email" binding:"required"`
}
type AddOnAction string type AddOnAction string
const ( const (

View file

@ -306,6 +306,25 @@ func (h *AdminHandler) UpdateSubscription(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{}) c.JSON(http.StatusOK, gin.H{})
} }
func (h *AdminHandler) ChangeEmail(c *gin.Context) {
var r ente.ChangeEmailRequest
if err := c.ShouldBindJSON(&r); err != nil {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "Bad request"))
return
}
adminID := auth.GetUserID(c.Request.Header)
go h.DiscordController.NotifyAdminAction(
fmt.Sprintf("Admin (%d) updating email for user: %d", adminID, r.UserID))
err := h.UserController.UpdateEmail(c, r.UserID, r.Email)
if err != nil {
logrus.WithError(err).Error("Failed to update email")
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
logrus.Info("Updated email")
c.JSON(http.StatusOK, gin.H{})
}
func (h *AdminHandler) ReQueueItem(c *gin.Context) { func (h *AdminHandler) ReQueueItem(c *gin.Context) {
var r ente.ReQueueItemRequest var r ente.ReQueueItemRequest
if err := c.ShouldBindJSON(&r); err != nil { if err := c.ShouldBindJSON(&r); err != nil {

View file

@ -197,7 +197,13 @@ func (c *UserController) ChangeEmail(ctx *gin.Context, request ente.EmailVerific
if err != nil { if err != nil {
return stacktrace.Propagate(err, "") return stacktrace.Propagate(err, "")
} }
_, err = c.UserRepo.GetUserIDWithEmail(email)
return c.UpdateEmail(ctx, auth.GetUserID(ctx.Request.Header), email)
}
// UpdateEmail updates the email address of the user with the provided userID
func (c *UserController) UpdateEmail(ctx *gin.Context, userID int64, email string) error {
_, err := c.UserRepo.GetUserIDWithEmail(email)
if err == nil { if err == nil {
// email already owned by a user // email already owned by a user
return stacktrace.Propagate(ente.ErrPermissionDenied, "") return stacktrace.Propagate(ente.ErrPermissionDenied, "")
@ -206,7 +212,6 @@ func (c *UserController) ChangeEmail(ctx *gin.Context, request ente.EmailVerific
// unknown error, rethrow // unknown error, rethrow
return stacktrace.Propagate(err, "") return stacktrace.Propagate(err, "")
} }
userID := auth.GetUserID(ctx.Request.Header)
user, err := c.UserRepo.Get(userID) user, err := c.UserRepo.Get(userID)
if err != nil { if err != nil {
return stacktrace.Propagate(err, "") return stacktrace.Propagate(err, "")

View file

@ -68,7 +68,12 @@
# #
# NEXT_PUBLIC_ENTE_FAMILY_PORTAL_ENDPOINT = http://localhost:3003 # NEXT_PUBLIC_ENTE_FAMILY_PORTAL_ENDPOINT = http://localhost:3003
# The path of the JSON file which contains the expected results of our # The JSON which describes the expected results of our integration tests. See
# integration tests. See `upload.test.ts` for more details. # `upload.test.ts` for more details of the expected format.
# #
# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH = /path/to/dataset/expected.json # This is perhaps easier to specify as an environment variable, since then we
# can directly read from the source file when running `yarn dev`. For example,
#
# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON=`cat path/to/expected.json` yarn dev
#
# NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON = {}

View file

@ -100,13 +100,13 @@ const FILE_NAME_TO_JSON_NAME = [
]; ];
export async function testUpload() { export async function testUpload() {
const jsonPath = process.env.NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH; const jsonString = process.env.NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON;
if (!jsonPath) { if (!jsonString) {
throw Error( throw Error(
"Please specify the NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON_PATH to run the upload tests", "Please specify the NEXT_PUBLIC_ENTE_TEST_EXPECTED_JSON to run the upload tests",
); );
} }
const expectedState = await import(jsonPath); const expectedState = JSON.parse(jsonString);
if (!expectedState) { if (!expectedState) {
throw Error("upload test failed expectedState missing"); throw Error("upload test failed expectedState missing");
} }