Added API for TTS Username Filters

This commit is contained in:
Tom 2024-01-02 18:00:11 +00:00
parent d49779e76f
commit c3e1c1cb60
5 changed files with 144 additions and 123 deletions

View File

@ -0,0 +1,82 @@
import { db } from "@/lib/db"
import { NextResponse } from "next/server";
import fetchUserUsingAPI from "@/lib/validate-api";
export async function GET(req: Request) {
try {
const user = await fetchUserUsingAPI(req)
if (!user) {
return new NextResponse("Unauthorized", { status: 401 });
}
const filters = await db.ttsUsernameFilter.findMany({
where: {
userId: user.id
}
});
return NextResponse.json(filters);
} catch (error) {
console.log("[TTS/FILTER/USER]", error);
return new NextResponse("Internal Error", { status: 500 });
}
}
export async function POST(req: Request) {
try {
const user = await fetchUserUsingAPI(req)
if (!user) {
return new NextResponse("Unauthorized", { status: 401 });
}
const { username, tag } = await req.json();
const filter = await db.ttsUsernameFilter.upsert({
where: {
userId_username: {
userId: user.id as string,
username
}
},
update: {
tag
},
create: {
userId: user.id as string,
username,
tag
}
});
return NextResponse.json(filter);
} catch (error) {
console.log("[TTS/FILTER/USER]", error);
return new NextResponse("Internal Error", { status: 500 });
}
}
export async function DELETE(req: Request) {
try {
const user = await fetchUserUsingAPI(req)
if (!user) {
return new NextResponse("Unauthorized", { status: 401 });
}
const { searchParams } = new URL(req.url)
const username = searchParams.get('username') as string
const filter = await db.ttsUsernameFilter.delete({
where: {
userId_username: {
userId: user.id as string,
username
}
}
});
return NextResponse.json(filter)
} catch (error) {
console.log("[TTS/FILTER/USER]", error);
return new NextResponse("Internal Error", { status: 500 });
}
}

View File

@ -3,7 +3,7 @@
import axios from "axios"; import axios from "axios";
import * as React from 'react'; import * as React from 'react';
import { Calendar, Check, ChevronsUpDown, MoreHorizontal, Plus, Tags, Trash, User } from "lucide-react" import { Calendar, Check, ChevronsUpDown, MoreHorizontal, Plus, Tags, Trash, User } from "lucide-react"
import { ApiKey, TwitchConnection } from "@prisma/client"; import { ApiKey, TtsUsernameFilter, TwitchConnection } from "@prisma/client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import Link from "next/link"; import Link from "next/link";
@ -23,6 +23,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { DropdownMenu, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from "@/components/ui/dropdown-menu"; import { DropdownMenu, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from "@/components/ui/dropdown-menu";
import { DropdownMenuContent, DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"; import { DropdownMenuContent, DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { db } from "@/lib/db";
const TTSFiltersPage = () => { const TTSFiltersPage = () => {
const { data: session, status } = useSession(); const { data: session, status } = useSession();
@ -30,33 +31,36 @@ const TTSFiltersPage = () => {
const [moreOpen, setMoreOpen] = useState<number>(0) const [moreOpen, setMoreOpen] = useState<number>(0)
const [tag, setTag] = useState("blacklisted") const [tag, setTag] = useState("blacklisted")
const [open, setOpen] = useState<boolean>(false) const [open, setOpen] = useState<boolean>(false)
const [userTags, setUserTag] = useState<{ username: string, tag: string }[]>([{ username: "test", tag:"blacklisted" }, { username: "hello world", tag:"moderator" }]) const [userTags, setUserTag] = useState<{ username: string, tag: string }[]>([])
const router = useRouter(); const router = useRouter();
const labels = [ const tags = [
"blacklisted", "blacklisted",
"priority" "priority"
] ]
// Username blacklist // Username blacklist
const usernameFilteredFormSchema = z.object({ const usernameFilteredFormSchema = z.object({
username: z.string().trim().min(4).max(25).regex(new RegExp("[a-zA-Z0-9][a-zA-Z0-9_]{3, 24}"), "Must be a valid twitch username.") //userId: z.string().trim().min(1),
username: z.string().trim().min(4).max(25), //.regex(new RegExp("[a-zA-Z0-9][a-zA-Z0-9_]{3, 24}"), "Must be a valid twitch username.")
tag: z.string().trim()
}); });
const usernameFilteredForm = useForm({ const usernameFilteredForm = useForm({
resolver: zodResolver(usernameFilteredFormSchema), resolver: zodResolver(usernameFilteredFormSchema),
defaultValues: { defaultValues: {
username: "" //userId: session?.user?.id ?? "",
username: "",
tag: ""
} }
}); });
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
try { try {
// const userFiltersData = (await axios.get("/api/settings/tts/filters/users")).data ?? {}; const userFiltersData = await axios.get("/api/settings/tts/filter/users");
// setApiKeys(userFiltersData) setUserTag(userFiltersData.data ?? [])
// console.log(userFiltersData);
} catch (error) { } catch (error) {
console.log("ERROR", error) console.log("ERROR", error)
} }
@ -65,47 +69,31 @@ const TTSFiltersPage = () => {
fetchData().catch(console.error); fetchData().catch(console.error);
}, []); }, []);
const onApiKeyAdd = async () => { const onDelete = () => {
///let usernames = blacklistedUsers.split("\n"); const username = userTags[Math.log2(moreOpen)].username
//console.log(usernames) axios.delete("/api/settings/tts/filter/users?username=" + username)
// try { .then(() => {
// await axios.post("/api/settings/tts/filters/users", { setUserTag(userTags.filter((u) => u.username != username))
// usernames: [] }).catch((e) => console.error(e))
// });
// setUserFilters(userFilters.concat(usernames))
// } catch (error) {
// console.log("ERROR", error)
// }
}
const onApiKeyDelete = async (username: string) => {
try {
await axios.delete("/api/settings/tts/filters/users/" + username);
//setUserFilters(userFilters.filter((u) => u != username))
} catch (error) {
console.log("ERROR", error)
}
} }
const isLoading = usernameFilteredForm.formState.isSubmitting; const isLoading = usernameFilteredForm.formState.isSubmitting;
const addTwitchUser = (values: z.infer<typeof usernameFilteredFormSchema>) => { const onAdd = (values: z.infer<typeof usernameFilteredFormSchema>) => {
let response = null; try {
console.log("TEST") values.tag = tag
console.log(values) axios.post("/api/settings/tts/filter/users", values)
.then((d) => {
userTags.push({ username: values.username, tag: tag })
setUserTag(userTags)
// try { usernameFilteredForm.reset();
// response = await axios.post("/api/settings/tts/filter/badges", values); router.refresh();
// } catch (error) { })
// console.log("[CONNECTIONS/TWITCH/POST]", error); } catch (error) {
// return; console.log("[TTS/FILTERS/USER]", error);
// } return;
}
userTags.push({ username: values.username, tag: tag })
setUserTag(userTags)
usernameFilteredForm.reset();
router.refresh();
//window.location.reload();
} }
return ( return (
@ -114,12 +102,12 @@ const TTSFiltersPage = () => {
<div className="px-10 py-10 w-full h-full flex-grow inset-y-1/2"> <div className="px-10 py-10 w-full h-full flex-grow inset-y-1/2">
<div> <div>
{userTags.map((user, index) => ( {userTags.map((user, index) => (
<div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center"> <div className="flex w-full items-start justify-between rounded-md border px-2 py-3">
<p className="text-sm font-medium leading-none"> <p className="text-base font-medium">
<span className="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground"> <span className="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
{user.tag} {user.tag}
</span> </span>
<span className="text-muted-foreground">{user.username}</span> <span className="text-white">{user.username}</span>
</p> </p>
<DropdownMenu open={(moreOpen & (1 << index)) > 0} onOpenChange={() => setMoreOpen((v) => v ^ (1 << index))}> <DropdownMenu open={(moreOpen & (1 << index)) > 0} onOpenChange={() => setMoreOpen((v) => v ^ (1 << index))}>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@ -144,17 +132,17 @@ const TTSFiltersPage = () => {
<CommandList> <CommandList>
<CommandEmpty>No label found.</CommandEmpty> <CommandEmpty>No label found.</CommandEmpty>
<CommandGroup> <CommandGroup>
{labels.map((label) => ( {tags.map((tag) => (
<CommandItem <CommandItem
key={label} key={tag}
value={label} value={tag}
onSelect={(value) => { onSelect={(value) => {
userTags[index].tag = value userTags[index].tag = value
setUserTag(userTags) setUserTag(userTags)
setMoreOpen(0) setMoreOpen(0)
}} }}
> >
{label} {tag}
</CommandItem> </CommandItem>
))} ))}
</CommandGroup> </CommandGroup>
@ -163,10 +151,7 @@ const TTSFiltersPage = () => {
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuSub> </DropdownMenuSub>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => { <DropdownMenuItem onClick={onDelete} className="text-red-600">
userTags.splice(index, 1)
setUserTag(userTags)
}} className="text-red-600">
<Trash className="mr-2 h-4 w-4" /> <Trash className="mr-2 h-4 w-4" />
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>
@ -176,9 +161,9 @@ const TTSFiltersPage = () => {
</div> </div>
))} ))}
<Form {...usernameFilteredForm}> <Form {...usernameFilteredForm}>
<form onSubmit={usernameFilteredForm.handleSubmit(addTwitchUser)}> <form onSubmit={usernameFilteredForm.handleSubmit(onAdd)}>
<div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center"> <div className="flex w-full items-start justify-between rounded-md border px-4 py-3">
<Label className=" mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground"> <Label className="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
{tag} {tag}
</Label> </Label>
<FormField <FormField
@ -219,15 +204,15 @@ const TTSFiltersPage = () => {
<CommandList> <CommandList>
<CommandEmpty>No label found.</CommandEmpty> <CommandEmpty>No label found.</CommandEmpty>
<CommandGroup> <CommandGroup>
{labels.map((label) => ( {tags.map((tag) => (
<CommandItem <CommandItem
value={label} value={tag}
onSelect={(value) => { onSelect={(value) => {
setTag(value) setTag(value)
setOpen(false) setOpen(false)
}} }}
> >
{label} {tag}
</CommandItem> </CommandItem>
))} ))}
</CommandGroup> </CommandGroup>

View File

@ -3,9 +3,8 @@ import { CardWrapper } from "./card-wrapper"
export const LoginForm = () => { export const LoginForm = () => {
return ( return (
<CardWrapper <CardWrapper
headerLabel="Welcome back" headerLabel="Welcome back">
> <div></div>
Login Form
</CardWrapper> </CardWrapper>
) )
} }

View File

@ -12,7 +12,7 @@ const SettingsNavigation = async () => {
</div> </div>
<div className="flex h-full z-20 inset-y-1/3 w-full"> <div className="flex h-full z-20 inset-y-1/3 w-full">
<ul className="rounded-lg shadow-md pl-[25px] flex flex-col w-full justify-between rounded-md text-center align-center"> <ul className="rounded-lg shadow-md pl-[25px] flex flex-col w-full justify-between text-center align-center">
<li className="text-xs text-gray-400"> <li className="text-xs text-gray-400">
Settings Settings
</li> </li>

View File

@ -18,8 +18,8 @@ model User {
apiKeys ApiKey[] apiKeys ApiKey[]
accounts Account[] accounts Account[]
twitchConnections TwitchConnection[] twitchConnections TwitchConnection[]
createdProfiles TtsProfile[] ttsUsernameFilter TtsUsernameFilter[]
profileStatus TtsProfileStatus[] ttsWordFilter TtsWordFilter[]
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
@ -65,69 +65,24 @@ model TwitchConnection {
@@index([userId]) @@index([userId])
} }
model TtsProfile {
id String @id @default(uuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
statuses TtsProfileStatus[]
badgeFilters TtsBadgeFilter[]
usernameFilters TtsUsernameFilter[]
wordFilters TtsWordReplacementFilter[]
creatorId String
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
@@index([creatorId])
@@unique([creatorId, name])
}
model TtsProfileStatus {
id String @id @default(uuid())
name String
enabled Boolean
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
profileId String
profile TtsProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([profileId])
}
model TtsBadgeFilter {
badgeId String
profileId String
profile TtsProfile @relation(fields: [profileId], references: [id], onDelete: Cascade)
@@index([profileId])
@@id([profileId, badgeId])
}
model TtsUsernameFilter { model TtsUsernameFilter {
username String username String
tag String tag String
profileId String userId String
profile TtsProfile @relation(fields: [profileId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([profileId]) @@index([userId])
@@id([profileId, username]) @@id([userId, username])
} }
model TtsWordReplacementFilter { model TtsWordFilter {
word String search String
replace String replace String
profileId String userId String
profile TtsProfile @relation(fields: [profileId], references: [id], onDelete: Cascade) profile User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([profileId])
@@id([profileId, word])
}
@@index([userId])
@@id([userId, search])
}