Skip to content

Commit

Permalink
Add fetch-user-from-id (Server)
Browse files Browse the repository at this point in the history
  • Loading branch information
katniny committed Jan 4, 2025
1 parent 0b9b303 commit 4d64430
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 2 deletions.
12 changes: 10 additions & 2 deletions functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@
// if you add an API, require it then add to module.exports
const { fetchUser } = require('./public/fetch-user');
const { fetchUserPriv } = require("./server/fetch-user-trans");
const { fetchUserFromId } = require("./server/fetch-user-from-id");

module.exports = {
fetchUser,
fetchUserPriv,
// public api
fetchUser,

// protected api
// ...

// server api
fetchUserPriv,
fetchUserFromId
};
82 changes: 82 additions & 0 deletions functions/server/fetch-user-from-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const rateLimit = require("express-rate-limit");
const cors = require("cors");
const express = require("express");

const isEmulator = process.env.FUNCTIONS_EMULATOR === "true";
if (isEmulator) {
console.log("Running in an emulator environment.");
} else {
console.log("Running in production.");
}

// init firebase admin if not already
if (!admin.apps.length) {
admin.initializeApp();
}
const db = admin.database();

// enable express
const app = express();

// apply cors
app.use(cors({ origin: "*" }));

app.use((req, res, next) => {
// only allow transs.social to access this data
const allowedDomain = "transs.social";
const requestHost = req.get("host");
const origin = req.headers["origin"];

if (requestHost && !requestHost.includes(allowedDomain) && origin && !origin.includes(allowedDomain)) {
console.log("Request from host: ", requestHost);
if (isEmulator && requestHost === "127.0.0.1:5001") {
console.log("Allowing request from emulator. If you're not authorized to be here, please report this as a vulnerability bug privately to @katniny on Discord.");
} else {
return res.status(405).send({ error: "You are not authorized to access this! Please do not try again." });
}
}

// only allow GET requests
if (req.method !== "GET") {
return res.status(405).send({ error: "Method not allowed. Only GET requests are allowed." });
}
next();
});

// define route
app.get("/", async (req, res) => {
try {
// get the username from the query params
const userId = req.query.id;
if (!userId) {
return res.status(400).send({
error: "UID is required. If you attempted to use a username, please use a UID instead."
});
}

// fetch user uid from realtime db
const userRef = db.ref(`users/${userId}`);
const snapshot = await userRef.once("value");

if (!snapshot.exists()) {
return res.status(404).send({ error: "User not found." });
}

// send user data
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
res.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");

const userData = snapshot.val();

return res.status(200).send(userData);
} catch (error) {
console.error("Error fetching user: ", error);
return res.status(500).send({ error: "Internal server error." });
}
});

// export the express app wrapped in functions.https.onRequest
exports.fetchUserFromId = functions.https.onRequest(app);
80 changes: 80 additions & 0 deletions testing/server/fetch-user-from-id.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Test: fetch-user.js (SERVER)</title>
<link rel="stylesheet" href="/assets/css/mainSite.css">
</head>

<body style="margin: 15px;">
<h1>Fetch User Data (from UID)</h1>
<p>Put in a UID to fetch their public data.</p>
<p>~100ms per request (on average)</p>
<form id="fetchUserForm">
<label for="userId">Username:</label>
<input type="text" id="userId" value="katniny" required />

<br />
<br />

<label for="httpMethod">HTTP Method:</label>
<select name="" id="httpMethod">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>

<br />
<br />

<textarea id="requestBody" placeholder="Optional request body (JSON)" rows="5" cols="30"></textarea>

<button type="submit">Fetch</button>
</form>

<div id="response">
<h2>Response:</h2>
<pre id="responseData"></pre>
</div>

<script>
document.getElementById("fetchUserForm").addEventListener("submit", async function (event) {
event.preventDefault();

const userId = document.getElementById("userId").value;
const httpMethod = document.getElementById("httpMethod").value;
const requestBody = document.getElementById("requestBody").value;

const url = `http://127.0.0.1:5001/chat-transsocial-test/us-central1/fetchUserFromId?id=${userId}`;

const options = {
method: httpMethod,
headers: {
"Content-Type": "application/json",
},
};

// only add body if it's a method that supports it
if (httpMethod === "POST" || httpMethod === "PUT") {
options.body = requestBody ? requestBody : "{}";
}

try {
const response = await fetch(url, options);

if (!response.ok) {
throw new Error(`Error: ${response.statusText} (HTTP ${response.status})`);
}

const data = await response.json();

document.getElementById("responseData").textContent = JSON.stringify(data, null, 2);
} catch (error) {
document.getElementById("responseData").textContent = `Failed to fetch data; ${error.message}`;
}
});
</script>
</body>
</html>

0 comments on commit 4d64430

Please sign in to comment.