Merge pull request 'dev-prettier' (#4) from dev-prettier 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#4
This commit is contained in:
		
							
								
								
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.husky/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| _ | ||||
							
								
								
									
										4
									
								
								.husky/pre-commit
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								.husky/pre-commit
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #!/bin/sh | ||||
| . "$(dirname "$0")/_/husky.sh" | ||||
|  | ||||
| npx lint-staged | ||||
							
								
								
									
										9
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| { | ||||
|     "trailingComma": "es5", | ||||
|     "tabWidth": 4, | ||||
|     "semi": true, | ||||
|     "singleQuote": true, | ||||
|     "printWidth": 120, | ||||
|     "quoteProps": "as-needed", | ||||
|     "arrowParens": "avoid" | ||||
| } | ||||
							
								
								
									
										40
									
								
								index.ts
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								index.ts
									
									
									
									
									
								
							| @@ -1,33 +1,33 @@ | ||||
| import { app } from "./app"; | ||||
| import { logger } from "./logger"; | ||||
| import { app } from './app'; | ||||
| import { logger } from './logger'; | ||||
|  | ||||
| const port = app.get("port"); | ||||
| const host = app.get("host"); | ||||
| const port = app.get('port'); | ||||
| const host = app.get('host'); | ||||
| const server = app.listen(port); | ||||
|  | ||||
| app.listen(port).then(() => { | ||||
|   logger.info(`Walias app listening on http://${host}:${port}`); | ||||
|     logger.info(`Walias app listening on http://${host}:${port}`); | ||||
| }); | ||||
|  | ||||
| process.on("SIGINT", () => { | ||||
|   logger.info("Received SIGINT signal. Shutting down gracefully."); | ||||
| process.on('SIGINT', () => { | ||||
|     logger.info('Received SIGINT signal. Shutting down gracefully.'); | ||||
|  | ||||
|   server.close(() => { | ||||
|     logger.info("HTTP server closed."); | ||||
|     process.exit(0); | ||||
|   }); | ||||
|     server.close(() => { | ||||
|         logger.info('HTTP server closed.'); | ||||
|         process.exit(0); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| process.on("SIGTERM", () => { | ||||
|   logger.info("Received SIGTERM signal. Shutting down gracefully."); | ||||
| process.on('SIGTERM', () => { | ||||
|     logger.info('Received SIGTERM signal. Shutting down gracefully.'); | ||||
|  | ||||
|   server.close(() => { | ||||
|     logger.info("HTTP server closed."); | ||||
|     process.exit(0); | ||||
|   }); | ||||
|     server.close(() => { | ||||
|         logger.info('HTTP server closed.'); | ||||
|         process.exit(0); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| process.on("unhandledRejection", (reason) => { | ||||
|   logger.error("Unhandled rejection", reason); | ||||
|   process.exit(1); | ||||
| process.on('unhandledRejection', reason => { | ||||
|     logger.error('Unhandled rejection', reason); | ||||
|     process.exit(1); | ||||
| }); | ||||
|   | ||||
							
								
								
									
										10
									
								
								logger.ts
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								logger.ts
									
									
									
									
									
								
							| @@ -1,10 +1,10 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html | ||||
| import { createLogger, format, transports } from "winston"; | ||||
| import { createLogger, format, transports } from 'winston'; | ||||
|  | ||||
| // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston | ||||
| export const logger = createLogger({ | ||||
|   // To see more detailed errors, change this to 'debug' | ||||
|   level: "info", | ||||
|   format: format.combine(format.splat(), format.simple()), | ||||
|   transports: [new transports.Console()], | ||||
|     // To see more detailed errors, change this to 'debug' | ||||
|     level: 'info', | ||||
|     format: format.combine(format.splat(), format.simple()), | ||||
|     transports: [new transports.Console()], | ||||
| }); | ||||
|   | ||||
							
								
								
									
										556
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										556
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -35,6 +35,8 @@ | ||||
|         "@types/mocha": "^10.0.1", | ||||
|         "@types/node": "^20.4.5", | ||||
|         "cross-env": "^7.0.3", | ||||
|         "husky": "^8.0.3", | ||||
|         "lint-staged": "^13.2.3", | ||||
|         "mocha": "^10.2.0", | ||||
|         "nodemon": "^3.0.1", | ||||
|         "prettier": "^3.0.0", | ||||
| @@ -705,6 +707,19 @@ | ||||
|         "node": ">=0.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/aggregate-error": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", | ||||
|       "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "clean-stack": "^2.0.0", | ||||
|         "indent-string": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/ajv": { | ||||
|       "version": "8.12.0", | ||||
|       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", | ||||
| @@ -832,6 +847,15 @@ | ||||
|       "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | ||||
|       "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" | ||||
|     }, | ||||
|     "node_modules/astral-regex": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", | ||||
|       "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/async": { | ||||
|       "version": "3.2.4", | ||||
|       "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", | ||||
| @@ -1074,6 +1098,15 @@ | ||||
|         "fsevents": "~2.3.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/clean-stack": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", | ||||
|       "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-cursor": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", | ||||
| @@ -1098,6 +1131,72 @@ | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-truncate": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", | ||||
|       "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "slice-ansi": "^5.0.0", | ||||
|         "string-width": "^5.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-truncate/node_modules/ansi-regex": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", | ||||
|       "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/ansi-regex?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-truncate/node_modules/emoji-regex": { | ||||
|       "version": "9.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", | ||||
|       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/cli-truncate/node_modules/string-width": { | ||||
|       "version": "5.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", | ||||
|       "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "eastasianwidth": "^0.2.0", | ||||
|         "emoji-regex": "^9.2.2", | ||||
|         "strip-ansi": "^7.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-truncate/node_modules/strip-ansi": { | ||||
|       "version": "7.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", | ||||
|       "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-regex": "^6.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/strip-ansi?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/cli-width": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", | ||||
| @@ -1161,6 +1260,12 @@ | ||||
|         "simple-swizzle": "^0.2.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/colorette": { | ||||
|       "version": "2.0.20", | ||||
|       "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", | ||||
|       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/colorspace": { | ||||
|       "version": "1.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", | ||||
| @@ -1422,6 +1527,12 @@ | ||||
|         "node": ">=0.3.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/eastasianwidth": { | ||||
|       "version": "0.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", | ||||
|       "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/ecdsa-sig-formatter": { | ||||
|       "version": "1.0.11", | ||||
|       "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", | ||||
| @@ -1550,6 +1661,68 @@ | ||||
|         "node": ">=0.8.x" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/execa": { | ||||
|       "version": "7.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", | ||||
|       "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "cross-spawn": "^7.0.3", | ||||
|         "get-stream": "^6.0.1", | ||||
|         "human-signals": "^4.3.0", | ||||
|         "is-stream": "^3.0.0", | ||||
|         "merge-stream": "^2.0.0", | ||||
|         "npm-run-path": "^5.1.0", | ||||
|         "onetime": "^6.0.0", | ||||
|         "signal-exit": "^3.0.7", | ||||
|         "strip-final-newline": "^3.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.18.0 || ^16.14.0 || >=18.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sindresorhus/execa?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/execa/node_modules/is-stream": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", | ||||
|       "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/execa/node_modules/mimic-fn": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", | ||||
|       "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/execa/node_modules/onetime": { | ||||
|       "version": "6.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", | ||||
|       "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "mimic-fn": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/express": { | ||||
|       "version": "4.18.2", | ||||
|       "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", | ||||
| @@ -1849,6 +2022,18 @@ | ||||
|         "url": "https://github.com/sponsors/ljharb" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/get-stream": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", | ||||
|       "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/glob": { | ||||
|       "version": "7.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", | ||||
| @@ -1969,6 +2154,30 @@ | ||||
|         "node": ">= 0.8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/human-signals": { | ||||
|       "version": "4.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", | ||||
|       "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=14.18.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/husky": { | ||||
|       "version": "8.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", | ||||
|       "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", | ||||
|       "dev": true, | ||||
|       "bin": { | ||||
|         "husky": "lib/bin.js" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=14" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/typicode" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/iconv-lite": { | ||||
|       "version": "0.4.24", | ||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||||
| @@ -2006,6 +2215,15 @@ | ||||
|       "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/indent-string": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", | ||||
|       "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/inflight": { | ||||
|       "version": "1.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", | ||||
| @@ -2272,6 +2490,146 @@ | ||||
|       "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", | ||||
|       "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" | ||||
|     }, | ||||
|     "node_modules/lilconfig": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", | ||||
|       "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lint-staged": { | ||||
|       "version": "13.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz", | ||||
|       "integrity": "sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "chalk": "5.2.0", | ||||
|         "cli-truncate": "^3.1.0", | ||||
|         "commander": "^10.0.0", | ||||
|         "debug": "^4.3.4", | ||||
|         "execa": "^7.0.0", | ||||
|         "lilconfig": "2.1.0", | ||||
|         "listr2": "^5.0.7", | ||||
|         "micromatch": "^4.0.5", | ||||
|         "normalize-path": "^3.0.0", | ||||
|         "object-inspect": "^1.12.3", | ||||
|         "pidtree": "^0.6.0", | ||||
|         "string-argv": "^0.3.1", | ||||
|         "yaml": "^2.2.2" | ||||
|       }, | ||||
|       "bin": { | ||||
|         "lint-staged": "bin/lint-staged.js" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.13.1 || >=16.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://opencollective.com/lint-staged" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lint-staged/node_modules/chalk": { | ||||
|       "version": "5.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", | ||||
|       "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": "^12.17.0 || ^14.13 || >=16.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/chalk?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lint-staged/node_modules/commander": { | ||||
|       "version": "10.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", | ||||
|       "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=14" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lint-staged/node_modules/debug": { | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | ||||
|       "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ms": "2.1.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=6.0" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "supports-color": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/lint-staged/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==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/listr2": { | ||||
|       "version": "5.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", | ||||
|       "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "cli-truncate": "^2.1.0", | ||||
|         "colorette": "^2.0.19", | ||||
|         "log-update": "^4.0.0", | ||||
|         "p-map": "^4.0.0", | ||||
|         "rfdc": "^1.3.0", | ||||
|         "rxjs": "^7.8.0", | ||||
|         "through": "^2.3.8", | ||||
|         "wrap-ansi": "^7.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.13.1 || >=16.0.0" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "enquirer": ">= 2.3.0 < 3" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "enquirer": { | ||||
|           "optional": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/listr2/node_modules/cli-truncate": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", | ||||
|       "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "slice-ansi": "^3.0.0", | ||||
|         "string-width": "^4.2.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/listr2/node_modules/slice-ansi": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", | ||||
|       "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^4.0.0", | ||||
|         "astral-regex": "^2.0.0", | ||||
|         "is-fullwidth-code-point": "^3.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/locate-path": { | ||||
|       "version": "6.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", | ||||
| @@ -2308,6 +2666,55 @@ | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/log-update": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", | ||||
|       "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-escapes": "^4.3.0", | ||||
|         "cli-cursor": "^3.1.0", | ||||
|         "slice-ansi": "^4.0.0", | ||||
|         "wrap-ansi": "^6.2.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/log-update/node_modules/slice-ansi": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", | ||||
|       "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^4.0.0", | ||||
|         "astral-regex": "^2.0.0", | ||||
|         "is-fullwidth-code-point": "^3.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/slice-ansi?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/log-update/node_modules/wrap-ansi": { | ||||
|       "version": "6.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", | ||||
|       "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^4.0.0", | ||||
|         "string-width": "^4.1.0", | ||||
|         "strip-ansi": "^6.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/logform": { | ||||
|       "version": "2.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", | ||||
| @@ -2361,6 +2768,12 @@ | ||||
|       "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", | ||||
|       "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" | ||||
|     }, | ||||
|     "node_modules/merge-stream": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", | ||||
|       "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/methods": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", | ||||
| @@ -2369,6 +2782,19 @@ | ||||
|         "node": ">= 0.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/micromatch": { | ||||
|       "version": "4.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", | ||||
|       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "braces": "^3.0.2", | ||||
|         "picomatch": "^2.3.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8.6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/mime": { | ||||
|       "version": "1.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", | ||||
| @@ -2695,6 +3121,33 @@ | ||||
|         "node": ">=0.10.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/npm-run-path": { | ||||
|       "version": "5.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", | ||||
|       "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "path-key": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/npm-run-path/node_modules/path-key": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", | ||||
|       "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/object-assign": { | ||||
|       "version": "4.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | ||||
| @@ -2854,6 +3307,21 @@ | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/p-map": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", | ||||
|       "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "aggregate-error": "^3.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/parseurl": { | ||||
|       "version": "1.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | ||||
| @@ -2912,6 +3380,18 @@ | ||||
|         "url": "https://github.com/sponsors/jonschlinkert" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/pidtree": { | ||||
|       "version": "0.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", | ||||
|       "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", | ||||
|       "dev": true, | ||||
|       "bin": { | ||||
|         "pidtree": "bin/pidtree.js" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=0.10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/prettier": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", | ||||
| @@ -3108,6 +3588,12 @@ | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/rfdc": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", | ||||
|       "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/run-async": { | ||||
|       "version": "2.4.1", | ||||
|       "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", | ||||
| @@ -3307,6 +3793,46 @@ | ||||
|         "node": ">=10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/slice-ansi": { | ||||
|       "version": "5.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", | ||||
|       "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^6.0.0", | ||||
|         "is-fullwidth-code-point": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/slice-ansi?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/slice-ansi/node_modules/ansi-styles": { | ||||
|       "version": "6.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", | ||||
|       "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/ansi-styles?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", | ||||
|       "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/socket.io": { | ||||
|       "version": "4.7.1", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", | ||||
| @@ -3429,6 +3955,15 @@ | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     "node_modules/string-argv": { | ||||
|       "version": "0.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", | ||||
|       "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=0.6.19" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/string-width": { | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | ||||
| @@ -3455,6 +3990,18 @@ | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/strip-final-newline": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", | ||||
|       "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=12" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/strip-json-comments": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", | ||||
| @@ -3843,6 +4390,15 @@ | ||||
|       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", | ||||
|       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" | ||||
|     }, | ||||
|     "node_modules/yaml": { | ||||
|       "version": "2.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", | ||||
|       "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">= 14" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/yargs": { | ||||
|       "version": "17.7.2", | ||||
|       "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", | ||||
|   | ||||
| @@ -68,11 +68,16 @@ | ||||
|     "@types/mocha": "^10.0.1", | ||||
|     "@types/node": "^20.4.5", | ||||
|     "cross-env": "^7.0.3", | ||||
|     "husky": "^8.0.3", | ||||
|     "lint-staged": "^13.2.3", | ||||
|     "mocha": "^10.2.0", | ||||
|     "nodemon": "^3.0.1", | ||||
|     "prettier": "^3.0.0", | ||||
|     "shx": "^0.3.4", | ||||
|     "ts-node": "^10.9.1", | ||||
|     "typescript": "^5.1.6" | ||||
|   }, | ||||
|   "lint-staged": { | ||||
|     "*.{ts,js,css,md}": "prettier --write" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										88
									
								
								src/app.ts
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								src/app.ts
									
									
									
									
									
								
							| @@ -1,25 +1,17 @@ | ||||
| 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 { 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 type { Application } from "./declarations"; | ||||
| 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 { logger } from './logger'; | ||||
| import { logError } from './hooks/log-error'; | ||||
| import { services } from './services/index'; | ||||
| import { channels } from './channels'; | ||||
| import { randomUUID } from 'crypto'; | ||||
|  | ||||
| const app: Application = express(feathers()); | ||||
|  | ||||
| @@ -27,42 +19,42 @@ const app: Application = express(feathers()); | ||||
| app.configure(configuration()); | ||||
| app.use(cors()); | ||||
| app.use( | ||||
|   json({ | ||||
|     limit: "20mb", | ||||
|   }), | ||||
|     json({ | ||||
|         limit: '20mb', | ||||
|     }) | ||||
| ); | ||||
|  | ||||
| app.use(cookieParser()); | ||||
| app.use( | ||||
|   session({ | ||||
|     secret: randomUUID(), | ||||
|     resave: false, | ||||
|     saveUninitialized: true, | ||||
|     cookie: { secure: false }, | ||||
|   }), | ||||
|     session({ | ||||
|         secret: randomUUID(), | ||||
|         resave: false, | ||||
|         saveUninitialized: true, | ||||
|         cookie: { secure: false }, | ||||
|     }) | ||||
| ); | ||||
|  | ||||
| // Propagate session to request.params in feathers services | ||||
| app.use(function (req, _res, next) { | ||||
|   req.feathers = { | ||||
|     ...req.feathers, | ||||
|     session: req.session, | ||||
|   }; | ||||
|   next(); | ||||
|     req.feathers = { | ||||
|         ...req.feathers, | ||||
|         session: req.session, | ||||
|     }; | ||||
|     next(); | ||||
| }); | ||||
|  | ||||
| app.use(urlencoded({ extended: true })); | ||||
| // Host the public folder | ||||
| app.use("/", serveStatic(app.get("public"))); | ||||
| app.use('/', serveStatic(app.get('public'))); | ||||
|  | ||||
| // Configure services and real-time functionality | ||||
| app.configure(rest()); | ||||
| app.configure( | ||||
|   socketio({ | ||||
|     cors: { | ||||
|       origin: app.get("origins"), | ||||
|     }, | ||||
|   }), | ||||
|     socketio({ | ||||
|         cors: { | ||||
|             origin: app.get('origins'), | ||||
|         }, | ||||
|     }) | ||||
| ); | ||||
| app.configure(services); | ||||
| app.configure(channels); | ||||
| @@ -73,17 +65,17 @@ app.use(errorHandler({ logger })); | ||||
|  | ||||
| // Register hooks that run on all service methods | ||||
| app.hooks({ | ||||
|   around: { | ||||
|     all: [logError], | ||||
|   }, | ||||
|   before: {}, | ||||
|   after: {}, | ||||
|   error: {}, | ||||
|     around: { | ||||
|         all: [logError], | ||||
|     }, | ||||
|     before: {}, | ||||
|     after: {}, | ||||
|     error: {}, | ||||
| }); | ||||
| // Register application setup and teardown hooks here | ||||
| app.hooks({ | ||||
|   setup: [], | ||||
|   teardown: [], | ||||
|     setup: [], | ||||
|     teardown: [], | ||||
| }); | ||||
|  | ||||
| export { app }; | ||||
|   | ||||
| @@ -1,41 +1,38 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/channels.html | ||||
| import type { RealTimeConnection, Params } from "@feathersjs/feathers"; | ||||
| import type { AuthenticationResult } from "@feathersjs/authentication"; | ||||
| import "@feathersjs/transport-commons"; | ||||
| import type { Application, HookContext } from "./declarations"; | ||||
| import { logger } from "./logger"; | ||||
| import type { RealTimeConnection, Params } from '@feathersjs/feathers'; | ||||
| import type { AuthenticationResult } from '@feathersjs/authentication'; | ||||
| import '@feathersjs/transport-commons'; | ||||
| import type { Application, HookContext } from './declarations'; | ||||
| import { logger } from './logger'; | ||||
|  | ||||
| export const channels = (app: Application) => { | ||||
|   logger.warn( | ||||
|     "Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.", | ||||
|   ); | ||||
|     logger.warn( | ||||
|         'Publishing all events to all authenticated users. See `channels.ts` and https://dove.feathersjs.com/api/channels.html for more information.' | ||||
|     ); | ||||
|  | ||||
|   app.on("connection", (connection: RealTimeConnection) => { | ||||
|     // On a new real-time connection, add it to the anonymous channel | ||||
|     app.channel("anonymous").join(connection); | ||||
|   }); | ||||
|     app.on('connection', (connection: RealTimeConnection) => { | ||||
|         // On a new real-time connection, add it to the anonymous channel | ||||
|         app.channel('anonymous').join(connection); | ||||
|     }); | ||||
|  | ||||
|   app.on( | ||||
|     "login", | ||||
|     (authResult: AuthenticationResult, { connection }: Params) => { | ||||
|       // connection can be undefined if there is no | ||||
|       // real-time connection, e.g. when logging in via REST | ||||
|       if (connection) { | ||||
|         // The connection is no longer anonymous, remove it | ||||
|         app.channel("anonymous").leave(connection); | ||||
|     app.on('login', (authResult: AuthenticationResult, { connection }: Params) => { | ||||
|         // connection can be undefined if there is no | ||||
|         // real-time connection, e.g. when logging in via REST | ||||
|         if (connection) { | ||||
|             // The connection is no longer anonymous, remove it | ||||
|             app.channel('anonymous').leave(connection); | ||||
|  | ||||
|         // Add it to the authenticated user channel | ||||
|         app.channel("authenticated").join(connection); | ||||
|       } | ||||
|     }, | ||||
|   ); | ||||
|             // Add it to the authenticated user channel | ||||
|             app.channel('authenticated').join(connection); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   // eslint-disable-next-line no-unused-vars | ||||
|   app.publish((data: any, context: HookContext) => { | ||||
|     // Here you can add event publishers to channels set up in `channels.js` | ||||
|     // To publish only for a specific event use `app.publish(eventname, () => {})` | ||||
|     // eslint-disable-next-line no-unused-vars | ||||
|     app.publish((data: any, context: HookContext) => { | ||||
|         // Here you can add event publishers to channels set up in `channels.js` | ||||
|         // To publish only for a specific event use `app.publish(eventname, () => {})` | ||||
|  | ||||
|     // e.g. to publish all service events to all authenticated users use | ||||
|     return app.channel("authenticated"); | ||||
|   }); | ||||
|         // e.g. to publish all service events to all authenticated users use | ||||
|         return app.channel('authenticated'); | ||||
|     }); | ||||
| }; | ||||
|   | ||||
| @@ -1,12 +1,12 @@ | ||||
| import axios from "axios"; | ||||
| import config from "config"; | ||||
| import axios from 'axios'; | ||||
| import config from 'config'; | ||||
|  | ||||
| const wildDuckClient = axios.create({ | ||||
|   baseURL: config.get("wildDuck.url"), | ||||
|   headers: { | ||||
|     "X-Access-Token": config.get("wildDuck.token"), | ||||
|   }, | ||||
|   responseType: "json", | ||||
|     baseURL: config.get('wildDuck.url'), | ||||
|     headers: { | ||||
|         'X-Access-Token': config.get('wildDuck.token'), | ||||
|     }, | ||||
|     responseType: 'json', | ||||
| }); | ||||
|  | ||||
| export default wildDuckClient; | ||||
|   | ||||
| @@ -1,9 +1,6 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/typescript.html | ||||
| import { | ||||
|   HookContext as FeathersHookContext, | ||||
|   NextFunction, | ||||
| } from "@feathersjs/feathers"; | ||||
| import { Application as FeathersApplication } from "@feathersjs/express"; | ||||
| import { HookContext as FeathersHookContext, NextFunction } from '@feathersjs/feathers'; | ||||
| import { Application as FeathersApplication } from '@feathersjs/express'; | ||||
| type ApplicationConfiguration = any; | ||||
|  | ||||
| export { NextFunction }; | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| import type { HookContext, NextFunction } from "../declarations"; | ||||
| import { logger } from "../logger"; | ||||
| import type { HookContext, NextFunction } from '../declarations'; | ||||
| import { logger } from '../logger'; | ||||
|  | ||||
| export const logError = async (context: HookContext, next: NextFunction) => { | ||||
|   try { | ||||
|     await next(); | ||||
|   } catch (error: any) { | ||||
|     logger.error(error.stack); | ||||
|     try { | ||||
|         await next(); | ||||
|     } catch (error: any) { | ||||
|         logger.error(error.stack); | ||||
|  | ||||
|     // Log validation errors | ||||
|     if (error.data) { | ||||
|       logger.error("Data: %O", error.data); | ||||
|         // Log validation errors | ||||
|         if (error.data) { | ||||
|             logger.error('Data: %O', error.data); | ||||
|         } | ||||
|  | ||||
|         throw error; | ||||
|     } | ||||
|  | ||||
|     throw error; | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import { NotAuthenticated } from "@feathersjs/errors"; | ||||
| import type { HookContext, NextFunction } from "../declarations"; | ||||
| import { NotAuthenticated } from '@feathersjs/errors'; | ||||
| import type { HookContext, NextFunction } from '../declarations'; | ||||
|  | ||||
| // Check if user is stored in session | ||||
| export const validateAuth = async (context: HookContext) => { | ||||
|   if (!context.params.session?.user) { | ||||
|     throw new NotAuthenticated("Not authenticated"); | ||||
|   } | ||||
|     if (!context.params.session?.user) { | ||||
|         throw new NotAuthenticated('Not authenticated'); | ||||
|     } | ||||
| }; | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/index.ts
									
									
									
									
									
								
							| @@ -1,13 +1,11 @@ | ||||
| import { app } from "./app"; | ||||
| import { logger } from "./logger"; | ||||
| import { app } from './app'; | ||||
| import { logger } from './logger'; | ||||
|  | ||||
| const port = app.get("port"); | ||||
| const host = app.get("host"); | ||||
| const port = app.get('port'); | ||||
| const host = app.get('host'); | ||||
|  | ||||
| process.on("unhandledRejection", (reason) => | ||||
|   logger.error("Unhandled Rejection %O", reason), | ||||
| ); | ||||
| process.on('unhandledRejection', reason => logger.error('Unhandled Rejection %O', reason)); | ||||
|  | ||||
| app.listen(port).then(() => { | ||||
|   logger.info(`Feathers app listening on http://${host}:${port}`); | ||||
|     logger.info(`Feathers app listening on http://${host}:${port}`); | ||||
| }); | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html | ||||
| import { createLogger, format, transports } from "winston"; | ||||
| import { createLogger, format, transports } from 'winston'; | ||||
|  | ||||
| // Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston | ||||
| export const logger = createLogger({ | ||||
|   // To see more detailed errors, change this to 'debug' | ||||
|   level: "info", | ||||
|   format: format.combine(format.splat(), format.simple()), | ||||
|   transports: [new transports.Console()], | ||||
|     // To see more detailed errors, change this to 'debug' | ||||
|     level: 'info', | ||||
|     format: format.combine(format.splat(), format.simple()), | ||||
|     transports: [new transports.Console()], | ||||
| }); | ||||
|   | ||||
| @@ -1,33 +1,29 @@ | ||||
| import type { | ||||
|   NullableId, | ||||
|   Params, | ||||
|   ServiceInterface, | ||||
| } from "@feathersjs/feathers"; | ||||
| 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 { BadRequest } from "@feathersjs/errors"; | ||||
| import config from "config"; | ||||
| import type { Application } from '../../declarations'; | ||||
| import wildDuckClient from '../../clients/wildduck.client'; | ||||
| import { faker } from '@faker-js/faker'; | ||||
| import { BadRequest } from '@feathersjs/errors'; | ||||
| import config from 'config'; | ||||
|  | ||||
| interface Alias { | ||||
|   success: boolean; | ||||
|   id: string; | ||||
|   address: string; | ||||
|   main: boolean; | ||||
|   user: string; | ||||
|   tags: string[]; | ||||
|   created: string; | ||||
|     success: boolean; | ||||
|     id: string; | ||||
|     address: string; | ||||
|     main: boolean; | ||||
|     user: string; | ||||
|     tags: string[]; | ||||
|     created: string; | ||||
| } | ||||
|  | ||||
| interface GetAddressInfoResponse { | ||||
|   success: boolean; | ||||
|   results: Alias[]; | ||||
|     success: boolean; | ||||
|     results: Alias[]; | ||||
| } | ||||
|  | ||||
| interface CreateAddressResponse { | ||||
|   success: boolean; | ||||
|   id: string; | ||||
|     success: boolean; | ||||
|     id: string; | ||||
| } | ||||
|  | ||||
| type AliasesData = any; | ||||
| @@ -37,102 +33,85 @@ type AliasesQuery = any; | ||||
| export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery }; | ||||
|  | ||||
| export interface AliasesServiceOptions { | ||||
|   app: Application; | ||||
|     app: Application; | ||||
| } | ||||
|  | ||||
| export interface AliasesParams extends Params<AliasesQuery> { | ||||
|   session?: any; | ||||
|     session?: any; | ||||
| } | ||||
|  | ||||
| export class AliasesService<ServiceParams extends AliasesParams = AliasesParams> | ||||
|   implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch> | ||||
|     implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch> | ||||
| { | ||||
|   constructor(public options: AliasesServiceOptions) {} | ||||
|     constructor(public options: AliasesServiceOptions) {} | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<Alias[]> { | ||||
|     const userId = await this.getUserIdByEmailAddress(params); | ||||
|     async find(params: ServiceParams): Promise<Alias[]> { | ||||
|         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[]> { | ||||
|     const userId = await this.getUserIdByEmailAddress(params); | ||||
|  | ||||
|     const randomString = faker.git.commitSha({ length: 4 }); | ||||
|  | ||||
|     // Replace all non-alphanumeric characters with nothing and spaces with dashes | ||||
|     const alias = | ||||
|       `${faker.color.human()}-${faker.animal.snake()}-${randomString}` | ||||
|         .replace(/\s+/g, "-") | ||||
|         .replace(/[^a-zA-Z0-9-]/g, "") | ||||
|         .toLowerCase(); | ||||
|  | ||||
|     const emailDomain = config.get("wildDuck.domain"); | ||||
|  | ||||
|     const createResult = await wildDuckClient.post<CreateAddressResponse>( | ||||
|       `/users/${userId}/addresses`, | ||||
|       { | ||||
|         address: `${alias}@${emailDomain}`, | ||||
|       }, | ||||
|     ); | ||||
|  | ||||
|     if (!createResult.data.success) { | ||||
|       throw new BadRequest("Failed to create alias"); | ||||
|         return this.getUserAddresses(userId); | ||||
|     } | ||||
|  | ||||
|     return this.getUserAddresses(userId); | ||||
|   } | ||||
|     async create(data: AliasesData, params: ServiceParams): Promise<Alias>; | ||||
|     async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> { | ||||
|         const userId = await this.getUserIdByEmailAddress(params); | ||||
|  | ||||
|   private async getUserIdByEmailAddress( | ||||
|     params: ServiceParams, | ||||
|   ): Promise<string> { | ||||
|     const emails = params.session?.user?.emails; | ||||
|         const randomString = faker.git.commitSha({ length: 4 }); | ||||
|  | ||||
|     const addressInfoResponse = await Promise.any( | ||||
|       emails | ||||
|       .filter((email: string) => email.endsWith(config.get("wildDuck.preferredDomain"))) | ||||
|       .map((email: string) => | ||||
|         wildDuckClient.get<Alias>(`addresses/resolve/${email}`), | ||||
|       ), | ||||
|     ); | ||||
|         // Replace all non-alphanumeric characters with nothing and spaces with dashes | ||||
|         const alias = `${faker.color.human()}-${faker.animal.snake()}-${randomString}` | ||||
|             .replace(/\s+/g, '-') | ||||
|             .replace(/[^a-zA-Z0-9-]/g, '') | ||||
|             .toLowerCase(); | ||||
|  | ||||
|     return addressInfoResponse.data.user; | ||||
|   } | ||||
|         const emailDomain = config.get('wildDuck.domain'); | ||||
|  | ||||
|   private async getUserAddresses(userId: string): Promise<Alias[]> { | ||||
|     const { data: userAddressesResponse } = | ||||
|       await wildDuckClient.get<GetAddressInfoResponse>( | ||||
|         `/users/${userId}/addresses`, | ||||
|       ); | ||||
|         const createResult = await wildDuckClient.post<CreateAddressResponse>(`/users/${userId}/addresses`, { | ||||
|             address: `${alias}@${emailDomain}`, | ||||
|         }); | ||||
|  | ||||
|     return userAddressesResponse.results; | ||||
|   } | ||||
|         if (!createResult.data.success) { | ||||
|             throw new BadRequest('Failed to create alias'); | ||||
|         } | ||||
|  | ||||
|   async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> { | ||||
|     const { data: addressInfoResponse } = await wildDuckClient.get<Alias>( | ||||
|       `addresses/resolve/${id}`, | ||||
|     ); | ||||
|     const allowedDomain: string = config.get("wildDuck.domain"); | ||||
|  | ||||
|     // If address does not match the allowed domain, throw an error | ||||
|     if ( | ||||
|       !allowedDomain || | ||||
|       !addressInfoResponse.address.endsWith(allowedDomain) | ||||
|     ) { | ||||
|       throw new BadRequest("Unable to delete address"); | ||||
|         return this.getUserAddresses(userId); | ||||
|     } | ||||
|     const userId = await this.getUserIdByEmailAddress(params); | ||||
|  | ||||
|     await wildDuckClient.delete<Alias>(`users/${userId}/addresses/${id}`); | ||||
|     private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> { | ||||
|         const emails = params.session?.user?.emails; | ||||
|  | ||||
|     return this.getUserAddresses(userId); | ||||
|   } | ||||
|         const addressInfoResponse = await Promise.any( | ||||
|             emails | ||||
|                 .filter((email: string) => email.endsWith(config.get('wildDuck.preferredDomain'))) | ||||
|                 .map((email: string) => wildDuckClient.get<Alias>(`addresses/resolve/${email}`)) | ||||
|         ); | ||||
|  | ||||
|         return addressInfoResponse.data.user; | ||||
|     } | ||||
|  | ||||
|     private async getUserAddresses(userId: string): Promise<Alias[]> { | ||||
|         const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>( | ||||
|             `/users/${userId}/addresses` | ||||
|         ); | ||||
|  | ||||
|         return userAddressesResponse.results; | ||||
|     } | ||||
|  | ||||
|     async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> { | ||||
|         const { data: addressInfoResponse } = await wildDuckClient.get<Alias>(`addresses/resolve/${id}`); | ||||
|         const allowedDomain: string = config.get('wildDuck.domain'); | ||||
|  | ||||
|         // If address does not match the allowed domain, throw an error | ||||
|         if (!allowedDomain || !addressInfoResponse.address.endsWith(allowedDomain)) { | ||||
|             throw new BadRequest('Unable to delete address'); | ||||
|         } | ||||
|         const userId = await this.getUserIdByEmailAddress(params); | ||||
|  | ||||
|         await wildDuckClient.delete<Alias>(`users/${userId}/addresses/${id}`); | ||||
|  | ||||
|         return this.getUserAddresses(userId); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const getOptions = (app: Application) => { | ||||
|   return { app }; | ||||
|     return { app }; | ||||
| }; | ||||
|   | ||||
| @@ -1,39 +1,39 @@ | ||||
| import type { Application } from "../../declarations"; | ||||
| import { validateAuth } from "../../hooks/validate-auth"; | ||||
| import { AliasesService, getOptions } from "./aliases.class"; | ||||
| import type { Application } from '../../declarations'; | ||||
| import { validateAuth } from '../../hooks/validate-auth'; | ||||
| import { AliasesService, getOptions } from './aliases.class'; | ||||
|  | ||||
| export const aliasesPath = "aliases"; | ||||
| export const aliasesMethods = ["find", "create", "remove"] as const; | ||||
| export const aliasesPath = 'aliases'; | ||||
| export const aliasesMethods = ['find', 'create', 'remove'] as const; | ||||
|  | ||||
| export * from "./aliases.class"; | ||||
| export * from './aliases.class'; | ||||
|  | ||||
| export const aliases = (app: Application) => { | ||||
|   app.use(aliasesPath, new AliasesService(getOptions(app)), { | ||||
|     methods: aliasesMethods, | ||||
|     events: [], | ||||
|   }); | ||||
|     app.use(aliasesPath, new AliasesService(getOptions(app)), { | ||||
|         methods: aliasesMethods, | ||||
|         events: [], | ||||
|     }); | ||||
|  | ||||
|   app.service(aliasesPath).hooks({ | ||||
|     around: { | ||||
|       all: [], | ||||
|     }, | ||||
|     before: { | ||||
|       all: [validateAuth], | ||||
|       find: [], | ||||
|       create: [], | ||||
|     }, | ||||
|     after: { | ||||
|       all: [], | ||||
|     }, | ||||
|     error: { | ||||
|       all: [], | ||||
|     }, | ||||
|   }); | ||||
|     app.service(aliasesPath).hooks({ | ||||
|         around: { | ||||
|             all: [], | ||||
|         }, | ||||
|         before: { | ||||
|             all: [validateAuth], | ||||
|             find: [], | ||||
|             create: [], | ||||
|         }, | ||||
|         after: { | ||||
|             all: [], | ||||
|         }, | ||||
|         error: { | ||||
|             all: [], | ||||
|         }, | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| // Add this service to the service type index | ||||
| declare module "../../declarations" { | ||||
|   interface ServiceTypes { | ||||
|     [aliasesPath]: AliasesService; | ||||
|   } | ||||
| declare module '../../declarations' { | ||||
|     interface ServiceTypes { | ||||
|         [aliasesPath]: AliasesService; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import type { Params, ServiceInterface } from "@feathersjs/feathers"; | ||||
| import type { Params, ServiceInterface } from '@feathersjs/feathers'; | ||||
|  | ||||
| import type { Application } from "../../declarations"; | ||||
| import type { Application } from '../../declarations'; | ||||
|  | ||||
| import { Issuer, generators } from "openid-client"; | ||||
| import config from "config"; | ||||
| import { Issuer, generators } from 'openid-client'; | ||||
| import config from 'config'; | ||||
|  | ||||
| type AuthOidcResponse = string; | ||||
| type AuthOidcQuery = any; | ||||
| @@ -11,43 +11,42 @@ type AuthOidcQuery = any; | ||||
| export type { AuthOidcResponse as AuthOidc, AuthOidcQuery }; | ||||
|  | ||||
| export interface AuthOidcServiceOptions { | ||||
|   app: Application; | ||||
|     app: Application; | ||||
| } | ||||
|  | ||||
| export interface AuthOidcParams extends Params<AuthOidcQuery> { | ||||
|   session?: any; | ||||
|     session?: any; | ||||
| } | ||||
|  | ||||
| export class AuthOidcService< | ||||
|   ServiceParams extends AuthOidcParams = AuthOidcParams, | ||||
| > implements ServiceInterface<AuthOidcResponse, ServiceParams> | ||||
| export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcParams> | ||||
|     implements ServiceInterface<AuthOidcResponse, ServiceParams> | ||||
| { | ||||
|   constructor(public options: AuthOidcServiceOptions) {} | ||||
|     constructor(public options: AuthOidcServiceOptions) {} | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<AuthOidcResponse> { | ||||
|     const issuer = await Issuer.discover(config.get("oidc.gatewayUri")); | ||||
|     const client = new issuer.Client({ | ||||
|       client_id: config.get("oidc.clientId"), | ||||
|       client_secret: config.get("oidc.clientSecret"), | ||||
|       redirect_uris: [config.get("oidc.redirectUris")], | ||||
|       response_types: ["code"], | ||||
|     }); | ||||
|     const codeVerifier = generators.codeVerifier(); | ||||
|     const codeChallenge = generators.codeChallenge(codeVerifier); | ||||
|     async find(params: ServiceParams): Promise<AuthOidcResponse> { | ||||
|         const issuer = await Issuer.discover(config.get('oidc.gatewayUri')); | ||||
|         const client = new issuer.Client({ | ||||
|             client_id: config.get('oidc.clientId'), | ||||
|             client_secret: config.get('oidc.clientSecret'), | ||||
|             redirect_uris: [config.get('oidc.redirectUris')], | ||||
|             response_types: ['code'], | ||||
|         }); | ||||
|         const codeVerifier = generators.codeVerifier(); | ||||
|         const codeChallenge = generators.codeChallenge(codeVerifier); | ||||
|  | ||||
|     const url = client.authorizationUrl({ | ||||
|       redirect_uri: config.get("clientUrl") + "/auth-oidc/callback", | ||||
|       scope: "openid profile offline_access", | ||||
|       response_type: "code", | ||||
|       code_challenge: codeChallenge, | ||||
|       code_challenge_method: "S256", | ||||
|     }); | ||||
|         const url = client.authorizationUrl({ | ||||
|             redirect_uri: config.get('clientUrl') + '/auth-oidc/callback', | ||||
|             scope: 'openid profile offline_access', | ||||
|             response_type: 'code', | ||||
|             code_challenge: codeChallenge, | ||||
|             code_challenge_method: 'S256', | ||||
|         }); | ||||
|  | ||||
|     params.session.codeVerifier = codeVerifier; | ||||
|     return url; | ||||
|   } | ||||
|         params.session.codeVerifier = codeVerifier; | ||||
|         return url; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const getOptions = (app: Application) => { | ||||
|   return { app }; | ||||
|     return { app }; | ||||
| }; | ||||
|   | ||||
| @@ -1,45 +1,45 @@ | ||||
| import type { Application } from "../../declarations"; | ||||
| import { AuthOidcService, getOptions } from "./auth-oidc.class"; | ||||
| import type { Application } from '../../declarations'; | ||||
| import { AuthOidcService, getOptions } from './auth-oidc.class'; | ||||
|  | ||||
| export const authOidcPath = "auth-oidc"; | ||||
| export const authOidcMethods = ["find"] as const; | ||||
| export const authOidcPath = 'auth-oidc'; | ||||
| export const authOidcMethods = ['find'] as const; | ||||
|  | ||||
| export * from "./auth-oidc.class"; | ||||
| export * from './auth-oidc.class'; | ||||
|  | ||||
| export const authOidc = (app: Application) => { | ||||
|   // TODO: fix this to use the correct type | ||||
|   // @ts-ignore | ||||
|   app.use( | ||||
|     authOidcPath, | ||||
|     new AuthOidcService(getOptions(app)), | ||||
|     { | ||||
|       methods: authOidcMethods, | ||||
|       events: [], | ||||
|     }, | ||||
|     (req: any, res: any) => { | ||||
|       return res.redirect(res.data); | ||||
|     }, | ||||
|   ); | ||||
|     // TODO: fix this to use the correct type | ||||
|     // @ts-ignore | ||||
|     app.use( | ||||
|         authOidcPath, | ||||
|         new AuthOidcService(getOptions(app)), | ||||
|         { | ||||
|             methods: authOidcMethods, | ||||
|             events: [], | ||||
|         }, | ||||
|         (req: any, res: any) => { | ||||
|             return res.redirect(res.data); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|   app.service(authOidcPath).hooks({ | ||||
|     around: { | ||||
|       all: [], | ||||
|     }, | ||||
|     before: { | ||||
|       all: [], | ||||
|       find: [], | ||||
|     }, | ||||
|     after: { | ||||
|       all: [], | ||||
|     }, | ||||
|     error: { | ||||
|       all: [], | ||||
|     }, | ||||
|   }); | ||||
|     app.service(authOidcPath).hooks({ | ||||
|         around: { | ||||
|             all: [], | ||||
|         }, | ||||
|         before: { | ||||
|             all: [], | ||||
|             find: [], | ||||
|         }, | ||||
|         after: { | ||||
|             all: [], | ||||
|         }, | ||||
|         error: { | ||||
|             all: [], | ||||
|         }, | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| declare module "../../declarations" { | ||||
|   interface ServiceTypes { | ||||
|     [authOidcPath]: AuthOidcService; | ||||
|   } | ||||
| declare module '../../declarations' { | ||||
|     interface ServiceTypes { | ||||
|         [authOidcPath]: AuthOidcService; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,68 +1,56 @@ | ||||
| import type { Params, ServiceInterface } from "@feathersjs/feathers"; | ||||
| import type { Application } from "../../../declarations"; | ||||
| import { Issuer } from "openid-client"; | ||||
| import type { Params, ServiceInterface } from '@feathersjs/feathers'; | ||||
| import type { Application } from '../../../declarations'; | ||||
| import { Issuer } from 'openid-client'; | ||||
|  | ||||
| import config from "config"; | ||||
| import config from 'config'; | ||||
|  | ||||
| type AuthOidcCallback = string; | ||||
| type AuthOidcCallbackData = any; | ||||
| type AuthOidcCallbackPatch = any; | ||||
| type AuthOidcCallbackQuery = any; | ||||
|  | ||||
| export type { | ||||
|   AuthOidcCallback, | ||||
|   AuthOidcCallbackData, | ||||
|   AuthOidcCallbackPatch, | ||||
|   AuthOidcCallbackQuery, | ||||
| }; | ||||
| export type { AuthOidcCallback, AuthOidcCallbackData, AuthOidcCallbackPatch, AuthOidcCallbackQuery }; | ||||
|  | ||||
| export interface AuthOidcCallbackServiceOptions { | ||||
|   app: Application; | ||||
|     app: Application; | ||||
| } | ||||
|  | ||||
| export interface AuthOidcCallbackParams extends Params<AuthOidcCallbackQuery> { | ||||
|   session?: any; | ||||
|   query: { | ||||
|     iss: string; | ||||
|     code: string; | ||||
|   }; | ||||
|     session?: any; | ||||
|     query: { | ||||
|         iss: string; | ||||
|         code: string; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| export class AuthOidcCallbackService< | ||||
|   ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams, | ||||
| > implements | ||||
|     ServiceInterface< | ||||
|       AuthOidcCallback, | ||||
|       AuthOidcCallbackData, | ||||
|       ServiceParams, | ||||
|       AuthOidcCallbackPatch | ||||
|     > | ||||
| export class AuthOidcCallbackService<ServiceParams extends AuthOidcCallbackParams = AuthOidcCallbackParams> | ||||
|     implements ServiceInterface<AuthOidcCallback, AuthOidcCallbackData, ServiceParams, AuthOidcCallbackPatch> | ||||
| { | ||||
|   constructor(public options: AuthOidcCallbackServiceOptions) {} | ||||
|     constructor(public options: AuthOidcCallbackServiceOptions) {} | ||||
|  | ||||
|   async find(params: ServiceParams): Promise<AuthOidcCallback> { | ||||
|     const issuer = await Issuer.discover(config.get("oidc.gatewayUri")); | ||||
|     const client = new issuer.Client({ | ||||
|       client_id: config.get("oidc.clientId"), | ||||
|       client_secret: config.get("oidc.clientSecret"), | ||||
|       redirect_uris: [config.get("oidc.redirectUris")], | ||||
|       response_types: ["code"], | ||||
|     }); | ||||
|     async find(params: ServiceParams): Promise<AuthOidcCallback> { | ||||
|         const issuer = await Issuer.discover(config.get('oidc.gatewayUri')); | ||||
|         const client = new issuer.Client({ | ||||
|             client_id: config.get('oidc.clientId'), | ||||
|             client_secret: config.get('oidc.clientSecret'), | ||||
|             redirect_uris: [config.get('oidc.redirectUris')], | ||||
|             response_types: ['code'], | ||||
|         }); | ||||
|  | ||||
|     const codeVerifier = params.session.codeVerifier; | ||||
|     const tokenSet = await client.callback( | ||||
|       config.get("clientUrl") + "/auth-oidc/callback", | ||||
|       { code: params.query.code, iss: params.query.iss }, | ||||
|       { code_verifier: codeVerifier }, | ||||
|     ); | ||||
|     const userinfo = await client.userinfo(tokenSet.access_token as string); | ||||
|         const codeVerifier = params.session.codeVerifier; | ||||
|         const tokenSet = await client.callback( | ||||
|             config.get('clientUrl') + '/auth-oidc/callback', | ||||
|             { code: params.query.code, iss: params.query.iss }, | ||||
|             { code_verifier: codeVerifier } | ||||
|         ); | ||||
|         const userinfo = await client.userinfo(tokenSet.access_token as string); | ||||
|  | ||||
|     params.session.user = userinfo; | ||||
|         params.session.user = userinfo; | ||||
|  | ||||
|     return "/"; | ||||
|   } | ||||
|         return '/'; | ||||
|     } | ||||
| } | ||||
|  | ||||
| export const getOptions = (app: Application) => { | ||||
|   return { app }; | ||||
|     return { app }; | ||||
| }; | ||||
|   | ||||
| @@ -1,49 +1,46 @@ | ||||
| import { http } from "@feathersjs/transport-commons"; | ||||
| import type { Application } from "../../../declarations"; | ||||
| import { | ||||
|   AuthOidcCallbackService, | ||||
|   getOptions, | ||||
| } from "./auth-oidc-callback.class"; | ||||
| import { http } from '@feathersjs/transport-commons'; | ||||
| import type { Application } from '../../../declarations'; | ||||
| import { AuthOidcCallbackService, getOptions } from './auth-oidc-callback.class'; | ||||
|  | ||||
| export const authOidcCallbackPath = "auth-oidc/callback"; | ||||
| export const authOidcCallbackMethods = ["find"] as const; | ||||
| export const authOidcCallbackPath = 'auth-oidc/callback'; | ||||
| export const authOidcCallbackMethods = ['find'] as const; | ||||
|  | ||||
| export * from "./auth-oidc-callback.class"; | ||||
| export * from './auth-oidc-callback.class'; | ||||
|  | ||||
| export const authOidcCallback = (app: Application) => { | ||||
|   // TODO: fix this to use the correct type | ||||
|   // @ts-ignore | ||||
|   app.use( | ||||
|     authOidcCallbackPath, | ||||
|     new AuthOidcCallbackService(getOptions(app)), | ||||
|     { | ||||
|       methods: authOidcCallbackMethods, | ||||
|       events: [], | ||||
|     }, | ||||
|     (req: any, res: any) => { | ||||
|       return res.redirect(res.data); | ||||
|     }, | ||||
|   ); | ||||
|     // TODO: fix this to use the correct type | ||||
|     // @ts-ignore | ||||
|     app.use( | ||||
|         authOidcCallbackPath, | ||||
|         new AuthOidcCallbackService(getOptions(app)), | ||||
|         { | ||||
|             methods: authOidcCallbackMethods, | ||||
|             events: [], | ||||
|         }, | ||||
|         (req: any, res: any) => { | ||||
|             return res.redirect(res.data); | ||||
|         } | ||||
|     ); | ||||
|  | ||||
|   app.service(authOidcCallbackPath).hooks({ | ||||
|     around: { | ||||
|       all: [], | ||||
|     }, | ||||
|     before: { | ||||
|       all: [], | ||||
|       find: [], | ||||
|     }, | ||||
|     after: { | ||||
|       all: [], | ||||
|     }, | ||||
|     error: { | ||||
|       all: [], | ||||
|     }, | ||||
|   }); | ||||
|     app.service(authOidcCallbackPath).hooks({ | ||||
|         around: { | ||||
|             all: [], | ||||
|         }, | ||||
|         before: { | ||||
|             all: [], | ||||
|             find: [], | ||||
|         }, | ||||
|         after: { | ||||
|             all: [], | ||||
|         }, | ||||
|         error: { | ||||
|             all: [], | ||||
|         }, | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| declare module "../../../declarations" { | ||||
|   interface ServiceTypes { | ||||
|     [authOidcCallbackPath]: AuthOidcCallbackService; | ||||
|   } | ||||
| declare module '../../../declarations' { | ||||
|     interface ServiceTypes { | ||||
|         [authOidcCallbackPath]: AuthOidcCallbackService; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { authOidcCallback } from "./auth-oidc/callback/auth-oidc-callback"; | ||||
| import { authOidc } from "./auth-oidc/auth-oidc"; | ||||
| import { aliases } from "./aliases/aliases"; | ||||
| import type { Application } from "../declarations"; | ||||
| import { authOidcCallback } from './auth-oidc/callback/auth-oidc-callback'; | ||||
| import { authOidc } from './auth-oidc/auth-oidc'; | ||||
| import { aliases } from './aliases/aliases'; | ||||
| import type { Application } from '../declarations'; | ||||
|  | ||||
| export const services = (app: Application) => { | ||||
|   app.configure(authOidcCallback); | ||||
|   app.configure(authOidc); | ||||
|   app.configure(aliases); | ||||
|     app.configure(authOidcCallback); | ||||
|     app.configure(authOidc); | ||||
|     app.configure(aliases); | ||||
| }; | ||||
|   | ||||
| @@ -1,29 +1,29 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html | ||||
| import { Ajv, addFormats } from "@feathersjs/schema"; | ||||
| import type { FormatsPluginOptions } from "@feathersjs/schema"; | ||||
| import { Ajv, addFormats } from '@feathersjs/schema'; | ||||
| import type { FormatsPluginOptions } from '@feathersjs/schema'; | ||||
|  | ||||
| const formats: FormatsPluginOptions = [ | ||||
|   "date-time", | ||||
|   "time", | ||||
|   "date", | ||||
|   "email", | ||||
|   "hostname", | ||||
|   "ipv4", | ||||
|   "ipv6", | ||||
|   "uri", | ||||
|   "uri-reference", | ||||
|   "uuid", | ||||
|   "uri-template", | ||||
|   "json-pointer", | ||||
|   "relative-json-pointer", | ||||
|   "regex", | ||||
|     'date-time', | ||||
|     'time', | ||||
|     'date', | ||||
|     'email', | ||||
|     'hostname', | ||||
|     'ipv4', | ||||
|     'ipv6', | ||||
|     'uri', | ||||
|     'uri-reference', | ||||
|     'uuid', | ||||
|     'uri-template', | ||||
|     'json-pointer', | ||||
|     'relative-json-pointer', | ||||
|     'regex', | ||||
| ]; | ||||
|  | ||||
| export const dataValidator: Ajv = addFormats(new Ajv({}), formats); | ||||
|  | ||||
| export const queryValidator: Ajv = addFormats( | ||||
|   new Ajv({ | ||||
|     coerceTypes: true, | ||||
|   }), | ||||
|   formats, | ||||
|     new Ajv({ | ||||
|         coerceTypes: true, | ||||
|     }), | ||||
|     formats | ||||
| ); | ||||
|   | ||||
| @@ -1,40 +1,40 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/app.test.html | ||||
| import assert from "assert"; | ||||
| import axios from "axios"; | ||||
| import type { Server } from "http"; | ||||
| import { app } from "../src/app"; | ||||
| import assert from 'assert'; | ||||
| import axios from 'axios'; | ||||
| import type { Server } from 'http'; | ||||
| import { app } from '../src/app'; | ||||
|  | ||||
| const port = app.get("port"); | ||||
| const appUrl = `http://${app.get("host")}:${port}`; | ||||
| const port = app.get('port'); | ||||
| const appUrl = `http://${app.get('host')}:${port}`; | ||||
|  | ||||
| describe("Feathers application tests", () => { | ||||
|   let server: Server; | ||||
| describe('Feathers application tests', () => { | ||||
|     let server: Server; | ||||
|  | ||||
|   before(async () => { | ||||
|     server = await app.listen(port); | ||||
|   }); | ||||
|     before(async () => { | ||||
|         server = await app.listen(port); | ||||
|     }); | ||||
|  | ||||
|   after(async () => { | ||||
|     await app.teardown(); | ||||
|   }); | ||||
|     after(async () => { | ||||
|         await app.teardown(); | ||||
|     }); | ||||
|  | ||||
|   it("starts and shows the index page", async () => { | ||||
|     const { data } = await axios.get<string>(appUrl); | ||||
|     it('starts and shows the index page', async () => { | ||||
|         const { data } = await axios.get<string>(appUrl); | ||||
|  | ||||
|     assert.ok(data.indexOf('<html lang="en">') !== -1); | ||||
|   }); | ||||
|         assert.ok(data.indexOf('<html lang="en">') !== -1); | ||||
|     }); | ||||
|  | ||||
|   it("shows a 404 JSON error", async () => { | ||||
|     try { | ||||
|       await axios.get(`${appUrl}/path/to/nowhere`, { | ||||
|         responseType: "json", | ||||
|       }); | ||||
|       assert.fail("should never get here"); | ||||
|     } catch (error: any) { | ||||
|       const { response } = error; | ||||
|       assert.strictEqual(response?.status, 404); | ||||
|       assert.strictEqual(response?.data?.code, 404); | ||||
|       assert.strictEqual(response?.data?.name, "NotFound"); | ||||
|     } | ||||
|   }); | ||||
|     it('shows a 404 JSON error', async () => { | ||||
|         try { | ||||
|             await axios.get(`${appUrl}/path/to/nowhere`, { | ||||
|                 responseType: 'json', | ||||
|             }); | ||||
|             assert.fail('should never get here'); | ||||
|         } catch (error: any) { | ||||
|             const { response } = error; | ||||
|             assert.strictEqual(response?.status, 404); | ||||
|             assert.strictEqual(response?.data?.code, 404); | ||||
|             assert.strictEqual(response?.data?.name, 'NotFound'); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html | ||||
| import assert from "assert"; | ||||
| import { app } from "../../../src/app"; | ||||
| import assert from 'assert'; | ||||
| import { app } from '../../../src/app'; | ||||
|  | ||||
| describe("aliases service", () => { | ||||
|   it("registered the service", () => { | ||||
|     const service = app.service("aliases"); | ||||
| describe('aliases service', () => { | ||||
|     it('registered the service', () => { | ||||
|         const service = app.service('aliases'); | ||||
|  | ||||
|     assert.ok(service, "Registered the service"); | ||||
|   }); | ||||
|         assert.ok(service, 'Registered the service'); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html | ||||
| import assert from "assert"; | ||||
| import { app } from "../../../src/app"; | ||||
| import assert from 'assert'; | ||||
| import { app } from '../../../src/app'; | ||||
|  | ||||
| describe("auth-oidc service", () => { | ||||
|   it("registered the service", () => { | ||||
|     const service = app.service("auth-oidc"); | ||||
| describe('auth-oidc service', () => { | ||||
|     it('registered the service', () => { | ||||
|         const service = app.service('auth-oidc'); | ||||
|  | ||||
|     assert.ok(service, "Registered the service"); | ||||
|   }); | ||||
|         assert.ok(service, 'Registered the service'); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/service.test.html | ||||
| import assert from "assert"; | ||||
| import { app } from "../../../../src/app"; | ||||
| import assert from 'assert'; | ||||
| import { app } from '../../../../src/app'; | ||||
|  | ||||
| describe("auth-oidc/callback service", () => { | ||||
|   it("registered the service", () => { | ||||
|     const service = app.service("auth-oidc/callback"); | ||||
| describe('auth-oidc/callback service', () => { | ||||
|     it('registered the service', () => { | ||||
|         const service = app.service('auth-oidc/callback'); | ||||
|  | ||||
|     assert.ok(service, "Registered the service"); | ||||
|   }); | ||||
|         assert.ok(service, 'Registered the service'); | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,29 +0,0 @@ | ||||
| // For more information about this file see https://dove.feathersjs.com/guides/cli/validators.html | ||||
| import { Ajv, addFormats } from "@feathersjs/schema"; | ||||
| import type { FormatsPluginOptions } from "@feathersjs/schema"; | ||||
|  | ||||
| const formats: FormatsPluginOptions = [ | ||||
|   "date-time", | ||||
|   "time", | ||||
|   "date", | ||||
|   "email", | ||||
|   "hostname", | ||||
|   "ipv4", | ||||
|   "ipv6", | ||||
|   "uri", | ||||
|   "uri-reference", | ||||
|   "uuid", | ||||
|   "uri-template", | ||||
|   "json-pointer", | ||||
|   "relative-json-pointer", | ||||
|   "regex", | ||||
| ]; | ||||
|  | ||||
| export const dataValidator: Ajv = addFormats(new Ajv({}), formats); | ||||
|  | ||||
| export const queryValidator: Ajv = addFormats( | ||||
|   new Ajv({ | ||||
|     coerceTypes: true, | ||||
|   }), | ||||
|   formats, | ||||
| ); | ||||
		Reference in New Issue
	
	Block a user