feat: initial project setup for Meme Studio

Next.js 14 App Router application for managing homophone pun game levels:

- Better Auth with Prisma adapter for authentication
- MySQL database with Prisma ORM
- Level CRUD operations with drag-and-drop reordering
- Tencent COS integration for image uploads
- shadcn/ui components with Tailwind CSS
- TanStack Query for server state management
This commit is contained in:
richarjiang
2026-03-15 15:01:47 +08:00
commit 4854f1cefc
43 changed files with 11543 additions and 0 deletions

89
lib/cos.ts Normal file
View File

@@ -0,0 +1,89 @@
import STS from 'qcloud-cos-sts'
export interface TempKeyResult {
credentials: {
tmpSecretId: string
tmpSecretKey: string
sessionToken: string
}
startTime: number
expiredTime: number
}
export function getBucketName(): string {
const bucket = process.env.COS_BUCKET || ''
const appid = process.env.COS_APPID || ''
if (bucket.includes('-')) {
return bucket
}
return `${bucket}-${appid}`
}
export async function getTempKey(): Promise<TempKeyResult> {
const secretId = process.env.COS_SECRET_ID || ''
const secretKey = process.env.COS_SECRET_KEY || ''
const bucket = getBucketName()
const region = process.env.COS_REGION || 'ap-guangzhou'
const appid = process.env.COS_APPID || ''
// Define the policy for upload permissions
const policy = {
version: '2.0',
statement: [
{
action: [
'name/cos:PutObject',
'name/cos:PostObject',
],
effect: 'allow',
principal: { qcs: ['qcs::cam::anyone:anyone'] },
resource: [
`qcs::cos:${region}:uid/${appid}:${bucket}/*`,
],
},
],
}
return new Promise((resolve, reject) => {
STS.getCredential(
{
secretId,
secretKey,
proxy: '',
durationSeconds: 1800,
policy,
},
(err, data) => {
if (err) {
reject(err)
return
}
const credentialData = data as {
credentials: {
tmpSecretId: string
tmpSecretKey: string
sessionToken: string
}
startTime: number
expiredTime: number
}
resolve({
credentials: {
tmpSecretId: credentialData.credentials.tmpSecretId,
tmpSecretKey: credentialData.credentials.tmpSecretKey,
sessionToken: credentialData.credentials.sessionToken,
},
startTime: credentialData.startTime,
expiredTime: credentialData.expiredTime,
})
}
)
})
}
export function getBucketConfig() {
return {
bucket: getBucketName(),
region: process.env.COS_REGION || 'ap-guangzhou',
}
}