feat(new-tool): add new tool user agent parser (#329)
* fix(docker-run-to-docker-compose-converter): use different version of converter which suppports more options and is mor failsafe * chore(docker-run-to-docker-compose-converter): add pnpm-lock.yaml again which was accidently removed in last commit * chore(docker-run-to-docker-compose-converter): add fixed version of composerize-ts * chore(user-agent-parser): changes requested by code review * chore(user-agent-parser): some more changes requested by code review
This commit is contained in:
		
							parent
							
								
									f3480fe560
								
							
						
					
					
						commit
						f350dc19aa
					
				| @ -68,6 +68,7 @@ | |||||||
|     "randombytes": "^2.1.0", |     "randombytes": "^2.1.0", | ||||||
|     "sql-formatter": "^8.2.0", |     "sql-formatter": "^8.2.0", | ||||||
|     "ts-pattern": "^4.2.2", |     "ts-pattern": "^4.2.2", | ||||||
|  |     "ua-parser-js": "^1.0.35", | ||||||
|     "uuid": "^8.3.2", |     "uuid": "^8.3.2", | ||||||
|     "vue": "^3.2.47", |     "vue": "^3.2.47", | ||||||
|     "vue-router": "^4.1.6" |     "vue-router": "^4.1.6" | ||||||
| @ -85,6 +86,7 @@ | |||||||
|     "@types/prettier": "^2.7.2", |     "@types/prettier": "^2.7.2", | ||||||
|     "@types/qrcode": "^1.5.0", |     "@types/qrcode": "^1.5.0", | ||||||
|     "@types/randombytes": "^2.0.0", |     "@types/randombytes": "^2.0.0", | ||||||
|  |     "@types/ua-parser-js": "^0.7.36", | ||||||
|     "@types/uuid": "^8.3.4", |     "@types/uuid": "^8.3.4", | ||||||
|     "@typescript-eslint/parser": "^5.57.0", |     "@typescript-eslint/parser": "^5.57.0", | ||||||
|     "@vitejs/plugin-vue": "^2.3.4", |     "@vitejs/plugin-vue": "^2.3.4", | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -109,6 +109,9 @@ dependencies: | |||||||
|   ts-pattern: |   ts-pattern: | ||||||
|     specifier: ^4.2.2 |     specifier: ^4.2.2 | ||||||
|     version: 4.2.2 |     version: 4.2.2 | ||||||
|  |   ua-parser-js: | ||||||
|  |     specifier: ^1.0.35 | ||||||
|  |     version: 1.0.35 | ||||||
|   uuid: |   uuid: | ||||||
|     specifier: ^8.3.2 |     specifier: ^8.3.2 | ||||||
|     version: 8.3.2 |     version: 8.3.2 | ||||||
| @ -156,6 +159,9 @@ devDependencies: | |||||||
|   '@types/randombytes': |   '@types/randombytes': | ||||||
|     specifier: ^2.0.0 |     specifier: ^2.0.0 | ||||||
|     version: 2.0.0 |     version: 2.0.0 | ||||||
|  |   '@types/ua-parser-js': | ||||||
|  |     specifier: ^0.7.36 | ||||||
|  |     version: 0.7.36 | ||||||
|   '@types/uuid': |   '@types/uuid': | ||||||
|     specifier: ^8.3.4 |     specifier: ^8.3.4 | ||||||
|     version: 8.3.4 |     version: 8.3.4 | ||||||
| @ -2218,6 +2224,10 @@ packages: | |||||||
|     resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} |     resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /@types/ua-parser-js@0.7.36: | ||||||
|  |     resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==} | ||||||
|  |     dev: true | ||||||
|  | 
 | ||||||
|   /@types/uuid@8.3.4: |   /@types/uuid@8.3.4: | ||||||
|     resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} |     resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} | ||||||
|     dev: true |     dev: true | ||||||
| @ -7511,6 +7521,10 @@ packages: | |||||||
|     engines: {node: '>=4.2.0'} |     engines: {node: '>=4.2.0'} | ||||||
|     hasBin: true |     hasBin: true | ||||||
| 
 | 
 | ||||||
|  |   /ua-parser-js@1.0.35: | ||||||
|  |     resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /uc.micro@1.0.6: |   /uc.micro@1.0.6: | ||||||
|     resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} |     resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; | |||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
| import { tool as benchmarkBuilder } from './benchmark-builder'; | import { tool as benchmarkBuilder } from './benchmark-builder'; | ||||||
|  | import { tool as userAgentParser } from './user-agent-parser'; | ||||||
| import { tool as ipv4SubnetCalculator } from './ipv4-subnet-calculator'; | import { tool as ipv4SubnetCalculator } from './ipv4-subnet-calculator'; | ||||||
| import { tool as dockerRunToDockerComposeConverter } from './docker-run-to-docker-compose-converter'; | import { tool as dockerRunToDockerComposeConverter } from './docker-run-to-docker-compose-converter'; | ||||||
| import { tool as htmlWysiwygEditor } from './html-wysiwyg-editor'; | import { tool as htmlWysiwygEditor } from './html-wysiwyg-editor'; | ||||||
| @ -79,6 +80,7 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|       keycodeInfo, |       keycodeInfo, | ||||||
|       slugifyString, |       slugifyString, | ||||||
|       htmlWysiwygEditor, |       htmlWysiwygEditor, | ||||||
|  |       userAgentParser, | ||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/tools/user-agent-parser/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/user-agent-parser/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { Browser } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'User-agent parser', | ||||||
|  |   path: '/user-agent-parser', | ||||||
|  |   description: 'Detect and parse Browser, Engine, OS, CPU, and Device type/model from an user-agent string.', | ||||||
|  |   keywords: ['user', 'agent', 'parser', 'browser', 'engine', 'os', 'cpu', 'device', 'user-agent', 'client'], | ||||||
|  |   component: () => import('./user-agent-parser.vue'), | ||||||
|  |   icon: Browser, | ||||||
|  |   createdAt: new Date('2023-04-06'), | ||||||
|  | }); | ||||||
							
								
								
									
										12
									
								
								src/tools/user-agent-parser/user-agent-parser.types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/user-agent-parser/user-agent-parser.types.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import type { Component } from 'vue'; | ||||||
|  | import { UAParser } from 'ua-parser-js'; | ||||||
|  | 
 | ||||||
|  | export type UserAgentResultSection = { | ||||||
|  |   heading: string; | ||||||
|  |   icon?: Component; | ||||||
|  |   content: { | ||||||
|  |     label: string; | ||||||
|  |     getValue: (blocks: UAParser.IResult) => string | undefined; | ||||||
|  |     undefinedFallback?: string; | ||||||
|  |   }[]; | ||||||
|  | }; | ||||||
							
								
								
									
										120
									
								
								src/tools/user-agent-parser/user-agent-parser.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/tools/user-agent-parser/user-agent-parser.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | |||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <n-form-item label="User agent string"> | ||||||
|  |       <n-input | ||||||
|  |         v-model:value="ua" | ||||||
|  |         type="textarea" | ||||||
|  |         placeholder="Put your user-agent here..." | ||||||
|  |         clearable | ||||||
|  |         :autosize="{ minRows: 2 }" | ||||||
|  |       /> | ||||||
|  |     </n-form-item> | ||||||
|  | 
 | ||||||
|  |     <user-agent-result-cards :user-agent-info="userAgentInfo" :sections="sections" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { computed, ref } from 'vue'; | ||||||
|  | import { UAParser } from 'ua-parser-js'; | ||||||
|  | import { withDefaultOnError } from '@/utils/defaults'; | ||||||
|  | import { Adjustments, Browser, Cpu, Devices, Engine } from '@vicons/tabler'; | ||||||
|  | import UserAgentResultCards from './user-agent-result-cards.vue'; | ||||||
|  | import type { UserAgentResultSection } from './user-agent-parser.types'; | ||||||
|  | 
 | ||||||
|  | const ua = ref(navigator.userAgent as string); | ||||||
|  | 
 | ||||||
|  | // If not input in the ua field is present return an empty object of type UAParser.IResult because otherwise | ||||||
|  | // UAParser returns the values for the current Browser. This is confusing because results are shown for an empty | ||||||
|  | // UA field value. | ||||||
|  | const getUserAgentInfo = (userAgent: string) => | ||||||
|  |   userAgent.trim().length > 0 | ||||||
|  |     ? UAParser(userAgent.trim()) | ||||||
|  |     : ({ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} } as UAParser.IResult); | ||||||
|  | const userAgentInfo = computed(() => withDefaultOnError(() => getUserAgentInfo(ua.value), undefined)); | ||||||
|  | 
 | ||||||
|  | const sections: UserAgentResultSection[] = [ | ||||||
|  |   { | ||||||
|  |     heading: 'Browser', | ||||||
|  |     icon: Browser, | ||||||
|  |     content: [ | ||||||
|  |       { | ||||||
|  |         label: 'Name', | ||||||
|  |         getValue: (block) => block.browser.name, | ||||||
|  |         undefinedFallback: 'No browser name available', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'Version', | ||||||
|  |         getValue: (block) => block.browser.version, | ||||||
|  |         undefinedFallback: 'No browser version available', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     heading: 'Engine', | ||||||
|  |     icon: Engine, | ||||||
|  |     content: [ | ||||||
|  |       { | ||||||
|  |         label: 'Name', | ||||||
|  |         getValue: (block) => block.engine.name, | ||||||
|  |         undefinedFallback: 'No engine name available', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'Version', | ||||||
|  |         getValue: (block) => block.engine.version, | ||||||
|  |         undefinedFallback: 'No engine version available', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     heading: 'OS', | ||||||
|  |     icon: Adjustments, | ||||||
|  |     content: [ | ||||||
|  |       { | ||||||
|  |         label: 'Name', | ||||||
|  |         getValue: (block) => block.os.name, | ||||||
|  |         undefinedFallback: 'No OS name available', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'Version', | ||||||
|  |         getValue: (block) => block.os.version, | ||||||
|  |         undefinedFallback: 'No OS version available', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     heading: 'Device', | ||||||
|  |     icon: Devices, | ||||||
|  |     content: [ | ||||||
|  |       { | ||||||
|  |         label: 'Model', | ||||||
|  |         getValue: (block) => block.device.model, | ||||||
|  |         undefinedFallback: 'No device model available', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'Type', | ||||||
|  |         getValue: (block) => block.device.type, | ||||||
|  |         undefinedFallback: 'No device type available', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'Vendor', | ||||||
|  |         getValue: (block) => block.device.vendor, | ||||||
|  |         undefinedFallback: 'No device vendor available', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     heading: 'CPU', | ||||||
|  |     icon: Cpu, | ||||||
|  |     content: [ | ||||||
|  |       { | ||||||
|  |         label: 'Architecture', | ||||||
|  |         getValue: (block) => block.cpu.architecture, | ||||||
|  |         undefinedFallback: 'No CPU architecture available', | ||||||
|  |       }, | ||||||
|  |     ], | ||||||
|  |   }, | ||||||
|  | ]; | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="less" scoped></style> | ||||||
							
								
								
									
										50
									
								
								src/tools/user-agent-parser/user-agent-result-cards.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/tools/user-agent-parser/user-agent-result-cards.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <n-grid :x-gap="12" :y-gap="8" cols="1 s:2" responsive="screen"> | ||||||
|  |       <n-gi v-for="{ heading, icon, content } in sections" :key="heading"> | ||||||
|  |         <n-card style="height: 100%"> | ||||||
|  |           <n-page-header> | ||||||
|  |             <template #title> | ||||||
|  |               {{ heading }} | ||||||
|  |             </template> | ||||||
|  |             <template v-if="icon" #avatar> | ||||||
|  |               <n-icon size="30" :component="icon" :depth="3" /> | ||||||
|  |             </template> | ||||||
|  |           </n-page-header> | ||||||
|  | 
 | ||||||
|  |           <br /> | ||||||
|  | 
 | ||||||
|  |           <n-space> | ||||||
|  |             <span v-for="{ label, getValue } in content" :key="label"> | ||||||
|  |               <n-tooltip v-if="getValue(userAgentInfo)" trigger="hover"> | ||||||
|  |                 <template #trigger> | ||||||
|  |                   <n-tag type="success" size="large" round :bordered="false"> | ||||||
|  |                     {{ getValue(userAgentInfo) }} | ||||||
|  |                   </n-tag> | ||||||
|  |                 </template> | ||||||
|  |                 {{ label }} | ||||||
|  |               </n-tooltip> | ||||||
|  |             </span> | ||||||
|  |           </n-space> | ||||||
|  |           <n-space vertical> | ||||||
|  |             <span v-for="{ label, getValue, undefinedFallback } in content" :key="label"> | ||||||
|  |               <n-text v-if="getValue(userAgentInfo) === undefined" depth="3">{{ undefinedFallback }}</n-text> | ||||||
|  |             </span> | ||||||
|  |           </n-space> | ||||||
|  |         </n-card> | ||||||
|  |       </n-gi> | ||||||
|  |     </n-grid> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import { toRefs } from 'vue'; | ||||||
|  | import { UAParser } from 'ua-parser-js'; | ||||||
|  | import type { UserAgentResultSection } from './user-agent-parser.types'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  |   userAgentInfo?: UAParser.IResult; | ||||||
|  |   sections: UserAgentResultSection[]; | ||||||
|  | }>(); | ||||||
|  | const { userAgentInfo, sections } = toRefs(props); | ||||||
|  | </script> | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user