diff --git a/backend/internal/access-list.js b/backend/internal/access-list.js index 88bf9df523..e9f3300f25 100644 --- a/backend/internal/access-list.js +++ b/backend/internal/access-list.js @@ -55,6 +55,7 @@ const internalAccessList = { await accessListClientModel.query().insert({ access_list_id: row.id, address: client.address, + note: client.note || null, directive: client.directive, }); } @@ -159,6 +160,7 @@ const internalAccessList = { await accessListClientModel.query().insert({ access_list_id: data.id, address: client.address, + note: client.note || null, directive: client.directive, }); } diff --git a/backend/lib/utils.js b/backend/lib/utils.js index af7ad3c952..50e2e86d30 100644 --- a/backend/lib/utils.js +++ b/backend/lib/utils.js @@ -92,13 +92,20 @@ const getRenderEngine = () => { }); /** - * nginxAccessRule expects the object given to have 2 properties: + * nginxAccessRule expects the object given to have 3 properties: * * directive string * address string + * note string */ renderEngine.registerFilter("nginxAccessRule", (v) => { if (typeof v.directive !== "undefined" && typeof v.address !== "undefined" && v.directive && v.address) { + const note = typeof v.note === "string" ? v.note.trim() : ""; + if (note) { + // prevent newline / comment-breaking characters + const safe = note.replace(/[\r\n#;]/g, " ").trim(); + return `${v.directive} ${v.address}; # ${safe}`; + } return `${v.directive} ${v.address};`; } return ""; diff --git a/backend/migrations/20260220111310_access_list_client_note.js b/backend/migrations/20260220111310_access_list_client_note.js new file mode 100644 index 0000000000..12b93e3213 --- /dev/null +++ b/backend/migrations/20260220111310_access_list_client_note.js @@ -0,0 +1,36 @@ +import { migrate as logger } from "../logger.js"; + +const migrateName = "access_list_client_note"; + +/** + * Migrate + * + * @see http://knexjs.org/#Schema + * + * @param {Object} knex + * @returns {Promise} + */ +const up = (knex) => { + logger.info(`[${migrateName}] Migrating Up...`); + + return knex.schema + .table("access_list_client", (access_list) => { + access_list.string("note", 200).nullable(); + }) + .then(() => { + logger.info(`[${migrateName}] access_list_client Table altered`); + }); +}; + +/** + * Undo Migrate + * + * @param {Object} knex + * @returns {Promise} + */ +const down = (_knex) => { + logger.warn(`[${migrateName}] You can't migrate down this one.`); + return Promise.resolve(true); +}; + +export { up, down }; diff --git a/backend/schema/common.json b/backend/schema/common.json index 00b06e005f..bc546a48d9 100644 --- a/backend/schema/common.json +++ b/backend/schema/common.json @@ -156,6 +156,12 @@ ], "example": "192.168.0.11" }, + "note": { + "description": "Note", + "type": "string", + "example": "Home", + "maxLength": 200 + }, "access_items": { "type": "array", "items": { @@ -191,19 +197,24 @@ "address": { "$ref": "#/properties/address" }, + "note": { + "$ref": "#/properties/note" + }, "directive": { "$ref": "#/properties/directive" } }, "example": { "directive": "allow", - "address": "192.168.0.0/24" + "address": "192.168.0.0/24", + "note": "Home" } }, "example": [ { "directive": "allow", - "address": "192.168.0.0/24" + "address": "192.168.0.0/24", + "note": "Home" } ] }, diff --git a/backend/schema/paths/nginx/access-lists/listID/put.json b/backend/schema/paths/nginx/access-lists/listID/put.json index 61e8044013..c06688b0cf 100644 --- a/backend/schema/paths/nginx/access-lists/listID/put.json +++ b/backend/schema/paths/nginx/access-lists/listID/put.json @@ -60,7 +60,8 @@ "clients": [ { "directive": "allow", - "address": "192.168.0.0/24" + "address": "192.168.0.0/24", + "note": "note" } ] } diff --git a/backend/schema/paths/nginx/access-lists/post.json b/backend/schema/paths/nginx/access-lists/post.json index 38b7003a1e..5dd8f71414 100644 --- a/backend/schema/paths/nginx/access-lists/post.json +++ b/backend/schema/paths/nginx/access-lists/post.json @@ -51,7 +51,8 @@ "clients": [ { "directive": "allow", - "address": "192.168.0.0/24" + "address": "192.168.0.0/24", + "note": "note" } ] } @@ -118,6 +119,7 @@ "modified_on": "2024-10-08T22:15:40.000Z", "access_list_id": 1, "address": "127.0.0.1", + "note": "note", "directive": "allow", "meta": {} } diff --git a/frontend/src/api/backend/models.ts b/frontend/src/api/backend/models.ts index 2ae0b08348..06894875f9 100644 --- a/frontend/src/api/backend/models.ts +++ b/frontend/src/api/backend/models.ts @@ -77,6 +77,7 @@ export type AccessListClient = { modifiedOn?: string; accessListId?: number; address: string; + note?: string; directive: "allow" | "deny"; meta?: Record; }; diff --git a/frontend/src/components/Form/AccessClientFields.tsx b/frontend/src/components/Form/AccessClientFields.tsx index 9dda8c3de0..f067f76988 100644 --- a/frontend/src/components/Form/AccessClientFields.tsx +++ b/frontend/src/components/Form/AccessClientFields.tsx @@ -78,6 +78,16 @@ export function AccessClientFields({ initialValues, name = "clients" }: Props) { onChange={(e) => handleChange(idx, "address", e.target.value)} placeholder={intl.formatMessage({ id: "access-list.rule-source.placeholder" })} /> + handleChange(idx, "note", e.target.value)} + placeholder={intl.formatMessage({ id: "access-list.rule-source.placeholder-note" })} + />
diff --git a/frontend/src/locale/src/bg.json b/frontend/src/locale/src/bg.json index 5183fe315b..0149bb51d1 100644 --- a/frontend/src/locale/src/bg.json +++ b/frontend/src/locale/src/bg.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 или 192.168.1.0/24 или 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Напр. „Дом“ или „Офис“" + }, "access-list.satisfy-any": { "defaultMessage": "Удовлетворяване на което и да е" }, diff --git a/frontend/src/locale/src/cs.json b/frontend/src/locale/src/cs.json index cd86b678dc..146d03875c 100644 --- a/frontend/src/locale/src/cs.json +++ b/frontend/src/locale/src/cs.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 nebo 192.168.1.0/24 nebo 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Např. „Domov“ nebo „Kancelář“" + }, "access-list.satisfy-any": { "defaultMessage": "Splnit kterékoliv" }, diff --git a/frontend/src/locale/src/de.json b/frontend/src/locale/src/de.json index f654e10858..7488b4ab38 100644 --- a/frontend/src/locale/src/de.json +++ b/frontend/src/locale/src/de.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "Keine Authentifizierung erforderlich" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 oder 192.168.1.0/24 oder 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Z. B. „Zuhause“ oder „Büro“" + }, "access-list.satisfy-any": { "defaultMessage": "Satisfy Any" }, diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json index bb00ac3322..f1f6bb028e 100644 --- a/frontend/src/locale/src/en.json +++ b/frontend/src/locale/src/en.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 or 192.168.1.0/24 or 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "E.g. \"Home\" or \"Office\"" + }, "access-list.satisfy-any": { "defaultMessage": "Satisfy Any" }, diff --git a/frontend/src/locale/src/es.json b/frontend/src/locale/src/es.json index c8b1edb075..b3497e0ba5 100644 --- a/frontend/src/locale/src/es.json +++ b/frontend/src/locale/src/es.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 o 192.168.1.0/24 o 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "P. ej., «Casa» u «Oficina»" + }, "access-list.satisfy-any": { "defaultMessage": "Satisfacer Cualquiera" }, diff --git a/frontend/src/locale/src/fr.json b/frontend/src/locale/src/fr.json index cdceb79dd2..d4ea12e49c 100644 --- a/frontend/src/locale/src/fr.json +++ b/frontend/src/locale/src/fr.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 ou 192.168.1.0/24 ou 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Par ex. « Domicile » ou « Bureau »" + }, "access-list.satisfy-any": { "defaultMessage": "Valide n'importe quelle règle" }, diff --git a/frontend/src/locale/src/ga.json b/frontend/src/locale/src/ga.json index 719b863bf0..1bce5e0df5 100644 --- a/frontend/src/locale/src/ga.json +++ b/frontend/src/locale/src/ga.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 nó 192.168.1.0/24 nó 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "M.sh. \"Baile\" nó \"Oifig\"" + }, "access-list.satisfy-any": { "defaultMessage": "Sásaigh Aon" }, diff --git a/frontend/src/locale/src/hu.json b/frontend/src/locale/src/hu.json index 4caf058344..29051d8ca2 100644 --- a/frontend/src/locale/src/hu.json +++ b/frontend/src/locale/src/hu.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 vagy 192.168.1.0/24 vagy 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Pl. „Otthon” vagy „Iroda”" + }, "access-list.satisfy-any": { "defaultMessage": "Bármely teljesítése" }, diff --git a/frontend/src/locale/src/id.json b/frontend/src/locale/src/id.json index cb498f0d88..3575a0033d 100644 --- a/frontend/src/locale/src/id.json +++ b/frontend/src/locale/src/id.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 atau 192.168.1.0/24 atau 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Mis. \"Rumah\" atau \"Kantor\"" + }, "access-list.satisfy-any": { "defaultMessage": "Penuhi Salah Satu" }, diff --git a/frontend/src/locale/src/it.json b/frontend/src/locale/src/it.json index 7e5ca77113..02ca037817 100644 --- a/frontend/src/locale/src/it.json +++ b/frontend/src/locale/src/it.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "Nessuna autenticazione base richiesta" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 o 192.168.1.0/24 o 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Es. \"Casa\" o \"Ufficio\"" + }, "access-list.satisfy-any": { "defaultMessage": "Soddisfa Qualsiasi" }, diff --git a/frontend/src/locale/src/ja.json b/frontend/src/locale/src/ja.json index 438dc218d3..84cc9ab433 100644 --- a/frontend/src/locale/src/ja.json +++ b/frontend/src/locale/src/ja.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "ベーシック認証を使用しません" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 または 192.168.1.0/24 または 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "例:「自宅」や「オフィス」" + }, "access-list.satisfy-any": { "defaultMessage": "いずれかを満たす" }, diff --git a/frontend/src/locale/src/ko.json b/frontend/src/locale/src/ko.json index 9c0093591b..a56e654439 100644 --- a/frontend/src/locale/src/ko.json +++ b/frontend/src/locale/src/ko.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 / 192.168.1.0/24 / IPv6" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "예: \"집\" 또는 \"사무실\"" + }, "access-list.satisfy-any": { "defaultMessage": "조건 중 하나라도 충족" }, diff --git a/frontend/src/locale/src/nl.json b/frontend/src/locale/src/nl.json index 86d49d95e2..5a9f319010 100644 --- a/frontend/src/locale/src/nl.json +++ b/frontend/src/locale/src/nl.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 of 192.168.1.0/24 of 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Bijv. \"Thuis\" of \"Kantoor\"" + }, "access-list.satisfy-any": { "defaultMessage": "Voldoe aan elke" }, diff --git a/frontend/src/locale/src/no.json b/frontend/src/locale/src/no.json index f14ea54b11..3e43293dc0 100644 --- a/frontend/src/locale/src/no.json +++ b/frontend/src/locale/src/no.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 eller 192.168.1.0/24 eller 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "F.eks. \"Hjem\" eller \"Kontor\"" + }, "access-list.satisfy-any": { "defaultMessage": "Oppfyll en av kravene" }, diff --git a/frontend/src/locale/src/pl.json b/frontend/src/locale/src/pl.json index a5fb2ad0be..d6ec523fcf 100644 --- a/frontend/src/locale/src/pl.json +++ b/frontend/src/locale/src/pl.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "Nie wymaga uwierzytelnienia podstawowego" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 lub 192.168.1.0/24 lub 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Np. „Dom” lub „Biuro”" + }, "access-list.satisfy-any": { "defaultMessage": "Spełnij dowolny warunek" }, diff --git a/frontend/src/locale/src/pt.json b/frontend/src/locale/src/pt.json index 0a789f484e..a94a7872df 100644 --- a/frontend/src/locale/src/pt.json +++ b/frontend/src/locale/src/pt.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 ou 192.168.1.0/24 ou 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Por ex., \"Casa\" ou \"Escritório\"" + }, "access-list.satisfy-any": { "defaultMessage": "Satisfazer Qualquer" }, diff --git a/frontend/src/locale/src/ru.json b/frontend/src/locale/src/ru.json index c18be998f9..523832919b 100644 --- a/frontend/src/locale/src/ru.json +++ b/frontend/src/locale/src/ru.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 или 192.168.1.0/24 или 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Напр. «Дом» или «Офис»" + }, "access-list.satisfy-any": { "defaultMessage": "Любое совпадение" }, diff --git a/frontend/src/locale/src/sk.json b/frontend/src/locale/src/sk.json index 8d48cf811e..874fa81b02 100644 --- a/frontend/src/locale/src/sk.json +++ b/frontend/src/locale/src/sk.json @@ -83,6 +83,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 alebo 192.168.1.0/24 alebo 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Napr. „Domov“ alebo „Kancelária“" + }, "access-list.satisfy-any": { "defaultMessage": "Splniť ktorékoľvek" }, diff --git a/frontend/src/locale/src/tr.json b/frontend/src/locale/src/tr.json index 972fa895ec..b9def934ae 100644 --- a/frontend/src/locale/src/tr.json +++ b/frontend/src/locale/src/tr.json @@ -26,6 +26,9 @@ "access-list.rule-source.placeholder": { "defaultMessage": "192.168.1.100 veya 192.168.1.0/24 veya 2001:0db8::/32" }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Örn. \"Ev\" veya \"Ofis\"" + }, "access-list.satisfy-any": { "defaultMessage": "Herhangi Birini Karşıla" }, diff --git a/frontend/src/locale/src/vi.json b/frontend/src/locale/src/vi.json index 32d26d5590..20279cc1b0 100644 --- a/frontend/src/locale/src/vi.json +++ b/frontend/src/locale/src/vi.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "Không cần xác thực cơ bản" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 hoặc 192.168.1.0/24 hoặc 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "Ví dụ: \"Nhà\" hoặc \"Văn phòng\"" + }, "access-list.satisfy-any": { "defaultMessage": "Thỏa mãn điều kiện bất kỳ" }, diff --git a/frontend/src/locale/src/zh.json b/frontend/src/locale/src/zh.json index 72494bb64f..920605962c 100644 --- a/frontend/src/locale/src/zh.json +++ b/frontend/src/locale/src/zh.json @@ -23,6 +23,12 @@ "access-list.public.subtitle": { "defaultMessage": "无需基本认证" }, + "access-list.rule-source.placeholder": { + "defaultMessage": "192.168.1.100 或 192.168.1.0/24 或 2001:0db8::/32" + }, + "access-list.rule-source.placeholder-note": { + "defaultMessage": "例如:“家庭”或“办公室”" + }, "access-list.satisfy-any": { "defaultMessage": "满足任意条件" }, diff --git a/frontend/src/modals/AccessListModal.tsx b/frontend/src/modals/AccessListModal.tsx index 79537f5cd5..8114bfc74f 100644 --- a/frontend/src/modals/AccessListModal.tsx +++ b/frontend/src/modals/AccessListModal.tsx @@ -63,10 +63,11 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => { password: i.password, })); - // Filter out "clients" to only use the "directive" and "address" fields + // Filter out "clients" to only use the "directive", "address" and "note" fields payload.clients = (values.clients || []).map((i: AccessListClient) => ({ directive: i.directive, address: i.address, + ...(i.note?.trim() ? { note: i.note.trim() } : {}), })); setAccessList(payload, { @@ -86,7 +87,7 @@ const AccessListModal = EasyModal.create(({ id, visible, remove }: Props) => { const toggleEnabled = cn(toggleClasses, "bg-cyan"); return ( - + {!isLoading && error && ( {error?.message || "Unknown error"}