finished: check update
This commit is contained in:
		
							parent
							
								
									b7d64cba7b
								
							
						
					
					
						commit
						e0dbd03397
					
				
							
								
								
									
										22
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								app.go
									
									
									
									
									
								
							| @ -1,7 +1,12 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"MacFastLookup/service" | ||||
| 	"context" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 
 | ||||
| 	"github.com/wailsapp/wails/v2/pkg/runtime" | ||||
| ) | ||||
| 
 | ||||
| // App struct | ||||
| @ -19,3 +24,20 @@ func NewApp() *App { | ||||
| func (a *App) startup(ctx context.Context) { | ||||
| 	a.ctx = ctx | ||||
| } | ||||
| 
 | ||||
| func (a *App) StartDownload() { | ||||
| 	dbFileDir := ".macfastlookup" | ||||
| 	userHomeDir, _ := os.UserHomeDir() | ||||
| 	dbFileName := "mac_vendors.sqlite3" | ||||
| 	dbFilePath := filepath.Join(userHomeDir, dbFileDir, dbFileName) | ||||
| 
 | ||||
| 	// create the folder if not exist | ||||
| 	path := filepath.Join(userHomeDir, dbFileDir) | ||||
| 	if _, err := os.Stat(path); os.IsNotExist(err) { | ||||
| 		os.Mkdir(path, 0755) | ||||
| 	} | ||||
| 
 | ||||
| 	service.DownloadFile("https://tools.taurusxin.com/macfastlookup/mac_vendors.sqlite3", dbFilePath, true, func(progress, total int64) { | ||||
| 		runtime.EventsEmit(a.ctx, "download-progress", float32(progress / total)) | ||||
| 	}) | ||||
| } | ||||
| @ -11,6 +11,8 @@ | ||||
|   "dependencies": { | ||||
|     "@radix-ui/react-alert-dialog": "^1.1.1", | ||||
|     "@radix-ui/react-checkbox": "^1.1.1", | ||||
|     "@radix-ui/react-dialog": "^1.1.1", | ||||
|     "@radix-ui/react-progress": "^1.1.0", | ||||
|     "@radix-ui/react-slot": "^1.1.0", | ||||
|     "@radix-ui/react-toast": "^1.2.1", | ||||
|     "class-variance-authority": "^0.7.0", | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| 6c7c0ac0f4deb7b7027a4a528f0c2aca | ||||
| 50363c2ee2f3f6316fa41844894a6d9c | ||||
							
								
								
									
										29
									
								
								frontend/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										29
									
								
								frontend/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -14,6 +14,12 @@ importers: | ||||
|       '@radix-ui/react-checkbox': | ||||
|         specifier: ^1.1.1 | ||||
|         version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) | ||||
|       '@radix-ui/react-dialog': | ||||
|         specifier: ^1.1.1 | ||||
|         version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) | ||||
|       '@radix-ui/react-progress': | ||||
|         specifier: ^1.1.0 | ||||
|         version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) | ||||
|       '@radix-ui/react-slot': | ||||
|         specifier: ^1.1.0 | ||||
|         version: 1.1.0(@types/react@18.3.3)(react@18.3.1) | ||||
| @ -417,6 +423,19 @@ packages: | ||||
|       '@types/react-dom': | ||||
|         optional: true | ||||
| 
 | ||||
|   '@radix-ui/react-progress@1.1.0': | ||||
|     resolution: {integrity: sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==} | ||||
|     peerDependencies: | ||||
|       '@types/react': '*' | ||||
|       '@types/react-dom': '*' | ||||
|       react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc | ||||
|       react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc | ||||
|     peerDependenciesMeta: | ||||
|       '@types/react': | ||||
|         optional: true | ||||
|       '@types/react-dom': | ||||
|         optional: true | ||||
| 
 | ||||
|   '@radix-ui/react-slot@1.1.0': | ||||
|     resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} | ||||
|     peerDependencies: | ||||
| @ -1685,6 +1704,16 @@ snapshots: | ||||
|       '@types/react': 18.3.3 | ||||
|       '@types/react-dom': 18.3.0 | ||||
| 
 | ||||
|   '@radix-ui/react-progress@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': | ||||
|     dependencies: | ||||
|       '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) | ||||
|       '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) | ||||
|       react: 18.3.1 | ||||
|       react-dom: 18.3.1(react@18.3.1) | ||||
|     optionalDependencies: | ||||
|       '@types/react': 18.3.3 | ||||
|       '@types/react-dom': 18.3.0 | ||||
| 
 | ||||
|   '@radix-ui/react-slot@1.1.0(@types/react@18.3.3)(react@18.3.1)': | ||||
|     dependencies: | ||||
|       '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) | ||||
|  | ||||
							
								
								
									
										120
									
								
								frontend/src/components/ui/dialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								frontend/src/components/ui/dialog.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,120 @@ | ||||
| import * as React from "react" | ||||
| import * as DialogPrimitive from "@radix-ui/react-dialog" | ||||
| import { X } from "lucide-react" | ||||
| 
 | ||||
| import { cn } from "@/lib/utils" | ||||
| 
 | ||||
| const Dialog = DialogPrimitive.Root | ||||
| 
 | ||||
| const DialogTrigger = DialogPrimitive.Trigger | ||||
| 
 | ||||
| const DialogPortal = DialogPrimitive.Portal | ||||
| 
 | ||||
| const DialogClose = DialogPrimitive.Close | ||||
| 
 | ||||
| const DialogOverlay = React.forwardRef< | ||||
|   React.ElementRef<typeof DialogPrimitive.Overlay>, | ||||
|   React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> | ||||
| >(({ className, ...props }, ref) => ( | ||||
|   <DialogPrimitive.Overlay | ||||
|     ref={ref} | ||||
|     className={cn( | ||||
|       "fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", | ||||
|       className | ||||
|     )} | ||||
|     {...props} | ||||
|   /> | ||||
| )) | ||||
| DialogOverlay.displayName = DialogPrimitive.Overlay.displayName | ||||
| 
 | ||||
| const DialogContent = React.forwardRef< | ||||
|   React.ElementRef<typeof DialogPrimitive.Content>, | ||||
|   React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> | ||||
| >(({ className, children, ...props }, ref) => ( | ||||
|   <DialogPortal> | ||||
|     <DialogOverlay /> | ||||
|     <DialogPrimitive.Content | ||||
|       ref={ref} | ||||
|       className={cn( | ||||
|         "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", | ||||
|         className | ||||
|       )} | ||||
|       {...props} | ||||
|     > | ||||
|       {children} | ||||
|       <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> | ||||
|         <X className="h-4 w-4" /> | ||||
|         <span className="sr-only">Close</span> | ||||
|       </DialogPrimitive.Close> | ||||
|     </DialogPrimitive.Content> | ||||
|   </DialogPortal> | ||||
| )) | ||||
| DialogContent.displayName = DialogPrimitive.Content.displayName | ||||
| 
 | ||||
| const DialogHeader = ({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.HTMLAttributes<HTMLDivElement>) => ( | ||||
|   <div | ||||
|     className={cn( | ||||
|       "flex flex-col space-y-1.5 text-center sm:text-left", | ||||
|       className | ||||
|     )} | ||||
|     {...props} | ||||
|   /> | ||||
| ) | ||||
| DialogHeader.displayName = "DialogHeader" | ||||
| 
 | ||||
| const DialogFooter = ({ | ||||
|   className, | ||||
|   ...props | ||||
| }: React.HTMLAttributes<HTMLDivElement>) => ( | ||||
|   <div | ||||
|     className={cn( | ||||
|       "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", | ||||
|       className | ||||
|     )} | ||||
|     {...props} | ||||
|   /> | ||||
| ) | ||||
| DialogFooter.displayName = "DialogFooter" | ||||
| 
 | ||||
| const DialogTitle = React.forwardRef< | ||||
|   React.ElementRef<typeof DialogPrimitive.Title>, | ||||
|   React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> | ||||
| >(({ className, ...props }, ref) => ( | ||||
|   <DialogPrimitive.Title | ||||
|     ref={ref} | ||||
|     className={cn( | ||||
|       "text-lg font-semibold leading-none tracking-tight", | ||||
|       className | ||||
|     )} | ||||
|     {...props} | ||||
|   /> | ||||
| )) | ||||
| DialogTitle.displayName = DialogPrimitive.Title.displayName | ||||
| 
 | ||||
| const DialogDescription = React.forwardRef< | ||||
|   React.ElementRef<typeof DialogPrimitive.Description>, | ||||
|   React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> | ||||
| >(({ className, ...props }, ref) => ( | ||||
|   <DialogPrimitive.Description | ||||
|     ref={ref} | ||||
|     className={cn("text-sm text-muted-foreground", className)} | ||||
|     {...props} | ||||
|   /> | ||||
| )) | ||||
| DialogDescription.displayName = DialogPrimitive.Description.displayName | ||||
| 
 | ||||
| export { | ||||
|   Dialog, | ||||
|   DialogPortal, | ||||
|   DialogOverlay, | ||||
|   DialogClose, | ||||
|   DialogTrigger, | ||||
|   DialogContent, | ||||
|   DialogHeader, | ||||
|   DialogFooter, | ||||
|   DialogTitle, | ||||
|   DialogDescription, | ||||
| } | ||||
							
								
								
									
										26
									
								
								frontend/src/components/ui/progress.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								frontend/src/components/ui/progress.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| import * as React from "react" | ||||
| import * as ProgressPrimitive from "@radix-ui/react-progress" | ||||
| 
 | ||||
| import { cn } from "@/lib/utils" | ||||
| 
 | ||||
| const Progress = React.forwardRef< | ||||
|   React.ElementRef<typeof ProgressPrimitive.Root>, | ||||
|   React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root> | ||||
| >(({ className, value, ...props }, ref) => ( | ||||
|   <ProgressPrimitive.Root | ||||
|     ref={ref} | ||||
|     className={cn( | ||||
|       "relative h-4 w-full overflow-hidden rounded-full bg-secondary", | ||||
|       className | ||||
|     )} | ||||
|     {...props} | ||||
|   > | ||||
|     <ProgressPrimitive.Indicator | ||||
|       className="h-full w-full flex-1 bg-primary transition-all" | ||||
|       style={{ transform: `translateX(-${100 - (value || 0)}%)` }} | ||||
|     /> | ||||
|   </ProgressPrimitive.Root> | ||||
| )) | ||||
| Progress.displayName = ProgressPrimitive.Root.displayName | ||||
| 
 | ||||
| export { Progress } | ||||
| @ -1,4 +1,3 @@ | ||||
| import React from 'react' | ||||
| import {createRoot} from 'react-dom/client' | ||||
| import './style.css' | ||||
| import App from './App' | ||||
| @ -8,7 +7,5 @@ const container = document.getElementById('root') | ||||
| const root = createRoot(container!) | ||||
| 
 | ||||
| root.render( | ||||
|     <React.StrictMode> | ||||
|   <App/> | ||||
|     </React.StrictMode> | ||||
| ) | ||||
|  | ||||
							
								
								
									
										59
									
								
								frontend/src/pages/Download.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								frontend/src/pages/Download.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| import { useEffect, useState } from 'react' | ||||
| import { EventsOn } from '../../wailsjs/runtime/runtime' | ||||
| import { StartDownload } from '../../wailsjs/go/main/App' | ||||
| import { Close } from '../../wailsjs/go/service/SQLiteHelper' | ||||
| import { | ||||
|   Dialog, | ||||
|   DialogContent, | ||||
|   DialogDescription, | ||||
|   DialogFooter, | ||||
|   DialogHeader, | ||||
|   DialogTitle, | ||||
| } from '@/components/ui/dialog' | ||||
| import { Button } from '@/components/ui/button' | ||||
| import { Progress } from '@/components/ui/progress' | ||||
| 
 | ||||
| const Download = (props: { onFinish: () => void }) => { | ||||
|   const [isOpen, setIsOpen] = useState(true) | ||||
| 
 | ||||
|   const [downloadProgress, setDownloadProgress] = useState<number>(0.0) | ||||
|   const [isDisabled, setIsDisabled] = useState(true) | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     Close().then(res => { | ||||
|       StartDownload().then(res => { | ||||
|         console.log(res) | ||||
|       }) | ||||
|       EventsOn('download-progress', (progress: number) => { | ||||
|         setDownloadProgress(progress * 100) | ||||
|         if (progress === 1) { | ||||
|           props.onFinish() | ||||
|           setIsDisabled(false) | ||||
|         } | ||||
|       }) | ||||
|     }) | ||||
|   }, []) | ||||
| 
 | ||||
|   return ( | ||||
|     <Dialog open={isOpen}> | ||||
|       <DialogContent> | ||||
|         <DialogHeader> | ||||
|           <DialogTitle>Downloading</DialogTitle> | ||||
|         </DialogHeader> | ||||
|         <Progress value={downloadProgress} /> | ||||
|         <DialogFooter> | ||||
|           <Button | ||||
|             onClick={() => { | ||||
|               setIsOpen(false) | ||||
|             }} | ||||
|             disabled={isDisabled} | ||||
|           > | ||||
|             Close | ||||
|           </Button> | ||||
|         </DialogFooter> | ||||
|       </DialogContent> | ||||
|     </Dialog> | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| export default Download | ||||
| @ -3,13 +3,33 @@ import React, { useEffect, useState } from 'react' | ||||
| import { Button } from '@/components/ui/button' | ||||
| import { Input } from '@/components/ui/input' | ||||
| import { Checkbox } from '@/components/ui/checkbox' | ||||
| 
 | ||||
| import { toast } from 'sonner' | ||||
| 
 | ||||
| import { QueryMacAddressByPrefix, QueryVersion } from '../../wailsjs/go/service/SQLiteHelper' | ||||
| import { | ||||
|   CheckBeforeInit, | ||||
|   InitSQLiteHelper, | ||||
|   QueryMacAddressByPrefix, | ||||
|   QueryVersion, | ||||
| } from '../../wailsjs/go/service/SQLiteHelper' | ||||
| import { GetLatestVersion } from '../../wailsjs/go/service/UpdateService' | ||||
| import { ReloadDatabase } from '../../wailsjs/go/service/SQLiteHelper' | ||||
| import { | ||||
|   AlertDialog, | ||||
|   AlertDialogAction, | ||||
|   AlertDialogCancel, | ||||
|   AlertDialogContent, | ||||
|   AlertDialogDescription, | ||||
|   AlertDialogFooter, | ||||
|   AlertDialogHeader, | ||||
|   AlertDialogTitle, | ||||
|   AlertDialogTrigger, | ||||
| } from '@/components/ui/alert-dialog' | ||||
| 
 | ||||
| import Download from './Download' | ||||
| 
 | ||||
| const Panel: React.FC = () => { | ||||
|   const [macInputDisabled, setMacInputDisabled] = useState(false) | ||||
| 
 | ||||
|   const [macAddress, setMacAddress] = useState('') | ||||
|   const [prefix, setPrefix] = useState('') | ||||
|   const [vendorName, setVendorName] = useState('') | ||||
| @ -20,14 +40,46 @@ const Panel: React.FC = () => { | ||||
| 
 | ||||
|   const [checkDisabled, setCheckDisabled] = useState(false) | ||||
| 
 | ||||
|   const [newVersion, setNewVersion] = useState('') | ||||
|   const [isOpen, setIsOpen] = useState(false) | ||||
|   const [isDownloading, setIsDownloading] = useState(false) | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     CheckBeforeInit().then(res => { | ||||
|       if (res) { | ||||
|         InitSQLiteHelper().then(res => { | ||||
|           if (res) { | ||||
|             QueryVersion().then(res => { | ||||
|               setDatabaseDate(res) | ||||
|             }) | ||||
|           } else { | ||||
|             toast('Init dababase failed.', { | ||||
|               description: '⛓️💥 Please check your internet connection.', | ||||
|               action: { | ||||
|                 label: 'Close', | ||||
|                 onClick: () => {}, | ||||
|               }, | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       } else { | ||||
|         setCheckDisabled(true) | ||||
|         setMacInputDisabled(true) | ||||
|         // download
 | ||||
|       } | ||||
|     }) | ||||
|   }, []) | ||||
| 
 | ||||
|   const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||
|     setMacAddress(e.target.value) | ||||
|     if (e.target.value === '') { | ||||
|       setPrefix('') | ||||
|       setVendorName('') | ||||
|       setIsPrivate(false) | ||||
|       setBlockType('') | ||||
|       setLastUpdate('') | ||||
|       return | ||||
|     } | ||||
|     let address = e.target.value | ||||
|       .trim() | ||||
|       .toUpperCase() | ||||
| @ -60,7 +112,7 @@ const Panel: React.FC = () => { | ||||
|       if (res.Version === null) { | ||||
|         setCheckDisabled(false) | ||||
|         toast('Check update failed.', { | ||||
|           description: 'Please check your internet connection.', | ||||
|           description: '⛓️💥 Please check your internet connection.', | ||||
|           action: { | ||||
|             label: 'Close', | ||||
|             onClick: () => {}, | ||||
| @ -77,6 +129,9 @@ const Panel: React.FC = () => { | ||||
|             }, | ||||
|           }) | ||||
|         } else { | ||||
|           // alert
 | ||||
|           setNewVersion(res) | ||||
|           setIsOpen(true) | ||||
|         } | ||||
|       } | ||||
|     }) | ||||
| @ -90,6 +145,7 @@ const Panel: React.FC = () => { | ||||
|           value={macAddress} | ||||
|           onChange={handleTextChange} | ||||
|           placeholder="Enter MAC Address" | ||||
|           disabled={macInputDisabled} | ||||
|         /> | ||||
|       </div> | ||||
|       <div className="mb-2 flex items-center"> | ||||
| @ -113,7 +169,7 @@ const Panel: React.FC = () => { | ||||
|         <Input className="w-2/3 p-1 border h-8" value={lastUpdate} readOnly /> | ||||
|       </div> | ||||
|       <div className="flex items-center"> | ||||
|         <label className="w-1/3 select-none">Database</label> | ||||
|         <label className="w-1/3 select-none">Mac Database</label> | ||||
|         <Input className="w-5/12 p-1 border h-8" value={databaseDate} readOnly /> | ||||
|         <Button | ||||
|           className="ml-2 p-1 h-8 w-1/4 select-none" | ||||
| @ -123,6 +179,46 @@ const Panel: React.FC = () => { | ||||
|           Check | ||||
|         </Button> | ||||
|       </div> | ||||
|       <AlertDialog open={isOpen}> | ||||
|         <AlertDialogContent> | ||||
|           <AlertDialogHeader> | ||||
|             <AlertDialogTitle>Update Available</AlertDialogTitle> | ||||
|             <AlertDialogDescription> | ||||
|               Version {newVersion} is available. Do you want to download it now? | ||||
|             </AlertDialogDescription> | ||||
|           </AlertDialogHeader> | ||||
|           <AlertDialogFooter> | ||||
|             <AlertDialogCancel | ||||
|               onClick={() => { | ||||
|                 setIsOpen(false) | ||||
|               }} | ||||
|             > | ||||
|               No | ||||
|             </AlertDialogCancel> | ||||
|             <AlertDialogAction | ||||
|               onClick={() => { | ||||
|                 setIsOpen(false) | ||||
|                 setIsDownloading(true) | ||||
|               }} | ||||
|             > | ||||
|               Yes | ||||
|             </AlertDialogAction> | ||||
|           </AlertDialogFooter> | ||||
|         </AlertDialogContent> | ||||
|       </AlertDialog> | ||||
|       {isDownloading && ( | ||||
|         <Download | ||||
|           onFinish={() => { | ||||
|             ReloadDatabase().then(res => { | ||||
|               if (res) { | ||||
|                 QueryVersion().then(res => { | ||||
|                   setDatabaseDate(res) | ||||
|                 }) | ||||
|               } | ||||
|             }) | ||||
|           }} | ||||
|         /> | ||||
|       )} | ||||
|     </div> | ||||
|   ) | ||||
| } | ||||
|  | ||||
							
								
								
									
										4
									
								
								frontend/wailsjs/go/main/App.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/wailsjs/go/main/App.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
 | ||||
| // This file is automatically generated. DO NOT EDIT
 | ||||
| 
 | ||||
| export function StartDownload():Promise<void>; | ||||
							
								
								
									
										7
									
								
								frontend/wailsjs/go/main/App.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								frontend/wailsjs/go/main/App.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| // @ts-check
 | ||||
| // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
 | ||||
| // This file is automatically generated. DO NOT EDIT
 | ||||
| 
 | ||||
| export function StartDownload() { | ||||
|   return window['go']['main']['App']['StartDownload'](); | ||||
| } | ||||
| @ -2,8 +2,12 @@ | ||||
| // This file is automatically generated. DO NOT EDIT
 | ||||
| import {service} from '../models'; | ||||
| 
 | ||||
| export function CheckBeforeInit():Promise<boolean>; | ||||
| 
 | ||||
| export function Close():Promise<void>; | ||||
| 
 | ||||
| export function InitSQLiteHelper():Promise<boolean>; | ||||
| 
 | ||||
| export function QueryMacAddressByPrefix(arg1:string):Promise<service.MacAddress>; | ||||
| 
 | ||||
| export function QueryVersion():Promise<string>; | ||||
|  | ||||
| @ -2,10 +2,18 @@ | ||||
| // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
 | ||||
| // This file is automatically generated. DO NOT EDIT
 | ||||
| 
 | ||||
| export function CheckBeforeInit() { | ||||
|   return window['go']['service']['SQLiteHelper']['CheckBeforeInit'](); | ||||
| } | ||||
| 
 | ||||
| export function Close() { | ||||
|   return window['go']['service']['SQLiteHelper']['Close'](); | ||||
| } | ||||
| 
 | ||||
| export function InitSQLiteHelper() { | ||||
|   return window['go']['service']['SQLiteHelper']['InitSQLiteHelper'](); | ||||
| } | ||||
| 
 | ||||
| export function QueryMacAddressByPrefix(arg1) { | ||||
|   return window['go']['service']['SQLiteHelper']['QueryMacAddressByPrefix'](arg1); | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								main.go
									
									
									
									
									
								
							| @ -3,8 +3,6 @@ package main | ||||
| import ( | ||||
| 	"MacFastLookup/service" | ||||
| 	"embed" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 
 | ||||
| 	"github.com/wailsapp/wails/v2" | ||||
| 	"github.com/wailsapp/wails/v2/pkg/options" | ||||
| @ -19,11 +17,7 @@ func main() { | ||||
| 	app := NewApp() | ||||
| 
 | ||||
| 	// Create service | ||||
| 	dbFileDir := "MacFastLookup" | ||||
| 	userHomeDir, _ := os.UserHomeDir() | ||||
| 	dbFileName := "mac_vendors.sqlite3" | ||||
| 	dbFilePath := filepath.Join(userHomeDir, dbFileDir, dbFileName) | ||||
| 	dbService := service.NewSQLiteHelper(dbFilePath) | ||||
| 	dbService := service.NewSQLiteHelper() | ||||
| 
 | ||||
| 	// create update service | ||||
| 	updateService := service.NewUpdateService("https://tools.taurusxin.com/macfastlookup/latest_version.txt") | ||||
|  | ||||
							
								
								
									
										83
									
								
								service/download.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								service/download.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| package service | ||||
| 
 | ||||
| import ( | ||||
|     "fmt" | ||||
|     "io" | ||||
|     "net/http" | ||||
|     "os" | ||||
|     "strconv" | ||||
| ) | ||||
| 
 | ||||
| // ProgressWriter is a custom writer to track progress and execute a callback | ||||
| type ProgressWriter struct { | ||||
|     io.Writer | ||||
|     Total     int64 | ||||
|     Progress  int64 | ||||
|     Callback  func(progress int64, total int64) | ||||
| } | ||||
| 
 | ||||
| func (pw *ProgressWriter) Write(p []byte) (int, error) { | ||||
|     n, err := pw.Writer.Write(p) | ||||
|     if err != nil { | ||||
|         return n, err | ||||
|     } | ||||
|     pw.Progress += int64(n) | ||||
|     pw.Callback(pw.Progress, pw.Total) | ||||
|     return n, nil | ||||
| } | ||||
| 
 | ||||
| func DownloadFile(url string, filePath string, overwrite bool, callback func(progress int64, total int64)) error { | ||||
|     // Check if the file exists | ||||
|     if _, err := os.Stat(filePath); err == nil { | ||||
|         if overwrite { | ||||
|             // Overwrite the file if it exists | ||||
|             if err := os.Remove(filePath); err != nil { | ||||
|                 return err | ||||
|             } | ||||
|         } else { | ||||
|             return fmt.Errorf("file %s already exists", filePath) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Create the file | ||||
|     out, err := os.Create(filePath) | ||||
|     if err != nil { | ||||
|         return err | ||||
|     } | ||||
|     defer out.Close() | ||||
| 
 | ||||
|     // Get the data | ||||
|     resp, err := http.Get(url) | ||||
|     if err != nil { | ||||
|         return err | ||||
|     } | ||||
|     defer resp.Body.Close() | ||||
| 
 | ||||
| 
 | ||||
|     // Check server response | ||||
|     if resp.StatusCode != http.StatusOK { | ||||
|         return fmt.Errorf("bad status: %s", resp.Status) | ||||
|     } | ||||
| 
 | ||||
|     // Get the size | ||||
|     size, err := strconv.Atoi(resp.Header.Get("Content-Length")) | ||||
|     if err != nil { | ||||
|         return err | ||||
|     } | ||||
| 
 | ||||
|     // Create progress writer | ||||
|     pw := &ProgressWriter{ | ||||
|         Writer:   out, | ||||
|         Total:    int64(size), | ||||
|         Callback: callback, | ||||
|     } | ||||
| 
 | ||||
|     // Write the body to file with progress | ||||
|     _, err = io.Copy(pw, resp.Body) | ||||
|     if err != nil { | ||||
|         return err | ||||
|     } | ||||
| 
 | ||||
|     fmt.Println("\nDownload complete") | ||||
|     return nil | ||||
| } | ||||
| @ -3,7 +3,8 @@ package service | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 
 | ||||
| 	_ "github.com/mattn/go-sqlite3" | ||||
| ) | ||||
| @ -18,21 +19,50 @@ type MacAddress struct { | ||||
| } | ||||
| 
 | ||||
| type SQLiteHelper struct { | ||||
| 	connectionString string | ||||
| 	db *sql.DB | ||||
| } | ||||
| 
 | ||||
| func NewSQLiteHelper(dbFilePath string) *SQLiteHelper { | ||||
| 	connectionString := fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbFilePath) | ||||
| 	db, err := sql.Open("sqlite3", connectionString) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| // create the instance of SQLiteHelper but not initialize the database connection | ||||
| func NewSQLiteHelper() *SQLiteHelper { | ||||
| 	return &SQLiteHelper{ | ||||
| 		db: nil, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 	return &SQLiteHelper{ | ||||
| 		connectionString: connectionString, | ||||
| 		db:               db, | ||||
| // check if the directory and file for the database exist | ||||
| func (helper *SQLiteHelper) CheckBeforeInit() bool { | ||||
| 	userHomeDir, _ := os.UserHomeDir() | ||||
| 	dbFileDir := filepath.Join(userHomeDir, ".macfastlookup") | ||||
| 	dbFileName := "mac_vendors.sqlite3" | ||||
| 	dbFilePath := filepath.Join(dbFileDir, dbFileName) | ||||
| 
 | ||||
| 	if _, err := os.Stat(dbFileDir); os.IsNotExist(err) { | ||||
| 		os.Mkdir(dbFileDir, 0755) | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := os.Stat(dbFilePath); os.IsNotExist(err) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // initialize the database connection | ||||
| func (helper *SQLiteHelper) InitSQLiteHelper() bool { | ||||
| 	dbFileDir := ".macfastlookup" | ||||
| 	userHomeDir, _ := os.UserHomeDir() | ||||
| 	dbFileName := "mac_vendors.sqlite3" | ||||
| 	dbFilePath := filepath.Join(userHomeDir, dbFileDir, dbFileName) | ||||
| 
 | ||||
| 	connectionString := fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbFilePath) | ||||
| 	db, err := sql.Open("sqlite3", connectionString) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	helper.db = db | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (helper *SQLiteHelper) Close() { | ||||
| @ -40,14 +70,7 @@ func (helper *SQLiteHelper) Close() { | ||||
| } | ||||
| 
 | ||||
| func (helper *SQLiteHelper) ReloadDatabase() bool { | ||||
| 	helper.db.Close() | ||||
| 	db, err := sql.Open("sqlite3", helper.connectionString) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 		return false | ||||
| 	} | ||||
| 	helper.db = db | ||||
| 	return true | ||||
| 	return helper.InitSQLiteHelper() | ||||
| } | ||||
| 
 | ||||
| func (helper *SQLiteHelper) QueryMacAddressByPrefix(prefix string) (*MacAddress, error) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user