[+] Base server
This commit is contained in:
+1
-1
@@ -39,4 +39,4 @@ yarn-error.log*
|
|||||||
**/*.tgz
|
**/*.tgz
|
||||||
**/*.log
|
**/*.log
|
||||||
package-lock.json
|
package-lock.json
|
||||||
**/*.bun
|
**/*.bun
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Folder-specific settings
|
||||||
|
//
|
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||||
|
{
|
||||||
|
"format_on_save": "off"
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "public-photos",
|
||||||
|
"dependencies": {
|
||||||
|
"elysia": "latest",
|
||||||
|
"exifreader": "^4.32.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"bun-types": "latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="],
|
||||||
|
|
||||||
|
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
|
||||||
|
|
||||||
|
"@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg=="],
|
||||||
|
|
||||||
|
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@24.9.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||||
|
|
||||||
|
"@xmldom/xmldom": ["@xmldom/xmldom@0.9.8", "", {}, "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
|
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
|
"elysia": ["elysia@1.4.13", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-6QaWQEm7QN1UCo1TPpEjaRJPHUmnM7R29y6LY224frDGk5PrpAnWmdHkoZxkcv+JRWp1j2ROr2IHbxHbG/jRjw=="],
|
||||||
|
|
||||||
|
"exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
|
||||||
|
|
||||||
|
"exifreader": ["exifreader@4.32.0", "", { "optionalDependencies": { "@xmldom/xmldom": "^0.9.4" } }, "sha512-sj1PzjpaPwSE/2MeUqoAYcfc2u7AZOGSby0FzmAkB4jjeCXgDryxzVgMwV+tJKGIkGdWkkWiUWoLSJoPHJ6V5Q=="],
|
||||||
|
|
||||||
|
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
|
||||||
|
|
||||||
|
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
||||||
|
|
||||||
|
"file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
|
||||||
|
|
||||||
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
|
"memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
||||||
|
|
||||||
|
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
|
||||||
|
|
||||||
|
"token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
|
||||||
|
|
||||||
|
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "public-photos",
|
"name": "project-instant",
|
||||||
"version": "1.0.50",
|
"version": "1.0.50",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"dev": "bun run --watch src/index.ts"
|
"dev": "bun run --watch src/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"elysia": "latest"
|
"elysia": "latest",
|
||||||
|
"exifreader": "^4.32.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bun-types": "latest"
|
"bun-types": "latest"
|
||||||
|
|||||||
+176
-5
@@ -1,7 +1,178 @@
|
|||||||
import { Elysia } from "elysia";
|
import { Elysia, t } from "elysia"
|
||||||
|
import ExifReader from "exifreader"
|
||||||
|
import { exists, mkdir } from "fs/promises"
|
||||||
|
|
||||||
const app = new Elysia().get("/", () => "Hello Elysia").listen(3000);
|
// --- Configuration ---
|
||||||
|
const FOLDER_PHOTOS = "./photos"
|
||||||
|
const FILE_METADATA = "./metadata.json"
|
||||||
|
|
||||||
console.log(
|
// --- Types ---
|
||||||
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
|
// Interface for our metadata entries
|
||||||
);
|
interface PhotoMetadata {
|
||||||
|
id: string
|
||||||
|
owner_key: string // This will be filtered out for public requests
|
||||||
|
upload_time: string
|
||||||
|
original_photo: { filename: string, path: string, exif: any }
|
||||||
|
edited_photo: { filename: string, path: string, exif: any }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the metadata.json file.
|
||||||
|
* Returns an empty array if the file doesn't exist.
|
||||||
|
*/
|
||||||
|
async function getMetadata(): Promise<PhotoMetadata[]> {
|
||||||
|
const file = Bun.file(FILE_METADATA)
|
||||||
|
if (!(await file.exists())) {
|
||||||
|
return [] // Return empty array if no metadata file yet
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return await file.json()
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`Warning: Could not parse 'metadata.json'. Returning empty array.`, e)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely saves the metadata array to metadata.json.
|
||||||
|
*/
|
||||||
|
async function saveMetadata(data: PhotoMetadata[]): Promise<void> {
|
||||||
|
try {
|
||||||
|
await Bun.write(FILE_METADATA, JSON.stringify(data, null, 2))
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error: Could not write to 'metadata.json'.", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts EXIF data from an uploaded file.
|
||||||
|
*/
|
||||||
|
async function getExifData(file: File): Promise<any> {
|
||||||
|
try {
|
||||||
|
const buffer = await file.arrayBuffer()
|
||||||
|
// Set options to not parse maker notes or unknown tags for speed
|
||||||
|
const tags = ExifReader.load(buffer, {
|
||||||
|
expanded: true,
|
||||||
|
includeUnknown: false,
|
||||||
|
})
|
||||||
|
return tags
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error parsing EXIF for ${file.name}:`, e)
|
||||||
|
return { error: "Could not parse EXIF data." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Elysia Server ---
|
||||||
|
|
||||||
|
// Load secrets and ensure directories exist before starting
|
||||||
|
const INSTANT_KEY = process.env.INSTANT_KEY
|
||||||
|
if (!INSTANT_KEY) throw new Error("INSTANT_KEY is not defined")
|
||||||
|
if (!await exists(FOLDER_PHOTOS)) await mkdir(FOLDER_PHOTOS)
|
||||||
|
|
||||||
|
console.log("Server starting with valid 'secrets.json'.")
|
||||||
|
|
||||||
|
export const app = new Elysia()
|
||||||
|
.post("/upload", async ({ body, status }) => {
|
||||||
|
const { key, id, owner_key, photo, edited_photo } = body
|
||||||
|
|
||||||
|
// 1. Authentication
|
||||||
|
if (key !== INSTANT_KEY) status(401, "Invalid authentication key")
|
||||||
|
|
||||||
|
// 2. Check for existing ID
|
||||||
|
const metadata = await getMetadata()
|
||||||
|
if (metadata.find((m) => m.id === id)) status(409, "This ID already exists")
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3. Process and save files
|
||||||
|
// We use {id}-1 and {id}-2 as requested by "{id}-{number}"
|
||||||
|
const originalExt = photo.name.split(".").pop() || "jpg"
|
||||||
|
const editedExt = edited_photo.name.split(".").pop() || "jpg"
|
||||||
|
|
||||||
|
// Using "1" for original and "2" for edited
|
||||||
|
const originalPath = `${FOLDER_PHOTOS}/${id}-1.${originalExt}`
|
||||||
|
const editedPath = `${FOLDER_PHOTOS}/${id}-2.${editedExt}`
|
||||||
|
|
||||||
|
// Run file saving and EXIF parsing in parallel
|
||||||
|
const [originalExif, editedExif] = await Promise.all([
|
||||||
|
getExifData(photo),
|
||||||
|
getExifData(edited_photo),
|
||||||
|
Bun.write(originalPath, photo),
|
||||||
|
Bun.write(editedPath, edited_photo),
|
||||||
|
])
|
||||||
|
|
||||||
|
// 4. Create new metadata entry
|
||||||
|
const newEntry: PhotoMetadata = {
|
||||||
|
id: id,
|
||||||
|
owner_key: owner_key,
|
||||||
|
upload_time: new Date().toISOString(),
|
||||||
|
original_photo: {
|
||||||
|
filename: photo.name,
|
||||||
|
path: originalPath,
|
||||||
|
exif: originalExif,
|
||||||
|
},
|
||||||
|
edited_photo: {
|
||||||
|
filename: edited_photo.name,
|
||||||
|
path: editedPath,
|
||||||
|
exif: editedExif,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Save updated metadata
|
||||||
|
metadata.push(newEntry)
|
||||||
|
await saveMetadata(metadata)
|
||||||
|
|
||||||
|
return status(201, { success: true, id: newEntry.id })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("File upload processing error:", error)
|
||||||
|
return status(500, { error: "Failed to process file upload." })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// --- Validation Schema ---
|
||||||
|
// We interpret "Part 1: JSON body" as the text fields of
|
||||||
|
// the multipart form, which is the standard way to send
|
||||||
|
// metadata alongside files.
|
||||||
|
body: t.Object({
|
||||||
|
key: t.String(),
|
||||||
|
id: t.String({
|
||||||
|
minLength: 3,
|
||||||
|
maxLength: 20,
|
||||||
|
// Simple pattern: letters, numbers, hyphens
|
||||||
|
pattern: "^[a-zA-Z0-9_-]+$",
|
||||||
|
error:
|
||||||
|
"ID must be 3-20 alphanumeric characters, hyphens, or underscores.",
|
||||||
|
}),
|
||||||
|
owner_key: t.String({
|
||||||
|
minLength: 4,
|
||||||
|
maxLength: 4,
|
||||||
|
pattern: "^[0-9]{4}$", // e.g., "2941"
|
||||||
|
error: "Owner key must be exactly 4 digits.",
|
||||||
|
}),
|
||||||
|
photo: t.File({
|
||||||
|
type: ["image/jpeg", "image/png", "image/webp", "image/avif", "image/heic", "image/heif"],
|
||||||
|
error: "Original photo must be a valid image (jpg, png, webp, avif, heic, heif).",
|
||||||
|
}),
|
||||||
|
edited_photo: t.File({
|
||||||
|
type: ["image/jpeg", "image/png", "image/webp", "image/avif", "image/heic", "image/heif"],
|
||||||
|
error: "Edited photo must be a valid image (jpg, png, webp, avif, heic, heif).",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
// ----- 2. GET /photos -----
|
||||||
|
// Returns all metadata entries *without* the 'owner_key'.
|
||||||
|
.get("/photos", async () => {
|
||||||
|
const metadata = await getMetadata()
|
||||||
|
|
||||||
|
// Map over the array and filter out the 'owner_key' from each object
|
||||||
|
const publicMetadata = metadata.map((entry) => {
|
||||||
|
const { owner_key, ...publicEntry } = entry
|
||||||
|
return publicEntry
|
||||||
|
})
|
||||||
|
|
||||||
|
return publicMetadata
|
||||||
|
})
|
||||||
|
|
||||||
|
.listen(3000)
|
||||||
|
|
||||||
|
console.log(`🦊 Elysia server running at http://${app.server?.hostname}:${app.server?.port}`)
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
// --- Configuration ---
|
||||||
|
// Make sure this key matches your secrets.json!
|
||||||
|
const API_KEY = "meowmeow";
|
||||||
|
const SERVER_URL = "http://localhost:3000";
|
||||||
|
|
||||||
|
// A unique ID for this test upload
|
||||||
|
const TEST_ID = `test-${Math.floor(Math.random() * 10000)}`;
|
||||||
|
const TEST_OWNER_KEY = "1234";
|
||||||
|
const TEST_IMAGE_PATH = "./UWU01721.JPG"; // The dummy image you will create
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a dummy image file if it doesn't exist.
|
||||||
|
* This is just for testing; in a real client, you'd use a real file.
|
||||||
|
*/
|
||||||
|
async function ensureTestImageExists() {
|
||||||
|
const file = Bun.file(TEST_IMAGE_PATH);
|
||||||
|
if (!(await file.exists())) {
|
||||||
|
console.log(`Creating dummy file at: ${TEST_IMAGE_PATH}`);
|
||||||
|
// Create a tiny, simple text file as a placeholder.
|
||||||
|
// The server validation only checks MIME type based on extension,
|
||||||
|
// so for this test, a fake "jpg" is okay.
|
||||||
|
// A real image would be better for EXIF parsing.
|
||||||
|
await Bun.write(TEST_IMAGE_PATH, "This is a test image buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 1: POST /upload
|
||||||
|
* Attempts to upload the test image with metadata.
|
||||||
|
*/
|
||||||
|
async function testUpload() {
|
||||||
|
console.log(`--- Testing POST /upload with ID: ${TEST_ID} ---`);
|
||||||
|
await ensureTestImageExists();
|
||||||
|
|
||||||
|
const testFile = Bun.file(TEST_IMAGE_PATH);
|
||||||
|
|
||||||
|
// 1. Create the multipart/form-data payload
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("key", API_KEY);
|
||||||
|
formData.append("id", TEST_ID);
|
||||||
|
formData.append("owner_key", TEST_OWNER_KEY);
|
||||||
|
|
||||||
|
// Append the files
|
||||||
|
// We'll use the same dummy image for both "original" and "edited"
|
||||||
|
formData.append("photo", testFile);
|
||||||
|
formData.append("edited_photo", testFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. Send the request
|
||||||
|
const response = await fetch(`${SERVER_URL}/upload`, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Log the response
|
||||||
|
const result = await response.json();
|
||||||
|
console.log(`Status: ${response.status}`);
|
||||||
|
console.log("Response:", result);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Upload test FAILED!");
|
||||||
|
} else {
|
||||||
|
console.log("Upload test PASSED!");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error during /upload test:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 2: GET /photos
|
||||||
|
* Fetches the public metadata.
|
||||||
|
*/
|
||||||
|
async function testGetPhotos() {
|
||||||
|
console.log("\n--- Testing GET /photos ---");
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${SERVER_URL}/photos`);
|
||||||
|
const metadata = await response.json();
|
||||||
|
|
||||||
|
console.log(`Status: ${response.status}`);
|
||||||
|
console.log(`Found ${metadata.length} entries.`);
|
||||||
|
|
||||||
|
// Find our test upload
|
||||||
|
const ourEntry = metadata.find((e: any) => e.id === TEST_ID);
|
||||||
|
if (ourEntry) {
|
||||||
|
console.log("Found our test entry:", ourEntry);
|
||||||
|
if (ourEntry.owner_key) {
|
||||||
|
console.error("GET /photos test FAILED: 'owner_key' was exposed!");
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"GET /photos test PASSED: 'owner_key' was correctly removed.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn("Could not find our test entry in /photos response.");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error during /photos test:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Run Tests ---
|
||||||
|
async function runTests() {
|
||||||
|
await testUpload();
|
||||||
|
await testGetPhotos();
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
||||||
Reference in New Issue
Block a user