From 1113b2f0725921ba79d5b7f45e6fb9da97692d42 Mon Sep 17 00:00:00 2001 From: Shaun Warman Date: Mon, 30 Sep 2024 18:21:36 -0500 Subject: [PATCH 1/2] feat: add alias and domain counts to respective admin pages --- app/models/domains.js | 1 + app/models/users.js | 5 + app/views/admin/domains/_table.pug | 3 + app/views/admin/users/_table.pug | 3 + config/index.js | 1 + jobs/index.js | 6 ++ jobs/update-alias-and-domain-counts.js | 139 +++++++++++++++++++++++++ locales/ar.json | 3 +- locales/cs.json | 3 +- locales/da.json | 3 +- locales/de.json | 3 +- locales/en.json | 14 ++- locales/es.json | 3 +- locales/fi.json | 3 +- locales/fr.json | 3 +- locales/he.json | 3 +- locales/hu.json | 3 +- locales/id.json | 3 +- locales/it.json | 3 +- locales/ja.json | 3 +- locales/ko.json | 3 +- locales/nl.json | 3 +- locales/no.json | 3 +- locales/pl.json | 3 +- locales/pt.json | 3 +- locales/ru.json | 3 +- locales/sv.json | 3 +- locales/th.json | 3 +- locales/tr.json | 3 +- locales/uk.json | 3 +- locales/vi.json | 3 +- locales/zh.json | 3 +- 32 files changed, 219 insertions(+), 25 deletions(-) create mode 100644 jobs/update-alias-and-domain-counts.js diff --git a/app/models/domains.js b/app/models/domains.js index 1409e60f58..5897e9563f 100644 --- a/app/models/domains.js +++ b/app/models/domains.js @@ -367,6 +367,7 @@ const Domains = new mongoose.Schema({ default: '25', validator: (value) => isPort(value) }, + alias_count: { type: Number }, members: [Member], invites: [Invite], name: { diff --git a/app/models/users.js b/app/models/users.js index 9bc07bf930..92b8dafd7a 100644 --- a/app/models/users.js +++ b/app/models/users.js @@ -272,6 +272,11 @@ object[config.userFields.defaultDomain] = { ref: 'Domains' }; +object[config.userFields.domainCount] = { + type: Number, + index: true +}; + // Rate limit whitelisting // TODO: change to allowlist object[config.userFields.isRateLimitWhitelisted] = { diff --git a/app/views/admin/domains/_table.pug b/app/views/admin/domains/_table.pug index d91d79ab47..ad428330de 100644 --- a/app/views/admin/domains/_table.pug +++ b/app/views/admin/domains/_table.pug @@ -10,6 +10,8 @@ include ../../_pagination th(scope="col") +sortHeader('is_global', 'Global', '#table-domains') th.align-middle(scope="col")= t("Members") + th(scope="col") + +sortHeader('aliases', 'Aliases', '#table-domains') th(scope="col") +sortHeader('plan', 'Plan', '#table-domains') th(scope="col") @@ -52,6 +54,7 @@ include ../../_pagination = "(" code.text-muted= member.user.id = ")" + td.align-middle.text-center= domain.alias_count td.align-middle.text-center = t(titleize(humanize(domain.plan))) td.align-middle.text-center diff --git a/app/views/admin/users/_table.pug b/app/views/admin/users/_table.pug index 6fa9b942de..49bc0ce1d3 100644 --- a/app/views/admin/users/_table.pug +++ b/app/views/admin/users/_table.pug @@ -11,6 +11,8 @@ include ../../_pagination +sortHeader(config.passport.fields.familyName, 'Last Name', '#table-users') th(scope="col") +sortHeader('email', null, '#table-users') + th(scope="col") + +sortHeader('domains', null, '#table-users') th(scope="col") +sortHeader('plan', null, '#table-users') th(scope="col") @@ -43,6 +45,7 @@ include ../../_pagination = "(" code.text-muted= user.id = ")" + td.align-middle= user.domain_count td.align-middle= titleize(humanize(user.plan)) td.align-middle= titleize(humanize(user.group)) td.align-middle.dayjs( diff --git a/config/index.js b/config/index.js index 3d83d04b9b..0fd67d73fd 100644 --- a/config/index.js +++ b/config/index.js @@ -562,6 +562,7 @@ const config = { paypalPayerID: 'paypal_payer_id', paypalSubscriptionID: 'paypal_subscription_id', defaultDomain: 'default_domain', + domainCount: 'domain_count', companyName: 'company_name', addressLine1: 'address_line1', addressLine2: 'address_line2', diff --git a/jobs/index.js b/jobs/index.js index 7971ce425a..3c3d71c4a2 100644 --- a/jobs/index.js +++ b/jobs/index.js @@ -219,6 +219,12 @@ const jobs = [ name: 'past-due-relief', interval: '1h', timeout: 0 + }, + // aggregate alias_count and domain_count each day + { + name: 'update-alias-and-domain-counts', + interval: '1d', + timeout: 0 } ]; diff --git a/jobs/update-alias-and-domain-counts.js b/jobs/update-alias-and-domain-counts.js new file mode 100644 index 0000000000..5d1894b1e1 --- /dev/null +++ b/jobs/update-alias-and-domain-counts.js @@ -0,0 +1,139 @@ +/** + * Copyright (c) Forward Email LLC + * SPDX-License-Identifier: BUSL-1.1 + */ + +// eslint-disable-next-line import/no-unassigned-import +require('#config/env'); + +const process = require('node:process'); +const { parentPort } = require('node:worker_threads'); + +// eslint-disable-next-line import/no-unassigned-import +require('#config/mongoose'); + +const Graceful = require('@ladjs/graceful'); +const mongoose = require('mongoose'); + +const Domains = require('#models/domains'); +const Users = require('#models/users'); +const logger = require('#helpers/logger'); +const setupMongoose = require('#helpers/setup-mongoose'); +const monitorServer = require('#helpers/monitor-server'); + +monitorServer(); + +const graceful = new Graceful({ + mongooses: [mongoose], + logger +}); + +graceful.listen(); + +(async () => { + await setupMongoose(logger); + + try { + // aggregating users and calculating domain count + const usersWithDomainCount = await Users.aggregate([ + { + $match: {} + }, + { + $lookup: { + from: 'domains', + let: { userId: '$_id' }, + pipeline: [ + { + $match: { + $expr: { + $in: ['$$userId', '$members.user'] + } + } + }, + { + $addFields: { + adminMembers: { + $filter: { + input: '$members', + as: 'member', + cond: { + $and: [ + { $eq: ['$$member.user', '$$userId'] }, + { $eq: ['$$member.group', 'admin'] } + ] + } + } + } + } + }, + { + $match: { + adminMembers: { $ne: [] } + } + } + ], + as: 'domainsWithAdminMember' + } + }, + { + $addFields: { + domain_count: { $size: '$domainsWithAdminMember' } + } + } + ]); + + // bulk update users with domain_count + if (usersWithDomainCount.length > 0) { + const bulkUserOperations = usersWithDomainCount.map((user) => ({ + updateOne: { + filter: { _id: user._id }, + update: { $set: { domain_count: user.domain_count } } + } + })); + + await Users.bulkWrite(bulkUserOperations); + } + + // aggregating domains and calculating alias count + const domainsWithAliases = await Domains.aggregate([ + { + $lookup: { + from: 'aliases', + localField: '_id', + foreignField: 'domain', + as: 'matchingAliases' + } + }, + { + $addFields: { + alias_count: { $size: '$matchingAliases' } + } + }, + { + $project: { + _id: 1, + domain_name: 1, + alias_count: 1 + } + } + ]); + + // bulk update domains with alias_count + if (domainsWithAliases.length > 0) { + const bulkDomainOperations = domainsWithAliases.map((domain) => ({ + updateOne: { + filter: { _id: domain._id }, + update: { $set: { alias_count: domain.alias_count } } + } + })); + + await Domains.bulkWrite(bulkDomainOperations); + } + } catch (err) { + await logger.error(err); + } + + if (parentPort) parentPort.postMessage('done'); + else process.exit(0); +})(); diff --git a/locales/ar.json b/locales/ar.json index ce45dcf94d..391a29242d 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -10334,5 +10334,6 @@ "bytes": "بايتات", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "الحد الأقصى لحصة التخزين لهذا الاسم المستعار. اتركه فارغًا لإعادة تعيين الحد الأقصى لحصة المجال الحالية أو أدخل قيمة مثل \"1 جيجابايت\" التي سيتم تحليلها بواسطة", ". This value can only be adjusted by domain admins.": "لا يمكن تعديل هذه القيمة إلا بواسطة مسؤولي المجال.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "تم حذف التقويم المسمى %s بنجاح مع %d حدث (مرفق نسخة احتياطية في حالة وقوع هذا الحادث)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "تم حذف التقويم المسمى %s بنجاح مع %d حدث (مرفق نسخة احتياطية في حالة وقوع هذا الحادث)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/cs.json b/locales/cs.json index 7fdd77833a..91260a5202 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -10334,5 +10334,6 @@ "bytes": "bajtů", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maximální kvóta úložiště pro tento alias. Ponechte prázdné, chcete-li obnovit aktuální maximální kvótu domény, nebo zadejte hodnotu, například „1 GB“, podle které bude analyzován", ". This value can only be adjusted by domain admins.": ". Tuto hodnotu mohou upravit pouze správci domény.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendář s názvem %s byl úspěšně smazán s %d událostmi (v příloze je záloha pro případ, že by to byla nehoda)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendář s názvem %s byl úspěšně smazán s %d událostmi (v příloze je záloha pro případ, že by to byla nehoda)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/da.json b/locales/da.json index 546288b2f7..35a57ab90f 100644 --- a/locales/da.json +++ b/locales/da.json @@ -7322,5 +7322,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maksimal lagringskvote for dette alias. Lad stå tomt for at nulstille til domænets nuværende maksimale kvote, eller indtast en værdi såsom \"1 GB\", der vil blive parset af", ". This value can only be adjusted by domain admins.": ". Denne værdi kan kun justeres af domæneadministratorer.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenderen med navnet %s blev slettet med %d begivenheder (vedhæftet er en sikkerhedskopi, hvis dette var et uheld)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenderen med navnet %s blev slettet med %d begivenheder (vedhæftet er en sikkerhedskopi, hvis dette var et uheld)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/de.json b/locales/de.json index 75110cfa56..44358d37d0 100644 --- a/locales/de.json +++ b/locales/de.json @@ -9373,5 +9373,6 @@ "bytes": "Bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maximales Speicherkontingent für diesen Alias. Lassen Sie das Feld leer, um das aktuelle maximale Kontingent der Domäne zurückzusetzen, oder geben Sie einen Wert wie „1 GB“ ein, der von", ". This value can only be adjusted by domain admins.": ". Dieser Wert kann nur von Domänenadministratoren angepasst werden.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Der Kalender mit dem Namen %s wurde mit %d Ereignissen erfolgreich gelöscht (anbei eine Sicherungskopie für den Fall, dass dies ein Versehen war)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Der Kalender mit dem Namen %s wurde mit %d Ereignissen erfolgreich gelöscht (anbei eine Sicherungskopie für den Fall, dass dies ein Versehen war)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index 7138686e0c..55b8b15399 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6714,5 +6714,17 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by", ". This value can only be adjusted by domain admins.": ". This value can only be adjusted by domain admins.", - "Private Business Email Service": "Private Business Email Service" + "Private Business Email Service": "Private Business Email Service", + "You do not belong to the administrative user group.": "You do not belong to the administrative user group.", + "Admin - Users": "Admin - Users", + "Search for users": "Search for users", + "Search by first name, last name, or email": "Search by first name, last name, or email", + "First Name": "First Name", + "Last Name": "Last Name", + "email": "email", + "domains": "domains", + "plan": "plan", + "group": "group", + "Last Login": "Last Login", + "Log in as user": "Log in as user" } \ No newline at end of file diff --git a/locales/es.json b/locales/es.json index 86aa88b659..2915709b95 100644 --- a/locales/es.json +++ b/locales/es.json @@ -10332,5 +10332,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Cuota máxima de almacenamiento para este alias. Déjelo en blanco para restablecer la cuota máxima actual del dominio o ingrese un valor como \"1 GB\" que será analizado por", ". This value can only be adjusted by domain admins.": "Este valor sólo puede ser ajustado por los administradores del dominio.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "El calendario llamado %s se eliminó correctamente con %d eventos (se adjunta una copia de seguridad en caso de que esto haya sido un accidente)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "El calendario llamado %s se eliminó correctamente con %d eventos (se adjunta una copia de seguridad en caso de que esto haya sido un accidente)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/fi.json b/locales/fi.json index a9009fc72d..31a1d52036 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -10181,5 +10181,6 @@ "bytes": "tavua", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Tämän aliaksen tallennustilan enimmäiskiintiö. Jätä tyhjäksi palauttaaksesi verkkotunnuksen nykyisen enimmäiskiintiön tai anna arvo, kuten \"1 Gt\", jonka jäsentää", ". This value can only be adjusted by domain admins.": ". Vain verkkotunnuksen järjestelmänvalvojat voivat säätää tätä arvoa.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenteri nimeltä %s poistettiin onnistuneesti %d tapahtumalla (liitteenä varmuuskopio siltä varalta, että kyseessä oli onnettomuus)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenteri nimeltä %s poistettiin onnistuneesti %d tapahtumalla (liitteenä varmuuskopio siltä varalta, että kyseessä oli onnettomuus)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/fr.json b/locales/fr.json index 25f5e7d1cc..8ac4e2e25a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -7859,5 +7859,6 @@ "bytes": "octets", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Quota de stockage maximal pour cet alias. Laissez ce champ vide pour réinitialiser le quota maximal actuel du domaine ou saisissez une valeur telle que « 1 Go » qui sera analysée par", ". This value can only be adjusted by domain admins.": "Cette valeur ne peut être ajustée que par les administrateurs de domaine.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Le calendrier nommé %s a été supprimé avec succès avec %d événements (une sauvegarde est jointe au cas où il s'agirait d'un accident)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Le calendrier nommé %s a été supprimé avec succès avec %d événements (une sauvegarde est jointe au cas où il s'agirait d'un accident)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/he.json b/locales/he.json index 40d5a18f2f..11d662c046 100644 --- a/locales/he.json +++ b/locales/he.json @@ -8355,5 +8355,6 @@ "bytes": "בתים", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "מכסת אחסון מקסימלית עבור הכינוי הזה. השאר ריק כדי לאפס למכסה המקסימלית הנוכחית של הדומיין או הזן ערך כגון \"1 GB\" שינתח על ידי", ". This value can only be adjusted by domain admins.": ". ערך זה יכול להיות מותאם רק על ידי מנהלי דומיין.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "לוח שנה בשם %s נמחק בהצלחה עם %d אירועים (מצורף גיבוי למקרה שזו הייתה תאונה)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "לוח שנה בשם %s נמחק בהצלחה עם %d אירועים (מצורף גיבוי למקרה שזו הייתה תאונה)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/hu.json b/locales/hu.json index 92686d92bc..4828a3fe62 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -10334,5 +10334,6 @@ "bytes": "bájtok", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maximális tárhelykvóta ehhez az aliashoz. Hagyja üresen a domain jelenlegi maximális kvótájának visszaállításához, vagy adjon meg egy értéket, például \"1 GB\", amelyet a rendszer elemezni fog", ". This value can only be adjusted by domain admins.": ". Ezt az értéket csak a domain rendszergazdái módosíthatják.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "A %s nevű naptár sikeresen törölve %d eseménnyel (mellékelve egy biztonsági másolat arra az esetre, ha baleset történt volna)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "A %s nevű naptár sikeresen törölve %d eseménnyel (mellékelve egy biztonsági másolat arra az esetre, ha baleset történt volna)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/id.json b/locales/id.json index bd6275bf08..2d150950e5 100644 --- a/locales/id.json +++ b/locales/id.json @@ -10334,5 +10334,6 @@ "bytes": "byte dalam bahasa inggris", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Kuota penyimpanan maksimum untuk alias ini. Kosongkan untuk mengatur ulang ke kuota maksimum domain saat ini atau masukkan nilai seperti \"1 GB\" yang akan diurai oleh", ". This value can only be adjusted by domain admins.": "Nilai ini hanya dapat disesuaikan oleh admin domain.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalender dengan nama %s berhasil dihapus dengan %d acara (terlampir adalah cadangan jika ini merupakan suatu kecelakaan)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalender dengan nama %s berhasil dihapus dengan %d acara (terlampir adalah cadangan jika ini merupakan suatu kecelakaan)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/it.json b/locales/it.json index 47389d9f63..416301ff5a 100644 --- a/locales/it.json +++ b/locales/it.json @@ -10334,5 +10334,6 @@ "bytes": "byte", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Quota massima di archiviazione per questo alias. Lascia vuoto per reimpostare la quota massima corrente del dominio o inserisci un valore come \"1 GB\" che verrà analizzato da", ". This value can only be adjusted by domain admins.": "Questo valore può essere modificato solo dagli amministratori di dominio.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Il calendario denominato %s è stato eliminato con successo con %d eventi (in allegato un backup nel caso in cui si sia verificato un incidente)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Il calendario denominato %s è stato eliminato con successo con %d eventi (in allegato un backup nel caso in cui si sia verificato un incidente)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/ja.json b/locales/ja.json index 440c2e1cf0..0104dc7bcc 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -10334,5 +10334,6 @@ "bytes": "バイト", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "このエイリアスのストレージの最大クォータ。空白のままにしておくとドメインの現在の最大クォータにリセットされます。または、「1 GB」などの値を入力すると、", ". This value can only be adjusted by domain admins.": "この値はドメイン管理者のみが調整できます。", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%sという名前のカレンダーが%d件のイベントとともに正常に削除されました (誤って削除された場合に備えてバックアップを添付しています)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%sという名前のカレンダーが%d件のイベントとともに正常に削除されました (誤って削除された場合に備えてバックアップを添付しています)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/ko.json b/locales/ko.json index dcfc4c29e1..b9d7d5d24d 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -10334,5 +10334,6 @@ "bytes": "바이트", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "이 별칭에 대한 최대 저장 할당량입니다. 도메인의 현재 최대 할당량으로 재설정하려면 비워두거나 \"1GB\"와 같이 구문 분석될 값을 입력하세요.", ". This value can only be adjusted by domain admins.": "이 값은 도메인 관리자만 조정할 수 있습니다.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%s 라는 이름의 달력이 %d 이벤트와 함께 성공적으로 삭제되었습니다(실수로 그런 일이 발생한 경우를 대비해 백업을 첨부했습니다)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%s 라는 이름의 달력이 %d 이벤트와 함께 성공적으로 삭제되었습니다(실수로 그런 일이 발생한 경우를 대비해 백업을 첨부했습니다)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/nl.json b/locales/nl.json index d99210eec6..cb8e287d18 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -10334,5 +10334,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Opslagmaximumquotum voor deze alias. Laat leeg om te resetten naar het huidige maximumquotum van het domein of voer een waarde in zoals \"1 GB\" die wordt geparseerd door", ". This value can only be adjusted by domain admins.": "Deze waarde kan alleen door domeinbeheerders worden aangepast.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Agenda met de naam %s is succesvol verwijderd met %d gebeurtenissen (bijgevoegd is een backup voor het geval dit per ongeluk was)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Agenda met de naam %s is succesvol verwijderd met %d gebeurtenissen (bijgevoegd is een backup voor het geval dit per ongeluk was)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/no.json b/locales/no.json index f9bc2600ae..f5e1e48f89 100644 --- a/locales/no.json +++ b/locales/no.json @@ -10339,5 +10339,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maksimal lagringskvote for dette aliaset. La stå tomt for å tilbakestille til domenets gjeldende maksimale kvote eller angi en verdi som \"1 GB\" som vil bli analysert av", ". This value can only be adjusted by domain admins.": ". Denne verdien kan bare justeres av domeneadministratorer.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenderen med navnet %s ble slettet med %d hendelser (vedlagt er en sikkerhetskopi i tilfelle dette var en ulykke)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalenderen med navnet %s ble slettet med %d hendelser (vedlagt er en sikkerhetskopi i tilfelle dette var en ulykke)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/pl.json b/locales/pl.json index 386044215b..bfc1a361e0 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -10334,5 +10334,6 @@ "bytes": "bajty", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maksymalny limit pamięci dla tego aliasu. Pozostaw puste, aby zresetować do bieżącego maksymalnego limitu domeny lub wprowadź wartość, taką jak „1 GB”, która zostanie przeanalizowana przez", ". This value can only be adjusted by domain admins.": "Wartość tę mogą zmienić tylko administratorzy domeny.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendarz o nazwie %s został pomyślnie usunięty z %d wydarzeniami (w załączeniu kopia zapasowa na wypadek gdyby to był przypadek)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendarz o nazwie %s został pomyślnie usunięty z %d wydarzeniami (w załączeniu kopia zapasowa na wypadek gdyby to był przypadek)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/pt.json b/locales/pt.json index 6778a15928..6e68e62f3e 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -10334,5 +10334,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Cota máxima de armazenamento para este alias. Deixe em branco para redefinir a cota máxima atual do domínio ou insira um valor como \"1 GB\" que será analisado por", ". This value can only be adjusted by domain admins.": ". Este valor só pode ser ajustado por administradores de domínio.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "O calendário chamado %s foi excluído com sucesso com %d eventos (em anexo está um backup caso isso tenha sido um acidente)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "O calendário chamado %s foi excluído com sucesso com %d eventos (em anexo está um backup caso isso tenha sido um acidente)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/ru.json b/locales/ru.json index 39f23d3ead..4f0938598c 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -10334,5 +10334,6 @@ "bytes": "байты", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Максимальная квота хранилища для этого псевдонима. Оставьте пустым, чтобы сбросить текущую максимальную квоту домена или введите значение, например \"1 ГБ\", которое будет проанализировано", ". This value can only be adjusted by domain admins.": ". Это значение может быть изменено только администраторами домена.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Календарь с именем %s был успешно удален с %d событиями (прилагается резервная копия на случай, если это произошло случайно)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Календарь с именем %s был успешно удален с %d событиями (прилагается резервная копия на случай, если это произошло случайно)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/sv.json b/locales/sv.json index f01bb8f339..e4bf2ec8be 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -10334,5 +10334,6 @@ "bytes": "bytes", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Maximal lagringskvot för detta alias. Lämna tomt för att återställa till domänens nuvarande maximala kvot eller ange ett värde som \"1 GB\" som kommer att tolkas av", ". This value can only be adjusted by domain admins.": ". Detta värde kan endast justeras av domänadministratörer.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendern med namnet %s har raderats med %d händelser (bifogad är en säkerhetskopia om detta var en olycka)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Kalendern med namnet %s har raderats med %d händelser (bifogad är en säkerhetskopia om detta var en olycka)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/th.json b/locales/th.json index bced2a270a..5ea1e3c83b 100644 --- a/locales/th.json +++ b/locales/th.json @@ -10334,5 +10334,6 @@ "bytes": "ไบต์", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "โควตาพื้นที่เก็บข้อมูลสูงสุดสำหรับนามแฝงนี้ ปล่อยว่างไว้เพื่อรีเซ็ตเป็นโควตาสูงสุดปัจจุบันของโดเมนหรือป้อนค่าเช่น \"1 GB\" ที่จะถูกวิเคราะห์โดย", ". This value can only be adjusted by domain admins.": "ค่านี้สามารถปรับได้โดยผู้ดูแลโดเมนเท่านั้น", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "ปฏิทินชื่อ %s ถูกลบสำเร็จแล้วโดยมี %d กิจกรรม (แนบเป็นข้อมูลสำรองในกรณีที่เป็นอุบัติเหตุ)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "ปฏิทินชื่อ %s ถูกลบสำเร็จแล้วโดยมี %d กิจกรรม (แนบเป็นข้อมูลสำรองในกรณีที่เป็นอุบัติเหตุ)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/tr.json b/locales/tr.json index 4e5ba59570..59c6d20e96 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -10334,5 +10334,6 @@ "bytes": "baytlar", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Bu takma ad için depolama maksimum kotası. Alan geçerli maksimum kotasına sıfırlamak için boş bırakın veya ayrıştırılacak \"1 GB\" gibi bir değer girin", ". This value can only be adjusted by domain admins.": "Bu değer yalnızca alan adı yöneticileri tarafından ayarlanabilir.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%s adlı takvim %d etkinlikle başarıyla silindi (bu bir kaza olması durumunda yedek ekte)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "%s adlı takvim %d etkinlikle başarıyla silindi (bu bir kaza olması durumunda yedek ekte)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/uk.json b/locales/uk.json index 17d3ac4353..27ad8b7f7f 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -10334,5 +10334,6 @@ "bytes": "байтів", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Максимальна квота пам’яті для цього псевдоніма. Залиште поле порожнім, щоб скинути поточну максимальну квоту домену, або введіть значення, як-от \"1 ГБ\", яке буде проаналізовано", ". This value can only be adjusted by domain admins.": ". Це значення можуть змінити лише адміністратори домену.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Календар під назвою %s було успішно видалено з %d подіями (додається резервна копія на випадок, якщо це сталося випадково)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Календар під назвою %s було успішно видалено з %d подіями (додається резервна копія на випадок, якщо це сталося випадково)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/vi.json b/locales/vi.json index 617b98e164..87f160b1a2 100644 --- a/locales/vi.json +++ b/locales/vi.json @@ -7864,5 +7864,6 @@ "bytes": "byte", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "Hạn ngạch lưu trữ tối đa cho bí danh này. Để trống để đặt lại hạn ngạch tối đa hiện tại của miền hoặc nhập giá trị như \"1 GB\" sẽ được phân tích cú pháp bởi", ". This value can only be adjusted by domain admins.": ". Giá trị này chỉ có thể được điều chỉnh bởi người quản trị miền.", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Lịch có tên %s đã được xóa thành công với %d sự kiện (đính kèm bản sao lưu trong trường hợp đây là sự cố)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "Lịch có tên %s đã được xóa thành công với %d sự kiện (đính kèm bản sao lưu trong trường hợp đây là sự cố)", + "domains": "domains" } \ No newline at end of file diff --git a/locales/zh.json b/locales/zh.json index 9d6bc605eb..0ffe46f093 100644 --- a/locales/zh.json +++ b/locales/zh.json @@ -10027,5 +10027,6 @@ "bytes": "字节", "Storage maximum quota for this alias. Leave blank to reset to domain current maximum quota or enter a value such as \"1 GB\" that will be parsed by": "此别名的最大存储配额。留空可重置为域当前的最大配额,或输入一个值(例如“1 GB”),该值将由", ". This value can only be adjusted by domain admins.": "。此值只能由域管理员调整。", - "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "名为%s的日历已成功删除,其中包含%d事件(附件为备份,以防发生意外)" + "Calendar named %s was successfully deleted with %d events (attached is a backup in case this was an accident)": "名为%s的日历已成功删除,其中包含%d事件(附件为备份,以防发生意外)", + "domains": "domains" } \ No newline at end of file From 88022198c6ccc52084d45ec8cf44f45279e19664 Mon Sep 17 00:00:00 2001 From: Shaun Warman Date: Thu, 3 Oct 2024 07:43:17 -0500 Subject: [PATCH 2/2] fix: pr feedback --- app/models/domains.js | 6 +++++- app/models/users.js | 1 + app/views/admin/domains/_table.pug | 2 +- app/views/admin/users/_table.pug | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/domains.js b/app/models/domains.js index 5897e9563f..978e354b64 100644 --- a/app/models/domains.js +++ b/app/models/domains.js @@ -367,7 +367,11 @@ const Domains = new mongoose.Schema({ default: '25', validator: (value) => isPort(value) }, - alias_count: { type: Number }, + alias_count: { + type: Number, + min: 0, + index: true + }, members: [Member], invites: [Invite], name: { diff --git a/app/models/users.js b/app/models/users.js index 92b8dafd7a..e2a2d998b4 100644 --- a/app/models/users.js +++ b/app/models/users.js @@ -274,6 +274,7 @@ object[config.userFields.defaultDomain] = { object[config.userFields.domainCount] = { type: Number, + min: 0, index: true }; diff --git a/app/views/admin/domains/_table.pug b/app/views/admin/domains/_table.pug index ad428330de..2fdb54ba42 100644 --- a/app/views/admin/domains/_table.pug +++ b/app/views/admin/domains/_table.pug @@ -11,7 +11,7 @@ include ../../_pagination +sortHeader('is_global', 'Global', '#table-domains') th.align-middle(scope="col")= t("Members") th(scope="col") - +sortHeader('aliases', 'Aliases', '#table-domains') + +sortHeader('alias_count', 'Aliases', '#table-domains') th(scope="col") +sortHeader('plan', 'Plan', '#table-domains') th(scope="col") diff --git a/app/views/admin/users/_table.pug b/app/views/admin/users/_table.pug index 49bc0ce1d3..9ba48dd786 100644 --- a/app/views/admin/users/_table.pug +++ b/app/views/admin/users/_table.pug @@ -12,7 +12,7 @@ include ../../_pagination th(scope="col") +sortHeader('email', null, '#table-users') th(scope="col") - +sortHeader('domains', null, '#table-users') + +sortHeader('domain_count', 'Domains', '#table-users') th(scope="col") +sortHeader('plan', null, '#table-users') th(scope="col")