missions s3
This commit is contained in:
parent
2be6e130e9
commit
f5d719ecc1
@ -2,6 +2,7 @@ import { NextResponse } from 'next/server';
|
|||||||
import { getServerSession } from 'next-auth';
|
import { getServerSession } from 'next-auth';
|
||||||
import { authOptions } from "@/app/api/auth/options";
|
import { authOptions } from "@/app/api/auth/options";
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
|
import { getPublicUrl, S3_CONFIG } from '@/lib/s3';
|
||||||
|
|
||||||
// Helper function to check authentication
|
// Helper function to check authentication
|
||||||
async function checkAuth(request: Request) {
|
async function checkAuth(request: Request) {
|
||||||
@ -60,7 +61,13 @@ export async function GET(request: Request, props: { params: Promise<{ missionId
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(attachments);
|
// Add public URLs to attachments
|
||||||
|
const attachmentsWithUrls = attachments.map(attachment => ({
|
||||||
|
...attachment,
|
||||||
|
publicUrl: getPublicUrl(attachment.filePath, S3_CONFIG.missionsBucket)
|
||||||
|
}));
|
||||||
|
|
||||||
|
return NextResponse.json(attachmentsWithUrls);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching mission attachments:', error);
|
console.error('Error fetching mission attachments:', error);
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { getServerSession } from 'next-auth';
|
|||||||
import { authOptions } from "@/app/api/auth/options";
|
import { authOptions } from "@/app/api/auth/options";
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import { deleteMissionLogo } from '@/lib/mission-uploads';
|
import { deleteMissionLogo } from '@/lib/mission-uploads';
|
||||||
import { getPublicUrl } from '@/lib/s3';
|
import { getPublicUrl, S3_CONFIG } from '@/lib/s3';
|
||||||
|
|
||||||
// Helper function to check authentication
|
// Helper function to check authentication
|
||||||
async function checkAuth(request: Request) {
|
async function checkAuth(request: Request) {
|
||||||
@ -82,10 +82,10 @@ export async function GET(request: Request, props: { params: Promise<{ missionId
|
|||||||
// Add public URLs to mission logo and attachments
|
// Add public URLs to mission logo and attachments
|
||||||
const missionWithUrls = {
|
const missionWithUrls = {
|
||||||
...mission,
|
...mission,
|
||||||
logoUrl: mission.logo ? getPublicUrl(mission.logo) : null,
|
logoUrl: mission.logo ? getPublicUrl(mission.logo, S3_CONFIG.missionsBucket) : null,
|
||||||
attachments: mission.attachments.map((attachment: { id: string; filename: string; filePath: string; fileType: string; fileSize: number; createdAt: Date }) => ({
|
attachments: mission.attachments.map((attachment: { id: string; filename: string; filePath: string; fileType: string; fileSize: number; createdAt: Date }) => ({
|
||||||
...attachment,
|
...attachment,
|
||||||
publicUrl: getPublicUrl(attachment.filePath)
|
publicUrl: getPublicUrl(attachment.filePath, S3_CONFIG.missionsBucket)
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { getServerSession } from 'next-auth';
|
|||||||
import { authOptions } from "@/app/api/auth/options";
|
import { authOptions } from "@/app/api/auth/options";
|
||||||
import { prisma } from '@/lib/prisma';
|
import { prisma } from '@/lib/prisma';
|
||||||
import { getPublicUrl } from '@/lib/s3';
|
import { getPublicUrl } from '@/lib/s3';
|
||||||
|
import { S3_CONFIG } from '@/lib/s3';
|
||||||
|
|
||||||
// Helper function to check authentication
|
// Helper function to check authentication
|
||||||
async function checkAuth(request: Request) {
|
async function checkAuth(request: Request) {
|
||||||
@ -86,7 +87,7 @@ export async function GET(request: Request) {
|
|||||||
// Transform logo paths to public URLs
|
// Transform logo paths to public URLs
|
||||||
const missionsWithPublicUrls = missions.map(mission => ({
|
const missionsWithPublicUrls = missions.map(mission => ({
|
||||||
...mission,
|
...mission,
|
||||||
logo: mission.logo ? getPublicUrl(mission.logo) : null
|
logo: mission.logo ? getPublicUrl(mission.logo, S3_CONFIG.missionsBucket) : null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
generateMissionLogoUploadUrl,
|
generateMissionLogoUploadUrl,
|
||||||
generateMissionAttachmentUploadUrl
|
generateMissionAttachmentUploadUrl
|
||||||
} from '@/lib/mission-uploads';
|
} from '@/lib/mission-uploads';
|
||||||
import { getPublicUrl } from '@/lib/s3';
|
import { getPublicUrl, S3_CONFIG } from '@/lib/s3';
|
||||||
|
|
||||||
// Helper function to check authentication
|
// Helper function to check authentication
|
||||||
async function checkAuth(request: Request) {
|
async function checkAuth(request: Request) {
|
||||||
@ -148,7 +148,7 @@ export async function POST(request: Request) {
|
|||||||
console.log('Logo uploaded successfully to path:', filePath);
|
console.log('Logo uploaded successfully to path:', filePath);
|
||||||
|
|
||||||
// Generate public URL
|
// Generate public URL
|
||||||
const publicUrl = getPublicUrl(filePath);
|
const publicUrl = getPublicUrl(filePath, S3_CONFIG.missionsBucket);
|
||||||
console.log('Public URL for logo:', publicUrl);
|
console.log('Public URL for logo:', publicUrl);
|
||||||
|
|
||||||
// Update mission record with logo path
|
// Update mission record with logo path
|
||||||
@ -184,7 +184,7 @@ export async function POST(request: Request) {
|
|||||||
console.log('Attachment uploaded successfully to path:', filePath);
|
console.log('Attachment uploaded successfully to path:', filePath);
|
||||||
|
|
||||||
// Generate public URL
|
// Generate public URL
|
||||||
const publicUrl = getPublicUrl(filePath);
|
const publicUrl = getPublicUrl(filePath, S3_CONFIG.missionsBucket);
|
||||||
console.log('Public URL for attachment:', publicUrl);
|
console.log('Public URL for attachment:', publicUrl);
|
||||||
|
|
||||||
// Create attachment record in database
|
// Create attachment record in database
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { s3Client, putObject, generatePresignedUrl, S3_CONFIG, deleteObject } from '@/lib/s3';
|
import { s3Client, putObject, generatePresignedUrl, S3_CONFIG, deleteObject } from '@/lib/s3';
|
||||||
import { PutObjectCommand } from '@aws-sdk/client-s3';
|
import { PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
||||||
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for mission-related file uploads using Minio
|
* Utilities for mission-related file uploads using Minio
|
||||||
@ -39,18 +40,18 @@ export async function uploadMissionLogo(
|
|||||||
const buffer = Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
console.log('Buffer created, size:', buffer.length);
|
console.log('Buffer created, size:', buffer.length);
|
||||||
|
|
||||||
// Upload to Minio
|
// Upload to Minio using the missions bucket
|
||||||
console.log('Creating S3 command with bucket:', S3_CONFIG.bucket);
|
console.log('Creating S3 command with bucket:', S3_CONFIG.missionsBucket);
|
||||||
console.log('S3 config:', {
|
console.log('S3 config:', {
|
||||||
endpoint: S3_CONFIG.endpoint || 'MISSING!',
|
endpoint: S3_CONFIG.endpoint || 'MISSING!',
|
||||||
region: S3_CONFIG.region || 'MISSING!',
|
region: S3_CONFIG.region || 'MISSING!',
|
||||||
bucket: S3_CONFIG.bucket || 'MISSING!',
|
bucket: S3_CONFIG.missionsBucket || 'MISSING!',
|
||||||
hasAccessKey: !!S3_CONFIG.accessKey || 'MISSING!',
|
hasAccessKey: !!S3_CONFIG.accessKey || 'MISSING!',
|
||||||
hasSecretKey: !!S3_CONFIG.secretKey || 'MISSING!'
|
hasSecretKey: !!S3_CONFIG.secretKey || 'MISSING!'
|
||||||
});
|
});
|
||||||
|
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: S3_CONFIG.bucket,
|
Bucket: S3_CONFIG.missionsBucket,
|
||||||
Key: filePath,
|
Key: filePath,
|
||||||
Body: buffer,
|
Body: buffer,
|
||||||
ContentType: file.type,
|
ContentType: file.type,
|
||||||
@ -92,9 +93,9 @@ export async function uploadMissionAttachment(
|
|||||||
const arrayBuffer = await file.arrayBuffer();
|
const arrayBuffer = await file.arrayBuffer();
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
|
||||||
// Upload to Minio
|
// Upload to Minio using missions bucket
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: S3_CONFIG.bucket,
|
Bucket: S3_CONFIG.missionsBucket,
|
||||||
Key: filePath,
|
Key: filePath,
|
||||||
Body: buffer,
|
Body: buffer,
|
||||||
ContentType: file.type,
|
ContentType: file.type,
|
||||||
@ -114,6 +115,21 @@ export async function uploadMissionAttachment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate presigned URL for missions bucket
|
||||||
|
async function generateMissionPresignedUrl(key: string, expiresIn = 3600): Promise<string> {
|
||||||
|
try {
|
||||||
|
const command = new PutObjectCommand({
|
||||||
|
Bucket: S3_CONFIG.missionsBucket,
|
||||||
|
Key: key
|
||||||
|
});
|
||||||
|
|
||||||
|
return await getSignedUrl(s3Client, command, { expiresIn });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating presigned URL for missions bucket:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate presigned URL for direct browser upload of mission logo
|
// Generate presigned URL for direct browser upload of mission logo
|
||||||
export async function generateMissionLogoUploadUrl(
|
export async function generateMissionLogoUploadUrl(
|
||||||
userId: string,
|
userId: string,
|
||||||
@ -126,7 +142,7 @@ export async function generateMissionLogoUploadUrl(
|
|||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const filePath = getMissionLogoPath(userId, missionId, fileExtension);
|
const filePath = getMissionLogoPath(userId, missionId, fileExtension);
|
||||||
const uploadUrl = await generatePresignedUrl(filePath, expiresIn);
|
const uploadUrl = await generateMissionPresignedUrl(filePath, expiresIn);
|
||||||
|
|
||||||
return { uploadUrl, filePath };
|
return { uploadUrl, filePath };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -147,7 +163,7 @@ export async function generateMissionAttachmentUploadUrl(
|
|||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const filePath = getMissionAttachmentPath(userId, missionId, filename);
|
const filePath = getMissionAttachmentPath(userId, missionId, filename);
|
||||||
const uploadUrl = await generatePresignedUrl(filePath, expiresIn);
|
const uploadUrl = await generateMissionPresignedUrl(filePath, expiresIn);
|
||||||
|
|
||||||
return { uploadUrl, filePath };
|
return { uploadUrl, filePath };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -156,10 +172,26 @@ export async function generateMissionAttachmentUploadUrl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete object from missions bucket
|
||||||
|
async function deleteMissionObject(key: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const command = new DeleteObjectCommand({
|
||||||
|
Bucket: S3_CONFIG.missionsBucket,
|
||||||
|
Key: key
|
||||||
|
});
|
||||||
|
|
||||||
|
await s3Client.send(command);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting mission object:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete mission attachment from Minio
|
// Delete mission attachment from Minio
|
||||||
export async function deleteMissionAttachment(filePath: string): Promise<boolean> {
|
export async function deleteMissionAttachment(filePath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await deleteObject(filePath);
|
await deleteMissionObject(filePath);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting mission attachment:', error);
|
console.error('Error deleting mission attachment:', error);
|
||||||
@ -170,7 +202,7 @@ export async function deleteMissionAttachment(filePath: string): Promise<boolean
|
|||||||
// Delete mission logo from Minio
|
// Delete mission logo from Minio
|
||||||
export async function deleteMissionLogo(filePath: string): Promise<boolean> {
|
export async function deleteMissionLogo(filePath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await deleteObject(filePath);
|
await deleteMissionObject(filePath);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting mission logo:', error);
|
console.error('Error deleting mission logo:', error);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export const S3_CONFIG = {
|
|||||||
endpoint: process.env.MINIO_S3_UPLOAD_BUCKET_URL,
|
endpoint: process.env.MINIO_S3_UPLOAD_BUCKET_URL,
|
||||||
region: process.env.MINIO_AWS_REGION,
|
region: process.env.MINIO_AWS_REGION,
|
||||||
bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME,
|
bucket: process.env.MINIO_AWS_S3_UPLOAD_BUCKET_NAME,
|
||||||
|
missionsBucket: process.env.MINIO_MISSIONS_BUCKET || 'missions',
|
||||||
accessKey: process.env.MINIO_ACCESS_KEY || process.env.AWS_ACCESS_KEY_ID,
|
accessKey: process.env.MINIO_ACCESS_KEY || process.env.AWS_ACCESS_KEY_ID,
|
||||||
secretKey: process.env.MINIO_SECRET_KEY || process.env.AWS_SECRET_ACCESS_KEY,
|
secretKey: process.env.MINIO_SECRET_KEY || process.env.AWS_SECRET_ACCESS_KEY,
|
||||||
}
|
}
|
||||||
@ -247,7 +248,7 @@ export async function generatePresignedUrl(key: string, expiresIn = 3600) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate a public URL for a file stored in Minio/S3
|
// Generate a public URL for a file stored in Minio/S3
|
||||||
export function getPublicUrl(filePath: string): string {
|
export function getPublicUrl(filePath: string, bucketName?: string): string {
|
||||||
if (!filePath) return '';
|
if (!filePath) return '';
|
||||||
if (filePath.startsWith('http')) return filePath; // Already a full URL
|
if (filePath.startsWith('http')) return filePath; // Already a full URL
|
||||||
|
|
||||||
@ -269,9 +270,11 @@ export function getPublicUrl(filePath: string): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine which bucket to use
|
||||||
|
const bucket = bucketName || S3_CONFIG.bucket;
|
||||||
|
|
||||||
// Construct the full URL using the standard approach
|
// Construct the full URL using the standard approach
|
||||||
const endpoint = S3_CONFIG.endpoint?.replace(/\/$/, ''); // Remove trailing slash if present
|
const endpoint = S3_CONFIG.endpoint?.replace(/\/$/, ''); // Remove trailing slash if present
|
||||||
const bucket = S3_CONFIG.bucket;
|
|
||||||
|
|
||||||
console.log('S3 Config for URL generation:', {
|
console.log('S3 Config for URL generation:', {
|
||||||
endpoint,
|
endpoint,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user