readme
This commit is contained in:
32
app/lib/media.ts
Normal file
32
app/lib/media.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
const MEDIA_PROXY_PATH = "/api/media/proxy";
|
||||
|
||||
function configuredMinioPublicUrl(): string {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_MINIO_PUBLIC_URL ||
|
||||
process.env.MINIO_PUBLIC_URL ||
|
||||
""
|
||||
).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
export function isExternalMediaUrl(value: string): boolean {
|
||||
return /^https?:\/\//i.test(value);
|
||||
}
|
||||
|
||||
export function isMinioUrl(value: string): boolean {
|
||||
const publicUrl = configuredMinioPublicUrl();
|
||||
return Boolean(publicUrl && value.startsWith(`${publicUrl}/`));
|
||||
}
|
||||
|
||||
export function mediaUrl(value?: string | null): string {
|
||||
const url = String(value || "").trim();
|
||||
if (!url) return "";
|
||||
if (/^(data:|blob:|#|mailto:|tel:)/i.test(url)) return url;
|
||||
if (/^file:/i.test(url)) return "";
|
||||
if (!isExternalMediaUrl(url)) return url;
|
||||
if (isMinioUrl(url)) return url;
|
||||
return `${MEDIA_PROXY_PATH}?src=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
export function mediaUrls(values: string[]): string[] {
|
||||
return values.map(mediaUrl).filter(Boolean);
|
||||
}
|
||||
101
app/lib/meetups.ts
Normal file
101
app/lib/meetups.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
export type MeetupMode = "offline" | "online" | "hybrid";
|
||||
export type MeetupAccessLevel = "public" | "members" | "vip" | "hosts";
|
||||
|
||||
export interface MeetupFeatureOption {
|
||||
key: string;
|
||||
zh: string;
|
||||
en: string;
|
||||
}
|
||||
|
||||
export interface MiroTalkMeetupLike {
|
||||
id: string;
|
||||
city?: string;
|
||||
date?: string;
|
||||
time?: string;
|
||||
meetingUrl?: string;
|
||||
mirotalkRoom?: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_MIROTALK_URL = "https://mirotalk.nomadro.cn";
|
||||
export const MIROTALK_PROVIDER_LABEL = "MiroTalk P2P";
|
||||
export const DEFAULT_LOUNGE_URL = "https://lounge.nomadro.cn";
|
||||
export const WEB_CHAT_PROVIDER_LABEL = "The Lounge + Ergo IRC";
|
||||
|
||||
export const MEETUP_FEATURE_OPTIONS: MeetupFeatureOption[] = [
|
||||
{ key: "group_video", zh: "多人视频", en: "Group video" },
|
||||
{ key: "screen_share", zh: "屏幕共享", en: "Screen share" },
|
||||
{ key: "whiteboard", zh: "白板协作", en: "Whiteboard" },
|
||||
{ key: "breakout", zh: "分组讨论", en: "Breakout rooms" },
|
||||
{ key: "recording", zh: "录制回放", en: "Recording" },
|
||||
{ key: "host_controls", zh: "主持人控场", en: "Host controls" },
|
||||
];
|
||||
|
||||
export const DEFAULT_ONLINE_FEATURES = ["group_video", "screen_share", "whiteboard", "host_controls"];
|
||||
|
||||
export function normalizeMiroTalkRoom(value: string): string {
|
||||
return value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.slice(0, 80);
|
||||
}
|
||||
|
||||
export function createMiroTalkRoomSlug(meetup: MiroTalkMeetupLike): string {
|
||||
const datePart = String(meetup.date || "").slice(0, 10).replace(/[^0-9]/g, "");
|
||||
const timePart = String(meetup.time || "").slice(0, 5).replace(/[^0-9]/g, "");
|
||||
const cityPart = normalizeMiroTalkRoom(meetup.city || "online");
|
||||
return normalizeMiroTalkRoom(["nomadcna", cityPart, datePart, timePart, meetup.id].filter(Boolean).join("-"));
|
||||
}
|
||||
|
||||
export function getMiroTalkBaseUrl(): string {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_MIROTALK_URL ||
|
||||
process.env.NEXT_PUBLIC_MIROTALK_P2P_URL ||
|
||||
process.env.NEXT_PUBLIC_MIROTALK_SFU_URL ||
|
||||
DEFAULT_MIROTALK_URL
|
||||
).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
export function getMiroTalkServerLabel(): string {
|
||||
const baseUrl = getMiroTalkBaseUrl();
|
||||
try {
|
||||
return new URL(baseUrl).host;
|
||||
} catch {
|
||||
return baseUrl.replace(/^https?:\/\//i, "");
|
||||
}
|
||||
}
|
||||
|
||||
export function buildMiroTalkJoinUrl(meetup: MiroTalkMeetupLike, displayName = "NomadCNA"): string {
|
||||
if (meetup.meetingUrl && /^https?:\/\//i.test(meetup.meetingUrl)) {
|
||||
return meetup.meetingUrl;
|
||||
}
|
||||
const room = normalizeMiroTalkRoom(meetup.mirotalkRoom || createMiroTalkRoomSlug(meetup));
|
||||
const name = displayName.trim() || "NomadCNA";
|
||||
const params = new URLSearchParams({ room, name });
|
||||
return `${getMiroTalkBaseUrl()}/join?${params.toString()}`;
|
||||
}
|
||||
|
||||
export function getLoungeBaseUrl(): string {
|
||||
return (process.env.NEXT_PUBLIC_LOUNGE_URL || DEFAULT_LOUNGE_URL).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
export function getLoungeServerLabel(): string {
|
||||
const baseUrl = getLoungeBaseUrl();
|
||||
try {
|
||||
return new URL(baseUrl).host;
|
||||
} catch {
|
||||
return baseUrl.replace(/^https?:\/\//i, "");
|
||||
}
|
||||
}
|
||||
|
||||
export function buildMeetupChatUrl(): string {
|
||||
return getLoungeBaseUrl();
|
||||
}
|
||||
|
||||
export function featureLabels(keys: string[], locale: string): string[] {
|
||||
const selected = new Set(keys);
|
||||
return MEETUP_FEATURE_OPTIONS.filter((feature) => selected.has(feature.key)).map((feature) =>
|
||||
locale === "zh" ? feature.zh : feature.en
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user