This commit is contained in:
oiov
2025-05-26 15:57:34 +08:00
parent 69878126f6
commit 8fab48f849
5 changed files with 146 additions and 74 deletions
+75
View File
@@ -0,0 +1,75 @@
"use server";
import { redirect } from "next/navigation";
import { siteConfig } from "@/config/site";
import { createUserShortUrlMeta, getUrlBySuffix } from "@/lib/dto/short-urls";
import { redirectMap } from "@/app/ss/[slug]/page";
export async function RedirectsTo(data: any) {
const {
slug,
referer,
ip,
city,
region,
country,
latitude,
longitude,
lang,
device,
browser,
password,
} = data;
if (!slug || !ip)
return redirect(`${siteConfig.url}${redirectMap["Missing[0000]"]}${slug}`);
const res = await getUrlBySuffix(slug);
if (!res)
return redirect(`${siteConfig.url}${redirectMap["Disabled[0002]"]}${slug}`);
if (res.active !== 1)
return redirect(`${siteConfig.url}${redirectMap["Disabled[0002]"]}${slug}`);
if (res.password !== "") {
if (!password) {
return redirect(
`${siteConfig.url}${redirectMap["PasswordRequired[0004]"]}${slug}`,
);
}
if (password !== res.password) {
return redirect(
`${siteConfig.url}${redirectMap["IncorrectPassword[0005]"]}${slug}`,
);
}
}
const now = Date.now();
const createdAt = new Date(res.updatedAt).getTime();
const expirationMilliseconds = Number(res.expiration) * 1000;
const expirationTime = createdAt + expirationMilliseconds;
if (res.expiration !== "-1" && now > expirationTime) {
return redirect(`${siteConfig.url}${redirectMap["Expired[0001]"]}`);
}
await createUserShortUrlMeta({
urlId: res.id,
click: 1,
ip,
city,
region,
country,
latitude,
longitude,
referer,
lang,
device,
browser,
});
console.log("到这了");
redirect(res.target);
}
+59 -56
View File
@@ -1,12 +1,11 @@
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { RedirectsTo } from "@/actions/redirect";
import UAParser from "ua-parser-js";
import { siteConfig } from "@/config/site";
import { createUserShortUrlMeta, getUrlBySuffix } from "@/lib/dto/short-urls";
import { extractRealIP, getClientGeolocation } from "@/lib/geo";
const redirectMap = {
export const redirectMap = {
"Missing[0000]": "/docs/short-urls#missing-links",
"Expired[0001]": "/docs/short-urls#expired-links",
"Disabled[0002]": "/docs/short-urls#disabled-links",
@@ -31,10 +30,7 @@ export default async function ShortUrlPage({
const { slug } = params;
const { password = "" } = searchParams;
if (!slug) redirect(redirectMap["Missing[0000]"]);
try {
// 获取请求头信息
const headersList = headers();
const userAgent = headersList.get("user-agent") || "";
const referer = headersList.get("referer") || "(None)";
@@ -48,63 +44,70 @@ export default async function ShortUrlPage({
const geo = await getClientGeolocation();
// const trackingData = {
// slug,
// referer,
// ip: realIP,
// city: geo?.city,
// region: geo?.region,
// country: geo?.country,
// latitude: geo?.latitude,
// longitude: geo?.longitude,
// // flag: geo?.flag,
// lang: acceptLanguage?.split(",")[0],
// device: device.model || "Unknown",
// browser: browser.name || "Unknown",
// password,
// };
const data = await getUrlBySuffix(slug);
if (!data || data.active !== 1) redirect(redirectMap["Disabled[0002]"]);
if (data.password !== "") {
if (!password) {
redirect(redirectMap["PasswordRequired[0004]"]);
}
if (password !== data.password) {
redirect(redirectMap["IncorrectPassword[0005]"]);
}
}
const now = Date.now();
const createdAt = new Date(data.updatedAt).getTime();
const expirationMilliseconds = Number(data.expiration) * 1000;
const expirationTime = createdAt + expirationMilliseconds;
if (data.expiration !== "-1" && now > expirationTime) {
redirect(redirectMap["Expired[0001]"]);
}
await createUserShortUrlMeta({
urlId: data.id,
click: 1,
ip: realIP,
city: geo?.city || "",
region: geo?.region || "",
country: geo?.country || "",
latitude: geo?.latitude || "",
longitude: geo?.longitude || "",
const trackingData = {
slug,
referer,
lang: acceptLanguage?.split(",")[0] || "",
ip: realIP,
city: geo?.city,
region: geo?.region,
country: geo?.country,
latitude: geo?.latitude,
longitude: geo?.longitude,
// flag: geo?.flag,
lang: acceptLanguage?.split(",")[0],
device: device.model || "Unknown",
browser: browser.name || "Unknown",
});
password,
};
// await RedirectsTo(trackingData);
// await fetch(`${siteConfig.url}/api/s`, {
// method: "POST",
// // headers: { "Content-Type": "application/json" },
// body: JSON.stringify(trackingData),
// });
// const data = await getUrlBySuffix(slug);
// if (!data || data.active !== 1) redirect(redirectMap["Disabled[0002]"]);
// if (data.password !== "") {
// if (!password) {
// redirect(redirectMap["PasswordRequired[0004]"]);
// }
// if (password !== data.password) {
// redirect(redirectMap["IncorrectPassword[0005]"]);
// }
// }
// const now = Date.now();
// const createdAt = new Date(data.updatedAt).getTime();
// const expirationMilliseconds = Number(data.expiration) * 1000;
// const expirationTime = createdAt + expirationMilliseconds;
// if (data.expiration !== "-1" && now > expirationTime) {
// redirect(redirectMap["Expired[0001]"]);
// }
// await createUserShortUrlMeta({
// urlId: data.id,
// click: 1,
// ip: realIP,
// city: geo?.city || "",
// region: geo?.region || "",
// country: geo?.country || "",
// latitude: geo?.latitude || "",
// longitude: geo?.longitude || "",
// referer,
// lang: acceptLanguage?.split(",")[0] || "",
// device: device.model || "Unknown",
// browser: browser.name || "Unknown",
// });
// 重定向到目标URL
window.location.href = data.target;
// window.location.href = data.target;
// redirect(data.target);
} catch (error) {
console.error("Short URL redirect error:", error);
redirect("/");
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ export function extractRealIP(headers: Headers): string {
}
}
return "";
return "::1";
}
function isValidIP(ip: string): boolean {
+10 -16
View File
@@ -1,8 +1,7 @@
import { NextResponse } from "next/server";
import { NextResponse, userAgent } from "next/server";
import { geolocation } from "@vercel/functions";
import { auth } from "auth";
import { NextAuthRequest } from "next-auth/lib";
import UAParser from "ua-parser-js";
import { siteConfig } from "./config/site";
@@ -19,7 +18,6 @@ const redirectMap = {
"IncorrectPassword[0005]": "/password-prompt?error=1&slug=",
};
// 提取短链接处理逻辑
async function handleShortUrl(req: NextAuthRequest) {
if (!req.url.includes("/s/")) return NextResponse.next();
@@ -29,7 +27,7 @@ async function handleShortUrl(req: NextAuthRequest) {
const geo = geolocation(req);
const headers = req.headers;
const { browser, device } = parseUserAgent(headers.get("user-agent") || "");
const ua = userAgent(req);
const url = new URL(req.url);
const password = url.searchParams.get("password") || "";
@@ -45,11 +43,17 @@ async function handleShortUrl(req: NextAuthRequest) {
longitude: geo?.longitude,
flag: geo?.flag,
lang: headers.get("accept-language")?.split(",")[0],
device: device.model || "Unknown",
browser: browser.name || "Unknown",
device: ua.device.model || "Unknown",
browser: ua.browser.name || "Unknown",
engine: ua.engine.name || "",
os: ua.os.name || "",
cpu: ua.cpu.architecture || "",
isBot: ua.isBot,
password,
};
console.log("Tracking data:", trackingData, siteConfig.url);
const res = await fetch(`${siteConfig.url}/api/s`, {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -96,16 +100,6 @@ function extractSlug(url: string): string | null {
return match ? match[1] : null;
}
// 解析用户代理
const parser = new UAParser();
function parseUserAgent(ua: string) {
parser.setUA(ua);
return {
browser: parser.getBrowser(),
device: parser.getDevice(),
};
}
export default auth(async (req) => {
try {
return await handleShortUrl(req);
+1 -1
View File
File diff suppressed because one or more lines are too long