let BASE_URL = typeof window !== 'undefined' && window.location ? `${window.location.protocol}//${window.location.host}` : ''; if (typeof Deno !== 'undefined') { await import('std/dotenv/load.ts'); BASE_URL = Deno.env.get('BASE_URL') || ''; } export const baseUrl = BASE_URL || 'http://localhost:8000'; export const defaultTitle = 'bewCloud is a modern and simpler alternative to Nextcloud and ownCloud'; export const defaultDescription = `Have your files under your own control.`; export const helpEmail = 'help@bewcloud.com'; export function isRunningLocally(request: Request) { return request.url.includes('localhost'); } export function escapeHtml(unsafe: string) { return unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"') .replaceAll("'", '''); } export function escapeXml(unsafe: string) { return escapeHtml(unsafe).replaceAll('\r', ' '); } export function generateRandomCode(length = 6) { const getRandomDigit = () => Math.floor(Math.random() * (10)); // 0-9 const codeDigits = Array.from({ length }).map(getRandomDigit); return codeDigits.join(''); } export async function generateHash(value: string, algorithm: AlgorithmIdentifier) { const hashedValueData = await crypto.subtle.digest( algorithm, new TextEncoder().encode(value), ); const hashedValue = Array.from(new Uint8Array(hashedValueData)).map( (byte) => byte.toString(16).padStart(2, '0'), ).join(''); return hashedValue; } export function splitArrayInChunks(array: T[], chunkLength: number) { const chunks = []; let chunkIndex = 0; const arrayLength = array.length; while (chunkIndex < arrayLength) { chunks.push(array.slice(chunkIndex, chunkIndex += chunkLength)); } return chunks; } export function validateEmail(email: string) { const trimmedEmail = (email || '').trim().toLocaleLowerCase(); if (!trimmedEmail) { return false; } const requiredCharsNotInEdges = ['@', '.']; return requiredCharsNotInEdges.every((char) => trimmedEmail.includes(char) && !trimmedEmail.startsWith(char) && !trimmedEmail.endsWith(char) ); } export function validateUrl(url: string) { const trimmedUrl = (url || '').trim().toLocaleLowerCase(); if (!trimmedUrl) { return false; } if (!trimmedUrl.includes('://')) { return false; } const protocolIndex = trimmedUrl.indexOf('://'); const urlAfterProtocol = trimmedUrl.substring(protocolIndex + 3); if (!urlAfterProtocol) { return false; } return true; } // Adapted from https://gist.github.com/fasiha/7f20043a12ce93401d8473aee037d90a export async function concurrentPromises( generators: (() => Promise)[], maxConcurrency: number, ): Promise { const iterator = generators.entries(); const results: T[] = []; let hasFailed = false; await Promise.all( Array.from(Array(maxConcurrency), async () => { for (const [index, promiseToExecute] of iterator) { if (hasFailed) { break; } try { results[index] = await promiseToExecute(); } catch (error) { hasFailed = true; throw error; } } }), ); return results; } const MAX_RESPONSE_TIME_IN_MS = 10 * 1000; export async function fetchUrl(url: string) { const abortController = new AbortController(); const requestCancelTimeout = setTimeout(() => { abortController.abort(); }, MAX_RESPONSE_TIME_IN_MS); const response = await fetch(url, { signal: abortController.signal, }); if (requestCancelTimeout) { clearTimeout(requestCancelTimeout); } const urlContents = await response.text(); return urlContents; } export async function fetchUrlAsGooglebot(url: string) { const abortController = new AbortController(); const requestCancelTimeout = setTimeout(() => { abortController.abort(); }, MAX_RESPONSE_TIME_IN_MS); const response = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', }, signal: abortController.signal, }); if (requestCancelTimeout) { clearTimeout(requestCancelTimeout); } const urlContents = await response.text(); return urlContents; } export async function fetchUrlWithProxy(url: string) { const abortController = new AbortController(); const requestCancelTimeout = setTimeout(() => { abortController.abort(); }, MAX_RESPONSE_TIME_IN_MS); const response = await fetch(`https://api.allorigins.win/raw?url=${url}`, { signal: abortController.signal, }); if (requestCancelTimeout) { clearTimeout(requestCancelTimeout); } const urlContents = await response.text(); return urlContents; } export async function fetchUrlWithRetries(url: string) { try { const text = await fetchUrl(url); return text; } catch (_error) { try { const text = await fetchUrlAsGooglebot(url); return text; } catch (_error) { const text = await fetchUrlWithProxy(url); return text; } } } export function convertFormDataToObject(formData: FormData): Record { return JSON.parse(JSON.stringify(Object.fromEntries(formData))); } export function convertObjectToFormData(formDataObject: Record): FormData { const formData = new FormData(); for (const key of Object.keys(formDataObject || {})) { if (Array.isArray(formDataObject[key])) { formData.append(key, formDataObject[key].join(',')); } else { formData.append(key, formDataObject[key]); } } return formData; } export const capitalizeWord = (string: string) => { return `${string.charAt(0).toLocaleUpperCase()}${string.slice(1)}`; }; export function getRandomItem(items: Readonly>): T { return items[Math.floor(Math.random() * items.length)]; }