A very performant and light (2mb in memory) link shortener and tracker. Written in Rust and React and uses Postgres/SQLite.
1import { useState } from 'react' 2import { useForm } from 'react-hook-form' 3import { zodResolver } from '@hookform/resolvers/zod' 4import * as z from 'zod' 5import { CreateLinkRequest, Link } from '../types/api' 6import { createShortLink } from '../api/client' 7import { Button } from "@/components/ui/button" 8import { 9 Form, 10 FormControl, 11 FormField, 12 FormItem, 13 FormLabel, 14 FormMessage, 15} from "@/components/ui/form" 16import { Input } from "@/components/ui/input" 17import { useToast } from "@/hooks/use-toast" 18 19const formSchema = z.object({ 20 url: z.string() 21 .min(1, 'URL is required') 22 .url('Must be a valid URL') 23 .refine(val => val.startsWith('http://') || val.startsWith('https://'), { 24 message: 'URL must start with http:// or https://' 25 }), 26 custom_code: z.string() 27 .regex(/^[a-zA-Z0-9_-]{0,32}$/, 'Custom code must contain only letters, numbers, underscores, and hyphens') 28 .optional() 29}) 30 31interface LinkFormProps { 32 onSuccess: (link: Link) => void 33} 34 35export function LinkForm({ onSuccess }: LinkFormProps) { 36 const [loading, setLoading] = useState(false) 37 const { toast } = useToast() 38 39 const form = useForm<z.infer<typeof formSchema>>({ 40 resolver: zodResolver(formSchema), 41 defaultValues: { 42 url: '', 43 custom_code: '', 44 }, 45 }) 46 47 const onSubmit = async (values: z.infer<typeof formSchema>) => { 48 try { 49 setLoading(true) 50 const link = await createShortLink(values as CreateLinkRequest) 51 form.reset() 52 onSuccess(link) 53 toast({ 54 description: "Short link created successfully", 55 }) 56 } catch (err: any) { 57 toast({ 58 variant: "destructive", 59 title: "Error", 60 description: err.response?.data?.error || 'An error occurred', 61 }) 62 } finally { 63 setLoading(false) 64 } 65 } 66 67 return ( 68 <div className="max-w-[500px] mx-auto"> 69 <Form {...form}> 70 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6"> 71 <FormField 72 control={form.control} 73 name="url" 74 render={({ field }) => ( 75 <FormItem> 76 <FormLabel>URL</FormLabel> 77 <FormControl> 78 <Input placeholder="https://example.com" {...field} /> 79 </FormControl> 80 <FormMessage /> 81 </FormItem> 82 )} 83 /> 84 85 <FormField 86 control={form.control} 87 name="custom_code" 88 render={({ field }) => ( 89 <FormItem> 90 <FormLabel>Custom Code (optional)</FormLabel> 91 <FormControl> 92 <Input placeholder="example" {...field} /> 93 </FormControl> 94 <FormMessage /> 95 </FormItem> 96 )} 97 /> 98 99 <div className="flex justify-end"> 100 <Button type="submit" disabled={loading}> 101 {loading ? "Creating..." : "Create Short Link"} 102 </Button> 103 </div> 104 </form> 105 </Form> 106 </div> 107 ) 108} 109