store express sessions in redis, delete only aliases from preferred domain #3
@ -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"
|
||||
}
|
@ -14,5 +14,8 @@
|
||||
"token": "aaaaa",
|
||||
"domain": "test-codemowers.eu",
|
||||
"preferredDomain": "k-space.ee"
|
||||
},
|
||||
"redis": {
|
||||
"url": "redis://localhost:6379"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
};
|
@ -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
|
||||
|
205
package-lock.json
generated
205
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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')
|
||||
|
15
readme.md
15
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)
|
||||
|
||||
- remove TS-related hacks
|
||||
- add tests
|
||||
- make nicer UI
|
||||
|
22
src/app.ts
22
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 },
|
||||
})
|
||||
);
|
||||
|
16
src/helpers/get-env.ts
Normal file
16
src/helpers/get-env.ts
Normal file
@ -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;
|
||||
}
|
||||
};
|
@ -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<AliasesQuery> {
|
||||
}
|
||||
|
||||
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch>
|
||||
implements ServiceInterface<AliasApiResponse, AliasesData, ServiceParams, AliasesPatch>
|
||||
{
|
||||
constructor(public options: AliasesServiceOptions) {}
|
||||
|
||||
async find(params: ServiceParams): Promise<Alias[]> {
|
||||
async find(params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
return this.getUserAddresses(userId);
|
||||
}
|
||||
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias>;
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> {
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse>;
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse | AliasApiResponse[]> {
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
const randomString = faker.git.commitSha({ length: 4 });
|
||||
@ -65,7 +72,7 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
|
||||
const emailDomain = config.get('wildDuck.domain');
|
||||
|
||||
const createResult = await wildDuckClient.post<CreateAddressResponse>(`/users/${userId}/addresses`, {
|
||||
const createResult = await wildDuckClient.post<CreateWildDuckAddressResponse>(`/users/${userId}/addresses`, {
|
||||
address: `${alias}@${emailDomain}`,
|
||||
});
|
||||
|
||||
@ -79,25 +86,31 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> {
|
||||
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<Alias>(`addresses/resolve/${email}`))
|
||||
.map((email: string) => wildDuckClient.get<WildDuckAddress>(`addresses/resolve/${email}`))
|
||||
);
|
||||
|
||||
return addressInfoResponse.data.user;
|
||||
}
|
||||
|
||||
private async getUserAddresses(userId: string): Promise<Alias[]> {
|
||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(
|
||||
private async getUserAddresses(userId: string): Promise<AliasApiResponse[]> {
|
||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetWildDuckAddressInfoResponse>(
|
||||
`/users/${userId}/addresses`
|
||||
);
|
||||
|
||||
return userAddressesResponse.results;
|
||||
return userAddressesResponse.results.map(this.sanitizeAliasResponse);
|
||||
}
|
||||
|
||||
async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> {
|
||||
const { data: addressInfoResponse } = await wildDuckClient.get<Alias>(`addresses/resolve/${id}`);
|
||||
async remove(id: NullableId, params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||
const { data: addressInfoResponse } = await wildDuckClient.get<WildDuckAddress>(`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<ServiceParams extends AliasesParams = AliasesParams>
|
||||
}
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
await wildDuckClient.delete<Alias>(`users/${userId}/addresses/${id}`);
|
||||
await wildDuckClient.delete<WildDuckAddress>(`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) => {
|
||||
|
Loading…
Reference in New Issue
Block a user