Added basic validation for requests
This commit is contained in:
parent
2d40d6fe09
commit
624b3fa63b
@ -47,7 +47,7 @@ export default function Home() {
|
|||||||
else
|
else
|
||||||
setLoaded(true)
|
setLoaded(true)
|
||||||
})
|
})
|
||||||
}, [session])
|
}, [session, status])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
@ -17,17 +17,13 @@ export default function Home() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status !== "authenticated" || previousUsername == session.user?.name) {
|
if (status !== "authenticated" || previousUsername == session.user?.name) {
|
||||||
console.log("CANCELED")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreviousUsername(session.user?.name as string)
|
setPreviousUsername(session.user?.name as string)
|
||||||
|
|
||||||
async function saveAccount() {
|
async function saveAccount() {
|
||||||
const data = await axios.post("/api/account")
|
await axios.post("/api/account")
|
||||||
if (data == null || data == undefined) {
|
|
||||||
console.log("ERROR")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAccount().catch(console.error)
|
saveAccount().catch(console.error)
|
||||||
|
@ -26,7 +26,6 @@ const permissionPaths = [
|
|||||||
{ path: "tts.commands.version", description: "To use !version command" },
|
{ path: "tts.commands.version", description: "To use !version command" },
|
||||||
{ path: "tts.commands.voice", description: "To use !voice command" },
|
{ path: "tts.commands.voice", description: "To use !voice command" },
|
||||||
{ path: "tts.commands.voice.admin", description: "To use !voice command on others" },
|
{ path: "tts.commands.voice.admin", description: "To use !voice command on others" },
|
||||||
|
|
||||||
].sort((a, b) => a.path.localeCompare(b.path))
|
].sort((a, b) => a.path.localeCompare(b.path))
|
||||||
|
|
||||||
const GroupPermissionPage = () => {
|
const GroupPermissionPage = () => {
|
||||||
@ -53,7 +52,6 @@ const GroupPermissionPage = () => {
|
|||||||
return
|
return
|
||||||
setPreviousUsername(session.user?.name)
|
setPreviousUsername(session.user?.name)
|
||||||
|
|
||||||
// TODO: fetch groups & permissions
|
|
||||||
axios.get('/api/settings/groups')
|
axios.get('/api/settings/groups')
|
||||||
.then(d => {
|
.then(d => {
|
||||||
for (let groupName of specialGroups)
|
for (let groupName of specialGroups)
|
||||||
@ -66,52 +64,48 @@ const GroupPermissionPage = () => {
|
|||||||
setGroups(d.data)
|
setGroups(d.data)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
// TODO: filter permissions by group?
|
|
||||||
|
|
||||||
}, [session])
|
}, [session])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="text-2xl text-center pt-[50px]">Groups & Permissions</div>
|
<div className="text-2xl text-center pt-[50px]">Groups & Permissions</div>
|
||||||
{/* <InfoNotice
|
|
||||||
message="Redemption actions are activated when specific Twitch channel point redeems have been activated. Aforementioned redeem need to be linked in the redemption part, together with the action, for the action to activate."
|
|
||||||
hidden={false} /> */}
|
|
||||||
<div className="grid sm:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-3">
|
<div className="grid sm:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-3">
|
||||||
{groups.map(group =>
|
{groups.map(group =>
|
||||||
|
<div
|
||||||
|
className="col-span-1"
|
||||||
|
key={group.id}>
|
||||||
|
<GroupElement
|
||||||
|
id={group.id}
|
||||||
|
name={group.name}
|
||||||
|
priority={group.priority}
|
||||||
|
permissionsLoaded={permissions.filter(p => p.groupId == group.id)}
|
||||||
|
edit={group.id.startsWith('$')}
|
||||||
|
showEdit={true}
|
||||||
|
isNewGroup={group.id.startsWith('$')}
|
||||||
|
permissionPaths={permissionPaths}
|
||||||
|
specialGroups={specialGroups}
|
||||||
|
adder={addGroup}
|
||||||
|
remover={removeGroup}
|
||||||
|
contains={containsGroup} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className="col-span-1"
|
className="col-span-1">
|
||||||
key={group.id}>
|
|
||||||
<GroupElement
|
<GroupElement
|
||||||
id={group.id}
|
id={undefined}
|
||||||
name={group.name}
|
name={""}
|
||||||
priority={group.priority}
|
priority={0}
|
||||||
permissionsLoaded={permissions.filter(p => p.groupId == group.id)}
|
permissionsLoaded={[]}
|
||||||
edit={group.id.startsWith('$')}
|
edit={true}
|
||||||
showEdit={true}
|
showEdit={false}
|
||||||
isNewGroup={group.id.startsWith('$')}
|
isNewGroup={true}
|
||||||
permissionPaths={permissionPaths}
|
permissionPaths={permissionPaths}
|
||||||
specialGroups={specialGroups}
|
specialGroups={specialGroups}
|
||||||
adder={addGroup}
|
adder={addGroup}
|
||||||
remover={removeGroup}
|
remover={removeGroup}
|
||||||
contains={containsGroup} />
|
contains={containsGroup} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className="col-span-1">
|
|
||||||
<GroupElement
|
|
||||||
id={undefined}
|
|
||||||
name={""}
|
|
||||||
priority={0}
|
|
||||||
permissionsLoaded={[]}
|
|
||||||
edit={true}
|
|
||||||
showEdit={false}
|
|
||||||
isNewGroup={true}
|
|
||||||
permissionPaths={permissionPaths}
|
|
||||||
specialGroups={specialGroups}
|
|
||||||
adder={addGroup}
|
|
||||||
remover={removeGroup}
|
|
||||||
contains={containsGroup} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,6 @@ import RedeemptionAction from "@/components/elements/redeemable-action";
|
|||||||
import OBSRedemption from "@/components/elements/redemption";
|
import OBSRedemption from "@/components/elements/redemption";
|
||||||
import { ActionType } from "@prisma/client";
|
import { ActionType } from "@prisma/client";
|
||||||
import InfoNotice from "@/components/elements/info-notice";
|
import InfoNotice from "@/components/elements/info-notice";
|
||||||
import { string } from "zod";
|
|
||||||
|
|
||||||
const obsTransformations = [
|
const obsTransformations = [
|
||||||
{ label: "scene_name", description: "", placeholder: "Name of the OBS scene" },
|
{ label: "scene_name", description: "", placeholder: "Name of the OBS scene" },
|
||||||
@ -66,7 +65,6 @@ const RedemptionsPage = () => {
|
|||||||
|
|
||||||
axios.get('/api/connection')
|
axios.get('/api/connection')
|
||||||
.then(d => {
|
.then(d => {
|
||||||
console.log(d.data.data)
|
|
||||||
setConnections(d.data.data)
|
setConnections(d.data.data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// TODO: remove this page.
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { db } from "@/lib/db"
|
import { db } from "@/lib/db"
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
@ -10,7 +12,7 @@ export async function GET(req: Request) {
|
|||||||
const state = searchParams.get('state') as string
|
const state = searchParams.get('state') as string
|
||||||
|
|
||||||
if (!code || !scope || !state) {
|
if (!code || !scope || !state) {
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Missing oauth2 data.', error: null, value: null }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify state against user id in user table.
|
// Verify state against user id in user table.
|
||||||
@ -21,7 +23,7 @@ export async function GET(req: Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'You do not have permissions for this.', error: null, value: null }, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post to https://id.twitch.tv/oauth2/token
|
// Post to https://id.twitch.tv/oauth2/token
|
||||||
@ -37,7 +39,7 @@ export async function GET(req: Request) {
|
|||||||
const { access_token, expires_in, refresh_token, token_type } = token
|
const { access_token, expires_in, refresh_token, token_type } = token
|
||||||
|
|
||||||
if (!access_token || !refresh_token || token_type !== "bearer") {
|
if (!access_token || !refresh_token || token_type !== "bearer") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = await axios.get("https://api.twitch.tv/helix/users?login=" + user.name, {
|
let info = await axios.get("https://api.twitch.tv/helix/users?login=" + user.name, {
|
||||||
@ -57,9 +59,9 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return new NextResponse("", { status: 200 });
|
return NextResponse.json({ message: null, error: null, value: null }, { status: 200 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ACCOUNT/AUTHORIZE]", error);
|
console.log("[ACCOUNT/AUTHORIZE]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user || user.role != "ADMIN") {
|
if (!user || user.role != "ADMIN") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const impersonation = await db.impersonation.findFirst({
|
const impersonation = await db.impersonation.findFirst({
|
||||||
@ -18,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(impersonation);
|
return NextResponse.json(impersonation);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user || user.role != "ADMIN") {
|
if (!user || user.role != "ADMIN") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { targetId } = await req.json();
|
const { targetId } = await req.json();
|
||||||
@ -41,7 +41,7 @@ export async function POST(req: Request) {
|
|||||||
return NextResponse.json(impersonation);
|
return NextResponse.json(impersonation);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ export async function PUT(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user || user.role != "ADMIN") {
|
if (!user || user.role != "ADMIN") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { targetId } = await req.json();
|
const { targetId } = await req.json();
|
||||||
@ -66,7 +66,7 @@ export async function PUT(req: Request) {
|
|||||||
return NextResponse.json(impersonation);
|
return NextResponse.json(impersonation);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user || user.role != "ADMIN") {
|
if (!user || user.role != "ADMIN") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const impersonation = await db.impersonation.delete({
|
const impersonation = await db.impersonation.delete({
|
||||||
@ -86,6 +86,6 @@ export async function DELETE(req: Request) {
|
|||||||
return NextResponse.json(impersonation)
|
return NextResponse.json(impersonation)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
console.log("[AUTH/ACCOUNT/IMPERSONATION]", error);
|
||||||
return new NextResponse("Internal Error" + error, { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong.', error: null, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ export async function GET(req: Request) {
|
|||||||
// Verify state against user id in user table.
|
// Verify state against user id in user table.
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = await db.twitchConnection.findFirst({
|
const connection = await db.twitchConnection.findFirst({
|
||||||
@ -18,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
return new NextResponse("Forbidden", { status: 403 });
|
return NextResponse.json({ message: 'You do not have permission for this.', error: null, value: null }, { status: 403 })
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -59,7 +59,7 @@ export async function GET(req: Request) {
|
|||||||
const { access_token, expires_in, refresh_token, token_type } = token
|
const { access_token, expires_in, refresh_token, token_type } = token
|
||||||
|
|
||||||
if (!access_token || !refresh_token || token_type !== "bearer") {
|
if (!access_token || !refresh_token || token_type !== "bearer") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.twitchConnection.update({
|
await db.twitchConnection.update({
|
||||||
@ -83,6 +83,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(data)
|
return NextResponse.json(data)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ACCOUNT]", error);
|
console.log("[ACCOUNT]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import { db } from "@/lib/db"
|
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import fetchUserWithImpersonation from '@/lib/fetch-user-impersonation';
|
import fetchUserWithImpersonation from '@/lib/fetch-user-impersonation';
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -7,16 +6,16 @@ import { updateTwitchToken } from "@/data/twitch-reauthorize";
|
|||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
if (!process.env.TWITCH_BOT_CLIENT_ID)
|
if (!process.env.TWITCH_BOT_CLIENT_ID)
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong.', error: null, value: null }, { status: 500 })
|
||||||
|
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = await updateTwitchToken(user.id)
|
const auth = await updateTwitchToken(user.id)
|
||||||
if (!auth)
|
if (!auth)
|
||||||
return new NextResponse("Bad Request", { status: 400 })
|
return NextResponse.json({ message: 'Failed to authorize to Twitch.', error: null, value: null }, { status: 403 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const redemptions = await axios.get("https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=" + auth.broadcaster_id,
|
const redemptions = await axios.get("https://api.twitch.tv/helix/channel_points/custom_rewards?broadcaster_id=" + auth.broadcaster_id,
|
||||||
@ -35,6 +34,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json([]);
|
return NextResponse.json([]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[REDEMPTIONS/ACTIONS]", error);
|
console.log("[REDEMPTIONS/ACTIONS]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import fetchUser from "@/lib/fetch-user";
|
|||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user) return new NextResponse("Internal Error", { status: 401 })
|
if (!user) return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 })
|
||||||
|
|
||||||
const account = await db.account.findFirst({
|
const account = await db.account.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -18,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json({ ... user, broadcasterId: account?.providerAccountId })
|
return NextResponse.json({ ... user, broadcasterId: account?.providerAccountId })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ACCOUNT]", error);
|
console.log("[ACCOUNT]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ export async function POST(req: Request) {
|
|||||||
const session = await auth()
|
const session = await auth()
|
||||||
const user = session?.user?.name
|
const user = session?.user?.name
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Internal Error", { status: 401 })
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const exist = await db.user.findFirst({
|
const exist = await db.user.findFirst({
|
||||||
@ -54,7 +54,6 @@ export async function POST(req: Request) {
|
|||||||
username: newUser.name
|
username: newUser.name
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ACCOUNT]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,6 +13,8 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
if (!token_type)
|
if (!token_type)
|
||||||
return NextResponse.json({ error: null, message: 'No token type given for the authorization.', success: false }, { status: 400 })
|
return NextResponse.json({ error: null, message: 'No token type given for the authorization.', success: false }, { status: 400 })
|
||||||
|
if (token_type !== "bearer")
|
||||||
|
return NextResponse.json({ error: null, message: 'Invalid token type given for the authorization.', success: false }, { status: 400 })
|
||||||
|
|
||||||
if (!access_token)
|
if (!access_token)
|
||||||
return NextResponse.json({ error: null, message: 'No access token given for the authorization.', success: false }, { status: 400 })
|
return NextResponse.json({ error: null, message: 'No access token given for the authorization.', success: false }, { status: 400 })
|
||||||
|
@ -17,7 +17,7 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json({ error: null, message: "", success: true, data }, { status: 200 });
|
return NextResponse.json({ error: null, message: null, success: true, data }, { status: 200 });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return NextResponse.json({ error, message: "Failed to get default connection", success: false }, { status: 500 });
|
return NextResponse.json({ error, message: "Failed to get default connection", success: false }, { status: 500 });
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ export async function PUT(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json({ error: null, message: "", success: true, data }, { status: 200 });
|
return NextResponse.json({ error: null, message: null, success: true, data }, { status: 200 });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return NextResponse.json({ error, message: "Failed to update default connection", success: false }, { status: 500 });
|
return NextResponse.json({ error, message: "Failed to update default connection", success: false }, { status: 500 });
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
minor_version: 3,
|
minor_version: 3,
|
||||||
download: "https://drive.proton.me/urls/YH86153EWM#W6VTyaoAVHKP",
|
download: "https://drive.proton.me/urls/YH86153EWM#W6VTyaoAVHKP",
|
||||||
changelog: "Reconnecting should be fixed.\nNew TTS messages when queue is empty will be played faster, up to 200 ms.\nRemoved subscriptions errors when reconnecting on Twitch\nAdded !refresh connections"
|
changelog: "Reconnecting should be fixed.\nNew TTS messages when queue is empty will be played faster, up to 200 ms.\nRemoved subscriptions errors when reconnecting on Twitch\nAdded !refresh connections"
|
||||||
|
|
||||||
//changelog: "When using multiple voices (ex: brian: hello amy: world), TTS messages are now merged as a single TTS message.\nNightbot integration\nTwitch authentication changed. Need to refresh connection every 30-60 days.\nFixed raid spam, probably."
|
//changelog: "When using multiple voices (ex: brian: hello amy: world), TTS messages are now merged as a single TTS message.\nNightbot integration\nTwitch authentication changed. Need to refresh connection every 30-60 days.\nFixed raid spam, probably."
|
||||||
//changelog: "Added raid spam prevention (lasts 30 seconds; works on joined chats as well).\nAdded permission check for chat messages with bits."
|
//changelog: "Added raid spam prevention (lasts 30 seconds; works on joined chats as well).\nAdded permission check for chat messages with bits."
|
||||||
//changelog: "Fixed group permissions.\nRemoved default permissions.\nSome commands may have additional permission requirements, which are more strict.\nAdded support for redeemable actions via adbreak, follow, subscription.\nMessage deletion and bans automatically remove TTS messages from queue and playing.\nAdded support to read TTS from multiple chats via !tts join.\nFixed some reconnection problems."
|
//changelog: "Fixed group permissions.\nRemoved default permissions.\nSome commands may have additional permission requirements, which are more strict.\nAdded support for redeemable actions via adbreak, follow, subscription.\nMessage deletion and bans automatically remove TTS messages from queue and playing.\nAdded support to read TTS from multiple chats via !tts join.\nFixed some reconnection problems."
|
||||||
|
@ -6,7 +6,7 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = await db.twitchConnection.deleteMany({
|
const connection = await db.twitchConnection.deleteMany({
|
||||||
@ -18,6 +18,6 @@ export async function POST(req: Request) {
|
|||||||
return NextResponse.json(connection);
|
return NextResponse.json(connection);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[CONNECTION/TWITCH]", error);
|
console.log("[CONNECTION/TWITCH]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
@ -27,6 +27,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(connection);
|
return NextResponse.json(connection);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[CONNECTION/TWITCH]", error);
|
console.log("[CONNECTION/TWITCH]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,12 +4,13 @@ import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { env } from "process";
|
import { env } from "process";
|
||||||
import { TwitchUpdateAuthorization } from "@/lib/twitch";
|
import { TwitchUpdateAuthorization } from "@/lib/twitch";
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const groupId = searchParams.get('groupId') as string
|
const groupId = searchParams.get('groupId') as string
|
||||||
@ -17,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
const search = searchParams.get('search') as string
|
const search = searchParams.get('search') as string
|
||||||
|
|
||||||
if (!groupId && search != 'all')
|
if (!groupId && search != 'all')
|
||||||
return new NextResponse("Bad Request", { status: 400 })
|
return NextResponse.json({ message: 'Something went wrong', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
let page = parseInt(pageString)
|
let page = parseInt(pageString)
|
||||||
if (isNaN(page) || page === undefined || page === null)
|
if (isNaN(page) || page === undefined || page === null)
|
||||||
@ -40,18 +41,15 @@ export async function GET(req: Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const paginated = search == 'all' ? chatters : chatters.slice(page * 50, (page + 1) * 50)
|
const paginated = search == 'all' ? chatters : chatters.slice(page * 50, (page + 1) * 50)
|
||||||
if (!paginated || paginated.length == 0) {
|
if (!paginated || paginated.length == 0)
|
||||||
console.log('No users returned from db')
|
|
||||||
return NextResponse.json([])
|
return NextResponse.json([])
|
||||||
}
|
|
||||||
|
|
||||||
const ids = chatters.map(c => c.chatterId)
|
const ids = chatters.map(c => c.chatterId)
|
||||||
const idsString = 'id=' + ids.map(i => i.toString()).reduce((a, b) => a + '&id=' + b)
|
const idsString = 'id=' + ids.map(i => i.toString()).reduce((a, b) => a + '&id=' + b)
|
||||||
|
|
||||||
const auth = await TwitchUpdateAuthorization(user.id)
|
const auth = await TwitchUpdateAuthorization(user.id)
|
||||||
if (!auth) {
|
if (!auth)
|
||||||
return new NextResponse("", { status: 403 })
|
return NextResponse.json({ message: 'Unauthorized', error: null, value: null }, { status: 403 })
|
||||||
}
|
|
||||||
|
|
||||||
const users = await axios.get("https://api.twitch.tv/helix/users?" + idsString, {
|
const users = await axios.get("https://api.twitch.tv/helix/users?" + idsString, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -60,31 +58,38 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!users) {
|
if (!users)
|
||||||
return new NextResponse("", { status: 400 })
|
return NextResponse.json({ message: 'No users found', error: null, value: null }, { status: 400 })
|
||||||
}
|
|
||||||
|
|
||||||
if (users.data.data.length == 0) {
|
if (users.data.data.length == 0)
|
||||||
console.log('No users returned from twitch')
|
|
||||||
return NextResponse.json([])
|
return NextResponse.json([])
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(users.data.data.map((u: any) => ({ id: u.id, username: u.login })));
|
return NextResponse.json(users.data.data.map((u: any) => ({ id: u.id, username: u.login })));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/USERS]", error);
|
return NextResponse.json({ message: 'Failed to get', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groupIdSchema = z.string({
|
||||||
|
required_error: "Group ID should be available.",
|
||||||
|
invalid_type_error: "Group ID must be a string"
|
||||||
|
}).regex(/^[\w\-\=]{1,32}$/, "Group ID must contain only letters, numbers, dashes, underscores & equal signs.")
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized', error: null, value: null }, { status: 401 })
|
||||||
|
|
||||||
const { groupId, users }: { groupId: string, users: { id: number, username: string }[] } = await req.json();
|
const { groupId, users }: { groupId: string, users: { id: number, username: string }[] } = await req.json();
|
||||||
if (!groupId || !users)
|
if (!groupId)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'groupId must be set.', error: null, value: null }, { status: 400 });
|
||||||
|
if (!users)
|
||||||
|
return NextResponse.json({ message: 'users must be set.', error: null, value: null }, { status: 400 });
|
||||||
|
|
||||||
|
const groupIdValidation = await groupIdSchema.safeParseAsync(groupId)
|
||||||
|
if (!groupIdValidation.success)
|
||||||
|
return NextResponse.json({ message: 'groupId does not meet requirements.', error: JSON.parse(groupIdValidation.error['message'])[0], value: null }, { status: 400 })
|
||||||
|
|
||||||
const chatters = await db.chatterGroup.createMany({
|
const chatters = await db.chatterGroup.createMany({
|
||||||
data: users.map(u => ({ userId: user.id, chatterId: u.id, groupId, chatterLabel: u.username }))
|
data: users.map(u => ({ userId: user.id, chatterId: u.id, groupId, chatterLabel: u.username }))
|
||||||
@ -92,8 +97,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(chatters, { status: 200 });
|
return NextResponse.json(chatters, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/USERS]", error);
|
return NextResponse.json({ message: 'Failed to create', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,13 +105,20 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized', error: null, value: null }, { status: 401 })
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const groupId = searchParams.get('groupId') as string
|
const groupId = searchParams.get('groupId') as string
|
||||||
const ids = searchParams.get('ids') as string
|
const ids = searchParams.get('ids') as string
|
||||||
if (!groupId || !ids)
|
if (!groupId)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'groupId must be set.', error: null, value: null }, { status: 400 });
|
||||||
|
|
||||||
|
if (!ids)
|
||||||
|
return NextResponse.json({ message: 'ids must be set.', error: null, value: null }, { status: 400 });
|
||||||
|
|
||||||
|
const groupIdValidation = await groupIdSchema.safeParseAsync(groupId)
|
||||||
|
if (!groupIdValidation.success)
|
||||||
|
return NextResponse.json({ message: 'groupId does not meet requirements.', error: JSON.parse(groupIdValidation.error['message'])[0], value: null }, { status: 400 })
|
||||||
|
|
||||||
const chatters = await db.chatterGroup.deleteMany({
|
const chatters = await db.chatterGroup.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
@ -121,7 +132,6 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(chatters);
|
return NextResponse.json(chatters);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/USERS]", error);
|
return NextResponse.json({ message: 'Failed to delete.', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
import { db } from "@/lib/db"
|
import { db } from "@/lib/db"
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
||||||
import { ActionType, Prisma } from "@prisma/client";
|
import { z } from "zod";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const commands = await db.groupPermission.findMany({
|
const commands = await db.groupPermission.findMany({
|
||||||
where: {
|
where: {
|
||||||
@ -17,20 +17,31 @@ export async function GET(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(commands.map(({userId, ...attrs}) => attrs));
|
return NextResponse.json(commands.map(({userId, ...attrs}) => attrs));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/PERMISSIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const permissionPathSchema = z.string({
|
||||||
|
required_error: "Permission path should be available.",
|
||||||
|
invalid_type_error: "Permission path must be a string"
|
||||||
|
}).regex(/^[\w\-\.]{1,64}$/, "Permission path must contain only letters, numbers, dashes, periods.")
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const { path, allow, groupId }: { path: string, allow: boolean, groupId: string } = await req.json();
|
const { path, allow, groupId }: { path: string, allow: boolean, groupId: string } = await req.json();
|
||||||
if (!path)
|
if (!path)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'path does not exist.', error: null, value: null }, { status: 400 });
|
||||||
|
const permissionPathValidation = permissionPathSchema.safeParse(path)
|
||||||
|
if (!permissionPathValidation.success)
|
||||||
|
return NextResponse.json({ message: 'path must meet certain requirements.', error: JSON.parse(permissionPathValidation.error['message'])[0], value: null }, { status: 400 });
|
||||||
|
if (!groupId)
|
||||||
|
return NextResponse.json({ message: 'groupId does not exist.', error: null, value: null }, { status: 400 });
|
||||||
|
if (groupId.length > 64)
|
||||||
|
return NextResponse.json({ message: 'groupId is too long.', error: null, value: null }, { status: 400 });
|
||||||
|
|
||||||
const permission = await db.groupPermission.create({
|
const permission = await db.groupPermission.create({
|
||||||
data: {
|
data: {
|
||||||
@ -43,8 +54,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(permission, { status: 200 });
|
return NextResponse.json(permission, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/PERMISSIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,30 +62,30 @@ export async function PUT(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const { id, path, allow }: { id: string, path: string, allow: boolean|null } = await req.json();
|
const { id, path, allow }: { id: string, path: string, allow: boolean|null } = await req.json();
|
||||||
if (!id)
|
if (!id)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'id does not exist.', error: null, value: null }, { status: 400 });
|
||||||
if (!path)
|
if (!path)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'path does not exist.', error: null, value: null }, { status: 400 });
|
||||||
|
const permissionPathValidation = permissionPathSchema.safeParse(path)
|
||||||
let data: any = {}
|
if (!permissionPathValidation.success)
|
||||||
if (!!path)
|
return NextResponse.json({ message: 'path must meet certain requirements.', error: JSON.parse(permissionPathValidation.error['message'])[0], value: null }, { status: 400 });
|
||||||
data = { ...data, path }
|
|
||||||
data = { ...data, allow }
|
|
||||||
|
|
||||||
const permission = await db.groupPermission.update({
|
const permission = await db.groupPermission.update({
|
||||||
where: {
|
where: {
|
||||||
id
|
id
|
||||||
},
|
},
|
||||||
data: data
|
data: {
|
||||||
|
path,
|
||||||
|
allow
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(permission, { status: 200 });
|
return NextResponse.json(permission, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/PERMISSIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +93,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const id = searchParams.get('id') as string
|
const id = searchParams.get('id') as string
|
||||||
@ -95,7 +105,6 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(permission);
|
return NextResponse.json(permission);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/PERMISSIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
import { db } from "@/lib/db"
|
import { db } from "@/lib/db"
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
||||||
import { ActionType, Prisma } from "@prisma/client";
|
import { z } from "zod";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = await db.group.findMany({
|
const actions = await db.group.findMany({
|
||||||
@ -16,23 +16,30 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json(actions.map(({userId, ...attrs}) => attrs));
|
return NextResponse.json(actions.map(({ userId, ...attrs }) => attrs));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const groupNameSchema = z.string({
|
||||||
|
required_error: "Group name is required.",
|
||||||
|
invalid_type_error: "Group name must be a string"
|
||||||
|
}).regex(/^[\w\-\s]{1,20}$/, "Group name must contain only letters, numbers, spaces, dashes, and underscores.")
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, priority }: { name: string, priority: number } = await req.json();
|
const { name, priority }: { name: string, priority: number } = await req.json();
|
||||||
if (!name)
|
if (!name)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'name does not exist.', error: null, value: null }, { status: 400 });
|
||||||
|
const groupNameValidation = await groupNameSchema.safeParseAsync(name)
|
||||||
|
if (!groupNameValidation.success)
|
||||||
|
return NextResponse.json({ message: 'name does not meet requirements.', error: JSON.parse(groupNameValidation.error['message'])[0], value: null }, { status: 400 })
|
||||||
|
|
||||||
const group = await db.group.create({
|
const group = await db.group.create({
|
||||||
data: {
|
data: {
|
||||||
@ -44,8 +51,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(group, { status: 200 });
|
return NextResponse.json(group, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,19 +59,24 @@ export async function PUT(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, name, priority }: { id: string, name: string, priority: number } = await req.json();
|
const { id, name, priority }: { id: string, name: string, priority: number } = await req.json();
|
||||||
if (!id)
|
if (!id)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'id does not exist.', error: null, value: null }, { status: 400 });
|
||||||
if (!name && !priority)
|
if (!name && !priority)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Either name or priority must not be null.', error: null, value: null }, { status: 400 });
|
||||||
|
if (name) {
|
||||||
|
const groupNameValidation = await groupNameSchema.safeParseAsync(name)
|
||||||
|
if (!groupNameValidation.success)
|
||||||
|
return NextResponse.json({ message: 'name does not meet requirements.', error: JSON.parse(groupNameValidation.error['message'])[0], value: null }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
let data: any = {}
|
let data: any = {}
|
||||||
if (!!name)
|
if (name)
|
||||||
data = { ...data, name: name.toLowerCase() }
|
data = { ...data, name: name.toLowerCase() }
|
||||||
if (!!priority)
|
if (priority)
|
||||||
data = { ...data, priority }
|
data = { ...data, priority }
|
||||||
|
|
||||||
const group = await db.group.update({
|
const group = await db.group.update({
|
||||||
@ -77,8 +88,7 @@ export async function PUT(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(group, { status: 200 });
|
return NextResponse.json(group, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +96,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
@ -99,7 +109,6 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(group);
|
return NextResponse.json(group);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,13 +8,13 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const logins = (searchParams.get('logins') as string)?.split(',').reduce((a,b) => a + ',&login=' + b)
|
const logins = (searchParams.get('logins') as string)?.split(',').reduce((a,b) => a + ',&login=' + b)
|
||||||
const ids = (searchParams.get('ids') as string)?.split(',').reduce((a,b) => a + ',&id=' + b)
|
const ids = (searchParams.get('ids') as string)?.split(',').reduce((a,b) => a + ',&id=' + b)
|
||||||
if (!logins && !ids) {
|
if (!logins && !ids) {
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Either logins or ids must not be null.', error: null, value: null }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let suffix = ""
|
let suffix = ""
|
||||||
@ -27,7 +27,7 @@ export async function GET(req: Request) {
|
|||||||
|
|
||||||
const auth = await TwitchUpdateAuthorization(user.id)
|
const auth = await TwitchUpdateAuthorization(user.id)
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
return new NextResponse("", { status: 403 })
|
return NextResponse.json({ message: 'You do not have permissions for this.', error: null, value: null }, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('TWITCH URL:', 'https://api.twitch.tv/helix/users' + suffix)
|
console.log('TWITCH URL:', 'https://api.twitch.tv/helix/users' + suffix)
|
||||||
@ -39,17 +39,11 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!users || !users.data) {
|
if (!users?.data?.data)
|
||||||
return new NextResponse("", { status: 400 })
|
return NextResponse.json([], { status: 200 });
|
||||||
}
|
|
||||||
|
|
||||||
if (users.data.data.length == 0) {
|
return NextResponse.json(users.data.data.map((u: any) => ({ id: u.id, username: u.login })), { status: 200 });
|
||||||
return NextResponse.json([])
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(users.data.data.map((u: any) => ({ id: u.id, username: u.login })));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/USERS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,31 @@
|
|||||||
import { db } from "@/lib/db"
|
import { db } from "@/lib/db"
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
||||||
import axios from "axios";
|
import { z } from "zod";
|
||||||
import { env } from "process";
|
|
||||||
import { TwitchUpdateAuthorization } from "@/lib/twitch";
|
const groupIdSchema = z.string({
|
||||||
|
required_error: "Group ID should be available.",
|
||||||
|
invalid_type_error: "Group ID must be a string"
|
||||||
|
}).regex(/^[\w\-\=]{1,32}$/, "Group ID must contain only letters, numbers, dashes, underscores & equal signs.")
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user)
|
if (!user)
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 })
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const groupId = searchParams.get('groupId') as string
|
const groupId = searchParams.get('groupId') as string
|
||||||
|
|
||||||
|
if (groupId) {
|
||||||
|
const groupIdValidation = await groupIdSchema.safeParseAsync(groupId)
|
||||||
|
if (!groupIdValidation.success)
|
||||||
|
return NextResponse.json({ message: 'groupId does not meet requirements.', error: JSON.parse(groupIdValidation.error['message'])[0], value: null }, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
let chatters: { userId: string, groupId: string, chatterId: bigint, chatterLabel: string }[]
|
let chatters: { userId: string, groupId: string, chatterId: bigint, chatterLabel: string }[]
|
||||||
|
|
||||||
if (!!groupId)
|
if (groupId)
|
||||||
chatters = await db.chatterGroup.findMany({
|
chatters = await db.chatterGroup.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
@ -31,10 +40,8 @@ export async function GET(req: Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json(chatters.map(u => ({ ...u, chatterId: Number(u.chatterId) }))
|
return NextResponse.json(chatters.map(u => ({ ...u, chatterId: Number(u.chatterId) }))
|
||||||
.map(({userId, chatterLabel, ...attrs}) => attrs))
|
.map(({ userId, chatterLabel, ...attrs }) => attrs))
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[GROUPS/USERS]", error);
|
return NextResponse.json({ message: 'Failed to get groups', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,12 +2,13 @@ import { db } from "@/lib/db"
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
import fetchUserWithImpersonation from "@/lib/fetch-user-impersonation";
|
||||||
import { ActionType, Prisma } from "@prisma/client";
|
import { ActionType, Prisma } from "@prisma/client";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = await db.action.findMany({
|
const actions = await db.action.findMany({
|
||||||
@ -17,17 +18,21 @@ export async function GET(req: Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json(actions.map(({ userId, ...attrs }) => attrs));
|
return NextResponse.json(actions.map(({ userId, ...attrs }) => attrs));
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.log("[REDEMPTIONS/ACTIONS]", error);
|
return NextResponse.json({ message: null, error: error, value: null }, { status: 500 });
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nameSchema = z.string({
|
||||||
|
required_error: "Name is required.",
|
||||||
|
invalid_type_error: "Name must be a string"
|
||||||
|
}).regex(/^[\w\-\s]{1,32}$/, "Name must contain only letters, numbers, spaces, dashes, and underscores.")
|
||||||
|
|
||||||
async function common(req: Request, action: (id: string, name: string, type: ActionType, data: any) => void) {
|
async function common(req: Request, action: (id: string, name: string, type: ActionType, data: any) => void) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { name, type, scene_name, scene_item_name, rotation, position_x, position_y, file_path, file_content, tts_voice, obs_visible, obs_index, sleep, oauth_name, oauth_type }:
|
const { name, type, scene_name, scene_item_name, rotation, position_x, position_y, file_path, file_content, tts_voice, obs_visible, obs_index, sleep, oauth_name, oauth_type }:
|
||||||
@ -35,16 +40,21 @@ async function common(req: Request, action: (id: string, name: string, type: Act
|
|||||||
name: string, type: ActionType, scene_name: string, scene_item_name: string, rotation: string, position_x: string, position_y: string, file_path: string, file_content: string, tts_voice: string, obs_visible: boolean, obs_index: number, sleep: number,
|
name: string, type: ActionType, scene_name: string, scene_item_name: string, rotation: string, position_x: string, position_y: string, file_path: string, file_content: string, tts_voice: string, obs_visible: boolean, obs_index: number, sleep: number,
|
||||||
oauth_name: string, oauth_type: string
|
oauth_name: string, oauth_type: string
|
||||||
} = await req.json();
|
} = await req.json();
|
||||||
if (!name && !type)
|
if (!name)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'name is required.', error: null, value: null }, { status: 400 });
|
||||||
|
const nameValidation = nameSchema.safeParse(name)
|
||||||
|
if (!nameValidation.success)
|
||||||
|
return NextResponse.json({ message: 'name must follow some requirements.', error: nameValidation.error, value: null }, { status: 400 });
|
||||||
|
if (!type)
|
||||||
|
return NextResponse.json({ message: 'type is required', error: null, value: null }, { status: 400 });
|
||||||
if (type == ActionType.OBS_TRANSFORM && (!scene_name || !scene_item_name || !rotation && !position_x && !position_y))
|
if (type == ActionType.OBS_TRANSFORM && (!scene_name || !scene_item_name || !rotation && !position_x && !position_y))
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: '"scene_name", "scene_item_name" and one of "rotation", "position_x", "position_y" are required.', error: null, value: null }, { status: 400 });
|
||||||
if ((type == ActionType.WRITE_TO_FILE || type == ActionType.APPEND_TO_FILE) && (!file_path || !file_content))
|
if ((type == ActionType.WRITE_TO_FILE || type == ActionType.APPEND_TO_FILE) && (!file_path || !file_content))
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: '"scene_name", "scene_item_name", "file_path" & "file_content" are required.', error: null, value: null }, { status: 400 });
|
||||||
if (type == ActionType.AUDIO_FILE && !file_path)
|
if (type == ActionType.AUDIO_FILE && !file_path)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: '"scene_name", "scene_item_name" & "file_path" are required.', error: null, value: null }, { status: 400 });
|
||||||
if ([ActionType.OAUTH, ActionType.NIGHTBOT_PLAY, ActionType.NIGHTBOT_PAUSE, ActionType.NIGHTBOT_SKIP, ActionType.NIGHTBOT_CLEAR_PLAYLIST, ActionType.NIGHTBOT_CLEAR_QUEUE, ActionType.TWITCH_OAUTH].some(t => t == type) && (!oauth_name || !oauth_type))
|
if ([ActionType.OAUTH, ActionType.NIGHTBOT_PLAY, ActionType.NIGHTBOT_PAUSE, ActionType.NIGHTBOT_SKIP, ActionType.NIGHTBOT_CLEAR_PLAYLIST, ActionType.NIGHTBOT_CLEAR_QUEUE, ActionType.TWITCH_OAUTH].some(t => t == type) && (!oauth_name || !oauth_type))
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: '"oauth_name" & "oauth_type" are required.', error: null, value: null }, { status: 400 });
|
||||||
|
|
||||||
let data: any = {}
|
let data: any = {}
|
||||||
if (type == ActionType.WRITE_TO_FILE || type == ActionType.APPEND_TO_FILE) {
|
if (type == ActionType.WRITE_TO_FILE || type == ActionType.APPEND_TO_FILE) {
|
||||||
@ -78,10 +88,9 @@ async function common(req: Request, action: (id: string, name: string, type: Act
|
|||||||
|
|
||||||
action(user.id, name, type, data)
|
action(user.id, name, type, data)
|
||||||
|
|
||||||
return new NextResponse("", { status: 200 });
|
return NextResponse.json({ message: null, error: null, value: null }, { status: 200 });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
//console.log("[REDEMPTIONS/ACTIONS]", error);
|
return NextResponse.json({ message: null, error: error, value: null }, { status: 500 });
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +129,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
@ -135,8 +144,7 @@ export async function DELETE(req: Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return NextResponse.json(redemptions);
|
return NextResponse.json(redemptions);
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.log("[REDEMPTIONS]", error);
|
return NextResponse.json({ message: null, error: error, value: null }, { status: 500 });
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const redemptions = await db.redemption.findMany({
|
const redemptions = await db.redemption.findMany({
|
||||||
@ -17,8 +17,7 @@ export async function GET(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(redemptions.map(({userId, ...attrs}) => attrs));
|
return NextResponse.json(redemptions.map(({userId, ...attrs}) => attrs));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[REDEMPTIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +25,15 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { actionName, redemptionId, order, state }: { actionName: string, redemptionId: string, order: number, state: boolean } = await req.json();
|
const { actionName, redemptionId, order, state }: { actionName: string, redemptionId: string, order: number, state: boolean } = await req.json();
|
||||||
if (!redemptionId || !actionName && !order && !state)
|
if (!redemptionId && !actionName && !order)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'One of the following fields must exist: redemptionId, actionName, order, state.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
|
if (actionName && actionName.length > 32)
|
||||||
|
return NextResponse.json({ message: 'actionName cannot be longer than 32 characters.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const action = await db.action.findFirst({
|
const action = await db.action.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -39,7 +41,7 @@ export async function POST(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!action)
|
if (!action)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Action does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
let data:any = {
|
let data:any = {
|
||||||
actionName,
|
actionName,
|
||||||
@ -66,8 +68,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(res, { status: 200 });
|
return NextResponse.json(res, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[REDEMPTIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +76,15 @@ export async function PUT(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id, actionName, redemptionId, order, state }: { id: string, actionName: string, redemptionId: string, order: number, state: boolean } = await req.json();
|
const { id, actionName, redemptionId, order, state }: { id: string, actionName: string, redemptionId: string, order: number, state: boolean } = await req.json();
|
||||||
if (!redemptionId || !actionName && !order && !state)
|
if (!redemptionId && !actionName && !order)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'One of the following fields must exist: redemptionId, actionName, order, state.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
|
if (actionName && actionName.length > 32)
|
||||||
|
return NextResponse.json({ message: 'actionName cannot be longer than 32 characters.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const action = await db.action.findFirst({
|
const action = await db.action.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -88,7 +92,7 @@ export async function PUT(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!action)
|
if (!action)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Action does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
let data:any = {
|
let data:any = {
|
||||||
actionName,
|
actionName,
|
||||||
@ -115,8 +119,7 @@ export async function PUT(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(res, { status: 200 });
|
return NextResponse.json(res, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[REDEMPTIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +127,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
@ -137,7 +140,6 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(redemptions);
|
return NextResponse.json(redemptions);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[REDEMPTIONS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,49 +6,48 @@ import voices from "@/data/tts";
|
|||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
try {
|
try {
|
||||||
if (!voices) {
|
if (!voices) {
|
||||||
return new NextResponse("Voices not available.", { status: 500 });
|
return NextResponse.json({ message: 'Failed to load voices', error: null, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json(user.ttsDefaultVoice);
|
return NextResponse.json(user.ttsDefaultVoice);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/DEFAULT]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
if (!voices) {
|
if (!voices) {
|
||||||
return new NextResponse("Voices not available.", { status: 500 });
|
return NextResponse.json({ message: 'Failed to load voices', error: null, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { voice } = await req.json();
|
const { voice } = await req.json();
|
||||||
if (!voice || !voices.map(v => v.toLowerCase()).includes(voice.toLowerCase())) return new NextResponse("Bad Request", { status: 400 });
|
if (!voice || !voices.map(v => v.toLowerCase()).includes(voice.toLowerCase()))
|
||||||
|
return NextResponse.json({ message: 'Voice does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const v = voices.find(v => v.toLowerCase() == voice.toLowerCase())
|
const v = voices.find(v => v.toLowerCase() == voice.toLowerCase())
|
||||||
|
|
||||||
await db.user.update({
|
await db.user.update({
|
||||||
where: {
|
where: {
|
||||||
id: user.id
|
id: user.id
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
ttsDefaultVoice: v
|
ttsDefaultVoice: v
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new NextResponse("", { status: 200 });
|
return NextResponse.json({ message: null, error: null, value: null }, { status: 200 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/DEFAULT]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const filters = await db.ttsUsernameFilter.findMany({
|
const filters = await db.ttsUsernameFilter.findMany({
|
||||||
@ -18,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(filters);
|
return NextResponse.json(filters);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/USERS]", error);
|
console.log("[TTS/FILTER/USERS]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +26,14 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { username, tag } = await req.json();
|
const { username, tag } = await req.json();
|
||||||
if (!username || username.length < 4 || username.length > 25) return new NextResponse("Bad Request", { status: 400 });
|
if (!username || username.length < 4 || username.length > 25)
|
||||||
if (!tag || !["blacklisted", "priority"].includes(tag)) return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Username does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
if (!tag || !["blacklisted", "priority"].includes(tag))
|
||||||
|
return NextResponse.json({ message: 'Tag does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const filter = await db.ttsUsernameFilter.upsert({
|
const filter = await db.ttsUsernameFilter.upsert({
|
||||||
where: {
|
where: {
|
||||||
@ -52,8 +54,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(filter);
|
return NextResponse.json(filter);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/USERS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +62,13 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const username = searchParams.get('username') as string
|
const username = searchParams.get('username') as string
|
||||||
if (!username || username.length < 4 || username.length > 25) return new NextResponse("Bad Request", { status: 400 });
|
if (!username || username.length < 4 || username.length > 25)
|
||||||
|
return NextResponse.json({ message: 'Username does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const filter = await db.ttsUsernameFilter.delete({
|
const filter = await db.ttsUsernameFilter.delete({
|
||||||
where: {
|
where: {
|
||||||
@ -79,7 +81,6 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(filter)
|
return NextResponse.json(filter)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/USERS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const filters = await db.ttsWordFilter.findMany({
|
const filters = await db.ttsWordFilter.findMany({
|
||||||
@ -18,7 +18,7 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(filters);
|
return NextResponse.json(filters);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/WORDS]", error);
|
console.log("[TTS/FILTER/WORDS]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,97 +26,102 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { search, replace } = await req.json();
|
const { search, replace } = await req.json();
|
||||||
if (!search || search.length < 4 || search.length > 200) return new NextResponse("Bad Request", { status: 400 });
|
if (!search || search.length < 3 || search.length > 127)
|
||||||
if (replace == null) return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'search must be between 3 to 127 characters.', error: null, value: null }, { status: 400 })
|
||||||
|
if (replace == null)
|
||||||
|
return NextResponse.json({ message: 'replace must not be null', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const filter = await db.ttsWordFilter.create({
|
const filter = await db.ttsWordFilter.create({
|
||||||
data: {
|
data: {
|
||||||
search,
|
search,
|
||||||
replace,
|
replace,
|
||||||
userId: user.id
|
userId: user.id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(filter);
|
return NextResponse.json(filter);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/WORDS]", error);
|
console.log("[TTS/FILTER/WORDS]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function PUT(req: Request) {
|
export async function PUT(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
|
||||||
|
|
||||||
const { id, search, replace } = await req.json();
|
|
||||||
if (!id || id.length < 1) return new NextResponse("Bad Request", { status: 400 });
|
|
||||||
if (!search || search.length < 4 || search.length > 200) return new NextResponse("Bad Request", { status: 400 });
|
|
||||||
if (replace == null) return new NextResponse("Bad Request", { status: 400 });
|
|
||||||
|
|
||||||
const filter = await db.ttsWordFilter.update({
|
|
||||||
where: {
|
|
||||||
id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
search,
|
|
||||||
replace,
|
|
||||||
userId: user.id
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json(filter);
|
const { id, search, replace } = await req.json();
|
||||||
} catch (error) {
|
if (!id || id.length < 1)
|
||||||
console.log("[TTS/FILTER/WORDS]", error);
|
return NextResponse.json({ message: 'id does not exist.', error: null, value: null }, { status: 400 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
if (!search || search.length < 3 || search.length > 127)
|
||||||
}
|
return NextResponse.json({ message: 'search must be between 3 to 127 characters.', error: null, value: null }, { status: 400 })
|
||||||
|
if (replace == null)
|
||||||
|
return NextResponse.json({ message: 'replace must not be null.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
|
const filter = await db.ttsWordFilter.update({
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
search,
|
||||||
|
replace,
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json(filter);
|
||||||
|
} catch (error) {
|
||||||
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DELETE(req: Request) {
|
export async function DELETE(req: Request) {
|
||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
const id = searchParams.get('id') as string
|
const id = searchParams.get('id') as string
|
||||||
const search = searchParams.get('search') as string
|
const search = searchParams.get('search') as string
|
||||||
if (!id && !search) return new NextResponse("Bad Request", { status: 400 });
|
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
if (search.length < 4 || search.length > 200) return new NextResponse("Bad Request", { status: 400 });
|
if (!search || search.length < 3 || search.length > 127)
|
||||||
|
return NextResponse.json({ message: 'search must be between 3 to 127 characters.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const filter = await db.ttsWordFilter.delete({
|
const filter = await db.ttsWordFilter.delete({
|
||||||
where: {
|
where: {
|
||||||
userId_search: {
|
userId_search: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
search
|
search
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(filter)
|
return NextResponse.json(filter)
|
||||||
} else if (id) {
|
} else if (id) {
|
||||||
if (id.length < 1) return new NextResponse("Bad Request", { status: 400 });
|
if (!id || id.length < 1)
|
||||||
|
return NextResponse.json({ message: 'id does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const filter = await db.ttsWordFilter.delete({
|
const filter = await db.ttsWordFilter.delete({
|
||||||
where: {
|
where: {
|
||||||
id: id
|
id: id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(filter)
|
return NextResponse.json(filter)
|
||||||
}
|
}
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
|
||||||
|
return NextResponse.json({ message: 'Either id or search must not be null.', error: null, value: null }, { status: 400 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/WORDS]", error);
|
return NextResponse.json({ message: 'Something went wrong.', error: null, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error" + error, { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const voiceStates = await db.ttsVoiceState.findMany({
|
const voiceStates = await db.ttsVoiceState.findMany({
|
||||||
@ -20,8 +20,7 @@ export async function GET(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(voiceStates.filter(v => v.state).map(v => voiceNamesMapped[v.ttsVoiceId]));
|
return NextResponse.json(voiceStates.filter(v => v.state).map(v => voiceNamesMapped[v.ttsVoiceId]));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { voice, state }: { voice: string, state: boolean } = await req.json();
|
const { voice, state }: { voice: string, state: boolean } = await req.json();
|
||||||
@ -37,7 +36,7 @@ export async function POST(req: Request) {
|
|||||||
const voiceIds = await db.ttsVoice.findMany();
|
const voiceIds = await db.ttsVoice.findMany();
|
||||||
const voiceIdsMapped: { [voice: string]: string } = Object.assign({}, ...voiceIds.map(v => ({ [v.name.toLowerCase()]: v.id })));
|
const voiceIdsMapped: { [voice: string]: string } = Object.assign({}, ...voiceIds.map(v => ({ [v.name.toLowerCase()]: v.id })));
|
||||||
if (!voiceIdsMapped[voice.toLowerCase()]) {
|
if (!voiceIdsMapped[voice.toLowerCase()]) {
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Voice does not exist.', error: null, value: null }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.ttsVoiceState.upsert({
|
await db.ttsVoiceState.upsert({
|
||||||
@ -57,9 +56,8 @@ export async function POST(req: Request) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new NextResponse("", { status: 200 });
|
return NextResponse.json({ message: null, error: null, value: null }, { status: 200 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/FILTER/USER]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const selected = await db.ttsChatVoice.findMany({
|
const selected = await db.ttsChatVoice.findMany({
|
||||||
@ -22,8 +22,7 @@ export async function GET(req: Request) {
|
|||||||
const data = selected.map(s => ({ chatter_id: new Number(s.chatterId), voice: voiceNamesMapped[s.ttsVoiceId] }))
|
const data = selected.map(s => ({ chatter_id: new Number(s.chatterId), voice: voiceNamesMapped[s.ttsVoiceId] }))
|
||||||
return NextResponse.json(data);
|
return NextResponse.json(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/SELECTED]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,11 +30,12 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { voice, chatterId }: { voice: string, chatterId: number } = await req.json();
|
const { voice, chatterId }: { voice: string, chatterId: number } = await req.json();
|
||||||
if (!voice || !voices.map(v => v.toLowerCase()).includes(voice.toLowerCase())) return new NextResponse("Bad Request", { status: 400 });
|
if (!voice || !voices.map(v => v.toLowerCase()).includes(voice.toLowerCase()))
|
||||||
|
return NextResponse.json({ message: 'Voice does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
const v = voices.find(v => v.toLowerCase() == voice.toLowerCase())
|
const v = voices.find(v => v.toLowerCase() == voice.toLowerCase())
|
||||||
const voiceData = await db.ttsVoice.findFirst({
|
const voiceData = await db.ttsVoice.findFirst({
|
||||||
@ -44,7 +44,7 @@ export async function POST(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!voiceData)
|
if (!voiceData)
|
||||||
return new NextResponse("Bad Request", { status: 400 });
|
return NextResponse.json({ message: 'Voice does not exist.', error: null, value: null }, { status: 400 })
|
||||||
|
|
||||||
await db.ttsChatVoice.upsert({
|
await db.ttsChatVoice.upsert({
|
||||||
where: {
|
where: {
|
||||||
@ -63,9 +63,8 @@ export async function POST(req: Request) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return new NextResponse("", { status: 200 });
|
return NextResponse.json({ message: null, error: null, value: null }, { status: 200 })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TTS/SELECTED]", error);
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request, { params } : { params: { id: string } })
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = req.headers?.get('x-api-key')
|
let id = req.headers?.get('x-api-key')
|
||||||
@ -23,7 +23,7 @@ export async function GET(req: Request, { params } : { params: { id: string } })
|
|||||||
return NextResponse.json(tokens);
|
return NextResponse.json(tokens);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKEN/GET]", error);
|
console.log("[TOKEN/GET]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ export async function DELETE(req: Request, { params } : { params: { id: string }
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = params
|
const { id } = params
|
||||||
@ -45,6 +45,6 @@ export async function DELETE(req: Request, { params } : { params: { id: string }
|
|||||||
return NextResponse.json(token);
|
return NextResponse.json(token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKEN/DELETE]", error);
|
console.log("[TOKEN/DELETE]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req);
|
const user = await fetchUserWithImpersonation(req);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const api = await db.twitchConnection.findFirst({
|
const api = await db.twitchConnection.findFirst({
|
||||||
@ -15,7 +15,7 @@ export async function GET(req: Request) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!api) {
|
if (!api) {
|
||||||
return new NextResponse("Forbidden", { status: 403 });
|
return NextResponse.json({ message: 'You do not have permission for this.', error: null, value: null }, { status: 403 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
@ -28,6 +28,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(data);
|
return NextResponse.json(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKENS/GET]", error);
|
console.log("[TOKENS/GET]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function POST(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
let { userId, label } = await req.json();
|
let { userId, label } = await req.json();
|
||||||
@ -29,8 +29,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(token);
|
return NextResponse.json(token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKEN/POST]", error);
|
return NextResponse.json({ message: 'Something went wrong.', error: error, value: null }, { status: 500 });
|
||||||
return new NextResponse("Internal Error", { status: 500});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ export async function DELETE(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id } = await req.json();
|
const { id } = await req.json();
|
||||||
@ -55,8 +54,7 @@ export async function DELETE(req: Request) {
|
|||||||
|
|
||||||
return NextResponse.json(token);
|
return NextResponse.json(token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKEN/DELETE]", error);
|
return NextResponse.json({ message: 'Something went wrong.', error: error, value: null }, { status: 500 });
|
||||||
return new NextResponse("Internal Error", { status: 500});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUserWithImpersonation(req)
|
const user = await fetchUserWithImpersonation(req)
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokens = await db.apiKey.findMany({
|
const tokens = await db.apiKey.findMany({
|
||||||
@ -18,6 +18,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(tokens);
|
return NextResponse.json(tokens);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[TOKENS/GET]", error);
|
console.log("[TOKENS/GET]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ export async function GET(req: Request) {
|
|||||||
try {
|
try {
|
||||||
const user = await fetchUser(req)
|
const user = await fetchUser(req)
|
||||||
if (!user || user.role != "ADMIN") {
|
if (!user || user.role != "ADMIN") {
|
||||||
return new NextResponse("Unauthorized", { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized.', error: null, value: null }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url)
|
const { searchParams } = new URL(req.url)
|
||||||
@ -36,6 +36,6 @@ export async function GET(req: Request) {
|
|||||||
return NextResponse.json(users)
|
return NextResponse.json(users)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[USERS]", error);
|
console.log("[USERS]", error);
|
||||||
return new NextResponse("Internal Error", { status: 500 });
|
return NextResponse.json({ message: 'Something went wrong', error: error, value: null }, { status: 500 })
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,58 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { v4 } from "uuid"
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import useWebSocket from 'react-use-websocket';
|
|
||||||
|
|
||||||
const socketUrl = 'wss://echo.websocket.org';
|
|
||||||
|
|
||||||
const SocketPage = () => {
|
|
||||||
const [mounted, setMounted] = useState(false)
|
|
||||||
|
|
||||||
const {
|
|
||||||
sendMessage,
|
|
||||||
sendJsonMessage,
|
|
||||||
lastMessage,
|
|
||||||
lastJsonMessage,
|
|
||||||
readyState,
|
|
||||||
getWebSocket,
|
|
||||||
} = useWebSocket(socketUrl, {
|
|
||||||
onOpen: () => console.log('opened'),
|
|
||||||
onMessage: (e) => console.log("MESSAGE", e),
|
|
||||||
onError: (e) => console.error(e),
|
|
||||||
shouldReconnect: (closeEvent) => { console.log("Reconnect"); return true; },
|
|
||||||
});
|
|
||||||
const [messageHistory, setMessageHistory] = useState<MessageEvent[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (lastMessage !== null) {
|
|
||||||
console.log("LAST", lastMessage)
|
|
||||||
setMessageHistory((prev) => prev.concat(lastMessage));
|
|
||||||
}
|
|
||||||
}, [lastMessage, setMessageHistory]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!mounted) {
|
|
||||||
setMounted(true)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (<div className="w-full bg-blue-300">
|
|
||||||
<p>Hello</p>
|
|
||||||
<p>{readyState}</p>
|
|
||||||
<Button onClick={() => sendMessage("uisdhnishdadasdfsd " + v4())}>
|
|
||||||
Click on me
|
|
||||||
</Button>
|
|
||||||
<div>
|
|
||||||
{lastMessage ? <span>Last message: {lastMessage.data}</span> : null}
|
|
||||||
<ul>
|
|
||||||
{messageHistory.map((message, idx) => (
|
|
||||||
<p className='block' key={idx}>{message ? message.data : null}</p>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SocketPage
|
|
@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { Label } from "../ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
export interface ConnectionDefault {
|
export interface ConnectionDefault {
|
||||||
@ -21,7 +21,10 @@ export const ConnectionDefaultElement = ({
|
|||||||
|
|
||||||
const OnDefaultConnectionUpdate = function (con: { name: string, clientId: string, token: string, type: string, scope: string, expiresAt: Date }) {
|
const OnDefaultConnectionUpdate = function (con: { name: string, clientId: string, token: string, type: string, scope: string, expiresAt: Date }) {
|
||||||
if (connection && con.name == connection.name)
|
if (connection && con.name == connection.name)
|
||||||
return;
|
return
|
||||||
|
|
||||||
|
if (connection && !connections.some(c => c.clientId == connection.clientId && c.name == connection.name && c.token == connection.token))
|
||||||
|
return
|
||||||
|
|
||||||
axios.put('/api/connection/default', { name: con.name, type: con.type })
|
axios.put('/api/connection/default', { name: con.name, type: con.type })
|
||||||
.then(d => {
|
.then(d => {
|
||||||
@ -33,7 +36,6 @@ export const ConnectionDefaultElement = ({
|
|||||||
const con = connections.filter((c: any) => c.type == type && c.default)
|
const con = connections.filter((c: any) => c.type == type && c.default)
|
||||||
if (con.length > 0)
|
if (con.length > 0)
|
||||||
OnDefaultConnectionUpdate(con[0])
|
OnDefaultConnectionUpdate(con[0])
|
||||||
console.log('default', type, connections.filter(c => c.type == type).length > 0)
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { Input } from "../ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
|
||||||
import { env } from "process";
|
import { env } from "process";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FaExclamationTriangle } from "react-icons/fa";
|
import { FaExclamationTriangle } from "react-icons/fa";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,32 +1,37 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { Label } from "../ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { HelpCircleIcon, Trash2Icon } from "lucide-react";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import {
|
import { z } from "zod";
|
||||||
Tooltip,
|
import { Trash2Icon } from "lucide-react";
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "../ui/tooltip"
|
|
||||||
import { Checkbox } from "../ui/checkbox";
|
|
||||||
|
|
||||||
interface Permission {
|
interface Permission {
|
||||||
id: string|undefined
|
id: string | undefined
|
||||||
path: string
|
path: string
|
||||||
allow: boolean|null
|
allow: boolean | null
|
||||||
groupId: string
|
groupId: string
|
||||||
edit: boolean
|
edit: boolean
|
||||||
showEdit: boolean
|
showEdit: boolean
|
||||||
isNew: boolean
|
isNew: boolean
|
||||||
permissionPaths: { path: string, description: string }[]
|
permissionPaths: { path: string, description: string }[]
|
||||||
adder: (id: string, path: string, allow: boolean|null) => void
|
adder: (id: string, path: string, allow: boolean | null) => void
|
||||||
remover: (redemption: { id: string, path: string, allow: boolean|null }) => void
|
remover: (redemption: { id: string, path: string, allow: boolean | null }) => void,
|
||||||
|
contains: (path: string) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const permissionPathSchema = z.string({
|
||||||
|
required_error: "Permission path should be available.",
|
||||||
|
invalid_type_error: "Permission path must be a string"
|
||||||
|
}).regex(/^[\w\-\.]{1,64}$/, "Permission path must contain only letters, numbers, dashes, periods.")
|
||||||
|
const permissionAllowSchema = z.boolean({
|
||||||
|
required_error: "Permission should be allowed, revoked or inherited from parent.",
|
||||||
|
invalid_type_error: "Something went wrong."
|
||||||
|
}).nullable()
|
||||||
|
|
||||||
const GroupPermission = ({
|
const GroupPermission = ({
|
||||||
id,
|
id,
|
||||||
path,
|
path,
|
||||||
@ -37,18 +42,43 @@ const GroupPermission = ({
|
|||||||
isNew,
|
isNew,
|
||||||
permissionPaths,
|
permissionPaths,
|
||||||
adder,
|
adder,
|
||||||
remover
|
remover,
|
||||||
|
contains
|
||||||
}: Permission) => {
|
}: Permission) => {
|
||||||
const [pathOpen, setPathOpen] = useState(false)
|
const [pathOpen, setPathOpen] = useState(false)
|
||||||
const [isEditable, setIsEditable] = useState(edit)
|
const [isEditable, setIsEditable] = useState(edit)
|
||||||
const [oldData, setOldData] = useState<{ path: string, allow: boolean|null } | undefined>(undefined)
|
const [oldData, setOldData] = useState<{ path: string, allow: boolean | null } | undefined>(undefined)
|
||||||
const [permission, setPermission] = useState<{ id: string|undefined, path: string, allow: boolean|null }>({ id, path, allow });
|
const [permission, setPermission] = useState<{ id: string | undefined, path: string, allow: boolean | null }>({ id, path, allow });
|
||||||
|
const [error, setError] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
function Save() {
|
function Save() {
|
||||||
|
setError(undefined)
|
||||||
if (!permission || !permission.path)
|
if (!permission || !permission.path)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (!permissionPaths.some(p => p.path == permission.path)) {
|
||||||
|
setError('Permission does not exist.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionPathValidation = permissionPathSchema.safeParse(permission.path)
|
||||||
|
if (!permissionPathValidation.success) {
|
||||||
|
setError(JSON.parse(permissionPathValidation.error['message'])[0].message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionAllowValidation = permissionAllowSchema.safeParse(permission.allow)
|
||||||
|
if (!permissionAllowValidation.success) {
|
||||||
|
setError(JSON.parse(permissionAllowValidation.error['message'])[0].message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
|
if (contains(permission.path)) {
|
||||||
|
setError("Permission already exists.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios.post("/api/settings/groups/permissions", {
|
axios.post("/api/settings/groups/permissions", {
|
||||||
path: permission.path,
|
path: permission.path,
|
||||||
allow: permission.allow,
|
allow: permission.allow,
|
||||||
@ -61,6 +91,11 @@ const GroupPermission = ({
|
|||||||
setPermission({ id: undefined, path: "", allow: true })
|
setPermission({ id: undefined, path: "", allow: true })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
if (!contains(permission.path)) {
|
||||||
|
setError("Permission does not exists.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios.put("/api/settings/groups/permissions", {
|
axios.put("/api/settings/groups/permissions", {
|
||||||
id: permission.id,
|
id: permission.id,
|
||||||
path: permission.path,
|
path: permission.path,
|
||||||
@ -74,7 +109,8 @@ const GroupPermission = ({
|
|||||||
function Cancel() {
|
function Cancel() {
|
||||||
if (!oldData)
|
if (!oldData)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
setError(undefined)
|
||||||
setPermission({ ...oldData, id: permission.id })
|
setPermission({ ...oldData, id: permission.id })
|
||||||
setIsEditable(false)
|
setIsEditable(false)
|
||||||
setOldData(undefined)
|
setOldData(undefined)
|
||||||
@ -124,7 +160,7 @@ const GroupPermission = ({
|
|||||||
value={p.path}
|
value={p.path}
|
||||||
key={p.path}
|
key={p.path}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
setPermission({ ...permission, path: permissionPaths.find(v => v.path.toLowerCase() == value.toLowerCase())?.path ?? value.toLowerCase()})
|
setPermission({ ...permission, path: permissionPaths.find(v => v.path.toLowerCase() == value.toLowerCase())?.path ?? value.toLowerCase() })
|
||||||
setPathOpen(false)
|
setPathOpen(false)
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div>
|
||||||
@ -135,7 +171,7 @@ const GroupPermission = ({
|
|||||||
{p.description}
|
{p.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
@ -171,6 +207,11 @@ const GroupPermission = ({
|
|||||||
}}
|
}}
|
||||||
disabled={!isEditable || permission === undefined} />
|
disabled={!isEditable || permission === undefined} />
|
||||||
</div>
|
</div>
|
||||||
|
{error &&
|
||||||
|
<p className="w-[380px] text-red-600 text-wrap text-sm">
|
||||||
|
{error}
|
||||||
|
</p>
|
||||||
|
}
|
||||||
<div>
|
<div>
|
||||||
{isEditable &&
|
{isEditable &&
|
||||||
<Button
|
<Button
|
||||||
|
@ -2,12 +2,18 @@ import axios from "axios";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "../ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Maximize2, Minimize2, Trash2Icon } from "lucide-react";
|
import { Maximize2, Minimize2, Trash2Icon } from "lucide-react";
|
||||||
import GroupPermission from "./group-permission";
|
import GroupPermission from "./group-permission";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import UserList from "./user-list-group";
|
import UserList from "./user-list-group";
|
||||||
|
|
||||||
|
const groupNameSchema = z.string({
|
||||||
|
required_error: "Group name is required.",
|
||||||
|
invalid_type_error: "Group name must be a string"
|
||||||
|
}).regex(/^[\w\-\s]{1,20}$/, "Group name must contain only letters, numbers, spaces, dashes, and underscores.")
|
||||||
|
const prioritySchema = z.string().regex(/^-?\d{1,5}$/, "Priority must be a valid number.")
|
||||||
|
|
||||||
interface Group {
|
interface Group {
|
||||||
id: string | undefined
|
id: string | undefined
|
||||||
name: string
|
name: string
|
||||||
@ -40,11 +46,11 @@ const GroupElement = ({
|
|||||||
const [isEditable, setIsEditable] = useState(edit)
|
const [isEditable, setIsEditable] = useState(edit)
|
||||||
const [isNew, setIsNew] = useState(isNewGroup)
|
const [isNew, setIsNew] = useState(isNewGroup)
|
||||||
const [isMinimized, setIsMinimized] = useState(true)
|
const [isMinimized, setIsMinimized] = useState(true)
|
||||||
const [oldData, setOldData] = useState<{ id: string|undefined, name: string, priority: number } | undefined>(undefined)
|
const [oldData, setOldData] = useState<{ id: string | undefined, name: string, priority: number } | undefined>(undefined)
|
||||||
const [group, setGroup] = useState<{ id: string|undefined, name: string, priority: number }>({ id, name, priority })
|
const [group, setGroup] = useState<{ id: string | undefined, name: string, priority: number }>({ id, name, priority })
|
||||||
const [permissions, setPermissions] = useState<{ id: string, path: string, allow: boolean | null }[]>(permissionsLoaded);
|
const [permissions, setPermissions] = useState<{ id: string, path: string, allow: boolean | null }[]>(permissionsLoaded);
|
||||||
const isSpecial = (isEditable || oldData === undefined) && !!group && specialGroups.includes(group?.name)
|
const isSpecial = (isEditable || oldData === undefined) && !!group && specialGroups.includes(group?.name)
|
||||||
const [error, setError] = useState<string|undefined>(undefined)
|
const [error, setError] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
|
|
||||||
function addPermission(id: string, path: string, allow: boolean | null) {
|
function addPermission(id: string, path: string, allow: boolean | null) {
|
||||||
@ -55,25 +61,25 @@ const GroupElement = ({
|
|||||||
setPermissions(permissions.filter(p => p.id != permission.id))
|
setPermissions(permissions.filter(p => p.id != permission.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameSchema = z.string({
|
function containsPermission(path: string) {
|
||||||
required_error: "Name is required.",
|
return permissions.some(p => p.path == path)
|
||||||
invalid_type_error: "Name must be a string"
|
}
|
||||||
}).regex(/^[\w\-\s]{1,20}$/, "Name must contain only letters, numbers, dashes, and underscores.")
|
|
||||||
const prioritySchema = z.string().regex(/^-?\d{1,5}$/, "Priority must be a valid number.")
|
|
||||||
|
|
||||||
function Save() {
|
function Save() {
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
if (!isNew && !id)
|
if (!isNew && !id) {
|
||||||
|
setError("Something went wrong.")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!group.name) {
|
if (!group.name) {
|
||||||
setError("Set a value for the group name")
|
setError("Set a value for the group name")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameValidation = nameSchema.safeParse(group.name)
|
const groupNameValidation = groupNameSchema.safeParse(group.name)
|
||||||
if (!nameValidation.success) {
|
if (!groupNameValidation.success) {
|
||||||
setError(JSON.parse(nameValidation.error['message'])[0].message)
|
setError(JSON.parse(groupNameValidation.error['message'])[0].message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +89,12 @@ const GroupElement = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contains(group.name)) {
|
|
||||||
setError("Group already exist. Use another name.")
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNew || group.id?.startsWith('$')) {
|
if (isNew || group.id?.startsWith('$')) {
|
||||||
|
if (contains(group.name) && !isSpecial) {
|
||||||
|
setError("Group already exists. Use another name.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios.post("/api/settings/groups", {
|
axios.post("/api/settings/groups", {
|
||||||
name: group.name,
|
name: group.name,
|
||||||
priority: group.priority
|
priority: group.priority
|
||||||
@ -97,7 +103,6 @@ const GroupElement = ({
|
|||||||
setError("Something went wrong.")
|
setError("Something went wrong.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log("DATA", d.data)
|
|
||||||
|
|
||||||
if (specialGroups.includes(group.name)) {
|
if (specialGroups.includes(group.name)) {
|
||||||
setIsNew(false)
|
setIsNew(false)
|
||||||
@ -111,6 +116,11 @@ const GroupElement = ({
|
|||||||
setError("Something went wrong.")
|
setError("Something went wrong.")
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
if (!contains(group.name)) {
|
||||||
|
setError("Group does not exist. Something went wrong.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
axios.put("/api/settings/groups", {
|
axios.put("/api/settings/groups", {
|
||||||
id: group.id,
|
id: group.id,
|
||||||
name: group.name,
|
name: group.name,
|
||||||
@ -124,10 +134,10 @@ const GroupElement = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Cancel() {
|
function Cancel() {
|
||||||
setError(undefined)
|
|
||||||
if (!oldData)
|
if (!oldData)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
setError(undefined)
|
||||||
setGroup({ ...oldData, id: group.id })
|
setGroup({ ...oldData, id: group.id })
|
||||||
setIsEditable(false)
|
setIsEditable(false)
|
||||||
setOldData(undefined)
|
setOldData(undefined)
|
||||||
@ -196,12 +206,10 @@ const GroupElement = ({
|
|||||||
onChange={e => setGroup(d => {
|
onChange={e => setGroup(d => {
|
||||||
let temp = { ...group }
|
let temp = { ...group }
|
||||||
const v = parseInt(e.target.value)
|
const v = parseInt(e.target.value)
|
||||||
if (e.target.value.length == 0) {
|
if (e.target.value.length == 0 || Number.isNaN(v)) {
|
||||||
temp.priority = 0
|
temp.priority = 0
|
||||||
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
||||||
temp.priority = v
|
temp.priority = v
|
||||||
} else if (Number.isNaN(v)) {
|
|
||||||
temp.priority = 0
|
|
||||||
}
|
}
|
||||||
return temp
|
return temp
|
||||||
})}
|
})}
|
||||||
@ -273,7 +281,8 @@ const GroupElement = ({
|
|||||||
isNew={false}
|
isNew={false}
|
||||||
permissionPaths={permissionPaths}
|
permissionPaths={permissionPaths}
|
||||||
adder={addPermission}
|
adder={addPermission}
|
||||||
remover={removePermission} />
|
remover={removePermission}
|
||||||
|
contains={containsPermission} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
@ -288,7 +297,8 @@ const GroupElement = ({
|
|||||||
isNew={true}
|
isNew={true}
|
||||||
permissionPaths={permissionPaths}
|
permissionPaths={permissionPaths}
|
||||||
adder={addPermission}
|
adder={addPermission}
|
||||||
remover={removePermission} />
|
remover={removePermission}
|
||||||
|
contains={containsPermission} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Info, X } from "lucide-react";
|
import { Info, X } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
|
||||||
interface NoticeProps {
|
interface NoticeProps {
|
||||||
|
@ -4,10 +4,10 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { Label } from "../ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Maximize2, Minimize2, Trash2Icon } from "lucide-react";
|
import { Maximize2, Minimize2, Trash2Icon } from "lucide-react";
|
||||||
import { ActionType } from "@prisma/client";
|
import { ActionType } from "@prisma/client";
|
||||||
import { boolean } from "zod";
|
import { boolean, z } from "zod";
|
||||||
|
|
||||||
|
|
||||||
const actionTypes = [
|
const actionTypes = [
|
||||||
@ -19,13 +19,15 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File path",
|
"label": "File path",
|
||||||
"key": "file_path",
|
"key": "file_path",
|
||||||
"placeholder": "Enter local file path, relative or full."
|
"placeholder": "Enter local file path, relative or full.",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File content",
|
"label": "File content",
|
||||||
"key": "file_content",
|
"key": "file_content",
|
||||||
"placeholder": "Enter the text to write to the file."
|
"placeholder": "Enter the text to write to the file.",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -37,13 +39,15 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File path",
|
"label": "File path",
|
||||||
"key": "file_path",
|
"key": "file_path",
|
||||||
"placeholder": "Enter local file path, relative or full."
|
"placeholder": "Enter local file path, relative or full.",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File content",
|
"label": "File content",
|
||||||
"key": "file_content",
|
"key": "file_content",
|
||||||
"placeholder": "Enter the text to append to the file."
|
"placeholder": "Enter the text to append to the file.",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -60,7 +64,8 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File path",
|
"label": "File path",
|
||||||
"key": "file_path",
|
"key": "file_path",
|
||||||
"placeholder": "Enter local file path, relative or full."
|
"placeholder": "Enter local file path, relative or full.",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -78,6 +83,7 @@ const actionTypes = [
|
|||||||
"label": "TTS Voice",
|
"label": "TTS Voice",
|
||||||
"key": "tts_voice",
|
"key": "tts_voice",
|
||||||
"placeholder": "Name of an enabled TTS voice",
|
"placeholder": "Name of an enabled TTS voice",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -89,13 +95,15 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Name",
|
"label": "Scene Name",
|
||||||
"key": "scene_name",
|
"key": "scene_name",
|
||||||
"placeholder": "Name of the OBS scene"
|
"placeholder": "Name of the OBS scene",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Item Name",
|
"label": "Scene Item Name",
|
||||||
"key": "scene_item_name",
|
"key": "scene_item_name",
|
||||||
"placeholder": "Name of the OBS scene item / source"
|
"placeholder": "Name of the OBS scene item / source",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -107,20 +115,23 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Name",
|
"label": "Scene Name",
|
||||||
"key": "scene_name",
|
"key": "scene_name",
|
||||||
"placeholder": "Name of the OBS scene"
|
"placeholder": "Name of the OBS scene",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Item Name",
|
"label": "Scene Item Name",
|
||||||
"key": "scene_item_name",
|
"key": "scene_item_name",
|
||||||
"placeholder": "Name of the OBS scene item / source"
|
"placeholder": "Name of the OBS scene item / source",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text-values",
|
"type": "text-values",
|
||||||
"label": "Visible",
|
"label": "Visible",
|
||||||
"key": "obs_visible",
|
"key": "obs_visible",
|
||||||
"placeholder": "true for visible; false otherwise",
|
"placeholder": "true for visible; false otherwise",
|
||||||
"values": ["true", "false"]
|
"values": ["true", "false"],
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -132,19 +143,22 @@ const actionTypes = [
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Name",
|
"label": "Scene Name",
|
||||||
"key": "scene_name",
|
"key": "scene_name",
|
||||||
"placeholder": "Name of the OBS scene"
|
"placeholder": "Name of the OBS scene",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Scene Item Name",
|
"label": "Scene Item Name",
|
||||||
"key": "scene_item_name",
|
"key": "scene_item_name",
|
||||||
"placeholder": "Name of the OBS scene item / source"
|
"placeholder": "Name of the OBS scene item / source",
|
||||||
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"label": "Index",
|
"label": "Index",
|
||||||
"key": "obs_index",
|
"key": "obs_index",
|
||||||
"placeholder": "index, starting from 0."
|
"placeholder": "index, starting from 0.",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -157,6 +171,7 @@ const actionTypes = [
|
|||||||
"label": "Sleep",
|
"label": "Sleep",
|
||||||
"key": "sleep",
|
"key": "sleep",
|
||||||
"placeholder": "Time in milliseconds to do nothing",
|
"placeholder": "Time in milliseconds to do nothing",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -169,6 +184,7 @@ const actionTypes = [
|
|||||||
"label": "nightbot.play",
|
"label": "nightbot.play",
|
||||||
"key": "nightbot_play",
|
"key": "nightbot_play",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -181,6 +197,7 @@ const actionTypes = [
|
|||||||
"label": "nightbot.pause",
|
"label": "nightbot.pause",
|
||||||
"key": "nightbot_pause",
|
"key": "nightbot_pause",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -193,6 +210,7 @@ const actionTypes = [
|
|||||||
"label": "nightbot.skip",
|
"label": "nightbot.skip",
|
||||||
"key": "nightbot_skip",
|
"key": "nightbot_skip",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -205,6 +223,7 @@ const actionTypes = [
|
|||||||
"label": "nightbot.clear_playlist",
|
"label": "nightbot.clear_playlist",
|
||||||
"key": "nightbot_clear_playlist",
|
"key": "nightbot_clear_playlist",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -217,11 +236,16 @@ const actionTypes = [
|
|||||||
"label": "nightbot.clear_queue",
|
"label": "nightbot.clear_queue",
|
||||||
"key": "nightbot_clear_queue",
|
"key": "nightbot_clear_queue",
|
||||||
"placeholder": "",
|
"placeholder": "",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const nameSchema = z.string({
|
||||||
|
required_error: "Name is required.",
|
||||||
|
invalid_type_error: "Name must be a string"
|
||||||
|
}).regex(/^[\w\-\s]{1,32}$/, "Name must contain only letters, numbers, spaces, dashes, and underscores.")
|
||||||
|
|
||||||
interface RedeemableAction {
|
interface RedeemableAction {
|
||||||
name: string
|
name: string
|
||||||
@ -256,44 +280,68 @@ const RedemptionAction = ({
|
|||||||
const [isEditable, setIsEditable] = useState(edit)
|
const [isEditable, setIsEditable] = useState(edit)
|
||||||
const [isMinimized, setIsMinimized] = useState(!isNew)
|
const [isMinimized, setIsMinimized] = useState(!isNew)
|
||||||
const [oldData, setOldData] = useState<{ n: string, t: ActionType | undefined, d: { [k: string]: string } } | undefined>(undefined)
|
const [oldData, setOldData] = useState<{ n: string, t: ActionType | undefined, d: { [k: string]: string } } | undefined>(undefined)
|
||||||
|
const [error, setError] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
function Save(name: string, type: ActionType | undefined, data: { [key: string]: string }, isNew: boolean) {
|
function Save(isNew: boolean) {
|
||||||
// TODO: validation
|
setError(undefined)
|
||||||
if (!name) {
|
if (!actionName) {
|
||||||
|
setError("Name is required.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('typeeee', type)
|
const nameValidation = nameSchema.safeParse(actionName)
|
||||||
if (!type) {
|
if (!nameValidation.success) {
|
||||||
|
setError(JSON.parse(nameValidation.error['message'])[0].message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!data) {
|
if (!actionType) {
|
||||||
|
setError("Action type is required.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!actionTypes.some(t => t.value == actionType.value)) {
|
||||||
|
setError("Invalid action type given.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!actionData) {
|
||||||
|
setError("Something went wrong with the data.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputs = actionTypes.find(a => a.value == actionType.value && a.name == actionType.name)!.inputs
|
||||||
|
const required = inputs.filter(i => i.required)
|
||||||
|
for (const input of required) {
|
||||||
|
if (!(input.key in actionData)) {
|
||||||
|
setError("The field '" + input.label + "' is required.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let info: any = {
|
let info: any = {
|
||||||
name,
|
name: actionName,
|
||||||
type
|
type: actionType.value,
|
||||||
}
|
}
|
||||||
|
|
||||||
info = { ...info, ...data }
|
info.data = actionData
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
axios.post("/api/settings/redemptions/actions", info)
|
axios.post("/api/settings/redemptions/actions", info)
|
||||||
.then(d => {
|
.then(d => {
|
||||||
adder(name, type, data)
|
adder(actionName, actionType.value, actionData)
|
||||||
setActionName("")
|
setActionName("")
|
||||||
setActionType(undefined)
|
setActionType(undefined)
|
||||||
setActionData({})
|
setActionData({})
|
||||||
})
|
})
|
||||||
|
.catch(error => setError(error.response.data.message))
|
||||||
} else {
|
} else {
|
||||||
axios.put("/api/settings/redemptions/actions", info)
|
axios.put("/api/settings/redemptions/actions", info)
|
||||||
.then(d => {
|
.then(d => {
|
||||||
setIsEditable(false)
|
setIsEditable(false)
|
||||||
})
|
})
|
||||||
|
.catch(error => setError(error.response.data.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Cancel(data: { n: string, t: ActionType | undefined, d: { [k: string]: any } } | undefined) {
|
function Cancel(data: { n: string, t: ActionType | undefined, d: { [k: string]: any } } | undefined) {
|
||||||
|
setError(undefined)
|
||||||
if (!data)
|
if (!data)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -305,7 +353,7 @@ const RedemptionAction = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Delete() {
|
function Delete() {
|
||||||
axios.delete("/api/settings/redemptions/actions?action_name=" + name)
|
axios.delete("/api/settings/redemptions/actions?action_name=" + actionName)
|
||||||
.then(d => {
|
.then(d => {
|
||||||
remover(d.data)
|
remover(d.data)
|
||||||
})
|
})
|
||||||
@ -431,12 +479,10 @@ const RedemptionAction = ({
|
|||||||
onChange={e => setActionData(d => {
|
onChange={e => setActionData(d => {
|
||||||
let abc = { ...actionData }
|
let abc = { ...actionData }
|
||||||
const v = parseInt(e.target.value)
|
const v = parseInt(e.target.value)
|
||||||
if (e.target.value.length == 0) {
|
if (e.target.value.length == 0 || Number.isNaN(v)) {
|
||||||
abc[i.key] = "0"
|
abc[i.key] = "0"
|
||||||
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
||||||
abc[i.key] = v.toString()
|
abc[i.key] = v.toString()
|
||||||
} else if (Number.isNaN(v)) {
|
|
||||||
abc[i.key] = "0"
|
|
||||||
}
|
}
|
||||||
return abc
|
return abc
|
||||||
})}
|
})}
|
||||||
@ -497,7 +543,7 @@ const RedemptionAction = ({
|
|||||||
if (!!connection) {
|
if (!!connection) {
|
||||||
setActionData({
|
setActionData({
|
||||||
'oauth_name': connection.name,
|
'oauth_name': connection.name,
|
||||||
'oauth_type' : connection.type
|
'oauth_type': connection.type
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -517,7 +563,6 @@ const RedemptionAction = ({
|
|||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
return <div key={i.key}></div>
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -548,11 +593,16 @@ const RedemptionAction = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
{error &&
|
||||||
|
<div className="text-red-600 font-bold">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div>
|
<div>
|
||||||
{isEditable &&
|
{isEditable &&
|
||||||
<Button
|
<Button
|
||||||
className="m-3"
|
className="m-3"
|
||||||
onClick={() => Save(actionName, actionType?.value, actionData, isNew)}>
|
onClick={() => Save(isNew)}>
|
||||||
{isNew ? "Add" : "Save"}
|
{isNew ? "Add" : "Save"}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,14 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { Label } from "../ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { HelpCircleIcon, Trash2Icon } from "lucide-react";
|
import { HelpCircleIcon, Trash2Icon } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "../ui/tooltip"
|
} from "@/components/ui/tooltip"
|
||||||
|
|
||||||
interface Redemption {
|
interface Redemption {
|
||||||
id: string | undefined
|
id: string | undefined
|
||||||
@ -47,17 +47,42 @@ const OBSRedemption = ({
|
|||||||
const [order, setOrder] = useState<number>(numbering)
|
const [order, setOrder] = useState<number>(numbering)
|
||||||
const [isEditable, setIsEditable] = useState(edit)
|
const [isEditable, setIsEditable] = useState(edit)
|
||||||
const [oldData, setOldData] = useState<{ r: { id: string, title: string } | undefined, a: string | undefined, o: number } | undefined>(undefined)
|
const [oldData, setOldData] = useState<{ r: { id: string, title: string } | undefined, a: string | undefined, o: number } | undefined>(undefined)
|
||||||
|
const [error, setError] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTwitchRedemption(twitchRedemptions.find(r => r.id == redemptionId))
|
setTwitchRedemption(twitchRedemptions.find(r => r.id == redemptionId))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
function Save() {
|
function Save() {
|
||||||
// TODO: validation
|
setError(undefined)
|
||||||
if (!isNew && !id)
|
if (!isNew && !id) {
|
||||||
|
setError('Something went wrong. Refresh the page.')
|
||||||
return
|
return
|
||||||
if (!action || !twitchRedemption)
|
}
|
||||||
|
|
||||||
|
if (!action) {
|
||||||
|
setError('An action must be selected.')
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
if (!actions.some(a => a == action)) {
|
||||||
|
setError('Ensure the action selected still exists.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!twitchRedemption) {
|
||||||
|
setError('A Twitch redemption must be selected.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!twitchRedemptions.some(r => r.id == twitchRedemption.id)) {
|
||||||
|
setError('Ensure the action selected still exists.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(order)) {
|
||||||
|
setError('The order cannot be NaN.')
|
||||||
|
setOrder(0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
axios.post("/api/settings/redemptions", {
|
axios.post("/api/settings/redemptions", {
|
||||||
@ -88,6 +113,7 @@ const OBSRedemption = ({
|
|||||||
if (!oldData)
|
if (!oldData)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
setError(undefined)
|
||||||
setAction(oldData.a)
|
setAction(oldData.a)
|
||||||
setTwitchRedemption(oldData.r)
|
setTwitchRedemption(oldData.r)
|
||||||
setOrder(oldData.o)
|
setOrder(oldData.o)
|
||||||
@ -143,7 +169,8 @@ const OBSRedemption = ({
|
|||||||
value={redemption.title}
|
value={redemption.title}
|
||||||
key={redemption.id}
|
key={redemption.id}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
setTwitchRedemption(twitchRedemptions.find(v => v.title.toLowerCase() == value.toLowerCase()))
|
console.log('tr', value, redemption.id, redemption.title)
|
||||||
|
setTwitchRedemption(redemption)
|
||||||
setRedemptionOpen(false)
|
setRedemptionOpen(false)
|
||||||
}}>
|
}}>
|
||||||
{redemption.title}
|
{redemption.title}
|
||||||
@ -218,23 +245,37 @@ const OBSRedemption = ({
|
|||||||
className="inline-block w-[300px]"
|
className="inline-block w-[300px]"
|
||||||
id="name"
|
id="name"
|
||||||
placeholder="Enter an order number for this action"
|
placeholder="Enter an order number for this action"
|
||||||
onChange={e => setOrder(e.target.value.length == 0 ? 0 : parseInt(e.target.value))}
|
onChange={e => setOrder(d => {
|
||||||
|
let temp = order
|
||||||
|
const v = parseInt(e.target.value)
|
||||||
|
if (e.target.value.length == 0 || Number.isNaN(v)) {
|
||||||
|
temp = 0
|
||||||
|
} else if (!Number.isNaN(v) && Number.isSafeInteger(v)) {
|
||||||
|
temp = v
|
||||||
|
}
|
||||||
|
return temp
|
||||||
|
})}
|
||||||
value={order}
|
value={order}
|
||||||
readOnly={!isEditable} />
|
readOnly={!isEditable} />
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<HelpCircleIcon
|
<HelpCircleIcon
|
||||||
className="inline-block ml-3"/>
|
className="inline-block ml-3" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>This decides when this action will be done relative to other actions for this Twitch redemption.<br/>
|
<p>This decides when this action will be done relative to other actions for this Twitch redemption.<br />
|
||||||
Action start from lowest to highest order number.<br/>
|
Action start from lowest to highest order number.<br />
|
||||||
Equal order numbers cannot be guaranteed proper order.</p>
|
Equal order numbers cannot be guaranteed proper order.</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
|
{error &&
|
||||||
|
<div className="text-red-600 font-bold">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div>
|
<div>
|
||||||
{isEditable &&
|
{isEditable &&
|
||||||
<Button
|
<Button
|
||||||
|
@ -28,17 +28,18 @@ import RoleGate from "@/components/auth/role-gate";
|
|||||||
interface UsersGroup {
|
interface UsersGroup {
|
||||||
groupId: string
|
groupId: string
|
||||||
groupName: string
|
groupName: string
|
||||||
//userList: { id: number, username: string }[]
|
|
||||||
//knownUsers: { id: number, username: string }[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEMS_PER_PAGE: number = 10;
|
const ITEMS_PER_PAGE: number = 10;
|
||||||
|
|
||||||
|
const usernameSchema = z.string({
|
||||||
|
required_error: "Name is required.",
|
||||||
|
invalid_type_error: "Name must be a string"
|
||||||
|
}).regex(/^[\w\-]{4,25}$/, "Invalid Twitch username.")
|
||||||
|
|
||||||
const UserList = ({
|
const UserList = ({
|
||||||
groupId,
|
groupId,
|
||||||
groupName,
|
groupName
|
||||||
//userList,
|
|
||||||
//knownUsers
|
|
||||||
}: UsersGroup) => {
|
}: UsersGroup) => {
|
||||||
const [usersListOpen, setUsersListOpen] = useState(false)
|
const [usersListOpen, setUsersListOpen] = useState(false)
|
||||||
const [users, setUsers] = useState<{ id: number, username: string }[]>([])
|
const [users, setUsers] = useState<{ id: number, username: string }[]>([])
|
||||||
@ -60,7 +61,8 @@ const UserList = ({
|
|||||||
}).then(d => {
|
}).then(d => {
|
||||||
setUsers(d.data)
|
setUsers(d.data)
|
||||||
setKnownUsers(d.data)
|
setKnownUsers(d.data)
|
||||||
setMaxPages(Math.ceil(d.data.length / ITEMS_PER_PAGE))
|
var maxPages = Math.ceil(d.data.length / ITEMS_PER_PAGE)
|
||||||
|
setMaxPages(maxPages)
|
||||||
})
|
})
|
||||||
}, [groupId, page])
|
}, [groupId, page])
|
||||||
|
|
||||||
@ -69,17 +71,12 @@ const UserList = ({
|
|||||||
setUsersListOpen(false)
|
setUsersListOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const usernameSchema = z.string({
|
|
||||||
required_error: "Name is required.",
|
|
||||||
invalid_type_error: "Name must be a string"
|
|
||||||
}).regex(/^[\w\-]{4,25}$/, "Invalid Twitch username.")
|
|
||||||
|
|
||||||
function AddUsername() {
|
function AddUsername() {
|
||||||
setError(undefined)
|
setError(undefined)
|
||||||
|
|
||||||
const nameValidation = usernameSchema.safeParse(newUser)
|
const usernameValidation = usernameSchema.safeParse(newUser)
|
||||||
if (!nameValidation.success) {
|
if (!usernameValidation.success) {
|
||||||
setError(JSON.parse(nameValidation.error['message'])[0].message)
|
setError(JSON.parse(usernameValidation.error['message'])[0].message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,12 +92,16 @@ const UserList = ({
|
|||||||
logins: newUser
|
logins: newUser
|
||||||
}
|
}
|
||||||
}).then(d => {
|
}).then(d => {
|
||||||
if (!d.data)
|
if (!d.data) {
|
||||||
|
setError("That was not a known Twitch username.")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
user = d.data[0]
|
user = d.data[0]
|
||||||
if (!user)
|
if (!user) {
|
||||||
|
setError("That was not a known Twitch username.")
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (deletedUsers.find(u => u.id == user!.id))
|
if (deletedUsers.find(u => u.id == user!.id))
|
||||||
setDeletedUsers(deletedUsers.filter(u => u.id != user!.id))
|
setDeletedUsers(deletedUsers.filter(u => u.id != user!.id))
|
||||||
@ -111,7 +112,7 @@ const UserList = ({
|
|||||||
setNewUser("")
|
setNewUser("")
|
||||||
setMaxPages(Math.ceil((users.length + 1) / ITEMS_PER_PAGE))
|
setMaxPages(Math.ceil((users.length + 1) / ITEMS_PER_PAGE))
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
setError("Username does not exist.")
|
setError("That was not a known Twitch username.")
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { FaExclamationCircle } from "react-icons/fa";
|
import { FaExclamationCircle } from "react-icons/fa";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
|
||||||
interface NoticeProps {
|
interface NoticeProps {
|
||||||
|
Loading…
Reference in New Issue
Block a user