2025-10-18 21:43:42 +00:00
|
|
|
import { PutObjectCommand, type S3Client } from "@aws-sdk/client-s3";
|
2025-10-11 10:32:52 +00:00
|
|
|
import type { SupabaseClient, User } from "@supabase/supabase-js";
|
|
|
|
|
import { type Context, Hono, type Next } from "hono";
|
2025-10-09 20:33:16 +00:00
|
|
|
import { getTabloFileNames, isTabloAdmin, isTabloMember } from "./helpers.js";
|
2025-10-12 09:31:41 +00:00
|
|
|
import {
|
|
|
|
|
authMiddleware,
|
|
|
|
|
r2Middleware,
|
|
|
|
|
streamChatMiddleware,
|
|
|
|
|
} from "./middleware.js";
|
2025-10-09 20:33:16 +00:00
|
|
|
|
|
|
|
|
export const tabloDataRouter = new Hono<{
|
|
|
|
|
Variables: {
|
|
|
|
|
user: User;
|
|
|
|
|
supabase: SupabaseClient;
|
|
|
|
|
s3_client: S3Client;
|
|
|
|
|
};
|
|
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
tabloDataRouter.use(authMiddleware);
|
|
|
|
|
tabloDataRouter.use(streamChatMiddleware);
|
|
|
|
|
tabloDataRouter.use(r2Middleware);
|
|
|
|
|
|
|
|
|
|
const checkTabloMember = async (c: Context, next: Next) => {
|
|
|
|
|
const supabase = c.get("supabase");
|
|
|
|
|
const user = c.get("user");
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const isMember = await isTabloMember(supabase, tabloId, user.id);
|
|
|
|
|
if (!isMember) {
|
|
|
|
|
return c.json({ error: "You are not a member of this tablo" }, 403);
|
|
|
|
|
}
|
|
|
|
|
await next();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const checkTabloAdmin = async (c: Context, next: Next) => {
|
|
|
|
|
const supabase = c.get("supabase");
|
|
|
|
|
const user = c.get("user");
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const isAdmin = await isTabloAdmin(supabase, tabloId, user.id);
|
|
|
|
|
if (!isAdmin) {
|
|
|
|
|
return c.json({ error: "You are not an admin of this tablo" }, 403);
|
|
|
|
|
}
|
|
|
|
|
await next();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// GET /tablo-data/:tabloId/filenames - Get all files for a tablo
|
|
|
|
|
tabloDataRouter.get("/:tabloId/filenames", checkTabloMember, async (c) => {
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const s3_client = c.get("s3_client");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const fileNames = await getTabloFileNames(s3_client, tabloId);
|
|
|
|
|
return c.json({ fileNames: fileNames || [] });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error fetching tablo files:", error);
|
|
|
|
|
return c.json({ error: "Failed to fetch tablo files" }, 500);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// GET /tablo-data/:tabloId/:fileName - Get a specific file
|
|
|
|
|
tabloDataRouter.get("/:tabloId/:fileName", checkTabloMember, async (c) => {
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const fileName = c.req.param("fileName");
|
|
|
|
|
|
|
|
|
|
const s3_client = c.get("s3_client");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const { GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
|
|
|
|
|
|
|
|
const response = await s3_client.send(
|
|
|
|
|
new GetObjectCommand({
|
|
|
|
|
Bucket: "tablo-data",
|
|
|
|
|
Key: `${tabloId}/${fileName}`,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!response.Body) {
|
|
|
|
|
return c.json({ error: "File not found" }, 404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const content = await response.Body.transformToString();
|
|
|
|
|
|
|
|
|
|
return c.json({
|
|
|
|
|
fileName,
|
|
|
|
|
content,
|
|
|
|
|
contentType: response.ContentType,
|
|
|
|
|
lastModified: response.LastModified,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error fetching file:", error);
|
|
|
|
|
return c.json({ error: "Failed to fetch file" }, 500);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// POST /tablo-data/:tabloId/:fileName - Create or update a file
|
|
|
|
|
tabloDataRouter.post("/:tabloId/:fileName", checkTabloAdmin, async (c) => {
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const fileName = c.req.param("fileName");
|
|
|
|
|
|
|
|
|
|
const s3_client = c.get("s3_client");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const body = await c.req.json();
|
|
|
|
|
const { content, contentType = "text/plain" } = body;
|
|
|
|
|
|
|
|
|
|
if (!content) {
|
|
|
|
|
return c.json({ error: "Content is required" }, 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await s3_client.send(
|
|
|
|
|
new PutObjectCommand({
|
|
|
|
|
Bucket: "tablo-data",
|
|
|
|
|
Key: `${tabloId}/${fileName}`,
|
|
|
|
|
Body: content,
|
|
|
|
|
ContentType: contentType,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return c.json({
|
|
|
|
|
message: "File uploaded successfully",
|
|
|
|
|
fileName,
|
|
|
|
|
tabloId,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error uploading file:", error);
|
|
|
|
|
return c.json({ error: "Failed to upload file" }, 500);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// // PUT /tablo-data/:tabloId/:fileName - Update a file
|
|
|
|
|
// tabloDataRouter.put("/:tabloId/:fileName", async (c) => {
|
|
|
|
|
// const tabloId = c.req.param("tabloId");
|
|
|
|
|
// const fileName = c.req.param("fileName");
|
|
|
|
|
// const s3_client = c.get("s3_client");
|
|
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
// const body = await c.req.json();
|
|
|
|
|
// const { content, contentType = "text/plain" } = body;
|
|
|
|
|
|
|
|
|
|
// if (!content) {
|
|
|
|
|
// return c.json({ error: "Content is required" }, 400);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// const { PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
|
|
|
|
|
|
|
|
// await s3_client.send(
|
|
|
|
|
// new PutObjectCommand({
|
|
|
|
|
// Bucket: "tablo-data",
|
|
|
|
|
// Key: `${tabloId}/${fileName}`,
|
|
|
|
|
// Body: content,
|
|
|
|
|
// ContentType: contentType,
|
|
|
|
|
// })
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
// return c.json({
|
|
|
|
|
// message: "File updated successfully",
|
|
|
|
|
// fileName,
|
|
|
|
|
// tabloId,
|
|
|
|
|
// });
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
// console.error("Error updating file:", error);
|
|
|
|
|
// return c.json({ error: "Failed to update file" }, 500);
|
|
|
|
|
// }
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// DELETE /tablo-data/:tabloId/:fileName - Delete a file
|
|
|
|
|
tabloDataRouter.delete("/:tabloId/:fileName", checkTabloAdmin, async (c) => {
|
|
|
|
|
const tabloId = c.req.param("tabloId");
|
|
|
|
|
const fileName = c.req.param("fileName");
|
|
|
|
|
const s3_client = c.get("s3_client");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
|
|
|
|
|
|
|
|
|
|
await s3_client.send(
|
|
|
|
|
new DeleteObjectCommand({
|
|
|
|
|
Bucket: "tablo-data",
|
|
|
|
|
Key: `${tabloId}/${fileName}`,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return c.json({
|
|
|
|
|
message: "File deleted successfully",
|
|
|
|
|
fileName,
|
|
|
|
|
tabloId,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error deleting file:", error);
|
|
|
|
|
return c.json({ error: "Failed to delete file" }, 500);
|
|
|
|
|
}
|
|
|
|
|
});
|