🏷️ Search for custom tailnet name offers with keywords.
at master 103 lines 2.6 kB view raw
1import { log } from '$helpers/logging'; 2import { getTailnetNames, validateTailnetNames } from '$helpers/tailnet'; 3import { extractMatchingTokens, storeTokens } from '$helpers/tokens'; 4import type { Offer } from '$types/tokens'; 5 6export const getTailcontrolCookie = async (): Promise<{ 7 value: string; 8} | null> => { 9 return await browser.cookies.get({ 10 url: 'https://login.tailscale.com', 11 name: 'tailcontrol', 12 }); 13}; 14 15export const fetchTailscaleOffers = async (cookieValue: string) => { 16 return await fetch('https://login.tailscale.com/admin/api/tcd/offers', { 17 headers: { 18 accept: 'application/json, text/plain, */*', 19 'accept-language': 'en-US,en;q=0.9', 20 'cache-control': 'no-cache', 21 pragma: 'no-cache', 22 cookie: `tailcontrol=${cookieValue}; tailwww-ui=true`, 23 }, 24 referrer: 'https://login.tailscale.com/admin/dns', 25 referrerPolicy: 'strict-origin-when-cross-origin', 26 method: 'GET', 27 mode: 'cors', 28 }); 29}; 30 31const handleCheckOffersError = (errMsg: string) => { 32 log(errMsg, 'ERROR', 'Offers'); 33 log( 34 'Checking offers process finished due to an error. See line above.', 35 'ERROR', 36 'Offers', 37 ); 38 39 browser.alarms.clear('checkOffersAlarm'); 40}; 41 42export const checkTailscaleOffers = async () => { 43 log( 44 'Checking offers process started. Searching Tailscale for tailnet name offers...', 45 'INFO', 46 'Offers', 47 ); 48 let tailnetNames: string[] = []; 49 50 try { 51 tailnetNames = await getTailnetNames(); 52 } catch { 53 log( 54 'Failed to get tailnetNames from storage. Defaulting to empty array.', 55 'WARNING', 56 'Offers', 57 ); 58 } 59 60 if (!validateTailnetNames(tailnetNames)) { 61 handleCheckOffersError( 62 'No tailnet keywords set. User needs to add at least one tailnet keyword.', 63 ); 64 return; 65 } 66 67 try { 68 const cookie = await getTailcontrolCookie(); 69 if (!cookie) { 70 handleCheckOffersError( 71 'Tailcontrol cookie not found. User needs to log in to Tailscale.', 72 ); 73 return; 74 } 75 76 const cookieValue = cookie.value; 77 const response = await fetchTailscaleOffers(cookieValue); 78 const body: { data?: Offer } = await response.json(); 79 80 if (body.data?.tcds) { 81 const tokens = extractMatchingTokens(body.data.tcds, tailnetNames); 82 83 if (tokens.length > 0) { 84 log( 85 `Found matching tokens: ${JSON.stringify(tokens)}`, 86 'INFO', 87 'Offers', 88 ); 89 90 storeTokens(tokens); 91 } else { 92 log( 93 "No matching tailnet name offers found that match the user's defined keywords.", 94 'INFO', 95 'Offers', 96 ); 97 } 98 } 99 } catch (e) { 100 const errorMsg = e instanceof Error ? e.message : 'Unknown error'; 101 handleCheckOffersError(errorMsg || 'Unknown error'); 102 } 103};