Skip to main content
Glama
utils.ts10.3 kB
// import dayjs from "dayjs"; // import relativeTime from "dayjs/plugin/relativeTime"; // dayjs.extend(relativeTime); // export function fromNow(strDate: string | Date): string { // return dayjs().to(dayjs(strDate)); // } export const fromNow = (date: string): string => { const now = new Date() const past = new Date(date) const diffInDays = Math.floor((now.getTime() - past.getTime()) / (1000 * 60 * 60 * 24)) if (diffInDays < 30) return `${diffInDays} 天前` if (diffInDays < 365) return `${Math.floor(diffInDays / 30)} 个月前` return `${Math.floor(diffInDays / 365)} 年前` } // 格式化日期 export const formatDate = (dateString?: string | Date | null) => { if (!dateString) return ""; return new Date(dateString).toLocaleDateString("zh-CN", { year: "numeric", month: "long", day: "numeric", }); }; export const getAssetUrl = (assetId?: string | null) => { if (assetId) { if (assetId.startsWith("http")) { return assetId; } else { return `/api/assets/${assetId}`; } } return "/placeholder.svg"; } export const formatNumber = (number: number | string | null) => { if (number) { return number.toLocaleString("zh-CN", { maximumFractionDigits: 2, }); } return "0" } // This is a mock implementation for demonstration purposes // In a real app, you would integrate with your authentication provider /** * Sends a verification code to the provided phone number */ export async function sendVerificationCode(phone: string): Promise<void> { // Simulate API call return new Promise((resolve) => { setTimeout(() => { console.log(`Verification code sent to ${phone}`) resolve() }, 1000) }) } /** * Verifies the code sent to the provided phone number */ export async function verifyPhoneCode(phone: string, code: string): Promise<void> { // Simulate API call return new Promise((resolve, reject) => { setTimeout(() => { // For demo purposes, any code "123456" is valid if (code === "123456") { console.log(`Phone ${phone} verified with code ${code}`) resolve() } else { reject(new Error("Invalid verification code")) } }, 1000) }) } /** * Sends an OTP code to the provided email address */ export async function sendEmailOTP(email: string): Promise<void> { // Simulate API call return new Promise((resolve) => { setTimeout(() => { console.log(`OTP code sent to ${email}`) resolve() }, 1000) }) } /** * Verifies the OTP code sent to the provided email address */ export async function verifyEmailOTP(email: string, otp: string): Promise<void> { // Simulate API call return new Promise((resolve, reject) => { setTimeout(() => { // For demo purposes, any code "123456" is valid if (otp === "123456") { console.log(`Email ${email} verified with OTP ${otp}`) resolve() } else { reject(new Error("Invalid OTP code")) } }, 1000) }) } /** * Checks if the user is authenticated */ export async function isAuthenticated(): Promise<boolean> { // In a real app, you would check the session/token return false } /** * Signs the user out */ export async function signOut(): Promise<void> { // Simulate API call return new Promise((resolve) => { setTimeout(() => { console.log("User signed out") resolve() }, 500) }) } /** * 上传文件到阿里云OSS(带进度跟踪) */ export interface OSSUploadResult { success: boolean; assetId?: string; url?: string; error?: string; } export interface OSSUploadOptions { onProgress?: (progress: number) => void; assetType?: string; } export async function uploadToOSS( file: File, assetType?: string, options?: OSSUploadOptions ): Promise<OSSUploadResult> { try { // 第一步:获取OSS临时上传凭证 const credentialResponse = await fetch("/api/assets/oss", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ fileName: file.name, fileType: file.type, fileSize: file.size, assetType, }), }); if (!credentialResponse.ok) { const errorData = await credentialResponse.json(); throw new Error(errorData.error || "获取上传凭证失败"); } const credentialData = await credentialResponse.json(); const { uploadUrl, assetId } = credentialData; // 第二步:直接上传文件到OSS(带进度跟踪) const uploadResponse = await fetch(uploadUrl, { method: "PUT", headers: { "Content-Type": file.type, }, body: file, }); if (!uploadResponse.ok) { throw new Error("文件上传到OSS失败"); } // 第三步:调用回调接口通知服务器上传完成 const callbackUrl = new URL("/api/assets/oss", window.location.origin); callbackUrl.searchParams.set("assetId", assetId); callbackUrl.searchParams.set("url", uploadUrl.split("?")[0]); // 获取不带参数的URL callbackUrl.searchParams.set("size", file.size.toString()); callbackUrl.searchParams.set("mimeType", file.type); const callbackResponse = await fetch(callbackUrl.toString(), { method: "GET", }); if (!callbackResponse.ok) { console.warn("回调通知失败,但文件已上传成功"); } return { success: true, assetId, url: uploadUrl.split("?")[0], // 返回不带参数的URL }; } catch (error) { console.error("OSS上传失败:", error); return { success: false, error: error instanceof Error ? error.message : "上传失败", }; } } /** * 带进度跟踪的OSS上传(使用XMLHttpRequest) */ export function uploadToOSSWithProgress( file: File, assetType?: string, onProgress?: (progress: number) => void ): Promise<OSSUploadResult> { return new Promise(async (resolve) => { try { // 第一步:获取OSS临时上传凭证 const credentialResponse = await fetch("/api/assets/oss", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ fileName: file.name, fileType: file.type, fileSize: file.size, assetType, }), }); if (!credentialResponse.ok) { const errorData = await credentialResponse.json(); throw new Error(errorData.error || "获取上传凭证失败"); } const credentialData = await credentialResponse.json(); const { uploadUrl, assetId } = credentialData; // 第二步:使用XMLHttpRequest上传文件到OSS(带进度跟踪) const xhr = new XMLHttpRequest(); xhr.upload.addEventListener("progress", (event) => { if (event.lengthComputable && onProgress) { const progress = Math.round((event.loaded / event.total) * 100); onProgress(progress); } }); xhr.addEventListener("load", async () => { if (xhr.status >= 200 && xhr.status < 300) { try { // 第三步:调用回调接口通知服务器上传完成 const callbackUrl = new URL("/api/assets/oss", window.location.origin); callbackUrl.searchParams.set("assetId", assetId); callbackUrl.searchParams.set("url", uploadUrl.split("?")[0]); callbackUrl.searchParams.set("size", file.size.toString()); callbackUrl.searchParams.set("mimeType", file.type); const callbackResponse = await fetch(callbackUrl.toString(), { method: "GET", }); if (!callbackResponse.ok) { console.warn("回调通知失败,但文件已上传成功"); } resolve({ success: true, assetId, url: uploadUrl.split("?")[0], }); } catch (error) { console.warn("回调通知失败,但文件已上传成功"); resolve({ success: true, assetId, url: uploadUrl.split("?")[0], }); } } else { resolve({ success: false, error: `上传失败: ${xhr.status}`, }); } }); xhr.addEventListener("error", () => { resolve({ success: false, error: "网络错误", }); }); xhr.open("PUT", uploadUrl); xhr.setRequestHeader("Content-Type", file.type); xhr.send(file); } catch (error) { resolve({ success: false, error: error instanceof Error ? error.message : "上传失败", }); } }); } /** * 获取OSS文件的完整URL */ export function getOSSFileUrl(ossFileName: string): string { const bucket = process.env.NEXT_PUBLIC_ALIYUN_OSS_BUCKET; const region = process.env.NEXT_PUBLIC_ALIYUN_OSS_REGION || "oss-cn-hangzhou"; return `https://${bucket}.${region}.aliyuncs.com/${ossFileName}`; } /** * Save form data to localStorage for later restoration */ export function saveFormData(formData: Record<string, any>, key: string = 'submit-form-data') { try { localStorage.setItem(key, JSON.stringify({ data: formData, timestamp: Date.now() })) } catch (error) { console.error('Failed to save form data:', error) } } /** * Get saved form data from localStorage */ export function getFormData(key: string = 'submit-form-data'): Record<string, any> | null { try { const saved = localStorage.getItem(key) if (!saved) return null const parsed = JSON.parse(saved) const { data, timestamp } = parsed // 检查数据是否过期(24小时) const isExpired = Date.now() - timestamp > 24 * 60 * 60 * 1000 if (isExpired) { localStorage.removeItem(key) return null } return data } catch (error) { console.error('Failed to get form data:', error) return null } } /** * Clear saved form data from localStorage */ export function clearFormData(key: string = 'submit-form-data') { try { localStorage.removeItem(key) } catch (error) { console.error('Failed to clear form data:', error) } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/metacode0602/open-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server