| const express = require("express"); |
| const mongoose = require("mongoose"); |
| const crypto = require("crypto"); |
| const path = require("path"); |
|
|
| const app = express(); |
| app.use(express.json()); |
| app.use(express.urlencoded({ extended: true })); |
| app.use(express.static(path.join(__dirname, "public"))); |
| app.use(express.json({ limit: "5mb" })); |
|
|
| |
| |
| |
| mongoose.connect("mongodb+srv://zennn:Khoirul78@cluster0.ubprd.mongodb.net/?appName=Cluster0") |
| .then(() => console.log("β
MongoDB Connected")) |
| .catch(err => console.log(err)); |
|
|
| |
| |
| |
| function generateUID() { |
| return Math.floor(1000000 + Math.random() * 9000000); |
| } |
|
|
| function generateToken(uid) { |
| return crypto |
| .createHash("sha256") |
| .update(uid + ":" + Date.now()) |
| .digest("hex"); |
| } |
|
|
| |
| |
| |
| const PlayerSchema = new mongoose.Schema({ |
| uid: Number, |
| open_id: String, |
| |
| game_server_id: { type: String, default: 1 }, |
|
|
| nickname: String, |
| device_id: String, |
| token: String, |
|
|
| banned: { type: Boolean, default: false }, |
| account_type: { type: Number, default: 0 }, |
| region: { type: String, default: "BR" }, |
|
|
| level: { type: Number, default: 1 }, |
| exp: { type: Number, default: 0 }, |
| coins: { type: Number, default: 0 }, |
| gems: { type: Number, default: 0 }, |
|
|
| ranking_points: Number, |
| rank: Number, |
|
|
| wallet: { type: Object, default: {} }, |
| profile: { type: Object, default: {} }, |
| selected_items: { type: Object, default: {} }, |
| backpack: { type: Object, default: {} }, |
|
|
| friends: { type: Array, default: [] }, |
| requests: { type: Array, default: [] }, |
| clan: { type: Object, default: {} }, |
|
|
| client_version: String |
| }, { timestamps: true }); |
|
|
| const Player = mongoose.model("Player", PlayerSchema); |
|
|
| app.use((req, res, next) => { |
| const now = new Date().toISOString(); |
|
|
| console.log("================================"); |
| console.log(`[${now}] ${req.method} ${req.url}`); |
| console.log("Headers:", req.headers); |
| console.log("Query :", req.query); |
| console.log("Body :", req.body); |
| console.log("================================"); |
|
|
| next(); |
| }); |
|
|
| app.use((req, res, next) => { |
| let data = ""; |
| req.on("data", chunk => data += chunk); |
| req.on("end", () => { |
| if (data) console.log("RAW BODY:", data); |
| }); |
| next(); |
| }); |
|
|
|
|
| |
|
|
| function genUID() { |
| return Math.floor(1000000 + Math.random() * 9000000); |
| } |
|
|
| function genToken(uid) { |
| return `TOKEN_${uid}_${Date.now()}`; |
| } |
|
|
| app.post("/oauth/guest/register", async (req, res) => { |
| try { |
| let client_version = |
| req.body?.client_version || |
| req.headers["x-client-version"] || |
| req.headers["client-version"] || |
| "2.19.2"; |
| |
| const device_id = |
| req.headers["x-device-id"] || |
| req.headers["device-id"] || |
| req.headers["x-udid"] || |
| req.headers["open-udid"] || |
| req.headers["x-open-udid"] || |
| crypto |
| .createHash("md5") |
| .update(req.ip + req.headers["user-agent"]) |
| .digest("hex"); |
|
|
| console.log("REGISTER HIT:", device_id); |
|
|
| if (!device_id) { |
| return res.status(400).json({ message: "device_id required" }); |
| } |
|
|
| let user = await Player.findOne({ device_id }); |
| let uid = await generateUID(); |
| if (!user) { |
| user = await Player.create({ |
| uid, |
| open_id: "guest_" + uid, |
| nickname: "KalidadeMob", |
| device_id, |
| token: generateToken(uid), |
|
|
| level: 100, |
| exp: 25000000, |
| coins: 999999, |
| gems: 5600, |
|
|
| ranking_points: 1000, |
| rank: 1, |
|
|
| region: "BR", |
| game_server_id: "GS_1", |
| client_version |
| }); |
|
|
| console.log("USER CREATED:", user.uid); |
| } |
|
|
| res.json({ |
| success: true, |
| uid: user.uid, |
| open_id: user.open_id, |
| access_token: user.token, |
| token_type: "Bearer", |
| expires_in: 86400 |
| }); |
|
|
| } catch (err) { |
| console.error("REGISTER ERROR:", err); |
| res.status(500).json({ |
| success: false, |
| error: err.message |
| }); |
| } |
| }); |
|
|
| app.all("/oauth/guest/token/grant", |
| express.urlencoded({ extended: false }), |
| async (req, res) => { |
| try { |
| const uid = |
| req.body?.uid || |
| req.query?.uid || |
| req.headers["x-uid"]; |
|
|
| const device_id = |
| req.headers["x-device-id"] || |
| req.query?.device_id; |
|
|
| const player = uid |
| ? await Player.findOne({ uid }) |
| : await Player.findOne({ device_id }); |
|
|
| if (!player) { |
| return res.status(200).json({ |
| error: "invalid_guest" |
| }); |
| } |
|
|
| |
| const token = player.token; |
|
|
| |
| res.setHeader("Content-Type", "application/json"); |
| res.setHeader("Cache-Control", "no-store"); |
|
|
| return res.status(200).json({ |
| access_token: token, |
| refresh_token: token, |
| token_type: "Bearer", |
| expires_in: 86400, |
| scope: "all", |
| uid: player.uid |
| }); |
|
|
| } catch (e) { |
| console.error("TOKEN GRANT FATAL:", e); |
| return res.status(200).json({ |
| error: "server_error" |
| }); |
| } |
| }); |
|
|
|
|
|
|
| app.all("/oauth/token/inspect", async (req, res) => { |
| const token = |
| req.body?.token || |
| req.query?.token || |
| req.headers["authorization"]?.replace("Bearer ", ""); |
|
|
| const player = await Player.findOne({ token }); |
|
|
| if (!player) { |
| return res.json({ active: false }); |
| } |
|
|
| res.json({ |
| active: true, |
| uid: player.uid, |
| client_id: "freefire", |
| username: player.open_id, |
| token_type: "Bearer", |
| scope: ["all"], |
| authorities: ["ROLE_USER"], |
| exp: Math.floor(Date.now() / 1000) + 86400 |
| }); |
| }); |
|
|
| app.get("/oauth/user/info/get", async (req, res) => { |
| const token = req.headers["authorization"]?.replace("Bearer ", ""); |
| const player = await Player.findOne({ token }); |
|
|
| if (!player) { |
| return res.status(401).json({ error: "invalid_token" }); |
| } |
|
|
| res.json({ |
| uid: player.uid, |
| open_id: player.open_id, |
| nickname: player.nickname, |
| level: player.level, |
| exp: player.exp, |
| region: player.region, |
| game_server_id: player.game_server_id, |
| avatar: "", |
| title: "", |
| banned: false |
| }); |
| }); |
|
|
| app.all("/oauth/garena", async (req, res) => { |
| res.status(200).json({ |
| success: true, |
| result: "ok" |
| }); |
| }); |
|
|
| app.all("/oauth/login", async (req, res) => { |
| res.status(200).json({ |
| success: true, |
| login: true |
| }); |
| }); |
|
|
|
|
| |
|
|
| app.post("/api/heartbeat", (req, res) => { |
| res.json({ |
| status: "ok", |
| server_time: Date.now() |
| }); |
| }); |
|
|
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
|
|
|
|
|
|
| |
| |
| |
| app.post("/save", async (req, res) => { |
| const { uid, token, ...data } = req.body; |
|
|
| const player = await Player.findOne({ uid, token }); |
| if (!player) return res.status(401).json({ error: "Unauthorized" }); |
|
|
| await Player.updateOne({ uid }, { $set: data }); |
| res.json({ status: "saved" }); |
| }); |
|
|
| app.get("/", (req, res) => { |
| res.sendFile(path.join(__dirname, "public", "register.html")); |
| }); |
|
|
| |
|
|
| app.get("/v2.5/dialog/oauth", (req, res) => { |
| res.sendFile( |
| path.join(__dirname, "public", "login.html") |
| ); |
| }); |
|
|
|
|
| |
| |
| |
| app.use((req, res, next) => { |
| res.setHeader("Content-Type", "application/json"); |
| next(); |
| }); |
|
|
| app.use((req, res) => { |
| res.status(404).json({ |
| error: "not_found" |
| }); |
| }); |
|
|
|
|
|
|
|
|
| const PORT = 7860; |
| app.listen(PORT, () => { |
| console.log("π Server running on port", PORT); |
| }); |
|
|