Fix: Finished Backend
This commit is contained in:
parent
6b388729d9
commit
ffaf8d989e
30 changed files with 1478 additions and 873 deletions
73
src/routes/api/[guildId]/config.ts
Normal file
73
src/routes/api/[guildId]/config.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
import { BasicAuth } from "~/lib/auth";
|
||||
import { buildConfig } from "~/lib/responseBuilders";
|
||||
import { ErrorResponse, Res } from "~/lib/responses";
|
||||
import { zodId } from "~/lib/zod";
|
||||
import { APIResponse } from "~/types/backend";
|
||||
|
||||
type Path = "/api/{guildId}/config";
|
||||
|
||||
export const GET = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "get">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
try {
|
||||
zodId.parse(event.params.guildId);
|
||||
} catch (e) {
|
||||
return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
|
||||
}
|
||||
|
||||
const guildQuery = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: { tpMessages: true, matches: true },
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guildQuery) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
return Res("OK", buildConfig(guildQuery));
|
||||
};
|
||||
|
||||
export const DELETE = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "delete">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
try {
|
||||
zodId.parse(event.params.guildId);
|
||||
} catch (e) {
|
||||
return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
|
||||
}
|
||||
|
||||
const guildQuery = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: { tpMessages: true, matches: true },
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guildQuery) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
await db.delete(guilds).where(eq(guilds.id, event.params.guildId)).execute();
|
||||
|
||||
return Res("NO_CONTENT", null);
|
||||
};
|
93
src/routes/api/[guildId]/matches.ts
Normal file
93
src/routes/api/[guildId]/matches.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds, matches } from "~/drizzle/schema";
|
||||
import { BasicAuth } from "~/lib/auth";
|
||||
import { buildMatches } from "~/lib/responseBuilders";
|
||||
import { ErrorResponse, Res } from "~/lib/responses";
|
||||
import { zodMatch } from "~/lib/zod";
|
||||
import { APIResponse, RequestBody } from "~/types/backend";
|
||||
|
||||
type Path = "/api/{guildId}/matches";
|
||||
|
||||
export const GET = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "get">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: {
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
console.log(event.params.guildId, guild);
|
||||
|
||||
if (!guild) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
if (guild.matches.length < 1) return Res("NO_CONTENT", null);
|
||||
|
||||
return Res("OK", {
|
||||
matches: buildMatches(guild.matches),
|
||||
timezone: guild.timezone,
|
||||
});
|
||||
};
|
||||
|
||||
export const POST = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "post">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: {
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
console.log(event.params.guildId, guild);
|
||||
|
||||
if (!guild) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
const unparsedBody = await new Response(event.request.body).json();
|
||||
|
||||
let body: RequestBody<Path, "post">;
|
||||
try {
|
||||
body = zodMatch.parse(unparsedBody);
|
||||
} catch (e) {
|
||||
return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
|
||||
}
|
||||
|
||||
if (body.timezone !== guild.timezone)
|
||||
return ErrorResponse(
|
||||
"BAD_REQUEST",
|
||||
"Match's timezone is different from guild's timezone",
|
||||
);
|
||||
|
||||
await db.insert(matches).values({
|
||||
...body.match,
|
||||
guildId: guild.id,
|
||||
utc_ts: new Date(body.match.utc_ts),
|
||||
});
|
||||
|
||||
return Res("NO_CONTENT", null);
|
||||
};
|
114
src/routes/api/[guildId]/tp_messages.ts
Normal file
114
src/routes/api/[guildId]/tp_messages.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds, tpMessages } from "~/drizzle/schema";
|
||||
import { BasicAuth } from "~/lib/auth";
|
||||
import { ErrorResponse, Res } from "~/lib/responses";
|
||||
import { zodTpMessages } from "~/lib/zod";
|
||||
import { APIResponse, RequestBody } from "~/types/backend";
|
||||
|
||||
type Path = "/api/{guildId}/tp_messages";
|
||||
|
||||
const DayKeys = ["0", "1", "2", "3", "4", "5", "6"] as const;
|
||||
type DayKeys = (typeof DayKeys)[number];
|
||||
type Messages = Record<DayKeys, string | null>;
|
||||
|
||||
export const GET = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "get">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
const guild = await db.query.guilds.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: {
|
||||
tpMessages: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!guild) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
if (!guild.tpEnabled || !guild.tpChannelId) return Res("NO_CONTENT", null);
|
||||
|
||||
const tpMessages = guild.tpMessages.reduce(
|
||||
(acc, message) => {
|
||||
const day = message.day.toString() as DayKeys;
|
||||
if (!/^[0-6]$/.test(day)) return acc;
|
||||
acc[day] = message.messageId;
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
"0": null,
|
||||
"1": null,
|
||||
"2": null,
|
||||
"3": null,
|
||||
"4": null,
|
||||
"5": null,
|
||||
"6": null,
|
||||
} as Messages,
|
||||
);
|
||||
|
||||
return Res("OK", {
|
||||
channelId: guild.tpChannelId,
|
||||
messageIds: tpMessages,
|
||||
});
|
||||
};
|
||||
|
||||
export const PUT = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "put">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: { tpMessages: true },
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild) return ErrorResponse("NOT_FOUND");
|
||||
|
||||
if (!guild.tpEnabled) return ErrorResponse("FORBIDDEN");
|
||||
|
||||
const unparsedBody = await new Response(event.request.body).json();
|
||||
|
||||
let body: RequestBody<Path, "put">;
|
||||
try {
|
||||
body = zodTpMessages.parse(unparsedBody);
|
||||
} catch (e) {
|
||||
return ErrorResponse("BAD_REQUEST", JSON.stringify(e));
|
||||
}
|
||||
|
||||
if (guild.tpChannelId !== body.channelId)
|
||||
await db
|
||||
.update(guilds)
|
||||
.set({ tpChannelId: body.channelId })
|
||||
.where(eq(guilds.id, guild.id))
|
||||
.execute();
|
||||
|
||||
await Promise.all(
|
||||
DayKeys.map(async (dayStr) => {
|
||||
const day = parseInt(dayStr);
|
||||
await db
|
||||
.update(tpMessages)
|
||||
.set({ messageId: body.messageIds[dayStr] })
|
||||
.where(and(eq(tpMessages.guildId, guild.id), eq(tpMessages.day, day)))
|
||||
.execute();
|
||||
}),
|
||||
);
|
||||
|
||||
return Res("NO_CONTENT", null);
|
||||
};
|
|
@ -2,6 +2,7 @@ import { createId } from "@paralleldrive/cuid2";
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { OAuth2RequestError } from "arctic";
|
||||
import { eq } from "drizzle-orm";
|
||||
import httpStatus from "http-status";
|
||||
import createClient from "openapi-fetch";
|
||||
import { getCookie, setCookie } from "vinxi/http";
|
||||
import db from "~/drizzle";
|
||||
|
@ -20,20 +21,20 @@ export async function GET(event: APIEvent): Promise<Response> {
|
|||
switch (error) {
|
||||
case "access_denied":
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
status: httpStatus.FOUND,
|
||||
headers: { Location: "/" },
|
||||
});
|
||||
default:
|
||||
console.log("Discord oauth error:", error_description);
|
||||
return new Response(decodeURI(error_description ?? ""), {
|
||||
status: 400,
|
||||
status: httpStatus.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
const storedState = getCookie("discord_oauth_state") ?? null;
|
||||
if (!code || !state || !storedState || state !== storedState) {
|
||||
return new Response(null, {
|
||||
status: 400,
|
||||
status: httpStatus.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -77,7 +78,7 @@ export async function GET(event: APIEvent): Promise<Response> {
|
|||
.execute();
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
status: httpStatus.FOUND,
|
||||
headers: { Location: "/config" },
|
||||
});
|
||||
}
|
||||
|
@ -112,7 +113,7 @@ export async function GET(event: APIEvent): Promise<Response> {
|
|||
sessionCookie.attributes,
|
||||
);
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
status: httpStatus.FOUND,
|
||||
headers: { Location: "/config" },
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -120,13 +121,13 @@ export async function GET(event: APIEvent): Promise<Response> {
|
|||
if (e instanceof OAuth2RequestError) {
|
||||
// invalid code
|
||||
return new Response(null, {
|
||||
status: 400,
|
||||
status: httpStatus.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
console.error("Unknown error on callback.");
|
||||
console.error(e);
|
||||
return new Response(null, {
|
||||
status: 500,
|
||||
status: httpStatus.INTERNAL_SERVER_ERROR,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { generateState } from "arctic";
|
||||
import httpStatus from "http-status";
|
||||
import { setCookie } from "vinxi/http";
|
||||
import { discord } from "~/lib/auth";
|
||||
|
||||
|
@ -18,7 +19,7 @@ export async function GET(event: APIEvent) {
|
|||
});
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
status: httpStatus.FOUND,
|
||||
headers: { Location: url.toString() },
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import httpStatus from "http-status";
|
||||
import { appendHeader } from "vinxi/http";
|
||||
import { lucia } from "~/lib/auth";
|
||||
|
||||
|
@ -13,7 +14,7 @@ export const GET = async (event: APIEvent) => {
|
|||
lucia.createBlankSessionCookie().serialize(),
|
||||
);
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
status: httpStatus.FOUND,
|
||||
headers: { Location: "/" },
|
||||
});
|
||||
};
|
||||
|
|
35
src/routes/api/boot.ts
Normal file
35
src/routes/api/boot.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
import { BasicAuth } from "~/lib/auth";
|
||||
import { buildConfig } from "~/lib/responseBuilders";
|
||||
import { ErrorResponse, Res } from "~/lib/responses";
|
||||
import { APIResponse } from "~/types/backend";
|
||||
|
||||
type Path = "/api/boot";
|
||||
|
||||
export const GET = async (
|
||||
event: APIEvent,
|
||||
): Promise<APIResponse<Path, "get">> => {
|
||||
switch (event.request.headers.get("authorization")) {
|
||||
case BasicAuth.unencoded:
|
||||
case BasicAuth.encoded:
|
||||
break;
|
||||
|
||||
default:
|
||||
return ErrorResponse("UNAUTHORIZED");
|
||||
}
|
||||
|
||||
const guildQuery = await db.query.guilds
|
||||
.findMany({
|
||||
where: eq(guilds.id, event.params.guildId),
|
||||
with: { tpMessages: true, matches: true },
|
||||
})
|
||||
.execute();
|
||||
|
||||
return Res(
|
||||
"OK",
|
||||
guildQuery.map((e) => buildConfig(e)),
|
||||
);
|
||||
};
|
|
@ -1,61 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
|
||||
export const GET = async ({ params }: APIEvent) => {
|
||||
if (params.guildId === "boot") {
|
||||
const guilds = await db.query.guilds
|
||||
.findMany({
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
return { guilds };
|
||||
}
|
||||
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
return guild;
|
||||
};
|
||||
|
||||
export const DELETE = async ({ params }: APIEvent) => {
|
||||
const guildQuery = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guildQuery)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
const guild = await db
|
||||
.delete(guilds)
|
||||
.where(eq(guilds.id, params.guildId))
|
||||
.returning()
|
||||
.execute();
|
||||
|
||||
return guild;
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
|
||||
export const PUT = async ({ params }: APIEvent) => {
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
return "TODO";
|
||||
// return guild.timePlanning;
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
|
||||
export const POST = async ({ params }: APIEvent) => {
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
return "TODO";
|
||||
// return guild.timePlanning;
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
|
||||
export const GET = async ({ params }: APIEvent) => {
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
return "TODO";
|
||||
// return guild.timePlanning;
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
import { APIEvent } from "@solidjs/start/server/types";
|
||||
import { eq } from "drizzle-orm";
|
||||
import db from "~/drizzle";
|
||||
import { guilds } from "~/drizzle/schema";
|
||||
|
||||
export const GET = async ({ params }: APIEvent) => {
|
||||
const guild = await db.query.guilds
|
||||
.findFirst({
|
||||
where: eq(guilds.id, params.guildId),
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (!guild)
|
||||
return new Response(JSON.stringify({ error: "No such guild found." }), {
|
||||
status: 404,
|
||||
});
|
||||
|
||||
return "TODO";
|
||||
// return guild.timePlanning;
|
||||
};
|
||||
|
||||
export const PUT = async () => {
|
||||
return "TODO";
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
import db from "~/drizzle";
|
||||
|
||||
export const GET = async () => {
|
||||
const guilds = await db.query.guilds
|
||||
.findMany({
|
||||
with: {
|
||||
timePlanning: { with: { messages: true } },
|
||||
matches: true,
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
|
||||
return { guilds };
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue