Merge pull request 'store express sessions in redis, delete only aliases from preferred domain' (#3) from dev into master
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				ci/woodpecker/push/woodpecker Pipeline was successful
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	ci/woodpecker/push/woodpecker Pipeline was successful
				
			Reviewed-on: k-space/walias#3
This commit is contained in:
		| @@ -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) => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user