mirror of
https://github.com/FoggedLens/deflock.git
synced 2026-08-16 15:40:23 +02:00
works with all browser-native formats
This commit is contained in:
@@ -12,6 +12,10 @@ import pekko.http.scaladsl.model._
|
||||
import pekko.http.scaladsl.server.Directives.{path, _}
|
||||
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
|
||||
import org.apache.pekko.http.scaladsl.server.RejectionHandler
|
||||
import services.DeleteObjectRequest
|
||||
import services.DeleteObjectRequestJsonProtocol._
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
import services.ImageSubmissionJsonProtocol._
|
||||
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
@@ -28,6 +32,11 @@ object ShotgunServer {
|
||||
|
||||
val client = new services.OverpassClient()
|
||||
val nominatim = new services.NominatimClient()
|
||||
val awsClient = new services.AWSClient(
|
||||
accessKeyId = sys.env("AWS_ACCESS_KEY_ID"),
|
||||
secretAccessKey = sys.env("AWS_SECRET_ACCESS_KEY"),
|
||||
region = "us-east-1"
|
||||
)
|
||||
|
||||
// CORS
|
||||
val allowedOrigins = List(
|
||||
@@ -72,6 +81,32 @@ object ShotgunServer {
|
||||
}
|
||||
}
|
||||
},
|
||||
path("presigned-urls") {
|
||||
get {
|
||||
parameters("count".as[Int], "contentType".as[String], "author".as[String]) { (imageCount, contentType, author) =>
|
||||
if (imageCount > 5)
|
||||
complete(StatusCodes.BadRequest, "Cannot request more than 5 presigned URLs at a time")
|
||||
else {
|
||||
val urls = awsClient.getMultiplePutPresignedUrls("deflock-photo-uploads", imageCount, contentType, author, 5)
|
||||
complete(urls)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
path("user-submissions") {
|
||||
get {
|
||||
val submissions = awsClient.getAllObjects("deflock-photo-uploads")
|
||||
complete(submissions)
|
||||
}
|
||||
},
|
||||
path("delete-object") {
|
||||
post {
|
||||
entity(as[DeleteObjectRequest]) { request =>
|
||||
val res = awsClient.deleteObject("deflock-photo-uploads", request.objectKey)
|
||||
complete(res)
|
||||
}
|
||||
}
|
||||
},
|
||||
path("oauth2" / "callback") {
|
||||
get {
|
||||
parameters(Symbol("code").?) { (code) =>
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package services
|
||||
|
||||
import software.amazon.awssdk.auth.credentials.{AwsBasicCredentials, StaticCredentialsProvider}
|
||||
import software.amazon.awssdk.regions.Region
|
||||
import software.amazon.awssdk.services.s3.S3Client
|
||||
import software.amazon.awssdk.services.s3.model.{GetObjectTaggingRequest, ListObjectsRequest, PutObjectRequest}
|
||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner
|
||||
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest
|
||||
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
import spray.json.RootJsonFormat
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import java.net.URL
|
||||
import java.time.Duration
|
||||
|
||||
case class ImageSubmission(bucketName: String, objectKey: String, author: String, publicUrl: String)
|
||||
object ImageSubmissionJsonProtocol {
|
||||
implicit val imageSubmissionFormat: RootJsonFormat[ImageSubmission] = jsonFormat4(ImageSubmission)
|
||||
}
|
||||
|
||||
case class DeleteObjectRequest(objectKey: String)
|
||||
object DeleteObjectRequestJsonProtocol {
|
||||
implicit val deleteObjectRequestFormat: RootJsonFormat[DeleteObjectRequest] = jsonFormat1(DeleteObjectRequest)
|
||||
}
|
||||
|
||||
class AWSClient(accessKeyId: String, secretAccessKey: String, region: String) {
|
||||
|
||||
private val credentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey)
|
||||
private val s3Client = S3Client.builder()
|
||||
.region(Region.of(region))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build()
|
||||
|
||||
private val s3Presigner = S3Presigner.builder()
|
||||
.region(Region.of(region))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build()
|
||||
|
||||
def putPresignedUrl(bucketName: String, objectKey: String, contentType: String, expirationMinutes: Long): URL = {
|
||||
val putObjectRequest = PutObjectRequest.builder()
|
||||
.bucket(bucketName)
|
||||
.key(objectKey)
|
||||
.contentType(contentType)
|
||||
.build()
|
||||
|
||||
val presignRequest = PutObjectPresignRequest.builder()
|
||||
.signatureDuration(Duration.ofMinutes(expirationMinutes))
|
||||
.putObjectRequest(putObjectRequest)
|
||||
.build()
|
||||
|
||||
s3Presigner.presignPutObject(presignRequest).url()
|
||||
}
|
||||
|
||||
private val contentTypeToExtension: Map[String, String] = Map(
|
||||
"image/jpeg" -> "jpg",
|
||||
"image/png" -> "png",
|
||||
"image/gif" -> "gif",
|
||||
"image/svg+xml" -> "svg",
|
||||
"image/webp" -> "webp",
|
||||
"image/tiff" -> "tiff",
|
||||
"image/bmp" -> "bmp",
|
||||
"image/heic" -> "heic",
|
||||
"image/heif" -> "heif"
|
||||
)
|
||||
|
||||
def getMultiplePutPresignedUrls(bucketName: String, count: Int, contentType: String, author: String, expirationMinutes: Long): Seq[String] = {
|
||||
if (!contentTypeToExtension.contains(contentType))
|
||||
throw new IllegalArgumentException(s"Unsupported content type: $contentType")
|
||||
|
||||
val uuids = (1 to count).map(_ => author + "/" + java.util.UUID.randomUUID().toString + "." + contentTypeToExtension(contentType))
|
||||
uuids.take(5).map { objectKey =>
|
||||
putPresignedUrl(bucketName, objectKey, contentType, expirationMinutes).toString
|
||||
}
|
||||
}
|
||||
|
||||
def getAllObjects(bucketName: String): Seq[ImageSubmission] = {
|
||||
val listObjectsResponse = s3Client.listObjects(
|
||||
ListObjectsRequest.builder().bucket(bucketName).build()
|
||||
).contents()
|
||||
|
||||
listObjectsResponse.asScala.map { obj =>
|
||||
val parts = obj.key().split("/")
|
||||
val author = parts.head
|
||||
val objectKey = parts.tail.mkString("/")
|
||||
ImageSubmission(bucketName, objectKey, author, s"https://$bucketName.s3.amazonaws.com/$author/$objectKey")
|
||||
}
|
||||
}
|
||||
|
||||
def deleteObject(bucketName: String, objectKey: String): String = {
|
||||
s3Client.deleteObject(
|
||||
software.amazon.awssdk.services.s3.model.DeleteObjectRequest.builder()
|
||||
.bucket(bucketName)
|
||||
.key(objectKey)
|
||||
.build()
|
||||
)
|
||||
s"Deleted object $objectKey from bucket $bucketName"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user