mirror of
https://github.com/ershisan99/www.git
synced 2025-12-17 12:34:17 +00:00
wip
This commit is contained in:
31
src/app/(home)/admin/releases/page.tsx
Normal file
31
src/app/(home)/admin/releases/page.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { ReleasesClient } from '@/app/(home)/admin/releases/releases-client'
|
||||||
|
import { auth } from '@/server/auth'
|
||||||
|
import { HydrateClient, api } from '@/trpc/server'
|
||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
export default async function ReleasesPage() {
|
||||||
|
const session = await auth()
|
||||||
|
const isAdmin = session?.user.role === 'admin'
|
||||||
|
console.log(session)
|
||||||
|
if (!isAdmin) {
|
||||||
|
return (
|
||||||
|
<div className={'container mx-auto pt-8'}>
|
||||||
|
<div className={'prose'}>
|
||||||
|
<h1>Forbidden</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.releases.getReleases.prefetch()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense>
|
||||||
|
<HydrateClient>
|
||||||
|
<div className={'container mx-auto pt-8'}>
|
||||||
|
<ReleasesClient />
|
||||||
|
</div>
|
||||||
|
</HydrateClient>
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
90
src/app/(home)/admin/releases/releases-client.tsx
Normal file
90
src/app/(home)/admin/releases/releases-client.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Card, CardContent } from '@/components/ui/card'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from '@/components/ui/table'
|
||||||
|
import { api } from '@/trpc/react'
|
||||||
|
import { useForm } from 'react-hook-form'
|
||||||
|
export function ReleasesClient() {
|
||||||
|
const [releases] = api.releases.getReleases.useSuspenseQuery()
|
||||||
|
const addRelease = api.releases.addRelease.useMutation()
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues: {
|
||||||
|
name: '',
|
||||||
|
version: '',
|
||||||
|
description: '',
|
||||||
|
url: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className={'text-2xl'}>Releases</h1>
|
||||||
|
<div className={'mt-4'}>
|
||||||
|
<Table className={'w-full table-auto'}>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Name</TableHead>
|
||||||
|
<TableHead>Version</TableHead>
|
||||||
|
<TableHead>Description</TableHead>
|
||||||
|
<TableHead>URL</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{releases.map((release) => (
|
||||||
|
<TableRow key={release.id}>
|
||||||
|
<TableCell>{release.name}</TableCell>
|
||||||
|
<TableCell>{release.version}</TableCell>
|
||||||
|
<TableCell>{release.description}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<a
|
||||||
|
href={release.url}
|
||||||
|
target={'_blank'}
|
||||||
|
rel={'noopener noreferrer'}
|
||||||
|
className={'hover:underline'}
|
||||||
|
>
|
||||||
|
{release.url}
|
||||||
|
</a>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<Card className={'mt-8 max-w-xl'}>
|
||||||
|
<CardContent>
|
||||||
|
<form
|
||||||
|
className={'space-y-4'}
|
||||||
|
onSubmit={form.handleSubmit((values) => addRelease.mutate(values))}
|
||||||
|
>
|
||||||
|
<div className={'grid grid-cols-1 gap-2'}>
|
||||||
|
<Label>Title</Label>
|
||||||
|
<Input {...form.register('name')} />
|
||||||
|
</div>
|
||||||
|
<div className={'grid grid-cols-1 gap-2'}>
|
||||||
|
<Label>Version</Label>
|
||||||
|
<Input {...form.register('version')} />
|
||||||
|
</div>
|
||||||
|
<div className={'grid grid-cols-1 gap-2'}>
|
||||||
|
<Label>Description</Label>
|
||||||
|
<Input {...form.register('description')} />
|
||||||
|
</div>
|
||||||
|
<div className={'grid grid-cols-1 gap-2'}>
|
||||||
|
<Label>URL</Label>
|
||||||
|
<Input {...form.register('url')} />
|
||||||
|
</div>
|
||||||
|
<Button type={'submit'}>Add new release</Button>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { discord_router } from '@/server/api/routers/discord'
|
|||||||
import { history_router } from '@/server/api/routers/history'
|
import { history_router } from '@/server/api/routers/history'
|
||||||
import { leaderboard_router } from '@/server/api/routers/leaderboard'
|
import { leaderboard_router } from '@/server/api/routers/leaderboard'
|
||||||
import { playerStateRouter } from '@/server/api/routers/player-state'
|
import { playerStateRouter } from '@/server/api/routers/player-state'
|
||||||
|
import { releasesRouter } from '@/server/api/routers/releases'
|
||||||
import { createCallerFactory, createTRPCRouter } from '@/server/api/trpc'
|
import { createCallerFactory, createTRPCRouter } from '@/server/api/trpc'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,6 +15,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
discord: discord_router,
|
discord: discord_router,
|
||||||
leaderboard: leaderboard_router,
|
leaderboard: leaderboard_router,
|
||||||
playerState: playerStateRouter,
|
playerState: playerStateRouter,
|
||||||
|
releases: releasesRouter,
|
||||||
})
|
})
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
38
src/server/api/routers/releases.ts
Normal file
38
src/server/api/routers/releases.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
adminProcedure,
|
||||||
|
createTRPCRouter,
|
||||||
|
publicProcedure,
|
||||||
|
} from '@/server/api/trpc'
|
||||||
|
import { db } from '@/server/db'
|
||||||
|
import { releases } from '@/server/db/schema'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
export const releasesRouter = createTRPCRouter({
|
||||||
|
getReleases: publicProcedure.query(async () => {
|
||||||
|
const res = await db.select().from(releases)
|
||||||
|
console.log(res)
|
||||||
|
return res
|
||||||
|
}),
|
||||||
|
addRelease: adminProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
version: z.string(),
|
||||||
|
url: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const res = await db
|
||||||
|
.insert(releases)
|
||||||
|
.values({
|
||||||
|
version: input.version,
|
||||||
|
url: input.url,
|
||||||
|
name: input.name,
|
||||||
|
description: input.description,
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
|
||||||
|
return res[0]
|
||||||
|
}),
|
||||||
|
})
|
||||||
@@ -131,3 +131,17 @@ export const protectedProcedure = t.procedure
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const adminProcedure = t.procedure
|
||||||
|
.use(timingMiddleware)
|
||||||
|
.use(({ ctx, next }) => {
|
||||||
|
if (!ctx.session?.user || ctx.session.user.role !== 'admin') {
|
||||||
|
throw new TRPCError({ code: 'FORBIDDEN' })
|
||||||
|
}
|
||||||
|
return next({
|
||||||
|
ctx: {
|
||||||
|
// infers the `session` as non-nullable
|
||||||
|
session: { ...ctx.session, user: ctx.session.user },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -124,3 +124,16 @@ export const verificationTokens = pgTable(
|
|||||||
}),
|
}),
|
||||||
(t) => [primaryKey({ columns: [t.identifier, t.token] })]
|
(t) => [primaryKey({ columns: [t.identifier, t.token] })]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const releases = pgTable('mod_release', {
|
||||||
|
id: integer('id').primaryKey().generatedByDefaultAsIdentity(),
|
||||||
|
name: text('name').notNull(),
|
||||||
|
description: text('description'),
|
||||||
|
version: text('version').notNull(),
|
||||||
|
url: text('url').notNull(),
|
||||||
|
createdAt: timestamp('created_at').notNull().defaultNow(),
|
||||||
|
updatedAt: timestamp('updated_at')
|
||||||
|
.notNull()
|
||||||
|
.defaultNow()
|
||||||
|
.$onUpdate(() => new Date()),
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user