feat: redis and valid url
This commit is contained in:
19
bun.lock
19
bun.lock
@@ -8,6 +8,7 @@
|
||||
"@telegram-apps/init-data-node": "^2.0.7",
|
||||
"@telegram-apps/sdk-react": "^3.3.0",
|
||||
"@telegram-apps/telegram-ui": "^2.1.8",
|
||||
"ioredis": "^5.6.1",
|
||||
"next": "15.3.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"qrcode.react": "^4.2.0",
|
||||
@@ -130,6 +131,8 @@
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.2", "", { "os": "win32", "cpu": "x64" }, "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw=="],
|
||||
|
||||
"@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
"@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
|
||||
@@ -434,6 +437,8 @@
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
|
||||
|
||||
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
@@ -470,6 +475,8 @@
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
|
||||
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
|
||||
@@ -636,6 +643,8 @@
|
||||
|
||||
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
||||
|
||||
"ioredis": ["ioredis@5.6.1", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA=="],
|
||||
|
||||
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
|
||||
@@ -750,6 +759,10 @@
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||
|
||||
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="],
|
||||
@@ -872,6 +885,10 @@
|
||||
|
||||
"react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
|
||||
|
||||
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
||||
|
||||
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
|
||||
|
||||
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
@@ -970,6 +987,8 @@
|
||||
|
||||
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
|
||||
|
||||
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
|
||||
|
||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||
|
||||
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
services:
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
command: ["redis-server", "--appendonly", "yes"]
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
@@ -16,3 +24,4 @@ services:
|
||||
- "3900:3000"
|
||||
volumes:
|
||||
db_data:
|
||||
redis-data:
|
||||
@@ -14,6 +14,7 @@
|
||||
"@telegram-apps/init-data-node": "^2.0.7",
|
||||
"@telegram-apps/sdk-react": "^3.3.0",
|
||||
"@telegram-apps/telegram-ui": "^2.1.8",
|
||||
"ioredis": "^5.6.1",
|
||||
"next": "15.3.3",
|
||||
"qrcode": "^1.5.4",
|
||||
"qrcode.react": "^4.2.0",
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
"use server";
|
||||
import { API_LINK, XUIApiLinks } from "@/lib/enum";
|
||||
import { authFetch } from "@/lib/login";
|
||||
import { getValidUrl } from "@/lib/url";
|
||||
import { prisma } from "@/utils/prisma";
|
||||
import { redis } from "@/utils/redis";
|
||||
import { parse, validate } from "@telegram-apps/init-data-node";
|
||||
import { ClientSettings, InboundResponse } from "./_dto/inbounds";
|
||||
|
||||
//TODO: Make it valid proxy url
|
||||
async function getUrlApi(email: string) {
|
||||
async function getInboundApi() {
|
||||
const res = await authFetch(API_LINK + XUIApiLinks.GET_INBOUNDS);
|
||||
const data: InboundResponse = await res.json();
|
||||
const inbound = data.obj.find((inbound) => inbound.remark === "WS");
|
||||
return inbound;
|
||||
}
|
||||
|
||||
async function getUrlApi(email: string) {
|
||||
const cachedInbound = await redis.get("inbound");
|
||||
const inbound = cachedInbound ? JSON.parse(cachedInbound) : await getInboundApi();
|
||||
if (!inbound) {
|
||||
return Response.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Inbound not found",
|
||||
},
|
||||
{ status: 404 }
|
||||
);
|
||||
throw new Error("Inbound not found");
|
||||
}
|
||||
await redis.set("inbound", JSON.stringify(inbound), "EX", 3600);
|
||||
const users: ClientSettings = JSON.parse(inbound.settings);
|
||||
const user = users.clients.find((user) => user.email === email);
|
||||
return user;
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
return getValidUrl({ email, id: user.id });
|
||||
}
|
||||
export async function getUrl(initData: string = "") {
|
||||
try {
|
||||
@@ -30,13 +35,23 @@ export async function getUrl(initData: string = "") {
|
||||
expiresIn: 3600,
|
||||
});
|
||||
const initDataParsed = parse(initData);
|
||||
const user = await prisma.user.findFirst({
|
||||
if (!initDataParsed.user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
const cachedUser = await redis.get(`user:${initDataParsed.user.id}`);
|
||||
const user = cachedUser
|
||||
? JSON.parse(cachedUser)
|
||||
: await prisma.user.findFirst({
|
||||
where: {
|
||||
tgId: initDataParsed.user ? initDataParsed.user.id.toString() : "0",
|
||||
tgId: initDataParsed.user.id.toString(),
|
||||
},
|
||||
});
|
||||
return await getUrlApi(user?.email || "");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
await redis.set(`user:${initDataParsed.user.id}`, JSON.stringify(user), "EX", 60);
|
||||
return await getUrlApi(user.email || "");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import { useEffect, useState } from "react";
|
||||
import { getUrl } from "./actions";
|
||||
|
||||
export default function Home() {
|
||||
const [url, setUrl] = useState("");
|
||||
const initData = useRawInitData();
|
||||
const onCopyClick = async () => {
|
||||
await navigator.clipboard.writeText(window.location.href);
|
||||
if (!url.length) return;
|
||||
await navigator.clipboard.writeText(url);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
@@ -17,8 +19,12 @@ export default function Home() {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getUrl(initData);
|
||||
console.log(data);
|
||||
setUrl(data);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [initData]);
|
||||
|
||||
13
src/lib/url.ts
Normal file
13
src/lib/url.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export function getValidUrl({ email, id }: { email: string; id: string }) {
|
||||
const obj = {
|
||||
fp: "chrome",
|
||||
alpn: "h2,h3",
|
||||
packetEncoding: "xudp",
|
||||
security: "tls",
|
||||
type: "ws",
|
||||
path: "/myverysecretpath",
|
||||
};
|
||||
const params = new URLSearchParams(obj).toString();
|
||||
const url = `vless://${id}@nwaifu.su:443?${params}#WS-${email}`;
|
||||
return url;
|
||||
}
|
||||
3
src/utils/redis.ts
Normal file
3
src/utils/redis.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Redis from "ioredis";
|
||||
|
||||
export const redis = new Redis(process.env.REDIS_URL || "redis://localhost:6379");
|
||||
Reference in New Issue
Block a user