diff --git a/bun.lock b/bun.lock index 3219264..29c4b75 100644 --- a/bun.lock +++ b/bun.lock @@ -79,6 +79,7 @@ "usehooks-ts": "^3.1.1", "uuid": "^11.1.0", "vaul": "^1.1.2", + "zlib": "^1.0.5", "zod": "^3.24.2", }, "devDependencies": { @@ -1137,6 +1138,8 @@ "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + "zlib": ["zlib@1.0.5", "", {}, "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w=="], + "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], diff --git a/package.json b/package.json index 2b60d78..86921d5 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "usehooks-ts": "^3.1.1", "uuid": "^11.1.0", "vaul": "^1.1.2", + "zlib": "^1.0.5", "zod": "^3.24.2" }, "devDependencies": { diff --git a/scripts/refresh-history-by-date.ts b/scripts/refresh-history-by-date.ts new file mode 100644 index 0000000..a15c920 --- /dev/null +++ b/scripts/refresh-history-by-date.ts @@ -0,0 +1,50 @@ +import { syncHistoryByDateRange } from '@/server/api/routers/history' + +async function refreshHistoryByDate(startDate?: string, endDate?: string) { + try { + console.log('Refreshing history by date range...') + if (startDate) { + console.log(`Start date: ${startDate}`) + } + if (endDate) { + console.log(`End date: ${endDate}`) + } + + await syncHistoryByDateRange(startDate, endDate) + console.log('History refresh completed successfully') + } catch (err) { + console.error('History refresh failed:', err) + throw err + } +} + +// Parse command line arguments +function parseArgs() { + const args = process.argv.slice(2) + let startDate: string | undefined + let endDate: string | undefined + + for (let i = 0; i < args.length; i++) { + if (args[i] === '--start-date' && i + 1 < args.length) { + startDate = args[i + 1] + i++ + } else if (args[i] === '--end-date' && i + 1 < args.length) { + endDate = args[i + 1] + i++ + } + } + + return { startDate, endDate } +} + +// Run if called directly +if (require.main === module) { + const { startDate, endDate } = parseArgs() + + refreshHistoryByDate(startDate, endDate) + .then(() => process.exit(0)) + .catch((err) => { + console.error('Refresh failed:', err) + process.exit(1) + }) +} \ No newline at end of file diff --git a/src/app/(home)/admin/releases/page.tsx b/src/app/(home)/admin/releases/page.tsx new file mode 100644 index 0000000..c1c5c86 --- /dev/null +++ b/src/app/(home)/admin/releases/page.tsx @@ -0,0 +1,35 @@ +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 ( +
+
+

Forbidden

+
+
+ ) + } + + await api.releases.getReleases.prefetch() + + return ( + + +
+ +
+
+
+ ) +} diff --git a/src/app/(home)/admin/releases/releases-client.tsx b/src/app/(home)/admin/releases/releases-client.tsx new file mode 100644 index 0000000..7484741 --- /dev/null +++ b/src/app/(home)/admin/releases/releases-client.tsx @@ -0,0 +1,419 @@ +'use client' + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +import { Textarea } from '@/components/ui/textarea' +import { api } from '@/trpc/react' +import { Pencil, Trash2 } from 'lucide-react' +import { useEffect, useState } from 'react' +import { useForm } from 'react-hook-form' +import { toast } from 'sonner' + +export function ReleasesClient() { + const utils = api.useUtils() + const [releases] = api.releases.getReleases.useSuspenseQuery() + const addRelease = api.releases.addRelease.useMutation({ + onSuccess: () => { + utils.releases.getReleases.invalidate() + toast.success('Release added successfully') + form.reset() + }, + onError: (error) => { + toast.error(`Error adding release: ${error.message}`) + }, + }) + const updateRelease = api.releases.updateRelease.useMutation({ + onSuccess: () => { + utils.releases.getReleases.invalidate() + toast.success('Release updated successfully') + setEditDialogOpen(false) + }, + onError: (error) => { + toast.error(`Error updating release: ${error.message}`) + }, + }) + const deleteRelease = api.releases.deleteRelease.useMutation({ + onSuccess: () => { + utils.releases.getReleases.invalidate() + toast.success('Release deleted successfully') + setDeleteDialogOpen(false) + }, + onError: (error) => { + toast.error(`Error deleting release: ${error.message}`) + }, + }) + + const [smodsVersions, setSmodsVersions] = useState(['latest']) + const [lovelyVersions, setLovelyVersions] = useState(['latest']) + const [editDialogOpen, setEditDialogOpen] = useState(false) + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + const [selectedRelease, setSelectedRelease] = useState< + (typeof releases)[0] | null + >(null) + + const SMODS_RELEASES_URL = + 'https://api.github.com/repos/Steamodded/smods/releases' + const LOVELY_RELEASES_BASE_URL = + 'https://github.com/ethangreen-dev/lovely-injector/releases' + + useEffect(() => { + // Fetch Steamodded versions + fetch(SMODS_RELEASES_URL) + .then((response) => response.json()) + .then((data) => { + const versions = data.map((release: any) => release.tag_name) + setSmodsVersions(['latest', ...versions]) + }) + .catch((error) => { + console.error('Error fetching Steamodded versions:', error) + }) + + // Fetch lovely injector versions + // Since we don't have a direct API for lovely injector, we'll use GitHub API + fetch( + 'https://api.github.com/repos/ethangreen-dev/lovely-injector/releases' + ) + .then((response) => response.json()) + .then((data) => { + const versions = data.map((release: any) => release.tag_name) + setLovelyVersions(['latest', ...versions]) + }) + .catch((error) => { + console.error('Error fetching lovely injector versions:', error) + }) + }, []) + + const form = useForm({ + defaultValues: { + name: '', + version: '', + description: '', + url: '', + smods_version: 'latest', + lovely_version: 'latest', + }, + }) + + const editForm = useForm({ + defaultValues: { + id: 0, + name: '', + version: '', + description: '', + url: '', + smods_version: 'latest', + lovely_version: 'latest', + }, + }) + + const handleEditRelease = (release: (typeof releases)[0]) => { + setSelectedRelease(release) + editForm.reset({ + id: release.id, + name: release.name, + version: release.version, + description: release.description || '', + url: release.url, + smods_version: release.smods_version || 'latest', + lovely_version: release.lovely_version || 'latest', + }) + setEditDialogOpen(true) + } + + const handleDeleteRelease = (release: (typeof releases)[0]) => { + setSelectedRelease(release) + setDeleteDialogOpen(true) + } + return ( +
+

Releases

+
+ + + + Name + Version + Description + URL + Steamodded Version + Lovely Injector Version + Actions + + + + {releases.map((release) => ( + + {release.name} + {release.version} + +
+ {release.description} +
+
+ + +
{release.url}
+
+
+ {release.smods_version || 'latest'} + {release.lovely_version || 'latest'} + + + + +
+ ))} +
+
+
+ +
+
+

Add New Release

+
addRelease.mutate(values))} + > +
+ + +
+
+ + +
+
+ +