diff --git a/.prettierrc.json b/.prettierrc.json index c92ebdc..db68f23 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,9 @@ { - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true, - "printWidth": 120, - "quoteProps": "as-needed", - "arrowParens": "avoid" + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "printWidth": 120, + "quoteProps": "as-needed", + "arrowParens": "avoid" } \ No newline at end of file diff --git a/config/default.json b/config/default.json index 62b451a..61c41f9 100644 --- a/config/default.json +++ b/config/default.json @@ -14,5 +14,8 @@ "token": "aaaaa", "domain": "test-codemowers.eu", "preferredDomain": "k-space.ee" + }, + "redis": { + "url": "redis://localhost:6379" } } \ No newline at end of file diff --git a/config/prod.js b/config/prod.js index 2d79fca..d1551f0 100644 --- a/config/prod.js +++ b/config/prod.js @@ -10,5 +10,8 @@ module.exports = { url: process.env.WILDDUCK_URL, token: process.env.WILDDUCK_TOKEN, domain: process.env.WILDDUCK_DOMAIN + }, + redis: { + url: process.env.REDIS_URL } }; \ No newline at end of file diff --git a/deployment.yaml b/deployment.yaml index 1c5ec76..7ef2aea 100644 --- a/deployment.yaml +++ b/deployment.yaml @@ -98,6 +98,8 @@ spec: secretKeyRef: name: walias-secrets key: WILDDUCK_DOMAIN + - name: REDIS_URL + value: walias-cache envFrom: - secretRef: name: oidc-client-walias-owner-secrets diff --git a/package-lock.json b/package-lock.json index 1295b2f..36ab29d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,10 @@ "axios": "^1.4.0", "compression": "^1.7.4", "config": "^3.3.9", + "connect-redis": "^7.1.0", "cookie-parser": "^1.4.6", "express-session": "^1.17.3", + "ioredis": "^5.3.2", "openid-client": "^5.4.3", "winston": "^3.10.0" }, @@ -34,6 +36,7 @@ "@types/express-session": "^1.17.7", "@types/mocha": "^10.0.1", "@types/node": "^20.4.5", + "@types/redis": "^4.0.11", "cross-env": "^7.0.3", "husky": "^8.0.3", "lint-staged": "^13.2.3", @@ -413,6 +416,11 @@ "url": "https://github.com/sponsors/daffl" } }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", @@ -438,6 +446,65 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "dev": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.8.tgz", + "integrity": "sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==", + "dev": true, + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz", + "integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==", + "dev": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz", + "integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==", + "dev": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", + "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", + "dev": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz", + "integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==", + "dev": true, + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -610,6 +677,16 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "node_modules/@types/redis": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@types/redis/-/redis-4.0.11.tgz", + "integrity": "sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg==", + "deprecated": "This is a stub types definition. redis provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "redis": "*" + } + }, "node_modules/@types/send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", @@ -1229,6 +1306,14 @@ "node": ">=0.8" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -1340,6 +1425,17 @@ "node": ">= 10.0.0" } }, + "node_modules/connect-redis": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.0.tgz", + "integrity": "sha512-UaqO1EirWjON2ENsyau7N5lbkrdYBpS6mYlXSeff/OYXsd6EGZ+SXSmNPoljL2PSua8fgjAEaldSA73PMZQ9Eg==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "express-session": ">=1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1501,6 +1597,14 @@ "node": ">=0.4.0" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1999,6 +2103,15 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2274,6 +2387,50 @@ "node": ">= 0.10" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ioredis/node_modules/debug": { + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ioredis/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2650,6 +2807,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -3536,6 +3703,39 @@ "node": ">= 0.10" } }, + "node_modules/redis": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.7.tgz", + "integrity": "sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==", + "dev": true, + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.8", + "@redis/graph": "1.1.0", + "@redis/json": "1.0.4", + "@redis/search": "1.1.3", + "@redis/time-series": "1.0.4" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -3920,6 +4120,11 @@ "node": "*" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index 03b6500..07fb99f 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,10 @@ "axios": "^1.4.0", "compression": "^1.7.4", "config": "^3.3.9", + "connect-redis": "^7.1.0", "cookie-parser": "^1.4.6", "express-session": "^1.17.3", + "ioredis": "^5.3.2", "openid-client": "^5.4.3", "winston": "^3.10.0" }, @@ -67,6 +69,7 @@ "@types/express-session": "^1.17.7", "@types/mocha": "^10.0.1", "@types/node": "^20.4.5", + "@types/redis": "^4.0.11", "cross-env": "^7.0.3", "husky": "^8.0.3", "lint-staged": "^13.2.3", diff --git a/public/index.js b/public/index.js index 86770f0..fe1c00a 100644 --- a/public/index.js +++ b/public/index.js @@ -31,29 +31,17 @@ function renderAliases(aliases) { const tdCreated = document.createElement('td') tdCreated.innerText = alias.created const tdActions = document.createElement('td') - const aDelete = document.createElement('a') - aDelete.addEventListener('click', async () => { - const res = await fetch(`/aliases/${alias.id}`, { - method: 'DELETE' - }) + const deleteButton = createDeleteButton(alias) - if (res.ok) { - const data = await res.json() - renderAliases(data) - return - } - }) - - aDelete.classList.add('btn', 'btn-danger') - aDelete.innerText = 'Delete' - tdActions.appendChild(aDelete) + tdActions.appendChild(deleteButton) tr.appendChild(tdAddress) tr.appendChild(tdCreated) tr.appendChild(tdActions) tbody.appendChild(tr) }) + table.appendChild(tbody) dataContainer.innerHTML = '' @@ -62,6 +50,32 @@ function renderAliases(aliases) { renderCreateButton(); } +function createDeleteButton(alias) { + const deleteButton = document.createElement('a') + deleteButton.classList.add('btn', 'btn-danger') + + if (!alias.id) { + deleteButton.classList.add('disabled') + } else { + + deleteButton.addEventListener('click', async () => { + const res = await fetch(`/aliases/${alias.id}`, { + method: 'DELETE' + }) + + if (res.ok) { + const data = await res.json() + renderAliases(data) + return + } + }) + } + + deleteButton.innerText = 'Delete' + + return deleteButton +} + function renderCreateButton() { const dataContainer = document.getElementById('container') const button = document.createElement('a') diff --git a/readme.md b/readme.md index d9b8b3a..55fd6eb 100644 --- a/readme.md +++ b/readme.md @@ -7,12 +7,13 @@ This project was build for [K-Space Hackathon2023](https://wiki.k-space.ee/en/hackathon/2023) ## Features - * auth with oidc - * headless only, no DB - * develop/debug with skaffold on k8s cluster + +- auth with oidc +- headless only, no DB +- develop/debug with skaffold on k8s cluster ## TODO - * remove TS-related hacks - * add tests - * make nicer UI - * implement persistent sessions for multi-docker deployment (redis, db, etc) \ No newline at end of file + +- remove TS-related hacks +- add tests +- make nicer UI diff --git a/src/app.ts b/src/app.ts index f9ad933..123a78c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,17 +1,19 @@ +import { randomUUID } from 'crypto'; import { feathers } from '@feathersjs/feathers'; import express, { rest, json, urlencoded, cors, serveStatic, notFound, errorHandler } from '@feathersjs/express'; import configuration from '@feathersjs/configuration'; import socketio from '@feathersjs/socketio'; import session from 'express-session'; import cookieParser from 'cookie-parser'; - +import RedisStore from 'connect-redis'; +import { createClient } from 'redis'; +import config from 'config'; import type { Application } from './declarations'; - import { logger } from './logger'; import { logError } from './hooks/log-error'; import { services } from './services/index'; import { channels } from './channels'; -import { randomUUID } from 'crypto'; +import { Env, getEnv } from './helpers/get-env'; const app: Application = express(feathers()); @@ -25,11 +27,23 @@ app.use( ); app.use(cookieParser()); + +const sessionStore = + getEnv() === Env.prod + ? new RedisStore({ + prefix: 'walias:', + client: createClient({ + url: config.get('redis.url'), + }), + }) + : undefined; + app.use( session({ + store: sessionStore, secret: randomUUID(), resave: false, - saveUninitialized: true, + saveUninitialized: false, cookie: { secure: false }, }) ); diff --git a/src/helpers/get-env.ts b/src/helpers/get-env.ts new file mode 100644 index 0000000..0c40f6b --- /dev/null +++ b/src/helpers/get-env.ts @@ -0,0 +1,16 @@ +export enum Env { + dev = 'dev', + prod = 'prod', + test = 'test', +} + +export const getEnv = (): Env => { + const env = process.env.NODE_ENV; + if (env === 'prod') { + return Env.prod; + } else if (env === 'test') { + return Env.test; + } else { + return Env.dev; + } +}; diff --git a/src/services/aliases/aliases.class.ts b/src/services/aliases/aliases.class.ts index 4d5a4fa..7f427d6 100644 --- a/src/services/aliases/aliases.class.ts +++ b/src/services/aliases/aliases.class.ts @@ -2,11 +2,11 @@ import type { NullableId, Params, ServiceInterface } from '@feathersjs/feathers' import type { Application } from '../../declarations'; import wildDuckClient from '../../clients/wildduck.client'; -import { faker } from '@faker-js/faker'; +import { faker, th } from '@faker-js/faker'; import { BadRequest } from '@feathersjs/errors'; import config from 'config'; -interface Alias { +interface WildDuckAddress { success: boolean; id: string; address: string; @@ -16,12 +16,19 @@ interface Alias { created: string; } -interface GetAddressInfoResponse { +interface GetWildDuckAddressInfoResponse { success: boolean; - results: Alias[]; + results: WildDuckAddress[]; } -interface CreateAddressResponse { +interface AliasApiResponse { + id: string | null; + address: string; + tags: string[]; + created: string; +} + +interface CreateWildDuckAddressResponse { success: boolean; id: string; } @@ -30,7 +37,7 @@ type AliasesData = any; type AliasesPatch = any; type AliasesQuery = any; -export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery }; +export type { WildDuckAddress as Aliases, AliasesData, AliasesPatch, AliasesQuery }; export interface AliasesServiceOptions { app: Application; @@ -41,18 +48,18 @@ export interface AliasesParams extends Params { } export class AliasesService - implements ServiceInterface + implements ServiceInterface { constructor(public options: AliasesServiceOptions) {} - async find(params: ServiceParams): Promise { + async find(params: ServiceParams): Promise { const userId = await this.getUserIdByEmailAddress(params); return this.getUserAddresses(userId); } - async create(data: AliasesData, params: ServiceParams): Promise; - async create(data: AliasesData, params: ServiceParams): Promise { + async create(data: AliasesData, params: ServiceParams): Promise; + async create(data: AliasesData, params: ServiceParams): Promise { const userId = await this.getUserIdByEmailAddress(params); const randomString = faker.git.commitSha({ length: 4 }); @@ -65,7 +72,7 @@ export class AliasesService const emailDomain = config.get('wildDuck.domain'); - const createResult = await wildDuckClient.post(`/users/${userId}/addresses`, { + const createResult = await wildDuckClient.post(`/users/${userId}/addresses`, { address: `${alias}@${emailDomain}`, }); @@ -79,25 +86,31 @@ export class AliasesService private async getUserIdByEmailAddress(params: ServiceParams): Promise { const emails = params.session?.user?.emails; + const preferredDomain = config.get('wildDuck.preferredDomain'); + + if (!emails.length || !preferredDomain) { + throw new BadRequest('Unable to find user'); + } + const addressInfoResponse = await Promise.any( emails .filter((email: string) => email.endsWith(config.get('wildDuck.preferredDomain'))) - .map((email: string) => wildDuckClient.get(`addresses/resolve/${email}`)) + .map((email: string) => wildDuckClient.get(`addresses/resolve/${email}`)) ); return addressInfoResponse.data.user; } - private async getUserAddresses(userId: string): Promise { - const { data: userAddressesResponse } = await wildDuckClient.get( + private async getUserAddresses(userId: string): Promise { + const { data: userAddressesResponse } = await wildDuckClient.get( `/users/${userId}/addresses` ); - return userAddressesResponse.results; + return userAddressesResponse.results.map(this.sanitizeAliasResponse); } - async remove(id: NullableId, params: ServiceParams): Promise { - const { data: addressInfoResponse } = await wildDuckClient.get(`addresses/resolve/${id}`); + async remove(id: NullableId, params: ServiceParams): Promise { + const { data: addressInfoResponse } = await wildDuckClient.get(`addresses/resolve/${id}`); const allowedDomain: string = config.get('wildDuck.domain'); // If address does not match the allowed domain, throw an error @@ -106,10 +119,22 @@ export class AliasesService } const userId = await this.getUserIdByEmailAddress(params); - await wildDuckClient.delete(`users/${userId}/addresses/${id}`); + await wildDuckClient.delete(`users/${userId}/addresses/${id}`); return this.getUserAddresses(userId); } + + sanitizeAliasResponse(alias: WildDuckAddress): AliasApiResponse { + // Hide the id if the alias is not removable + const isRemovable = alias.main || !alias.address.endsWith(config.get('wildDuck.preferredDomain')); + + return { + id: isRemovable ? null : alias.id, + address: alias.address, + tags: alias.tags, + created: alias.created, + }; + } } export const getOptions = (app: Application) => {