Get image upload to cloudinary with the same public id
This means we can put a consistent url in the head for the card image
This commit is contained in:
72
app/api/src/functions/image-upload.ts
Normal file
72
app/api/src/functions/image-upload.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { APIGatewayEvent } from 'aws-lambda'
|
||||
import { logger } from 'src/lib/logger'
|
||||
import { v2 as cloudinary, UploadApiResponse } from 'cloudinary'
|
||||
// import { requireOwnership } from 'src/lib/owner'
|
||||
// import { requireAuth } from 'src/lib/auth'
|
||||
|
||||
cloudinary.config({
|
||||
cloud_name: 'irevdev',
|
||||
api_key: process.env.CLOUDINARY_API_KEY,
|
||||
api_secret: process.env.CLOUDINARY_API_SECRET,
|
||||
})
|
||||
|
||||
/**
|
||||
* The handler function is your code that processes http request events.
|
||||
* You can use return and throw to send a response or error, respectively.
|
||||
*
|
||||
* Important: When deployed, a custom serverless function is an open API endpoint and
|
||||
* is your responsibility to secure appropriately.
|
||||
*
|
||||
* @see {@link https://redwoodjs.com/docs/serverless-functions#security-considerations|Serverless Function Considerations}
|
||||
* in the RedwoodJS documentation for more information.
|
||||
*
|
||||
* @typedef { import('aws-lambda').APIGatewayEvent } APIGatewayEvent
|
||||
* @typedef { import('aws-lambda').Context } Context
|
||||
* @param { APIGatewayEvent } event - an object which contains information from the invoker.
|
||||
* @param { Context } context - contains information about the invocation,
|
||||
* function, and execution environment.
|
||||
*/
|
||||
export const handler = async (event: APIGatewayEvent, _context) => {
|
||||
logger.info('Invoked image-upload function')
|
||||
// requireAuth()
|
||||
const {
|
||||
image64,
|
||||
upload_preset,
|
||||
public_id,
|
||||
invalidate,
|
||||
projectId,
|
||||
}: {
|
||||
image64: string
|
||||
upload_preset: string
|
||||
public_id: string
|
||||
invalidate: boolean
|
||||
projectId: string
|
||||
} = JSON.parse(event.body)
|
||||
// await requireOwnership({projectId})
|
||||
|
||||
const uploadResult: UploadApiResponse = await new Promise(
|
||||
(resolve, reject) => {
|
||||
cloudinary.uploader.upload(
|
||||
image64,
|
||||
{ upload_preset, public_id, invalidate },
|
||||
(error, result) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
resolve(result)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
publicId: uploadResult.public_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ export const schema = gql`
|
||||
userId: String!
|
||||
deleted: Boolean!
|
||||
cadPackage: CadPackage!
|
||||
socialCard: SocialCard
|
||||
Comment: [Comment]!
|
||||
Reaction(userId: String): [ProjectReaction]!
|
||||
}
|
||||
|
||||
35
app/api/src/graphql/socialCards.sdl.ts
Normal file
35
app/api/src/graphql/socialCards.sdl.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export const schema = gql`
|
||||
type SocialCard {
|
||||
id: String!
|
||||
projectId: String!
|
||||
project: Project!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
url: String
|
||||
outOfDate: Boolean!
|
||||
}
|
||||
|
||||
type Query {
|
||||
socialCards: [SocialCard!]!
|
||||
socialCard(id: String!): SocialCard
|
||||
}
|
||||
|
||||
input CreateSocialCardInput {
|
||||
projectId: String!
|
||||
url: String
|
||||
outOfDate: Boolean!
|
||||
}
|
||||
|
||||
input UpdateSocialCardInput {
|
||||
projectId: String
|
||||
url: String
|
||||
outOfDate: Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createSocialCard(input: CreateSocialCardInput!): SocialCard!
|
||||
updateSocialCard(id: String!, input: UpdateSocialCardInput!): SocialCard!
|
||||
deleteSocialCard(id: String!): SocialCard!
|
||||
updateSocialCardByProjectId(projectId: String!, url: String!): SocialCard!
|
||||
}
|
||||
`
|
||||
@@ -5,10 +5,16 @@ export const requireOwnership = async ({
|
||||
userId,
|
||||
userName,
|
||||
projectId,
|
||||
}: { userId?: string; userName?: string; projectId?: string } = {}) => {
|
||||
sub,
|
||||
}: {
|
||||
userId?: string
|
||||
userName?: string
|
||||
projectId?: string
|
||||
sub?: string
|
||||
} = {}) => {
|
||||
// IMPORTANT, don't forget to await this function, as it will only block
|
||||
// unwanted db actions if it has time to look up resources in the db.
|
||||
if (!context.currentUser) {
|
||||
if (!(context?.currentUser || sub)) {
|
||||
throw new AuthenticationError("You don't have permission to do that.")
|
||||
}
|
||||
if (!userId && !userName && !projectId) {
|
||||
@@ -22,7 +28,7 @@ export const requireOwnership = async ({
|
||||
return
|
||||
}
|
||||
|
||||
const netlifyUserId = context.currentUser?.sub
|
||||
const netlifyUserId = context?.currentUser?.sub || sub
|
||||
if (userId && userId !== netlifyUserId) {
|
||||
throw new ForbiddenError("You don't own this resource.")
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ export const deleteProject = async ({ id }: Prisma.ProjectWhereUniqueInput) => {
|
||||
export const Project = {
|
||||
user: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
|
||||
db.project.findUnique({ where: { id: root.id } }).user(),
|
||||
socialCard: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
|
||||
db.project.findUnique({ where: { id: root.id } }).socialCard(),
|
||||
Comment: (_obj, { root }: ResolverArgs<ReturnType<typeof project>>) =>
|
||||
db.project
|
||||
.findUnique({ where: { id: root.id } })
|
||||
|
||||
84
app/api/src/services/socialCards/socialCards.ts
Normal file
84
app/api/src/services/socialCards/socialCards.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { Prisma } from '@prisma/client'
|
||||
import type { ResolverArgs, BeforeResolverSpecType } from '@redwoodjs/api'
|
||||
|
||||
import { db } from 'src/lib/db'
|
||||
import { requireAuth } from 'src/lib/auth'
|
||||
|
||||
// Used when the environment variable REDWOOD_SECURE_SERVICES=1
|
||||
export const beforeResolver = (rules: BeforeResolverSpecType) => {
|
||||
rules.add(requireAuth)
|
||||
}
|
||||
|
||||
export const socialCards = () => {
|
||||
return db.socialCard.findMany()
|
||||
}
|
||||
|
||||
export const socialCard = ({ id }: Prisma.SocialCardWhereUniqueInput) => {
|
||||
return db.socialCard.findUnique({
|
||||
where: { id },
|
||||
})
|
||||
}
|
||||
|
||||
interface CreateSocialCardArgs {
|
||||
input: Prisma.SocialCardCreateInput
|
||||
}
|
||||
|
||||
export const createSocialCard = ({ input }: CreateSocialCardArgs) => {
|
||||
return db.socialCard.create({
|
||||
data: input,
|
||||
})
|
||||
}
|
||||
|
||||
interface UpdateSocialCardArgs extends Prisma.SocialCardWhereUniqueInput {
|
||||
input: Prisma.SocialCardUpdateInput
|
||||
}
|
||||
|
||||
export const updateSocialCard = ({ id, input }: UpdateSocialCardArgs) => {
|
||||
return db.socialCard.update({
|
||||
data: input,
|
||||
where: { id },
|
||||
})
|
||||
}
|
||||
|
||||
export const updateSocialCardByProjectId = async ({
|
||||
projectId,
|
||||
url,
|
||||
}: {
|
||||
url: string
|
||||
projectId: string
|
||||
}) => {
|
||||
let id: string
|
||||
try {
|
||||
const socialCard = await db.project
|
||||
.findUnique({ where: { id: projectId } })
|
||||
.socialCard()
|
||||
id = socialCard.id
|
||||
} catch (e) {
|
||||
return db.socialCard.create({
|
||||
data: {
|
||||
url,
|
||||
project: {
|
||||
connect: {
|
||||
id: projectId,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return db.socialCard.update({
|
||||
data: { url },
|
||||
where: { id },
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteSocialCard = ({ id }: Prisma.SocialCardWhereUniqueInput) => {
|
||||
return db.socialCard.delete({
|
||||
where: { id },
|
||||
})
|
||||
}
|
||||
|
||||
export const SocialCard = {
|
||||
project: (_obj, { root }: ResolverArgs<ReturnType<typeof socialCard>>) =>
|
||||
db.socialCard.findUnique({ where: { id: root.id } }).project(),
|
||||
}
|
||||
Reference in New Issue
Block a user