KI-AGENT: Interne MinIO-Links durch Backend-Downloads ersetzen
This commit is contained in:
@@ -177,11 +177,49 @@ export default async function exportRoutes(server: FastifyInstance) {
|
|||||||
.from(generatedexports)
|
.from(generatedexports)
|
||||||
.where(eq(generatedexports.tenantId, req.user.tenant_id))
|
.where(eq(generatedexports.tenantId, req.user.tenant_id))
|
||||||
|
|
||||||
console.log(data)
|
reply.send(data.map(item => ({
|
||||||
reply.send(data)
|
...item,
|
||||||
|
url: `/api/exports/${item.id}/download`,
|
||||||
|
})))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
server.get("/exports/:id/download", async (req, reply) => {
|
||||||
|
const { id } = req.params as { id: string }
|
||||||
|
const exportId = Number(id)
|
||||||
|
if (!Number.isSafeInteger(exportId)) {
|
||||||
|
return reply.code(404).send({ error: "Export not found" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const [item] = await server.db
|
||||||
|
.select()
|
||||||
|
.from(generatedexports)
|
||||||
|
.where(eq(generatedexports.id, exportId))
|
||||||
|
|
||||||
|
if (
|
||||||
|
!item ||
|
||||||
|
item.tenantId !== req.user.tenant_id ||
|
||||||
|
item.validUntil.getTime() <= Date.now()
|
||||||
|
) {
|
||||||
|
return reply.code(404).send({ error: "Export not found" })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { Body, ContentType } = await s3.send(new GetObjectCommand({
|
||||||
|
Bucket: secrets.S3_BUCKET,
|
||||||
|
Key: item.filePath,
|
||||||
|
}))
|
||||||
|
const filename = item.filePath.split("/").pop() || `export-${item.id}`
|
||||||
|
|
||||||
|
reply.header("Content-Type", ContentType || "application/octet-stream")
|
||||||
|
reply.header("Content-Disposition", `attachment; filename="${filename}"`)
|
||||||
|
return reply.send(Body as any)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
return reply.code(500).send({ error: "Could not download export" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { s3 } from "../utils/s3"
|
|||||||
import {
|
import {
|
||||||
GetObjectCommand
|
GetObjectCommand
|
||||||
} from "@aws-sdk/client-s3"
|
} from "@aws-sdk/client-s3"
|
||||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"
|
|
||||||
import archiver from "archiver"
|
import archiver from "archiver"
|
||||||
import { secrets } from "../utils/secrets"
|
import { secrets } from "../utils/secrets"
|
||||||
import { saveFile } from "../utils/files"
|
import { saveFile } from "../utils/files"
|
||||||
@@ -241,6 +240,28 @@ export default async function fileRoutes(server: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Dateien über das Backend ausliefern, damit interne S3-Endpunkte wie
|
||||||
|
// http://minio:9000 niemals als URL an den Browser gelangen.
|
||||||
|
server.get("/files/content/:id", async (req, reply) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params as { id: string }
|
||||||
|
const file = await loadSingleFileForRequest(req, id)
|
||||||
|
if (!file) return reply.code(404).send({ error: "File not found" })
|
||||||
|
|
||||||
|
const { Body, ContentType } = await s3.send(new GetObjectCommand({
|
||||||
|
Bucket: secrets.S3_BUCKET,
|
||||||
|
Key: file.path!
|
||||||
|
}))
|
||||||
|
|
||||||
|
reply.header("Content-Type", ContentType || "application/octet-stream")
|
||||||
|
reply.header("Content-Disposition", "inline")
|
||||||
|
return reply.send(Body as any)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return reply.code(500).send({ error: "Could not load file" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@@ -261,13 +282,7 @@ export default async function fileRoutes(server: FastifyInstance) {
|
|||||||
const file = await loadSingleFileForRequest(req, id)
|
const file = await loadSingleFileForRequest(req, id)
|
||||||
if (!file) return reply.code(404).send({ error: "Not found" })
|
if (!file) return reply.code(404).send({ error: "Not found" })
|
||||||
|
|
||||||
const url = await getSignedUrl(
|
return { ...file, url: `/api/files/content/${file.id}` }
|
||||||
s3,
|
|
||||||
new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }),
|
|
||||||
{ expiresIn: 900 }
|
|
||||||
)
|
|
||||||
|
|
||||||
return { ...file, url }
|
|
||||||
} else {
|
} else {
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// MULTIPLE PRESIGNED URLs
|
// MULTIPLE PRESIGNED URLs
|
||||||
@@ -283,26 +298,10 @@ export default async function fileRoutes(server: FastifyInstance) {
|
|||||||
|
|
||||||
const selected = rows.filter(f => ids.includes(f.id) && f.path)
|
const selected = rows.filter(f => ids.includes(f.id) && f.path)
|
||||||
|
|
||||||
console.log(selected)
|
const output = selected.map(file => ({
|
||||||
|
...file,
|
||||||
const url = await getSignedUrl(
|
url: `/api/files/content/${file.id}`
|
||||||
s3,
|
}))
|
||||||
new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: selected[0].path! }),
|
|
||||||
{ expiresIn: 900 }
|
|
||||||
)
|
|
||||||
console.log(url)
|
|
||||||
console.log(selected.filter(f => !f.path))
|
|
||||||
|
|
||||||
const output = await Promise.all(
|
|
||||||
selected.map(async (file) => {
|
|
||||||
const url = await getSignedUrl(
|
|
||||||
s3,
|
|
||||||
new GetObjectCommand({ Bucket: secrets.S3_BUCKET, Key: file.path! }),
|
|
||||||
{ expiresIn: 900 }
|
|
||||||
)
|
|
||||||
return { ...file, url }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return { files: output }
|
return { files: output }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user