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", | ||||
|     "sql-formatter": "^8.2.0", | ||||
|     "ts-pattern": "^4.2.2", | ||||
|     "ua-parser-js": "^1.0.35", | ||||
|     "uuid": "^8.3.2", | ||||
|     "vue": "^3.2.47", | ||||
|     "vue-router": "^4.1.6" | ||||
| @ -85,6 +86,7 @@ | ||||
|     "@types/prettier": "^2.7.2", | ||||
|     "@types/qrcode": "^1.5.0", | ||||
|     "@types/randombytes": "^2.0.0", | ||||
|     "@types/ua-parser-js": "^0.7.36", | ||||
|     "@types/uuid": "^8.3.4", | ||||
|     "@typescript-eslint/parser": "^5.57.0", | ||||
|     "@vitejs/plugin-vue": "^2.3.4", | ||||
|  | ||||
							
								
								
									
										14
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -109,6 +109,9 @@ dependencies: | ||||
|   ts-pattern: | ||||
|     specifier: ^4.2.2 | ||||
|     version: 4.2.2 | ||||
|   ua-parser-js: | ||||
|     specifier: ^1.0.35 | ||||
|     version: 1.0.35 | ||||
|   uuid: | ||||
|     specifier: ^8.3.2 | ||||
|     version: 8.3.2 | ||||
| @ -156,6 +159,9 @@ devDependencies: | ||||
|   '@types/randombytes': | ||||
|     specifier: ^2.0.0 | ||||
|     version: 2.0.0 | ||||
|   '@types/ua-parser-js': | ||||
|     specifier: ^0.7.36 | ||||
|     version: 0.7.36 | ||||
|   '@types/uuid': | ||||
|     specifier: ^8.3.4 | ||||
|     version: 8.3.4 | ||||
| @ -2218,6 +2224,10 @@ packages: | ||||
|     resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /@types/ua-parser-js@0.7.36: | ||||
|     resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /@types/uuid@8.3.4: | ||||
|     resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} | ||||
|     dev: true | ||||
| @ -7511,6 +7521,10 @@ packages: | ||||
|     engines: {node: '>=4.2.0'} | ||||
|     hasBin: true | ||||
| 
 | ||||
|   /ua-parser-js@1.0.35: | ||||
|     resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==} | ||||
|     dev: false | ||||
| 
 | ||||
|   /uc.micro@1.0.6: | ||||
|     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 basicAuthGenerator } from './basic-auth-generator'; | ||||
| 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 dockerRunToDockerComposeConverter } from './docker-run-to-docker-compose-converter'; | ||||
| import { tool as htmlWysiwygEditor } from './html-wysiwyg-editor'; | ||||
| @ -79,6 +80,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       keycodeInfo, | ||||
|       slugifyString, | ||||
|       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