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:
7
lib/auth-client.ts
Normal file
7
lib/auth-client.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createAuthClient } from 'better-auth/react'
|
||||
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000',
|
||||
})
|
||||
|
||||
export const { signIn, signOut, useSession } = authClient
|
||||
20
lib/auth.ts
Normal file
20
lib/auth.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { betterAuth } from 'better-auth'
|
||||
import { prismaAdapter } from 'better-auth/adapters/prisma'
|
||||
import { prisma } from './prisma'
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: prismaAdapter(prisma, {
|
||||
provider: 'mysql',
|
||||
}),
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
trustedOrigins: [process.env.BETTER_AUTH_URL || 'http://localhost:3000'],
|
||||
secret: process.env.BETTER_AUTH_SECRET,
|
||||
session: {
|
||||
expiresIn: 60 * 60 * 24 * 7, // 7 days
|
||||
updateAge: 60 * 60 * 24, // 1 day
|
||||
},
|
||||
})
|
||||
|
||||
export type Auth = typeof auth
|
||||
89
lib/cos.ts
Normal file
89
lib/cos.ts
Normal 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',
|
||||
}
|
||||
}
|
||||
9
lib/prisma.ts
Normal file
9
lib/prisma.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
||||
6
lib/utils.ts
Normal file
6
lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user