Skip to content

Commit

Permalink
S3 bucket (#120)
Browse files Browse the repository at this point in the history
* feat: Add S3 integration for modules

Add new function `allS3` to upload module images to S3 bucket if not already uploaded.
Update the Nuxt config to include S3 credentials.
Update module gallery component to display S3 image if available.
Update scheduler to run `allS3` every 3 hours.

# Please enter the commit message for your changes. Lines starting 
# with '#' will be ignored, and an empty message aborts the commit. 
# On branch main
# Your branch is ahead of 'origin/main' by 1 commit.
#   (use "git push" to publish your local commits) 
# Changes to be committed:
#       modified:   server/flowing.ts
#       modified:   nuxt.config.ts
#       modified:   components/module/Gallery.vue
#       modified:   server/plugins/scheduler.ts
#       modified:   prisma/schema.prisma
#       modified:   .gitignore
#       modified:   pnpm-lock.yaml

* feat: Upload images to S3 and log success/error messages

Uploaded images to S3 in 'allS3' function and added success and error messages using 'consola'. Added conditional check in 'startScheduler' to only run 'allS3' if S3 access key is provided.
  • Loading branch information
nexmoe authored May 4, 2024
1 parent 2ea9fac commit 55ead43
Show file tree
Hide file tree
Showing 10 changed files with 1,381 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ sw.*
# Auto generate on postinstall step
auto-imports.d.ts
components.d.ts

server/api/test.ts
4 changes: 3 additions & 1 deletion components/module/Gallery.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface Props {
}
const props = defineProps<Props>()
const cover = props.module.s3Key ? `https://space.r2.102415.xyz/${props.module.s3Key}` : props.module.image
</script>

<template>
Expand All @@ -37,7 +39,7 @@ const props = defineProps<Props>()
class="w-full h-full"
format="webp"
placeholder
:src="props.module!.image"
:src="cover || undefined"
:alt="module.title"
referrerpolicy="no-referrer"
loading="lazy"
Expand Down
2 changes: 1 addition & 1 deletion components/public/Loading.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="text-center">
<div role="status">
<svg aria-hidden="true" class="inline w-10 h-10 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
<svg aria-hidden="true" class="inline w-10 h-10 text-gray-200 animate-spin dark:text-gray-600 fill-primary"
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
Expand Down
9 changes: 7 additions & 2 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export default defineNuxtConfig({

runtimeConfig: {
version: '0.0.1',
s3: {
endpoint: '',
bucket: '',
accessKeyId: '',
secretAccessKey: '',
},
},

routeRules: {
Expand Down Expand Up @@ -107,9 +113,7 @@ export default defineNuxtConfig({
domains: [
'p3-juejin.byteimg.com',
'sns-webpic-qc.xhscdn.com',
'cdn.dribbble.com',
'picx.zhimg.com',
'img.zcool.cn',
'image.coolapk.com',
'pic1.zhimg.com',
'pic2.zhimg.com',
Expand All @@ -118,6 +122,7 @@ export default defineNuxtConfig({
'unavatar.io',
'i.dawnlab.me',
'ipfs.crossbell.io',
'space.r2.102415.xyz',
],
},

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"studio": "prisma studio"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.568.0",
"@nuxt/image": "^1.5.0",
"@pinia/nuxt": "^0.5.1",
"@prisma/client": "^5.12.1",
Expand Down
1,334 changes: 1,278 additions & 56 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ model Module {
image String?
imageWidth Int?
imageHeight Int?
s3Key String?
content String
date DateTime
platform String[]
Expand Down
32 changes: 32 additions & 0 deletions server/flowing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import probe from 'probe-image-size'
import type { NModule } from '~/composables/adapter/types'
import useAdapter from '~/composables/adapter/useAdapter'
import config from '~/config/config.json'
import { uploadRemoteImageToS3 } from '~/server/s3'

const prisma = new PrismaClient()

Expand Down Expand Up @@ -249,3 +250,34 @@ export async function allSize() {
}
}
}

export async function allS3() {
const modules = await prisma.module.findMany({
where: {
image: { not: null },
s3Key: null,
},
})

for (const module of modules) {
if (!module.image) continue
try {
await uploadRemoteImageToS3(module.image, `module/cover/${module.id}`)
await prisma.module.update({
where: { id: module.id },
data: {
s3Key: `module/cover/${module.id}`,
},
})
consola.success('Image uploaded to S3: ', module.title)
}
catch (e) {
consola.error('Error uploading image:', e)
continue
}
}

return {
status: 'healthy',
}
}
12 changes: 7 additions & 5 deletions server/plugins/scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { useScheduler } from '#scheduler'
import { flowing, allSize } from '~/server/flowing'
import { flowing, allSize, allS3 } from '~/server/flowing'

export default defineNitroPlugin(() => {
startScheduler()
})

function startScheduler() {
const config = useRuntimeConfig()

const scheduler = useScheduler()

// fetch every 3 hour
scheduler.run(async () => {
await flowing()
}).everyHours(3)

scheduler.run(async () => {
await allSize()
}).everyDays(14)
if (config.s3.accessKeyId) {
await allS3()
}
}).everyHours(3)
}
49 changes: 49 additions & 0 deletions server/s3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
S3Client,
PutObjectCommand,

} from '@aws-sdk/client-s3'

const config = useRuntimeConfig()

const S3 = new S3Client({
region: 'auto',
endpoint: config.s3.endpoint,
credentials: {
accessKeyId: config.s3.accessKeyId,
secretAccessKey: config.s3.secretAccessKey,
},
})

export async function uploadRemoteImageToS3(imageUrl: string, s3Key: string) {
try {
// 下载图片
const response = await fetch(imageUrl)
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.statusText}`)
}

// 获取图片的二进制数据
const blob = await response.blob()

// 将Blob转换为ArrayBuffer,因为PutObjectCommand需要Body为Buffer类型的参数
const arrayBuffer = await new Response(blob).arrayBuffer()
const buffer = Buffer.from(arrayBuffer)

// 上传到S3
const command = new PutObjectCommand(
{
Bucket: config.s3.bucket,
Key: s3Key, // 图片在S3中的路径和名称
Body: buffer,
ContentType: response.headers.get('content-type') || undefined, // 保持原始的MIME类型
},
)
const result = await S3.send(command)
return result
}
catch (error) {
console.error('Error uploading image:', error)
throw error
}
}

0 comments on commit 55ead43

Please sign in to comment.