From e829bf568bd7b0e3cdc68f0d3daa1cbafa11523c Mon Sep 17 00:00:00 2001 From: Ethanc <69158160+0EAC@users.noreply.github.com> Date: Thu, 2 Nov 2023 11:08:30 +0800 Subject: [PATCH 01/12] Update zh.json sync with en.json --- client/locales/zh.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/client/locales/zh.json b/client/locales/zh.json index 066e2c1..5a6e91c 100644 --- a/client/locales/zh.json +++ b/client/locales/zh.json @@ -34,6 +34,9 @@ "accIfYouAreAlsUploaderAcceptTorrent": "分,需要您同时也是被采纳种子的发种人", "accInviteLinkCopiedClipboard": "邀请链接已复制到剪贴板", "accInvites": "邀请", + "accRemaining": "剩余", + "accRoleUser": "角色: 用户", + "accRoleAdmin": "角色: 管理员", "accInviteSentSuccess": "邀请已成功发送", "accInviteText1": "请输入受邀用户的邮箱地址。受邀用户需使用该邮箱地址注册。您也可以在此直接复制邀请链接。", "accItemsPurchasedSuccess": "购买成功", @@ -70,6 +73,7 @@ "annCouldNotUpdateAnnounce": "无法更新公告", "annEditAnnounce": "编辑公告", "annUpdateAnnounce": "更新公告", + "annAnnounce": "公告", "bmYourBM": "您的书签", "bmYouNotHaveAnyBM": "您没有任何书签。", "catCategories": "分类", @@ -222,6 +226,12 @@ "torrNo": "否", "torrGroupTorr": "已分组的种子", "torrThereAreNoOtherTorrGroup": "此组中没有其他种子。", + "torrResults": "结果", + "torrPage": "页面", + "torrOf": "的", + "torrRemovedFrom": "移除于", + "torrAddedTo": "增加至", + "torrTorrNoTags": "这个种子还没有标签", "totp": "一次性代码", "uploadAddTag": "添加标签", "uploadAnnounceURL": "Tracker地址", @@ -247,6 +257,7 @@ "uploadTorrentFile": "Torrent 文件", "uploadTorrentUploadSuccess": "Torrent 上传成功", "uploadUpload": "上传", + "uploadRemove": "移除", "username": "用户名", "userUser": "用户", "userSuccessfully": "成功", @@ -259,6 +270,7 @@ "userEmailVerified": "邮箱已验证", "userRemainingInvites": "剩余邀请", "userRatio": "分享率", + "userHitNRuns": "H&R", "userDownloaded": "已下载", "userComments": "评论", "userNoComments": "无评论。", @@ -271,6 +283,7 @@ "userBan": "封禁", "userUnbanned": "已解封", "userBanned": "已封禁", + "userAdmin": "设为管理", "veCouldNotVerifyEmailAddress": "无法验证邮件地址:", "veEmailAddressVerifiedSuccess": "邮箱地址验证成功。", "veNoVerificationTokenProvided": "未提供验证令牌", From 7dd66f10de807cf297c0142439dc8b42ad12d7d0 Mon Sep 17 00:00:00 2001 From: "Cereal_Killer.js" Date: Sat, 6 Jan 2024 18:28:54 +0100 Subject: [PATCH 02/12] Added Spanish Language --- client/locales/es.json | 310 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 client/locales/es.json diff --git a/client/locales/es.json b/client/locales/es.json new file mode 100644 index 0000000..2912eb1 --- /dev/null +++ b/client/locales/es.json @@ -0,0 +1,310 @@ +{ + "404NotFound": "No encontrado", + "404PageDoesNotExist": "Esa página no existe.", + "404ReturnHome": "Volver a inicio", + "acc2FADisabled": "2FA desactivado", + "acc2FAEnabled": "2FA activado", + "acc2FAScanQR": "Escanea el código QR con tu aplicación de autenticación y introduce el código de un solo uso", + "acc2FAText1": "2FA habilitado con éxito. Estos códigos de respaldo pueden ser utilizados para iniciar sesión si pierdes el acceso a tu aplicación de autenticación. Guárdalos ahora, no serán visibles nuevamente.", + "acc2FAUseApp": "Usa una aplicación de autenticación para agregar otra capa de seguridad a tu cuenta", + "acc2FAuth": "Autenticación de dos factores", + "accBonusPoints": "Puntos de bonificación", + "accBonusPointsHave": "punto(s) de bonificación", + "accBuy": "Comprar", + "accCancel": "Cancelar", + "accChangePass": "Cambiar contraseña", + "accClaimed": "Reclamado", + "accCopyLink": "Copiar enlace", + "accCouldNotBuyItems": "No se pudieron comprar los artículos", + "accCouldNotChangePass": "No se pudo cambiar la contraseña", + "accCouldNotDelAcc": "No se pudo eliminar la cuenta", + "accCouldNotSendInvite": "No se pudo enviar la invitación", + "accCouldNotToggle2FA": "No se pudo activar/desactivar la autenticación de dos factores (2FA)", + "accCreated": "Creado", + "accCurrentPass": "Contraseña actual", + "accDangerZone": "Zona peligrosa", + "accDelAccText1": "¿Estás seguro de que deseas eliminar tu cuenta? Esta acción no se puede deshacer y es posible que no puedas registrarte nuevamente. Tu información personal se eliminará, pero tus torrents cargados permanecerán.", + "accDeleteMyAcc": "Eliminar mi cuenta", + "accDeleteMyAccYes": "Sí, eliminar mi cuenta", + "accDisable": "Desactivar", + "accEnable": "Activar", + "accEnable2FA": "Activar 2FA", + "accEveryRequestYouFulfill": "por cada solicitud que cumplas, o", + "accForEveryGBYouUpload": "por cada GB que cargues", + "accIfYouAreAlsUploaderAcceptTorrent": "si también eres el cargador del torrent aceptado", + "accInviteLinkCopiedClipboard": "Enlace de invitación copiado al portapapeles", + "accInvites": "Invitaciones", + "accRemaining": "restantes", + "accRoleUser": "Rol: usuario", + "accRoleAdmin": "Rol: administrador", + "accInviteSentSuccess": "Invitación enviada exitosamente", + "accInviteText1": "Ingresa una dirección de correo electrónico para enviar una invitación. El usuario invitado deberá registrarse con la misma dirección de correo electrónico. Una vez que se genere la invitación, también puedes copiar un enlace directo de invitación.", + "accItemsPurchasedSuccess": "Artículos comprados exitosamente", + "accMyAccount": "Mi cuenta", + "accPassChangedSuccess": "Contraseña cambiada exitosamente", + "accPurchaseInvites": "Comprar invitaciones", + "accPurchaseUpload1GB": "Comprar carga (1 GB)", + "accRole": "Rol", + "accSendInvite": "Enviar invitación", + "accThisIsAdminAcc": "Esta es una cuenta de administrador.", + "accValidUntil": "Válido hasta", + "accYouCurrentlyHave": "Actualmente tienes", + "accYouWillEarn": "Ganarás", + "accNotAvailableToBuy": "No disponible para comprar", + "annPinnedAnnounce": "Anuncios fijados", + "annOtherAnnounce": "Otros anuncios", + "annAnnounceCreatSuccess": "Anuncio creado exitosamente", + "annCouldNotCreateAnnounce": "No se pudo crear el anuncio", + "annNewAnnounce": "Nuevo anuncio", + "annBody": "Cuerpo", + "annPinThisAnnounceQ": "¿Fijar este anuncio?", + "annAllowCommentsQ": "¿Permitir comentarios?", + "annCreateAnnounce": "Crear anuncio", + "annAnnounceDelSuccess": "Anuncio eliminado exitosamente", + "annCouldNotDelAnnounce": "No se pudo eliminar el anuncio", + "annUnpin": "Desfijar", + "annPin": "Fijar", + "annUnpinned": "desfijado", + "annPinned": "fijado", + "annLastUpdated": "Última actualización", + "annCommentsDisabled": "Comentarios desactivados.", + "annAreYouSureYouWantToDelThisannounceQ": "¿Estás seguro de que deseas eliminar este anuncio? Esto no se puede deshacer.", + "annAnnounceUpdatedSuccess": "Anuncio actualizado exitosamente", + "annCouldNotUpdateAnnounce": "No se pudo actualizar el anuncio", + "annEditAnnounce": "Editar anuncio", + "annUpdateAnnounce": "Actualizar anuncio", + "annAnnounce": "Anuncio", + "bmYourBM": "Tus marcadores", + "bmYouNotHaveAnyBM": "No tienes ningún marcador.", + "catCategories": "Categorías", + "catNoCategoryHaveBeenDefined": "No se han definido categorías.", + "catNoTagsHaveBeenDefined": "No se han definido etiquetas.", + "catNoResults": "Sin resultados.", + "comCommentBy": "Comentario de", + "comDelUser": "usuario eliminado", + "comOn": "en", + "email": "Correo electrónico", + "errSomethingWentWrong": "Algo salió mal", + "errTooManyRequests": "¡Demasiadas solicitudes! Se ha aplicado un límite de velocidad. Por favor, espera un tiempo antes de intentarlo nuevamente.", + "errIfErrorPersist": "Si el error persiste, por favor", + "errReportIt": "informa", + "errForNow": "Por ahora,", + "indexLatestAnnounce": "Último anuncio", + "indexLatestTorrents": "Últimos torrents", + "indexSearch": "Buscar", + "indexSearchTorrents": "Buscar torrents", + "indexText1": "Tu dirección de correo electrónico aún no está verificada. No podrás cargar ni descargar datos hasta que esto se haga.", + "indexTime": "HH:mm Do MMM YYYY", + "listNoItemShow": "No hay elementos para mostrar.", + "logIn": "Iniciar sesión", + "logInFailed": "No se pudo iniciar sesión", + "mdMarkdownPreview": "Vista previa de Markdown", + "navAnnouncements": "Anuncios", + "navBookmarks": "Marcadores", + "navBrowse": "Navegar", + "navHome": "Inicio", + "navLogOut": "Cerrar sesión", + "navReports": "Informes", + "navRequests": "Solicitudes", + "navRSS": "RSS", + "navSearch": "Buscar", + "navStats": "Estadísticas", + "navUpload": "Subir", + "navWiki": "Wiki", + "newPassword": "Nueva contraseña", + "password": "Contraseña", + "passwordResetFailed": "No se pudo completar el restablecimiento de la contraseña", + "passwordResetRequestFailed": "No se pudo iniciar la solicitud de restablecimiento de contraseña", + "passwordResetRequestSuccess": "Si existe una cuenta con esa dirección de correo electrónico, recibirás un correo electrónico en breve", + "passwordResetSuccess": "La contraseña se restableció con éxito", + "posterImage": "Imagen de portada", + "poweredBy": "Proporcionado por", + "register": "Registrarse", + "registerFailed": "No se pudo registrar", + "registrationClosed": "Registro cerrado", + "reqCreateNew": "Crear nuevo", + "reqCreateReq": "Crear solicitud", + "reqTitle": "Título", + "reqPostedBy": "Publicado por", + "reqPost": "Publicar", + "reqRequestCreatedSuccess": "Solicitud creada exitosamente", + "reqCouldNotCreateReq": "No se pudo crear la solicitud", + "reqNewRequest": "Nueva solicitud", + "reqWhatYouLookForQ": "¿Qué estás buscando?", + "reqRequestDelSuccess": "Solicitud eliminada exitosamente", + "reqCouldNotDelReq": "No se pudo eliminar la solicitud", + "reqCommentPostSuccess": "Comentario publicado exitosamente", + "reqCommentNotPost": "No se pudo publicar el comentario", + "reqSuggestionAddSuccess": "Sugerencia añadida exitosamente", + "reqSuggestionNotAdded": "No se pudo agregar la sugerencia", + "reqSuggestionAcceptSuccess": "Sugerencia aceptada exitosamente", + "reqCouldNotAcceptSuggestion": "No se pudo aceptar la sugerencia", + "reqDelete": "Eliminar", + "reqSuggestedTorrents": "Torrents sugeridos", + "reqSuggestATorrent": "Sugerir un torrent", + "reqAccepted": "Aceptado", + "reqAccept": "Aceptar", + "reqNoTorrentsHaveBeenSuggestedYet": "Aún no se han sugerido torrents.", + "reqPostAComment": "Publicar un comentario", + "reqEnterInfohashTorrentBelow": "Ingresa el infohash de un torrent a continuación.", + "reqFulfilled": "Cumplido", + "reqPosted": "Publicado", + "reqBy": "por", + "reqInfohash": "Infohash", + "reqSuggest": "Sugerir", + "repUnresolvedRep": "Informes no resueltos", + "repRepBy": "Reportado por", + "repReason": "Motivo", + "repRepMarkSolved": "Informe marcado como resuelto", + "repCouldNotResolveRep": "No se pudo resolver el informe", + "repRepOn": "Informe sobre", + "repMarkSolved": "Marcar como resuelto", + "repRep": "Reportado", + "repTorrDetail": "Detalles del torrent", + "resetPassword": "Restablecer contraseña", + "rssThereRSSFeedAt": "Hay un feed RSS en", + "rssToAuthenticateYourself": "Para autenticarte, debes proporcionar las cookies", + "rssAnd": "y", + "rssToRSSEndpoint": "al punto final de RSS, conteniendo tu nombre de usuario y tu contraseña respectivamente.", + "rssNoQueryParametersAreProvided": "Si no se proporcionan parámetros de consulta, el feed RSS contendrá los 100 torrents más recientes.", + "rssOnlyIncludeMatchingResults": "Para incluir solo resultados coincidentes en el feed, puedes agregar el", + "rssQueryParameter": "parámetro de consulta, por ejemplo", + "searchSearchResults": "Resultados de búsqueda para", + "searchSearchError": "Error de búsqueda", + "statYouNotPermission": "No tienes permiso para hacer eso.", + "statTrackerStat": "Estadísticas del tracker", + "statActiveTorrents": "Torrents activos", + "statBannedUsers": "Usuarios prohibidos", + "statCompletedDownloads": "Descargas completadas", + "statFilledRequests": "Solicitudes completadas", + "statInvitesAccepted": "Invitaciones aceptadas", + "statLeechers": "Leechers", + "statPeers": "Pares", + "statRegisteredUsers": "Usuarios registrados", + "statSeeders": "Seeders", + "statTotalComments": "Comentarios totales", + "statTotalInvitesSent": "Invitaciones totales enviadas", + "statTotalRequests": "Solicitudes totales", + "statUploadedTorrents": "Torrents subidos", + "tagTaggedWith": "Etiquetado con", + "tokenError": "Error de token", + "torrSeeders": "Seeders", + "torrLeechers": "Leechers", + "torrDownloads": "Descargas", + "torrUploaded": "Subido", + "torrTorrEditSuccess": "Torrent editado exitosamente", + "torrCouldEditTorr": "No se pudo editar el torrent", + "torrTorrDelSuccess": "Torrent eliminado exitosamente", + "torrCouldNotDelTorr": "No se pudo eliminar el torrent", + "torrVoteSubmitSuccess": "Voto enviado exitosamente", + "torrCouldNotSubmitVote": "No se pudo enviar el voto", + "torrReportSubmitSuccess": "Informe enviado exitosamente", + "torrCouldNotSubmitReport": "No se pudo enviar el informe", + "torrFLToggleSuccess": "Freeleech cambiado exitosamente", + "torrCouldNotToggleFL": "No se pudo cambiar a freeleech", + "torrTorrRemFromGroupSuccess": "Torrent eliminado del grupo exitosamente", + "torrCouldNotRemTorrFromGroup": "No se pudo eliminar el torrent del grupo", + "torrTorrent": "Torrent", + "torrCouldNotBookmarkTorr": "No se pudo marcar el torrent", + "torrFL": "FL!", + "torrEdit": "Editar", + "torrDownload": "Descargar", + "torrLogInDownload": "Inicia sesión para descargar", + "torrFiles": "Archivos", + "torrReport": "Informe", + "torrRemTorr": "Eliminar este torrent", + "torrAddTorr": "Añadir un torrent", + "torrReasonForReport": "Motivo del informe", + "torrSureDeleteTorr": "¿Estás seguro de que quieres eliminar este torrent? Esto no se puede deshacer.", + "torrFreeleech": "Freeleech", + "torrUnset": "Desactivar", + "torrSet": "Activar", + "torrUploadedBy": "Subido por", + "torrDate": "Fecha", + "torrSize": "Tamaño", + "torrYes": "Sí", + "torrNo": "No", + "torrGroupTorr": "Torrents agrupados", + "torrThereAreNoOtherTorrGroup": "No hay otros torrents en este grupo.", + "torrResults": "resultados", + "torrPage": "Página", + "torrOf": "de", + "torrRemovedFrom": "eliminado de", + "torrAddedTo": "añadido a", + "torrTorrNoTags": "Este torrent no tiene etiquetas.", + "totp": "Código de un solo uso", + "uploadAddTag": "Añadir etiqueta", + "uploadAnnounceURL": "URL de anuncio", + "uploadAnonymousUpload": "Subida anónima", + "uploadCategory": "Categoría", + "uploadCouldNotGetGroupSuggestions": "No se pudieron obtener sugerencias de grupos", + "uploadCouldNotUploadFile": "No se pudo cargar el archivo", + "uploadCouldNotUploadTorrent": "No se pudo cargar el archivo .torrent", + "uploadDescription": "Descripción", + "uploadDragDropClickSelect": "Arrastra y suelta el archivo .torrent aquí, o haz clic para seleccionar", + "uploadDragDropClickSelectPoster": "Arrastra y suelta tu imagen de portada aquí, o haz clic para seleccionar", + "uploadDropFileHere": "Suelta el archivo aquí...", + "uploadGroupWith": "Agrupar con", + "uploadGroupWithThisTorrent": "Agrupar con este torrent", + "uploadInfoBox1": "Las siguientes extensiones de archivo están en la lista negra. Cualquier torrent que contenga archivos de estos tipos no se cargará.", + "uploadInfoBox2": "Parece que estos torrents existentes tienen nombres similares. ¿Te gustaría agrupar tu carga con alguno de ellos? Los grupos deben contener solo contenido muy similar, por ejemplo, la misma película en diferentes formatos.", + "uploadInfoBox3": "Nota: si has comenzado a compartir un torrent antes de cargarlo, es posible que necesites actualizar los rastreadores en tu cliente de torrents una vez que la carga esté completa.", + "uploadMarkdownSupport": "Compatibilidad con Markdown", + "uploadMediaInfo": "Información multimedia", + "uploadName": "Nombre", + "uploadSource": "Fuente", + "uploadTags": "Etiquetas", + "uploadTorrentFile": "Archivo Torrent", + "uploadTorrentUploadSuccess": "Torrent cargado exitosamente", + "uploadUpload": "Cargar", + "uploadRemove": "Eliminar", + "username": "Nombre de usuario", + "userUser": "Usuario", + "userSuccessfully": "exitosamente", + "userCouldNot": "No se pudo", + "userProfile": "perfil", + "userUserSince": "Usuario desde", + "userUserSinceTime": "DD MMM AAAA", + "userOnlyAdminsSee": "Solo los administradores pueden ver esto", + "userInvitedBy": "Invitado por", + "userEmailVerified": "Correo electrónico verificado", + "userRemainingInvites": "Invitaciones restantes", + "userRatio": "Ratio", + "userHitNRuns": "Hit'n'runs", + "userDownloaded": "Descargado", + "userComments": "Comentarios", + "userNoComments": "Sin comentarios.", + "userYouSureWant": "¿Estás seguro de que quieres", + "userThisUserQ": "a este usuario?", + "userUploaded": "Subido", + "userMyUploads": "Mis subidas", + "usernameRules": "Solo puede consistir en letras, números y “.”", + "userUnban": "desbanear", + "userBan": "banear", + "userUnbanned": "desbaneado", + "userBanned": "baneado", + "userAdmin": "Administrador", + "veCouldNotVerifyEmailAddress": "No se pudo verificar la dirección de correo electrónico:", + "veEmailAddressVerifiedSuccess": "Dirección de correo electrónico verificada exitosamente.", + "veNoVerificationTokenProvided": "No se proporcionó un token de verificación", + "veVerifyEmail": "Verificar correo electrónico", + "welcome": "Bienvenido", + "welcomeBack": "Bienvenido de nuevo", + "wikiPath": "Ruta", + "wikiPageWillBeVisibleAt": "La página será visible en", + "wikiAllowUnregisteredView": "Permitir vista no registrada", + "wikiPageCreateSuccess": "Página wiki creada exitosamente", + "wikiCouldNotCreatePage": "No se pudo crear la página wiki", + "wikiNewPage": "Nueva página wiki", + "wikiCreatePage": "Crear página wiki", + "wikiPageDelSuccess": "Página wiki eliminada exitosamente", + "wikiCouldNotDelPage": "No se pudo eliminar la página wiki", + "wikiPageUpdateSuccess": "Página wiki actualizada exitosamente", + "wikiCouldNotUpdatePage": "No se pudo actualizar la página wiki", + "wikiAddPage": "Agregar página", + "wikiLastEdited": "Última edición", + "wikiPages": "Páginas", + "wikiSaveChanges": "Guardar cambios", + "wikiThereNothingHereYet": "Todavía no hay nada aquí.", + "wikiDelThisPageQ": "¿Estás seguro de que quieres eliminar esta página wiki? Esto no se puede deshacer." +} From 4abf10a1bee214a2fbf9a8234e0b7a4fa537eb36 Mon Sep 17 00:00:00 2001 From: "Cereal_Killer.js" Date: Sat, 6 Jan 2024 19:08:23 +0100 Subject: [PATCH 03/12] Added Spanish languaje to index.js --- client/locales/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/locales/index.js b/client/locales/index.js index 8beda2c..f95a1ea 100644 --- a/client/locales/index.js +++ b/client/locales/index.js @@ -1,4 +1,5 @@ import en from "./en.json"; +import es from "./es.json"; import ru from "./ru.json"; import de from "./de.json"; import zh from "./zh.json"; @@ -7,6 +8,7 @@ import fr from "./fr.json"; export default { en, + es, ru, de, zh, From 034c4935ebd696c25f321a3b24b7ebbb4dec6988 Mon Sep 17 00:00:00 2001 From: Mercurio <47455213+NotLugozzi@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:12:41 +0100 Subject: [PATCH 04/12] Add files via upload --- client/locales/it.json | 310 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 client/locales/it.json diff --git a/client/locales/it.json b/client/locales/it.json new file mode 100644 index 0000000..ee0e9d3 --- /dev/null +++ b/client/locales/it.json @@ -0,0 +1,310 @@ +{ + "404NotFound": "Non Trovato", + "404PageDoesNotExist": "Quella pagina non esiste", + "404ReturnHome": "Torna alla home", + "acc2FADisabled": "2FA disabilitato", + "acc2FAEnabled": "2FA abilitato", + "acc2FAScanQR": "Scansiona il codice QR con la tua app authenticator ed inserisci il codice", + "acc2FAText1": "2FA abilitato con successo. Questi codici di backup possono essere utilizzati per accedere se si perde l'accesso all'app Authenticator. Salvateli ora, non saranno più visibili.", + "acc2FAUseApp": "Utilizzate un'app di autenticazione per aggiungere un ulteriore livello di sicurezza al vostro account.", + "acc2FAuth": "Autenticazione a due fattori", + "accBonusPoints": "Punti Bonus", + "accBonusPointsHave": "punti bonus", + "accBuy": "Compra", + "accCancel": "Canceclla", + "accChangePass": "Cambia Password", + "accClaimed": "Rivendicato", + "accCopyLink": "Copia link", + "accCouldNotBuyItems": "Non è stato possibile acquistare articoli", + "accCouldNotChangePass": "Non è stato possibile cambiare la password", + "accCouldNotDelAcc": "Non è stato possibile eliminare il profilo", + "accCouldNotSendInvite": "Non è stato possibile inviare l'invito", + "accCouldNotToggle2FA": "Impossibile attivare la 2FA", + "accCreated": "Creato", + "accCurrentPass": "Password attuale", + "accDangerZone": "Danger zone", + "accDelAccText1": "Sei sicuro di voler cancellare il tuo account? Questa azione non può essere annullata e potresti non essere in grado di registrarti di nuovo. I tuoi dati personali saranno cancellati, ma i torrent caricati rimarranno.", + "accDeleteMyAcc": "Cancella il mio account", + "accDeleteMyAccYes": "Si, cancella il mio account", + "accDisable": "Disabilita", + "accEnable": "Abilita", + "accEnable2FA": "Abilita 2FA", + "accEveryRequestYouFulfill": "per ogni richiesta soddisfatta, oppure", + "accForEveryGBYouUpload": "per ogni GB caricato", + "accIfYouAreAlsUploaderAcceptTorrent": "se si è anche l'uploader del torrent accettato", + "accInviteLinkCopiedClipboard": "Collegamento all'invito copiato negli appunti", + "accInvites": "Inviti", + "accRemaining": "rimasti", + "accRoleUser": "Ruolo: utente", + "accRoleAdmin": "Ruolo: admin", + "accInviteSentSuccess": "Invito inviato con successo", + "accInviteText1": "Immettere un indirizzo e-mail per inviare un invito. L'utente invitato dovrà registrarsi con lo stesso indirizzo e-mail. Una volta generato l'invito, è possibile anche copiare un link diretto", + "accItemsPurchasedSuccess": "Articoli acquistati con successo", + "accMyAccount": "Il mio account", + "accPassChangedSuccess": "La password è stata modificata con successo", + "accPurchaseInvites": "Compra inviti", + "accPurchaseUpload1GB": "Compra Upload (1 GB)", + "accRole": "Ruolo", + "accSendInvite": "Invia Invito", + "accThisIsAdminAcc": "Questo è un account amministratore.", + "accValidUntil": "Valido fino a", + "accYouCurrentlyHave": "Attualmente hai", + "accYouWillEarn": "Guadagnerai", + "accNotAvailableToBuy": "Non disponibile per l'acquisto", + "annPinnedAnnounce": "Annunci in evidenza", + "annOtherAnnounce": "Altri annunci", + "annAnnounceCreatSuccess": "Annuncio creato con successo", + "annCouldNotCreateAnnounce": "Impossibile creare l'annuncio", + "annNewAnnounce": "Nuovo annuncio", + "annBody": "Corpo", + "annPinThisAnnounceQ": "Fissare questo annuncio?", + "annAllowCommentsQ": "Consentire commenti?", + "annCreateAnnounce": "Crea annuncio", + "annAnnounceDelSuccess": "Annuncio eliminato con successo", + "annCouldNotDelAnnounce": "Impossibile eliminare l'annuncio", + "annUnpin": "Scollega", + "annPin": "Fissa", + "annUnpinned": "scollegato", + "annPinned": "fissato", + "annLastUpdated": "Ultimo aggiornamento", + "annCommentsDisabled": "Commenti disabilitati.", + "annAreYouSureYouWantToDelThisannounceQ": "Sei sicuro di voler eliminare questo annuncio? Questo non può essere annullato.", + "annAnnounceUpdatedSuccess": "Annuncio aggiornato con successo", + "annCouldNotUpdateAnnounce": "Impossibile aggiornare l'annuncio", + "annEditAnnounce": "Modifica annuncio", + "annUpdateAnnounce": "Aggiorna annuncio", + "annAnnounce": "Annuncio", + "bmYourBM": "I tuoi segnalibri", + "bmYouNotHaveAnyBM": "Non hai alcun segnalibro.", + "catCategories": "Categorie", + "catNoCategoryHaveBeenDefined": "Non sono state definite categorie.", + "catNoTagsHaveBeenDefined": "Non sono stati definiti tag.", + "catNoResults": "Nessun risultato.", + "comCommentBy": "Commento di", + "comDelUser": "utente eliminato", + "comOn": "su", + "email": "Email", + "errSomethingWentWrong": "Qualcosa è andato storto", + "errTooManyRequests": "Troppe richieste! Sei stato limitato. Attendi qualche minuto prima di riprovare.", + "errIfErrorPersist": "Se l'errore persiste, per favore", + "errReportIt": "segnalalo", + "errForNow": "Per ora,", + "indexLatestAnnounce": "Ultimo annuncio", + "indexLatestTorrents": "Ultimi torrent", + "indexSearch": "Cerca", + "indexSearchTorrents": "Cerca torrent", + "indexText1": "Il tuo indirizzo email non è ancora verificato. Non sarai in grado di caricare o scaricare dati finché non sarà completata la verifica.", + "indexTime": "HH:mm Do MMM YYYY", + "listNoItemShow": "Nessun elemento da mostrare.", + "logIn": "Accedi", + "logInFailed": "Accesso non riuscito", + "mdMarkdownPreview": "Anteprima Markdown", + "navAnnouncements": "Annunci", + "navBookmarks": "Segnalibri", + "navBrowse": "Esplora", + "navHome": "Home", + "navLogOut": "Esci", + "navReports": "Segnalazioni", + "navRequests": "Richieste", + "navRSS": "RSS", + "navSearch": "Cerca", + "navStats": "Statistiche", + "navUpload": "Carica", + "navWiki": "Wiki", + "newPassword": "Nuova password", + "password": "Password", + "passwordResetFailed": "Impossibile reimpostare la password", + "passwordResetRequestFailed": "Impossibile iniziare il reset della password", + "passwordResetRequestSuccess": "Se un account con questo indirizzo email esiste, riceverai presto una email", + "passwordResetSuccess": "Password reimpostata con successo", + "posterImage": "Immagine di copertina", + "poweredBy": "Powered by", + "register": "Registrati", + "registerFailed": "Registrazione non riuscita", + "registrationClosed": "Registrazione chiusa", + "reqCreateNew": "Crea nuovo", + "reqCreateReq": "Crea richiesta", + "reqTitle": "Titolo", + "reqPostedBy": "Pubblicato da", + "reqPost": "Pubblica", + "reqRequestCreatedSuccess": "Richiesta creata con successo", + "reqCouldNotCreateReq": "Impossibile creare la richiesta", + "reqNewRequest": "Nuova richiesta", + "reqWhatYouLookForQ": "Cosa stai cercando?", + "reqRequestDelSuccess": "Richiesta eliminata con successo", + "reqCouldNotDelReq": "Impossibile eliminare la richiesta", + "reqCommentPostSuccess": "Commento pubblicato con successo", + "reqCommentNotPost": "Impossibile pubblicare il commento", + "reqSuggestionAddSuccess": "Suggerimento aggiunto con successo", + "reqSuggestionNotAdded": "Impossibile aggiungere il suggerimento", + "reqSuggestionAcceptSuccess": "Suggerimento accettato con successo", + "reqCouldNotAcceptSuggestion": "Impossibile accettare il suggerimento", + "reqDelete": "Elimina", + "reqSuggestedTorrents": "Torrent suggeriti", + "reqSuggestATorrent": "Suggerisci un torrent", + "reqAccepted": "Accettato", + "reqAccept": "Accetta", + "reqNoTorrentsHaveBeenSuggestedYet": "Non sono stati ancora suggeriti torrent.", + "reqPostAComment": "Pubblica un commento", + "reqEnterInfohashTorrentBelow": "Inserisci l'infohash di un torrent qui sotto.", + "reqFulfilled": "Soddisfatto", + "reqPosted": "Pubblicato", + "reqBy": "da", + "reqInfohash": "Infohash", + "reqSuggest": "Suggerisci", + "repUnresolvedRep": "Segnalazioni non risolte", + "repRepBy": "Segnalato da", + "repReason": "Motivo", + "repRepMarkSolved": "Segnalazione contrassegnata come risolta", + "repCouldNotResolveRep": "Impossibile risolvere la segnalazione", + "repRepOn": "Segnala", + "repMarkSolved": "Segna come risolto", + "repRep": "Segnalato", + "repTorrDetail": "Dettagli torrent", + "resetPassword": "Ripristina password", + "rssThereRSSFeedAt": "C'è un feed RSS su", + "rssToAuthenticateYourself": "Per autenticarti, devi fornire i cookie", + "rssAnd": "e", + "rssToRSSEndpoint": "all'endpoint RSS, contenenti rispettivamente il tuo nome utente e la tua password.", + "rssNoQueryParametersAreProvided": "Se non vengono forniti parametri di query, il feed RSS conterrà i 100 torrent più recenti.", + "rssOnlyIncludeMatchingResults": "Per includere solo risultati corrispondenti nel feed, puoi aggiungere il", + "rssQueryParameter": "parametro di query, ad esempio", + "searchSearchResults": "Risultati della ricerca per", + "searchSearchError": "Errore di ricerca", + "statYouNotPermission": "Non hai il permesso per farlo.", + "statTrackerStat": "Statistiche del tracker", + "statActiveTorrents": "Torrent attivi", + "statBannedUsers": "Utenti banditi", + "statCompletedDownloads": "Download completati", + "statFilledRequests": "Richieste soddisfatte", + "statInvitesAccepted": "Inviti accettati", + "statLeechers": "Leecher", + "statPeers": "Peers", + "statRegisteredUsers": "Utenti registrati", + "statSeeders": "Seeders", + "statTotalComments": "Commenti totali", + "statTotalInvitesSent": "Inviti totali inviati", + "statTotalRequests": "Richieste totali", + "statUploadedTorrents": "Torrent caricati", + "tagTaggedWith": "Taggato con", + "tokenError": "Errore del token", + "torrSeeders": "Seeders", + "torrLeechers": "Leecher", + "torrDownloads": "Download", + "torrUploaded": "Caricato", + "torrTorrEditSuccess": "Torrent modificato con successo", + "torrCouldEditTorr": "Non è stato possibile modificare il torrent", + "torrTorrDelSuccess": "Torrent eliminato con successo", + "torrCouldNotDelTorr": "Impossibile eliminare il torrent", + "torrVoteSubmitSuccess": "Voto inviato con successo", + "torrCouldNotSubmitVote": "Impossibile inviare il voto", + "torrReportSubmitSuccess": "Segnalazione inviata con successo", + "torrCouldNotSubmitReport": "Impossibile inviare la segnalazione", + "torrFLToggleSuccess": "Freeleech attivato/disattivato con successo", + "torrCouldNotToggleFL": "Impossibile attivare/disattivare freeleech", + "torrTorrRemFromGroupSuccess": "Torrent rimosso dal gruppo con successo", + "torrCouldNotRemTorrFromGroup": "Impossibile rimuovere il torrent dal gruppo", + "torrTorrent": "Torrent", + "torrCouldNotBookmarkTorr": "Impossibile aggiungere il torrent ai segnalibri", + "torrFL": "FL!", + "torrEdit": "Modifica", + "torrDownload": "Scarica", + "torrLogInDownload": "Accedi per scaricare", + "torrFiles": "File", + "torrReport": "Segnala", + "torrRemTorr": "Rimuovi questo torrent", + "torrAddTorr": "Aggiungi un torrent", + "torrReasonForReport": "Motivo della segnalazione", + "torrSureDeleteTorr": "Sei sicuro di voler eliminare questo torrent? Questa azione non può essere annullata.", + "torrFreeleech": "Freeleech", + "torrUnset": "Non impostato", + "torrSet": "Impostato", + "torrUploadedBy": "Caricato da", + "torrDate": "Data", + "torrSize": "Dimensione", + "torrYes": "Sì", + "torrNo": "No", + "torrGroupTorr": "Torrent raggruppati", + "torrThereAreNoOtherTorrGroup": "Non ci sono altri torrent in questo gruppo.", + "torrResults": "risultati", + "torrPage": "Pagina", + "torrOf": "di", + "torrRemovedFrom": "rimosso da", + "torrAddedTo": "aggiunto a", + "torrTorrNoTags": "Questo torrent non ha tag.", + "totp": "Codice monouso", + "uploadAddTag": "Aggiungi tag", + "uploadAnnounceURL": "URL di annuncio", + "uploadAnonymousUpload": "Caricamento anonimo", + "uploadCategory": "Categoria", + "uploadCouldNotGetGroupSuggestions": "Impossibile ottenere suggerimenti per il gruppo", + "uploadCouldNotUploadFile": "Impossibile caricare il file", + "uploadCouldNotUploadTorrent": "Impossibile caricare il file .torrent", + "uploadDescription": "Descrizione", + "uploadDragDropClickSelect": "Trascina e rilascia il file .torrent qui, o clicca per selezionare", + "uploadDragDropClickSelectPoster": "Trascina e rilascia la tua immagine di copertina qui, o clicca per selezionare", + "uploadDropFileHere": "Rilascia il file qui...", + "uploadGroupWith": "Raggruppa con", + "uploadGroupWithThisTorrent": "Raggruppa con questo torrent", + "uploadInfoBox1": "Le seguenti estensioni di file sono inserite nella lista nera. Qualsiasi torrent contenente file di questi tipi non verrà caricato.", + "uploadInfoBox2": "Sembra che questi torrent esistenti abbiano nomi simili. Vorresti raggruppare il tuo caricamento con uno di essi? I gruppi dovrebbero contenere solo contenuti molto simili, ad esempio lo stesso film in formati diversi.", + "uploadInfoBox3": "Nota: se hai iniziato a seedare un torrent prima di caricarlo, potresti dover aggiornare i tracker nel tuo client torrent una volta completato il caricamento.", + "uploadMarkdownSupport": "Supporto Markdown", + "uploadMediaInfo": "Informazioni sui media", + "uploadName": "Nome", + "uploadSource": "Fonte", + "uploadTags": "Tag", + "uploadTorrentFile": "File torrent", + "uploadTorrentUploadSuccess": "Torrent caricato con successo", + "uploadUpload": "Carica", + "uploadRemove": "Rimuovi", + "username": "Nome utente", + "userUser": "Utente", + "userSuccessfully": "con successo", + "userCouldNot": "Impossibile", + "userProfile": "profilo", + "userUserSince": "Utente dal", + "userUserSinceTime": "Do MMM YYYY", + "userOnlyAdminsSee": "Solo gli amministratori possono vedere questo", + "userInvitedBy": "Invitato da", + "userEmailVerified": "Email verificata", + "userRemainingInvites": "Inviti rimanenti", + "userRatio": "Rapporto", + "userHitNRuns": "Hit'n'runs", + "userDownloaded": "Scaricato", + "userComments": "Commenti", + "userNoComments": "Nessun commento.", + "userYouSureWant": "Sei sicuro di voler", + "userThisUserQ": "questo utente?", + "userUploaded": "Caricato", + "userMyUploads": "I miei caricamenti", + "usernameRules": "Può contenere solo lettere, numeri e “.”", + "userUnban": "rimuovere il ban a", + "userBan": "bannare", + "userUnbanned": "rimosso il ban", + "userBanned": "bannato", + "userAdmin": "Admin", + "veCouldNotVerifyEmailAddress": "Impossibile verificare l'indirizzo email:", + "veEmailAddressVerifiedSuccess": "Indirizzo email verificato con successo.", + "veNoVerificationTokenProvided": "Nessun token di verifica fornito", + "veVerifyEmail": "Verifica email", + "welcome": "Benvenuto", + "welcomeBack": "Bentornato", + "wikiPath": "Percorso", + "wikiPageWillBeVisibleAt": "La pagina sarà visibile su", + "wikiAllowUnregisteredView": "Consenti visualizzazione non registrata", + "wikiPageCreateSuccess": "Pagina wiki creata con successo", + "wikiCouldNotCreatePage": "Impossibile creare la pagina wiki", + "wikiNewPage": "Nuova pagina wiki", + "wikiCreatePage": "Crea pagina wiki", + "wikiPageDelSuccess": "Pagina wiki eliminata con successo", + "wikiCouldNotDelPage": "Impossibile eliminare la pagina wiki", + "wikiPageUpdateSuccess": "Pagina wiki aggiornata con successo", + "wikiCouldNotUpdatePage": "Impossibile aggiornare la pagina wiki", + "wikiAddPage": "Aggiungi pagina", + "wikiLastEdited": "Ultima modifica", + "wikiPages": "Pagine", + "wikiSaveChanges": "Salva modifiche", + "wikiThereNothingHereYet": "Non c'è ancora nulla qui.", + "wikiDelThisPageQ": "Sei sicuro di voler eliminare questa pagina della wiki? Questa azione non può essere annullata." + } \ No newline at end of file From b66f2fb8d1c38ea1a36e7bdf92b73f0eb3196957 Mon Sep 17 00:00:00 2001 From: Mercurio <47455213+NotLugozzi@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:16:41 +0100 Subject: [PATCH 05/12] Add italian to index.js --- client/locales/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/locales/index.js b/client/locales/index.js index 8beda2c..a9bb622 100644 --- a/client/locales/index.js +++ b/client/locales/index.js @@ -1,12 +1,16 @@ import en from "./en.json"; +import es from "./es.json"; import ru from "./ru.json"; import de from "./de.json"; import zh from "./zh.json"; import eo from "./eo.json"; import fr from "./fr.json"; +import it from "./it.json"; export default { en, + es, + it, ru, de, zh, From 17824c69010b7dfafbf4529154492d0dce4034fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=AE=E9=B2=81=E6=96=87?= Date: Fri, 12 Jan 2024 14:00:29 +0800 Subject: [PATCH 06/12] Update zh.json Add spaces between Chinese and English characters Add missing translations --- client/locales/zh.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/locales/zh.json b/client/locales/zh.json index 066e2c1..be5d8cf 100644 --- a/client/locales/zh.json +++ b/client/locales/zh.json @@ -196,8 +196,8 @@ "torrCouldNotSubmitVote": "无法提交投票", "torrReportSubmitSuccess": "报告提交成功", "torrCouldNotSubmitReport": "无法提交报告", - "torrFLToggleSuccess": "FREE状态切换成功", - "torrCouldNotToggleFL": "无法切换至FREE状态", + "torrFLToggleSuccess": "FREE 状态切换成功", + "torrCouldNotToggleFL": "无法切换至 FREE 状态", "torrTorrRemFromGroupSuccess": "成功从组中删除种子", "torrCouldNotRemTorrFromGroup": "无法从组中删除种子", "torrTorrent": "种子", @@ -224,7 +224,7 @@ "torrThereAreNoOtherTorrGroup": "此组中没有其他种子。", "totp": "一次性代码", "uploadAddTag": "添加标签", - "uploadAnnounceURL": "Tracker地址", + "uploadAnnounceURL": "Tracker 地址", "uploadAnonymousUpload": "匿名上传", "uploadCategory": "分类", "uploadCouldNotGetGroupSuggestions": "无法获取组建议", @@ -238,7 +238,7 @@ "uploadGroupWithThisTorrent": "与此种子同组", "uploadInfoBox1": "以下文件扩展名被列入黑名单。包含这些类型文件的种子将无法发布。", "uploadInfoBox2": "看起来与现有的种子有着相似的名称。您是否想要将您的种子分到其中的任意分组?分组应只包含非常相似的内容,例如相同电影的不同格式。", - "uploadInfoBox3": "注意:如果您在上传前已开始做种,上传完成后可能需要在您的客户端中强制重新汇报Tracker。", + "uploadInfoBox3": "注意:如果您在上传前已开始做种,上传完成后可能需要在您的客户端中强制重新汇报 Tracker。", "uploadMarkdownSupport": "支持 Markdown", "uploadMediaInfo": "MediaInfo", "uploadName": "名称", @@ -293,5 +293,5 @@ "wikiPages": "页面", "wikiSaveChanges": "保存更改", "wikiThereNothingHereYet": "目前还没有内容。", - "wikiDelThisPageQ": "您确定要删除这个Wiki页面吗?此操作无法撤销。" + "wikiDelThisPageQ": "您确定要删除这个维基页面吗?此操作无法撤销。" } From 1bd0568bae7390bad8a5928dd4845fbd6c1e0f3f Mon Sep 17 00:00:00 2001 From: Nils Jacobsen Date: Fri, 26 Jan 2024 15:39:37 +0100 Subject: [PATCH 07/12] feat: add localization editor --- client/project.inlang/project_id | 1 + client/project.inlang/settings.json | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 client/project.inlang/project_id create mode 100644 client/project.inlang/settings.json diff --git a/client/project.inlang/project_id b/client/project.inlang/project_id new file mode 100644 index 0000000..43f52dc --- /dev/null +++ b/client/project.inlang/project_id @@ -0,0 +1 @@ +00f4e84726876387f04dba9ca472f3815f3554ed6d960568f621f4ee6af15f4c \ No newline at end of file diff --git a/client/project.inlang/settings.json b/client/project.inlang/settings.json new file mode 100644 index 0000000..19c5b01 --- /dev/null +++ b/client/project.inlang/settings.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "sourceLanguageTag": "en", + "languageTags": [ + "en", "de", "eo", "fr", "ru", "zh" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@4/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js" + ], + "plugin.inlang.i18next": { + "pathPattern": "./locales/{languageTag}.json" + } +} + From 9e07fbb247123eaf096e8d0e6e96e9116eeaf651 Mon Sep 17 00:00:00 2001 From: Tom Snelling Date: Wed, 21 Feb 2024 09:31:16 +0000 Subject: [PATCH 08/12] readme translation updates --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ca1314c..7d33c8f 100644 --- a/README.md +++ b/README.md @@ -116,16 +116,20 @@ To add a new translation in your own language, create a new JSON file with your The best place to start is to copy the `en.json` file and work through it, translating each English string. +There is also an [inlang project](https://fink.inlang.com/github.com/tdjsnelling/sqtracker) to aid with translation. + ### Existing translations -| Language | Complete (estimate) | Contributed by | -|--------------------|---------------------|----------------------------------------------------| -| English | 100% | | -| Russian | 100% | [@smlinux](https://github.com/smlinux) | -| Esperanto | 100% | [@smlinux](https://github.com/smlinux) | -| German | 100% | [@EchterAlsFake](https://github.com/EchterAlsFake) | -| Simplified Chinese | 95% | [@0EAC](https://github.com/0EAC) | -| French | 100% | [@Klaiment](https://github.com/Klaiment) | +| Language | Contributed by | +|--------------------|------------------------------------------------------| +| English | | +| Russian | [@smlinux](https://github.com/smlinux) | +| Esperanto | [@smlinux](https://github.com/smlinux) | +| German | [@EchterAlsFake](https://github.com/EchterAlsFake) | +| Simplified Chinese | [@0EAC](https://github.com/0EAC) | +| French | [@Klaiment](https://github.com/Klaiment) | +| Spanish | [@CerealKillerjs](https://github.com/CerealKillerjs) | +| Italian | [@NotLugozzi](https://github.com/NotLugozzi) | ## Screenshots From 41155da91e3faf68bb4daea86e3b8599a896ed8a Mon Sep 17 00:00:00 2001 From: Tom Snelling Date: Wed, 21 Feb 2024 09:48:08 +0000 Subject: [PATCH 09/12] adds default locale setting --- api/src/utils/validateConfig.js | 11 + client/pages/_app.js | 4 +- config.example.js | 3 + yarn.lock | 481 ++++++++++++++------------------ 4 files changed, 221 insertions(+), 278 deletions(-) diff --git a/api/src/utils/validateConfig.js b/api/src/utils/validateConfig.js index 096a15a..5ccf91c 100644 --- a/api/src/utils/validateConfig.js +++ b/api/src/utils/validateConfig.js @@ -1,4 +1,12 @@ import * as yup from "yup"; +import en from "@sqtracker/client/locales/en.json"; +import es from "@sqtracker/client/locales/es.json"; +import it from "@sqtracker/client/locales/it.json"; +import ru from "@sqtracker/client/locales/ru.json"; +import de from "@sqtracker/client/locales/de.json"; +import zh from "@sqtracker/client/locales/zh.json"; +import eo from "@sqtracker/client/locales/eo.json"; +import fr from "@sqtracker/client/locales/fr.json"; const httpRegex = /http(s)?:\/\/.*/; const mongoRegex = /mongodb:\/\/.*/; @@ -50,6 +58,9 @@ const configSchema = yup grey: yup.string().matches(hexRegex), }), SQ_EXTENSION_BLACKLIST: yup.array().of(yup.string()).min(0), + SQ_SITE_DEFAULT_LOCALE: yup + .string() + .oneOf(["en", "es", "it", "ru", "de", "zh", "eo", "fr"]), SQ_BASE_URL: yup.string().matches(httpRegex).required(), SQ_API_URL: yup.string().matches(httpRegex).required(), SQ_MONGO_URL: yup.string().matches(mongoRegex).required(), diff --git a/client/pages/_app.js b/client/pages/_app.js index 4dc7070..8950ac3 100644 --- a/client/pages/_app.js +++ b/client/pages/_app.js @@ -164,7 +164,6 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => { const [isServer, setIsServer] = useState(true); const [loading, setLoading] = useState(false); const [userStats, setUserStats] = useState(); - const [locale, setLocale] = useState("en"); const router = useRouter(); @@ -181,9 +180,12 @@ const SqTracker = ({ Component, pageProps, initialTheme }) => { SQ_API_URL, SQ_MINIMUM_RATIO, SQ_MAXIMUM_HIT_N_RUNS, + SQ_SITE_DEFAULT_LOCALE, }, } = getConfig(); + const [locale, setLocale] = useState(SQ_SITE_DEFAULT_LOCALE ?? "en"); + const allowThemeToggle = !Object.keys(SQ_CUSTOM_THEME ?? {}).some( (key) => key !== "primary" ); diff --git a/config.example.js b/config.example.js index a4344f2..ce2f57a 100644 --- a/config.example.js +++ b/config.example.js @@ -73,6 +73,9 @@ module.exports = { // An array of blacklisted file extensions. Torrents containing files with these extensions will fail to upload. SQ_EXTENSION_BLACKLIST: ["exe"], + // Default site locale. See `client/locales/index.js` for available options. + SQ_SITE_DEFAULT_LOCALE: "en", + // The URL of your tracker site. SQ_BASE_URL: "https://sqtracker.dev", diff --git a/yarn.lock b/yarn.lock index d44a97f..65c29f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,13 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" @@ -24,12 +17,19 @@ dependencies: "@babel/highlight" "^7.10.4" +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.20.14" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz" integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.14.6", "@babel/core@^7.4.0-0": +"@babel/core@^7.14.6": version "7.20.12" resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz" integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== @@ -1017,7 +1017,7 @@ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@1.4.14": +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": version "1.4.14" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== @@ -1057,6 +1057,66 @@ dependencies: glob "7.1.7" +"@next/swc-android-arm-eabi@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz#903a5479ab4c2705d9c08d080907475f7bacf94d" + integrity sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA== + +"@next/swc-android-arm64@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz#2f9a98ec4166c7860510963b31bda1f57a77c792" + integrity sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg== + +"@next/swc-darwin-arm64@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz#31b1c3c659d54be546120c488a1e1bad21c24a1d" + integrity sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg== + +"@next/swc-darwin-x64@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz#2e44dd82b2b7fef88238d1bc4d3bead5884cedfd" + integrity sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A== + +"@next/swc-freebsd-x64@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz#e24e75d8c2581bfebc75e4f08f6ddbd116ce9dbd" + integrity sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw== + +"@next/swc-linux-arm-gnueabihf@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz#46d8c514d834d2b5f67086013f0bd5e3081e10b9" + integrity sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg== + +"@next/swc-linux-arm64-gnu@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz#91f725ac217d3a1f4f9f53b553615ba582fd3d9f" + integrity sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ== + +"@next/swc-linux-arm64-musl@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz#e627e8c867920995810250303cd9b8e963598383" + integrity sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg== + +"@next/swc-linux-x64-gnu@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz#83a5e224fbc4d119ef2e0f29d0d79c40cc43887e" + integrity sha512-0szyAo8jMCClkjNK0hknjhmAngUppoRekW6OAezbEYwHXN/VNtsXbfzgYOqjKWxEx3OoAzrT3jLwAF0HdX2MEw== + +"@next/swc-linux-x64-musl@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.5.tgz#be700d48471baac1ec2e9539396625584a317e95" + integrity sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g== + +"@next/swc-win32-arm64-msvc@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz#a93e958133ad3310373fda33a79aa10af2a0aa97" + integrity sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw== + +"@next/swc-win32-ia32-msvc@12.2.5": + version "12.2.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz#4f5f7ba0a98ff89a883625d4af0125baed8b2e19" + integrity sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw== + "@next/swc-win32-x64-msvc@12.2.5": version "12.2.5" resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz" @@ -1070,7 +1130,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -1191,7 +1251,7 @@ stacktrace-parser "^0.1.10" tslib "^2.4.1 || ^1.9.3" -"@sentry/node@^7.36.0", "@sentry/node@7.73.0": +"@sentry/node@7.73.0", "@sentry/node@^7.36.0": version "7.73.0" resolved "https://registry.npmjs.org/@sentry/node/-/node-7.73.0.tgz" integrity sha512-i50bRfmgkRRx0XXUbg9jGD/RuznDJxJXc4rBILhoJuhl+BjRIaoXA3ayplfJn8JLZxsNh75uJaCq4IUK70SORw== @@ -1279,68 +1339,6 @@ "@sentry/cli" "^1.74.6" webpack-sources "^2.0.0 || ^3.0.0" -"@sqtracker/api@file:C:\\sqt\\sqtracker\\api": - version "1.4.0" - resolved "file:api" - dependencies: - "@babel/core" "^7.14.6" - "@babel/node" "^7.14.7" - "@babel/preset-env" "^7.14.7" - "@sentry/node" "^7.36.0" - "@sentry/tracing" "^7.36.0" - bcrypt "^5.0.1" - bencode "^2.0.1" - bittorrent-tracker "9.19.0" - body-parser "^1.19.0" - chalk "^4.1.1" - content-disposition "^0.5.4" - cookie-parser "^1.4.6" - cors "^2.8.5" - dotenv "^10.0.0" - express "^4.17.1" - express-rate-limit "^6.7.0" - jsonwebtoken "^8.5.1" - memoizee "^0.4.15" - mongoose "^5.13.2" - mongoose-fuzzy-searching "^2.0.2" - morgan "^1.10.0" - multer "^1.4.2" - node-fetch "^2.6.1" - nodemailer "^6.7.8" - qrcode "^1.5.1" - qs "^6.11.0" - slugify "^1.6.5" - speakeasy "^2.0.0" - yup "^0.32.11" - -"@sqtracker/client@file:C:\\sqt\\sqtracker\\client": - version "1.4.0" - resolved "file:client" - dependencies: - "@sentry/nextjs" "^7.36.0" - "@styled-icons/boxicons-regular" "^10.38.0" - "@styled-icons/boxicons-solid" "^10.38.0" - "@styled-system/css" "^5.1.5" - copy-to-clipboard "^3.3.1" - dotenv "^10.0.0" - jsonwebtoken "^8.5.1" - lodash "^4.17.21" - moment "^2.29.1" - next "12.2.5" - pluralize "^8.0.0" - polished "^4.1.3" - pretty-bytes "^5.6.0" - qs "^6.11.0" - react "17.0.2" - react-cookie "^4.1.1" - react-dom "17.0.2" - react-dropzone "^14.2.3" - react-markdown "^7.1.2" - remark-gfm "^3.0.1" - slugify "^1.6.5" - styled-components "^5.3.3" - styled-system "^5.1.5" - "@styled-icons/boxicons-regular@^10.38.0": version "10.47.0" resolved "https://registry.npmjs.org/@styled-icons/boxicons-regular/-/boxicons-regular-10.47.0.tgz" @@ -1545,7 +1543,7 @@ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== -"@types/react@*", "@types/react@>=16": +"@types/react@*": version "18.2.27" resolved "https://registry.npmjs.org/@types/react/-/react-18.2.27.tgz" integrity sha512-Wfv7B7FZiR2r3MIqbAlXoY1+tXm4bOqfz4oRr+nyXdBqapDBZ0l/IGcSlAfvxIHEEJjkPU0MYAc/BlFPOcrgLw== @@ -1626,7 +1624,7 @@ acorn-jsx@^5.3.1: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^7.4.0: +acorn@^7.4.0: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -1962,7 +1960,7 @@ bn.js@^5.2.0: resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@^1.19.0, body-parser@1.20.1: +body-parser@1.20.1, body-parser@^1.19.0: version "1.20.1" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== @@ -2002,7 +2000,7 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.21.3, browserslist@^4.21.5, "browserslist@>= 4.21.0": +browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -2035,7 +2033,7 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -bufferutil@^4.0.1, bufferutil@^4.0.3: +bufferutil@^4.0.3: version "4.0.7" resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz" integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== @@ -2088,16 +2086,15 @@ ccount@^2.0.0: resolved "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== +chalk@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" + ansi-styles "^4.1.0" + supports-color "^7.1.0" -chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2114,14 +2111,6 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - character-entities@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz" @@ -2201,16 +2190,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-support@^1.1.2: version "1.1.3" resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz" @@ -2273,7 +2262,7 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0: resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -content-disposition@^0.5.4, content-disposition@0.5.4: +content-disposition@0.5.4, content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -2303,20 +2292,20 @@ cookie-signature@1.0.6: resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -cookie@^0.5.0, cookie@0.5.0: +cookie@0.5.0, cookie@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== +cookie@^0.4.0: + version "0.4.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== copy-to-clipboard@^3.3.1: version "3.3.3" @@ -2389,7 +2378,7 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== -d@^1.0.1, d@1: +d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz" integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== @@ -2407,20 +2396,6 @@ date-fns@^2.29.1: resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz" integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: - version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -2435,6 +2410,20 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" @@ -2500,7 +2489,7 @@ denque@^1.4.1: resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== -depd@~2.0.0, depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -2812,7 +2801,7 @@ eslint-module-utils@^2.7.4: dependencies: debug "^3.2.7" -eslint-plugin-import@*, eslint-plugin-import@^2.22.1: +eslint-plugin-import@^2.22.1: version "2.27.5" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz" integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== @@ -2913,7 +2902,7 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^5.0.0 || ^6.0.0 || ^7.0.0", eslint@^7.23.0, eslint@^7.30.0, eslint@>=5.0.0, eslint@>=7.0.0, eslint@7: +eslint@7, eslint@^7.30.0: version "7.32.0" resolved "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz" integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== @@ -3025,7 +3014,7 @@ express-rate-limit@^6.7.0: resolved "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz" integrity sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA== -"express@^4 || ^5", express@^4.17.1: +express@^4.17.1: version "4.18.2" resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== @@ -3212,6 +3201,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" @@ -3291,6 +3285,18 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob@7.1.7: + version "7.1.7" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.3, glob@^7.2.0: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -3314,31 +3320,12 @@ glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" -glob@7.1.7: - version "7.1.7" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0: - version "13.20.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - -globals@^13.9.0: +globals@^13.6.0, globals@^13.9.0: version "13.20.0" resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== @@ -3523,7 +3510,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3552,16 +3539,16 @@ ip@^2.0.0: resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== -ipaddr.js@^2.0.0, "ipaddr.js@>= 0.1.5": - version "2.0.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +"ipaddr.js@>= 0.1.5", ipaddr.js@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" @@ -3758,6 +3745,11 @@ is-weakset@^2.0.1: call-bind "^1.0.2" get-intrinsic "^1.1.1" +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + isarray@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" @@ -3768,11 +3760,6 @@ isarray@~1.0.0: resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -4025,11 +4012,6 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru_map@^0.3.3: - version "0.3.3" - resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" - integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" @@ -4058,6 +4040,11 @@ lru@^3.1.0: dependencies: inherits "^2.0.1" +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + magic-string@^0.27.0: version "0.27.0" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz" @@ -4657,7 +4644,7 @@ mongoose-legacy-pluralize@1.0.2: resolved "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz" integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ== -mongoose@*, mongoose@^5.13.2, mongoose@5.10.x: +mongoose@^5.13.2: version "5.13.15" resolved "https://registry.npmjs.org/mongoose/-/mongoose-5.13.15.tgz" integrity sha512-cxp1Gbb8yUWkaEbajdhspSaKzAvsIvOtRlYD87GN/P2QEUhpd6bIvebi36T6M0tIVAMauNaK9SPA055N3PwF8Q== @@ -4709,16 +4696,16 @@ mri@^1.1.0: resolved "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz" integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== -ms@^2.1.1, ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -4758,12 +4745,12 @@ negotiator@0.6.3: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -next-tick@^1.1.0, next-tick@1: +next-tick@1, next-tick@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== -"next@^10.0.8 || ^11.0 || ^12.0 || ^13.0", next@>=10.2.0, next@12.2.5: +next@12.2.5: version "12.2.5" resolved "https://registry.npmjs.org/next/-/next-12.2.5.tgz" integrity sha512-tBdjqX5XC/oFs/6gxrZhjmiq90YWizUYU6qOWAfat7zJwrwapJ+BYgX2PmiacunXMaRpeVT4vz5MSPSLgNkrpA== @@ -4977,7 +4964,7 @@ object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" -on-finished@^2.3.0, on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -5003,6 +4990,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +optional-require@1.0.x: + version "1.0.3" + resolved "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz" + integrity sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA== + optional-require@^1.1.8: version "1.1.8" resolved "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz" @@ -5010,11 +5002,6 @@ optional-require@^1.1.8: dependencies: require-at "^1.0.6" -optional-require@1.0.x: - version "1.0.3" - resolved "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz" - integrity sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA== - optionator@^0.9.1: version "0.9.1" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" @@ -5205,7 +5192,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.3.2, prettier@>=1.13.0: +prettier@^2.3.2: version "2.8.4" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== @@ -5277,7 +5264,7 @@ qrcode@^1.5.1: pngjs "^5.0.0" yargs "^15.3.1" -qs@^6.11.0, qs@6.11.0: +qs@6.11.0, qs@^6.11.0: version "6.11.0" resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -5325,7 +5312,7 @@ react-cookie@^4.1.1: hoist-non-react-statics "^3.0.0" universal-cookie "^4.0.0" -"react-dom@^17.0.2 || ^18.0.0-0", "react-dom@>= 16.8.0", react-dom@17.0.2: +react-dom@17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -5343,7 +5330,7 @@ react-dropzone@^14.2.3: file-selector "^0.6.0" prop-types "^15.8.1" -react-is@^16.13.1, react-is@^16.7.0, "react-is@>= 16.8.0": +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -5373,7 +5360,7 @@ react-markdown@^7.1.2: unist-util-visit "^4.0.0" vfile "^5.0.0" -react@*, "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.2 || ^18.0.0-0", "react@>= 16.3.0", "react@>= 16.8 || 18.0.0", "react@>= 16.8.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16, "react@15.x || 16.x || 17.x || 18.x", "react@16.x || 17.x || 18.x", react@17.0.2: +react@17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -5390,20 +5377,17 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== dependencies: core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" -readable-stream@^2.3.5: +readable-stream@^2.2.2, readable-stream@^2.3.5: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -5425,16 +5409,6 @@ readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" - integrity sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" @@ -5466,7 +5440,7 @@ regenerator-transform@^0.15.1: dependencies: "@babel/runtime" "^7.8.4" -regexp-clone@^1.0.0, regexp-clone@1.0.0: +regexp-clone@1.0.0, regexp-clone@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz" integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw== @@ -5588,7 +5562,7 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^1.20.0||^2.0.0||^3.0.0, rollup@^2.68.0||^3.0.0, rollup@2.78.0: +rollup@2.78.0: version "2.78.0" resolved "https://registry.npmjs.org/rollup/-/rollup-2.78.0.tgz" integrity sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg== @@ -5621,25 +5595,15 @@ sade@^1.7.3: dependencies: mri "^1.1.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@5.2.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.0.0: version "1.0.0" @@ -5670,44 +5634,17 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^5.6.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^5.7.0: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^5.7.1: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.2.1: - version "7.3.8" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.5: +semver@^7.2.1, semver@^7.3.5: version "7.3.8" resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== @@ -5719,11 +5656,6 @@ semver@~7.0.0: resolved "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -"semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - send@0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" @@ -6002,18 +5934,6 @@ streamsearch@0.1.2: resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== -string_decoder@^1.1.1, string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -6072,6 +5992,18 @@ string2compact@^1.3.0: addr-to-ip-port "^1.0.1" ipaddr.js "^2.0.0" +string_decoder@^1.1.1, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -6096,7 +6028,7 @@ style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" -styled-components@*, styled-components@^5.3.3, "styled-components@>= 2", "styled-components@>=4.1.0 <6": +styled-components@^5.3.3: version "5.3.6" resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz" integrity sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg== @@ -6262,12 +6194,7 @@ tslib@^1.8.1, tslib@^1.9.3, "tslib@^2.4.1 || ^1.9.3": resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0: - version "2.5.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== - -tslib@^2.4.0: +tslib@^2.1.0, tslib@^2.4.0: version "2.5.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -6440,7 +6367,7 @@ unordered-array-remove@^1.0.2: resolved "https://registry.npmjs.org/unordered-array-remove/-/unordered-array-remove-1.0.2.tgz" integrity sha512-45YsfD6svkgaCBNyvD+dFHm4qFX9g3wRSIVgWVPtm2OCnphvPxzJoe20ATsiNpNJrmzHifnxm+BN5F7gFT/4gw== -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -6465,7 +6392,7 @@ use-sync-external-store@1.2.0: resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== -utf-8-validate@^5.0.2, utf-8-validate@^5.0.5: +utf-8-validate@^5.0.5: version "5.0.10" resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== From e2ba154eda84c67f15d3727829c465b6b08516f3 Mon Sep 17 00:00:00 2001 From: Tom Snelling Date: Wed, 21 Feb 2024 11:32:06 +0000 Subject: [PATCH 10/12] adds setting to disable email/smtp --- api/src/controllers/user.js | 76 ++++++++++++++++++-------------- api/src/index.js | 22 +++++---- api/src/setup/createAdminUser.js | 29 ++++++------ api/src/utils/validateConfig.js | 41 ++++++++++++++--- client/locales/en.json | 9 ++-- client/pages/account.js | 26 +++++++++-- config.example.js | 10 +++++ 7 files changed, 144 insertions(+), 69 deletions(-) diff --git a/api/src/controllers/user.js b/api/src/controllers/user.js index 9491498..7fba1b5 100644 --- a/api/src/controllers/user.js +++ b/api/src/controllers/user.js @@ -110,7 +110,7 @@ export const register = (mail) => async (req, res, next) => { role, invitedBy: invite?.invitingUser, remainingInvites: 0, - emailVerified: false, + emailVerified: process.env.SQ_DISABLE_EMAIL, bonusPoints: 0, totp: { enabled: false, @@ -125,19 +125,21 @@ export const register = (mail) => async (req, res, next) => { const createdUser = await newUser.save(); - const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000; - const emailVerificationToken = jwt.sign( - { - user: req.body.email, - validUntil: emailVerificationValidUntil, - }, - process.env.SQ_JWT_SECRET - ); - await sendVerificationEmail( - mail, - req.body.email, - emailVerificationToken - ); + if (!process.env.SQ_DISABLE_EMAIL) { + const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000; + const emailVerificationToken = jwt.sign( + { + user: req.body.email, + validUntil: emailVerificationValidUntil, + }, + process.env.SQ_JWT_SECRET + ); + await sendVerificationEmail( + mail, + req.body.email, + emailVerificationToken + ); + } if (createdUser) { if (req.body.invite) { @@ -293,14 +295,16 @@ export const generateInvite = (mail) => async (req, res, next) => { const createdInvite = await invite.save(); if (createdInvite) { - await mail.sendMail({ - from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, - to: email, - subject: "Invite", - text: `You have been invited to join ${process.env.SQ_SITE_NAME}. Please follow the link below to register. + if (!process.env.SQ_DISABLE_EMAIL) { + await mail.sendMail({ + from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, + to: email, + subject: "Invite", + text: `You have been invited to join ${process.env.SQ_SITE_NAME}. Please follow the link below to register. ${process.env.SQ_BASE_URL}/register?token=${createdInvite.token}`, - }); + }); + } res.send(createdInvite); } } else { @@ -343,18 +347,20 @@ export const changePassword = (mail) => async (req, res, next) => { { $set: { password: hash } } ); - await mail.sendMail({ - from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, - to: user.email, - subject: "Your password was changed", - text: `Your password was updated successfully at ${new Date().toISOString()} from ${ - req.ip - }. + if (!process.env.SQ_DISABLE_EMAIL) { + await mail.sendMail({ + from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, + to: user.email, + subject: "Your password was changed", + text: `Your password was updated successfully at ${new Date().toISOString()} from ${ + req.ip + }. If you did not perform this action, follow the link below immediately to reset your password. If this was you, no action is required. ${process.env.SQ_BASE_URL}/reset-password/initiate`, - }); + }); + } res.sendStatus(200); } catch (e) { @@ -388,14 +394,16 @@ export const initiatePasswordReset = (mail) => async (req, res, next) => { process.env.SQ_JWT_SECRET ); - await mail.sendMail({ - from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, - to: user.email, - subject: "Password reset", - text: `Please follow the link below to reset your password. + if (!process.env.SQ_DISABLE_EMAIL) { + await mail.sendMail({ + from: `"${process.env.SQ_SITE_NAME}" <${process.env.SQ_MAIL_FROM_ADDRESS}>`, + to: user.email, + subject: "Password reset", + text: `Please follow the link below to reset your password. ${process.env.SQ_BASE_URL}/reset-password/finalise?token=${token}`, - }); + }); + } res.sendStatus(200); } catch (e) { diff --git a/api/src/index.js b/api/src/index.js index ad9fc33..09968e5 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -52,15 +52,19 @@ validateConfig(config).then(() => { }); } - const mail = nodemailer.createTransport({ - host: process.env.SQ_SMTP_HOST, - port: process.env.SQ_SMTP_PORT, - secure: process.env.SQ_SMTP_SECURE, - auth: { - user: process.env.SQ_SMTP_USER, - pass: process.env.SQ_SMTP_PASS, - }, - }); + let mail; + + if (!process.env.SQ_DISABLE_EMAIL) { + mail = nodemailer.createTransport({ + host: process.env.SQ_SMTP_HOST, + port: process.env.SQ_SMTP_PORT, + secure: process.env.SQ_SMTP_SECURE, + auth: { + user: process.env.SQ_SMTP_USER, + pass: process.env.SQ_SMTP_PASS, + }, + }); + } const connectToDb = () => { console.log("[sq] initiating db connection..."); diff --git a/api/src/setup/createAdminUser.js b/api/src/setup/createAdminUser.js index 63b9dc0..5b0dfb6 100644 --- a/api/src/setup/createAdminUser.js +++ b/api/src/setup/createAdminUser.js @@ -18,6 +18,7 @@ const createAdminUser = async (mail) => { password: hash, created, remainingInvites: Number.MAX_SAFE_INTEGER, + emailVerified: process.env.SQ_DISABLE_EMAIL, }); adminUser.uid = crypto .createHash("sha256") @@ -35,19 +36,21 @@ const createAdminUser = async (mail) => { await adminUser.save(); - const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000; - const emailVerificationToken = jwt.sign( - { - user: process.env.SQ_ADMIN_EMAIL, - validUntil: emailVerificationValidUntil, - }, - process.env.SQ_JWT_SECRET - ); - await sendVerificationEmail( - mail, - process.env.SQ_ADMIN_EMAIL, - emailVerificationToken - ); + if (!process.env.SQ_DISABLE_EMAIL) { + const emailVerificationValidUntil = created + 48 * 60 * 60 * 1000; + const emailVerificationToken = jwt.sign( + { + user: process.env.SQ_ADMIN_EMAIL, + validUntil: emailVerificationValidUntil, + }, + process.env.SQ_JWT_SECRET + ); + await sendVerificationEmail( + mail, + process.env.SQ_ADMIN_EMAIL, + emailVerificationToken + ); + } console.log("[sq] created initial admin user"); } diff --git a/api/src/utils/validateConfig.js b/api/src/utils/validateConfig.js index 5ccf91c..38cc5c1 100644 --- a/api/src/utils/validateConfig.js +++ b/api/src/utils/validateConfig.js @@ -64,10 +64,31 @@ const configSchema = yup SQ_BASE_URL: yup.string().matches(httpRegex).required(), SQ_API_URL: yup.string().matches(httpRegex).required(), SQ_MONGO_URL: yup.string().matches(mongoRegex).required(), - SQ_MAIL_FROM_ADDRESS: yup.string().email().required(), - SQ_SMTP_HOST: yup.string().required(), - SQ_SMTP_PORT: yup.number().integer().min(1).max(65535).required(), - SQ_SMTP_SECURE: yup.boolean().required(), + SQ_DISABLE_EMAIL: yup.boolean(), + SQ_MAIL_FROM_ADDRESS: yup + .string() + .email() + .when("SQ_DISABLE_EMAIL", { + is: (val) => val !== true, + then: (schema) => schema.required(), + }), + SQ_SMTP_HOST: yup.string().when("SQ_DISABLE_EMAIL", { + is: (val) => val !== true, + then: (schema) => schema.required(), + }), + SQ_SMTP_PORT: yup + .number() + .integer() + .min(1) + .max(65535) + .when("SQ_DISABLE_EMAIL", { + is: (val) => val !== true, + then: (schema) => schema.required(), + }), + SQ_SMTP_SECURE: yup.boolean().when("SQ_DISABLE_EMAIL", { + is: (val) => val !== true, + then: (schema) => schema.required(), + }), }) .strict() .noUnknown() @@ -77,8 +98,16 @@ const configSchema = yup SQ_JWT_SECRET: yup.string().required(), SQ_SERVER_SECRET: yup.string().required(), SQ_ADMIN_EMAIL: yup.string().email().required(), - SQ_SMTP_USER: yup.string().required(), - SQ_SMTP_PASS: yup.string().required(), + SQ_SMTP_USER: yup.string(), + SQ_SMTP_PASS: yup.string(), + }) + .when("envs.SQ_DISABLE_EMAIL", { + is: (val) => val !== true, + then: (schema) => { + schema.fields.SQ_SMTP_USER = yup.string().required(); + schema.fields.SQ_SMTP_PASS = yup.string().required(); + return schema; + }, }) .strict() .noUnknown() diff --git a/client/locales/en.json b/client/locales/en.json index c839a33..646f16b 100644 --- a/client/locales/en.json +++ b/client/locales/en.json @@ -31,14 +31,16 @@ "accEnable2FA": "Enable 2FA", "accEveryRequestYouFulfill": "for every request you fulfill, or", "accForEveryGBYouUpload": "for every GB you upload", - "accIfYouAreAlsUploaderAcceptTorrent": "if you are als the uploader of the accepted torrent", + "accIfYouAreAlsUploaderAcceptTorrent": "if you are also the uploader of the accepted torrent", "accInviteLinkCopiedClipboard": "Invite link copied to clipboard", "accInvites": "Invites", "accRemaining": "remaining", "accRoleUser": "Role: user", "accRoleAdmin": "Role: admin", "accInviteSentSuccess": "Invite sent successfully", + "accInviteSentSuccessNoEmail": "Invite created successfully", "accInviteText1": "Enter an email address to send an invite. The invited user will need to sign up with the same email address. Once the invite is generated, you can also copy a direct invite link.", + "accInviteText1NoEmail": "Enter an email address to generate an invite. The invited user will need to sign up with the same email address. This tracker has email sending disabled, so you will need to copy and send the invite link yourself.", "accItemsPurchasedSuccess": "Items purchased successfully", "accMyAccount": "My account", "accPassChangedSuccess": "Password changed successfully", @@ -46,6 +48,7 @@ "accPurchaseUpload1GB": "Purchase upload (1 GB)", "accRole": "Role", "accSendInvite": "Send invite", + "accSendInviteNoEmail": "Create invite", "accThisIsAdminAcc": "This is an admin account.", "accValidUntil": "Valid until", "accYouCurrentlyHave": "You currently have", @@ -279,8 +282,8 @@ "userUploaded": "Uploaded", "userMyUploads": "My uploads", "usernameRules": "Can only consist of letters, numbers, and “.”", - "userUnban": "unban", - "userBan": "ban", + "userUnban": "Unban", + "userBan": "Ban", "userUnbanned": "unbanned", "userBanned": "banned", "userAdmin": "Admin", diff --git a/client/pages/account.js b/client/pages/account.js index e725081..8335c0e 100644 --- a/client/pages/account.js +++ b/client/pages/account.js @@ -98,6 +98,7 @@ const Account = ({ token, invites = [], user, userRole }) => { SQ_BP_COST_PER_INVITE, SQ_BP_COST_PER_GB, SQ_ALLOW_REGISTER, + SQ_DISABLE_EMAIL, }, } = getConfig(); @@ -133,7 +134,14 @@ const Account = ({ token, invites = [], user, userRole }) => { return currentInvitesList; }); - addNotification("success", `${getLocaleString("accInviteSentSuccess")}`); + addNotification( + "success", + `${getLocaleString( + SQ_DISABLE_EMAIL + ? "accInviteSentSuccessNoEmail" + : "accInviteSentSuccess" + )}` + ); setRemainingInvites((r) => r - 1); @@ -425,7 +433,9 @@ const Account = ({ token, invites = [], user, userRole }) => { onClick={() => setShowInviteModal(true)} disabled={remainingInvites < 1} > - {getLocaleString("accSendInvite")} + {getLocaleString( + SQ_DISABLE_EMAIL ? "accSendInviteNoEmail" : "accSendInvite" + )} @@ -650,7 +660,11 @@ const Account = ({ token, invites = [], user, userRole }) => { )} {showInviteModal && ( setShowInviteModal(false)}> - {getLocaleString("accInviteText1")} + + {getLocaleString( + SQ_DISABLE_EMAIL ? "accInviteText1NoEmail" : "accInviteText1" + )} +
{ > {getLocaleString("accCancel")} - +
diff --git a/config.example.js b/config.example.js index ce2f57a..8fbd1e1 100644 --- a/config.example.js +++ b/config.example.js @@ -85,18 +85,26 @@ module.exports = { // The URL of your MongoDB server. Under the recommended setup, it should be `mongodb://sq_mongodb/sqtracker`. SQ_MONGO_URL: "mongodb://sq_mongodb/sqtracker", + // Disables sending of any emails and removes the need for an SMTP server. + // Fine for testing, not recommended in production as users will not be able to reset their passwords. + SQ_DISABLE_EMAIL: false, + // The email address that mail will be sent from. + // Not required if SQ_DISABLE_EMAIL=true. SQ_MAIL_FROM_ADDRESS: "mail@sqtracker.dev", // The hostname of your SMTP server. + // Not required if SQ_DISABLE_EMAIL=true. SQ_SMTP_HOST: "smtp.example.com", // The port of your SMTP server. + // Not required if SQ_DISABLE_EMAIL=true. SQ_SMTP_PORT: 587, // Whether to force SMTP TLS: if true the connection will use TLS when connecting to server. // If false (the default) then TLS is used if server supports the STARTTLS extension. // In most cases set this value to true if you are connecting to port 465. For port 587 or 25 keep it false. + // Not required if SQ_DISABLE_EMAIL=true. SQ_SMTP_SECURE: false, }, secrets: { @@ -111,9 +119,11 @@ module.exports = { SQ_ADMIN_EMAIL: "admin@example.com", // The username to authenticate with your SMTP server with. + // Not required if SQ_DISABLE_EMAIL=true. SQ_SMTP_USER: "smtp_username", // The password to authenticate with your SMTP server with. + // Not required if SQ_DISABLE_EMAIL=true. SQ_SMTP_PASS: "smtp_password", }, }; From 5e1e955508d2c4103050e403b2eb3a9942af57b9 Mon Sep 17 00:00:00 2001 From: Tom Snelling Date: Wed, 21 Feb 2024 13:47:48 +0000 Subject: [PATCH 11/12] update dockerfiles, add nginx config --- README.md | 2 ++ api/Dockerfile | 2 +- api/src/utils/validateConfig.js | 8 -------- client/Dockerfile | 2 +- docker-compose.dev.yml | 10 +++++++-- docker-compose.yml | 8 ++++++++ nginx.conf | 36 +++++++++++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 nginx.conf diff --git a/README.md b/README.md index 7d33c8f..a49d947 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ The sqtracker client service provides the modern, responsive web interface that The HTTP proxy allows the client, API, and BitTorrent tracker to all be accessible via a single endpoint. +Traefik is recommended and is configured by default. An Nginx config file is also provided for those that prefer it and the `docker-compose.yml` file contains an Nginx block that can be enabled. + ### Deploying with Docker compose The sqtracker platform is designed to be deployed via Docker. Once a configuration file is created, deploying is as simple as running `docker compose up -d` at the root of the project. diff --git a/api/Dockerfile b/api/Dockerfile index 9cd92df..1cfed1d 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine +FROM node:16 ENV NODE_ENV=production ENV SENTRY_DSN="https://9b9761216607428180ea3b32bd1c8e58@o140996.ingest.sentry.io/4504645996576768" LABEL org.opencontainers.image.source=https://github.com/tdjsnelling/sqtracker diff --git a/api/src/utils/validateConfig.js b/api/src/utils/validateConfig.js index 38cc5c1..554b4c3 100644 --- a/api/src/utils/validateConfig.js +++ b/api/src/utils/validateConfig.js @@ -1,12 +1,4 @@ import * as yup from "yup"; -import en from "@sqtracker/client/locales/en.json"; -import es from "@sqtracker/client/locales/es.json"; -import it from "@sqtracker/client/locales/it.json"; -import ru from "@sqtracker/client/locales/ru.json"; -import de from "@sqtracker/client/locales/de.json"; -import zh from "@sqtracker/client/locales/zh.json"; -import eo from "@sqtracker/client/locales/eo.json"; -import fr from "@sqtracker/client/locales/fr.json"; const httpRegex = /http(s)?:\/\/.*/; const mongoRegex = /mongodb:\/\/.*/; diff --git a/client/Dockerfile b/client/Dockerfile index 0b19958..d76cb29 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine AS builder +FROM node:16 AS builder ARG SENTRY_AUTH_TOKEN ENV NODE_ENV=production ENV SENTRY_ORG=tdjsnelling diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 8874d31..c676757 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -17,11 +17,17 @@ services: volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - ./traefik.yml:/config/traefik.yml +# nginx: +# image: "nginx:latest" +# container_name: "sq_nginx" +# restart: always +# ports: +# - "80:80" +# volumes: +# - ./nginx.conf:/etc/nginx/nginx.conf database: container_name: sq_mongodb image: mongo:6.0 - ports: - - "127.0.0.1:27017:27017" volumes: - ./data:/data/db api: diff --git a/docker-compose.yml b/docker-compose.yml index 245287d..ca4737a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,14 @@ services: - "/var/run/docker.sock:/var/run/docker.sock:ro" - ./letsencrypt:/letsencrypt - ./traefik.yml:/config/traefik.yml +# nginx: +# image: "nginx:latest" +# container_name: "sq_nginx" +# restart: always +# ports: +# - "80:80" +# volumes: +# - ./nginx.conf:/etc/nginx/nginx.conf database: container_name: sq_mongodb image: mongo:6.0 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..b93617a --- /dev/null +++ b/nginx.conf @@ -0,0 +1,36 @@ +events {} + +http { + server { + listen 80; + resolver 127.0.0.11; + + location / { + proxy_pass http://sq_client:3000; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location /api/ { + rewrite /api/(.*) /$1 break; + proxy_pass http://sq_api:3001; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + + location /sq/ { + proxy_pass http://sq_api:3001; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } + } +} From f8bced481d52e1c8c3dd4fccf458382f2d9651d1 Mon Sep 17 00:00:00 2001 From: Tom Snelling Date: Tue, 18 Jun 2024 13:53:02 +0100 Subject: [PATCH 12/12] include development URLs in config.example.js --- config.example.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config.example.js b/config.example.js index 8fbd1e1..cd1e816 100644 --- a/config.example.js +++ b/config.example.js @@ -77,12 +77,15 @@ module.exports = { SQ_SITE_DEFAULT_LOCALE: "en", // The URL of your tracker site. + // For local development, this should be `http://127.0.0.1:3000`. SQ_BASE_URL: "https://sqtracker.dev", // The URL of your API. Under the recommended setup, it should be `${SQ_BASE_URL}/api`. + // For local development, this should be `http://127.0.0.1:3001`. SQ_API_URL: "https://sqtracker.dev/api", // The URL of your MongoDB server. Under the recommended setup, it should be `mongodb://sq_mongodb/sqtracker`. + // For local development, this should be `mongodb://127.0.0.1/sqtracker`. SQ_MONGO_URL: "mongodb://sq_mongodb/sqtracker", // Disables sending of any emails and removes the need for an SMTP server.