Harden gameplay timing and map loading
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import "server-only";
|
||||
import { admin, required } from "./supabase";
|
||||
import { z } from "zod";
|
||||
import { ServiceError } from "./service-error";
|
||||
export type MapListing = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -23,12 +25,22 @@ export type MapListing = {
|
||||
credit?: string;
|
||||
};
|
||||
export async function mediaUrl(path: string | null, expires = 3600) {
|
||||
if (!path) return null;
|
||||
return (await mediaUrls([path], expires))[0];
|
||||
}
|
||||
export async function mediaUrls(paths: (string | null)[], expires = 3600) {
|
||||
const unique = [...new Set(paths.filter((path): path is string => !!path))];
|
||||
if (!unique.length) return paths.map(() => null);
|
||||
const { data, error } = await admin()
|
||||
.storage.from("minegeo-media")
|
||||
.createSignedUrl(path, expires);
|
||||
if (error) throw new Error(error.message);
|
||||
return data.signedUrl;
|
||||
.createSignedUrls(unique, expires);
|
||||
if (error || !data || data.some((item) => item.error || !item.signedUrl))
|
||||
throw new ServiceError(
|
||||
"Map images are temporarily unavailable. Please try again.",
|
||||
);
|
||||
const signed = new Map(
|
||||
data.map((item, index) => [unique[index], item.signedUrl]),
|
||||
);
|
||||
return paths.map((path) => (path ? (signed.get(path) ?? null) : null));
|
||||
}
|
||||
export async function catalog() {
|
||||
const client = admin();
|
||||
@@ -50,35 +62,40 @@ export async function catalog() {
|
||||
.in("id", [...new Set(maps.map((m) => m.owner_id))]),
|
||||
)
|
||||
: [];
|
||||
return Promise.all(
|
||||
maps.map(async (m) => ({
|
||||
...m,
|
||||
credit:
|
||||
owners.find((p) => p.id === m.owner_id)?.display_name ?? "Explorer",
|
||||
image: (await mediaUrl(m.map_path || m.cover_path)) ?? undefined,
|
||||
mapImage: (await mediaUrl(m.map_path)) ?? undefined,
|
||||
server: servers.find((s) => s.id === m.server_id),
|
||||
})),
|
||||
const signed = await mediaUrls(
|
||||
maps.flatMap((map) => [map.map_path || map.cover_path, map.map_path]),
|
||||
);
|
||||
return maps.map((m, index) => ({
|
||||
...m,
|
||||
credit: owners.find((p) => p.id === m.owner_id)?.display_name ?? "Explorer",
|
||||
image: signed[index * 2] ?? undefined,
|
||||
mapImage: signed[index * 2 + 1] ?? undefined,
|
||||
server: servers.find((s) => s.id === m.server_id),
|
||||
}));
|
||||
}
|
||||
export async function getMap(id: string) {
|
||||
const map = required(
|
||||
await admin()
|
||||
.from("datasets")
|
||||
.select("*")
|
||||
.eq("id", id)
|
||||
.eq("state", "published")
|
||||
.single(),
|
||||
) as MapListing;
|
||||
const owner = await admin()
|
||||
.from("profiles")
|
||||
.select("display_name")
|
||||
.eq("id", map.owner_id)
|
||||
if (!z.string().uuid().safeParse(id).success) return null;
|
||||
const result = await admin()
|
||||
.from("datasets")
|
||||
.select("*")
|
||||
.eq("id", id)
|
||||
.eq("state", "published")
|
||||
.maybeSingle();
|
||||
if (result.error) required(result);
|
||||
if (!result.data) return null;
|
||||
const map = result.data as unknown as MapListing;
|
||||
const [owner, signed] = await Promise.all([
|
||||
admin()
|
||||
.from("profiles")
|
||||
.select("display_name")
|
||||
.eq("id", map.owner_id)
|
||||
.maybeSingle(),
|
||||
mediaUrls([map.cover_path, map.map_path]),
|
||||
]);
|
||||
return {
|
||||
...map,
|
||||
credit: owner.data?.display_name ?? "Explorer",
|
||||
image: await mediaUrl(map.cover_path),
|
||||
mapImage: await mediaUrl(map.map_path),
|
||||
image: signed[0],
|
||||
mapImage: signed[1],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user