Skip to content

Commit

Permalink
let users reset their auth token + settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
quick007 committed Feb 13, 2024
1 parent 3e12d43 commit 57076ae
Show file tree
Hide file tree
Showing 14 changed files with 276 additions and 39 deletions.
8 changes: 4 additions & 4 deletions deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
// Do items like reordering imports
"fmt": "deno fmt && npx prettier . --write",
"start": "deno run -A --unstable --watch=static/,routes/ dev.ts",
"build": "deno run -A --unstable dev.ts build",
"preview": "deno run --unstable -A main.ts",
"update": "deno run --unstable -A -r https://fresh.deno.dev/update ."
"start": "deno run -A --unstable-kv --watch=static/,routes/ dev.ts",
"build": "deno run -A --unstable-kv dev.ts build",
"preview": "deno run --unstable-kv -A main.ts",
"update": "deno run --unstable-kv -A -r https://fresh.deno.dev/update ."
},
"lint": {
"rules": { "tags": ["fresh", "recommended"], "include": ["no-unused-vars"] }
Expand Down
7 changes: 5 additions & 2 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,15 @@ import * as $kv_insights_layout from "./routes/kv-insights/_layout.tsx";
import * as $kv_insights_middleware from "./routes/kv-insights/_middleware.ts";
import * as $login from "./routes/login.tsx";
import * as $user_settings_layout from "./routes/user/settings/_layout.tsx";
import * as $user_settings_authentication from "./routes/user/settings/authentication.ts";
import * as $user_settings_auth from "./routes/user/settings/auth.tsx";
import * as $user_settings_index from "./routes/user/settings/index.tsx";
import * as $components_dropinUI_trash from "./islands/components/dropinUI/trash.tsx";
import * as $components_pickers_calender from "./islands/components/pickers/calender.tsx";
import * as $components_pickers_dropdown from "./islands/components/pickers/dropdown.tsx";
import * as $components_pickers_image from "./islands/components/pickers/image.tsx";
import * as $components_pickers_select from "./islands/components/pickers/select.tsx";
import * as $components_pickers_time from "./islands/components/pickers/time.tsx";
import * as $components_pieces_deleteToken from "./islands/components/pieces/deleteToken.tsx";
import * as $components_pieces_navDropDown from "./islands/components/pieces/navDropDown.tsx";
import * as $components_pieces_ticket from "./islands/components/pieces/ticket.tsx";
import * as $entriesManagement from "./islands/entriesManagement.tsx";
Expand Down Expand Up @@ -147,7 +148,7 @@ const manifest = {
"./routes/kv-insights/_middleware.ts": $kv_insights_middleware,
"./routes/login.tsx": $login,
"./routes/user/settings/_layout.tsx": $user_settings_layout,
"./routes/user/settings/authentication.ts": $user_settings_authentication,
"./routes/user/settings/auth.tsx": $user_settings_auth,
"./routes/user/settings/index.tsx": $user_settings_index,
},
islands: {
Expand All @@ -157,6 +158,8 @@ const manifest = {
"./islands/components/pickers/image.tsx": $components_pickers_image,
"./islands/components/pickers/select.tsx": $components_pickers_select,
"./islands/components/pickers/time.tsx": $components_pickers_time,
"./islands/components/pieces/deleteToken.tsx":
$components_pieces_deleteToken,
"./islands/components/pieces/navDropDown.tsx":
$components_pieces_navDropDown,
"./islands/components/pieces/ticket.tsx": $components_pieces_ticket,
Expand Down
21 changes: 21 additions & 0 deletions islands/components/pieces/deleteToken.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import CTA from "@/components/buttons/cta.tsx";
import Deletion from "@/islands/events/components/delete.tsx";
import { useSignal } from "@preact/signals";

const DeleteToken = () => {
const open = useSignal(false);

return (
<Deletion
name="authentication token"
fetch={() => fetch("/api/auth/regen")}
open={open}
routeTo="/"
customMsg="Resetting your authentication token will log you out of all devices. Please proceed with caution!"
>
<CTA btnType="secondary" onClick={() => open.value = true} btnSize="sm">Delete Token</CTA>
</Deletion>
);
};

export default DeleteToken;
15 changes: 10 additions & 5 deletions islands/events/components/delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,32 @@ const Deletion = ({
open,
routeTo,
children,
customMsg,
}: {
fetch: () => Promise<Response>;
routeTo: string;
routeTo?: string;
name: string;
open: Signal<boolean>;
children: ComponentChildren;
customMsg?: string;
}) => {
const [loading, setLoading] = useState<boolean | string>(false);

const deleteEvent = async () => {
setLoading(true);
setLoading(false);

const res = await fetch();
const data = await res.json();
if (data.error) {
setLoading(data.error);
} else if (!res.ok) {
setLoading("An unknown error occurred");
} else {
window.location.href = routeTo;
if (routeTo) {
location.href = routeTo;
}
}
setLoading(false);
};

const DeleteUI = () => {
Expand All @@ -44,8 +49,8 @@ const Deletion = ({
Delete <span class="capitalize">{name}</span>
</h5>
<p>
Are you sure you want to delete this {name}? This action is
irrevocable and cannot be undone!
{customMsg || `Are you sure you want to delete this ${name}? This action is
irrevocable and cannot be undone!`}
</p>
<CTA
btnType="secondary"
Expand Down
14 changes: 10 additions & 4 deletions islands/events/viewing/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { useSignal } from "@preact/signals";
import { Toggle } from "@/components/buttons/toggle.tsx";
import Minus from "$tabler/minus.tsx";
import Plus from "$tabler/plus.tsx";
import { fmtDate, fmtHour, fmtTime } from "@/utils/dates.ts";
import Button from "@/components/buttons/button.tsx";
import ChevronLeft from "$tabler/chevron-left.tsx";
import { createPortal } from "preact/compat";
Expand All @@ -18,7 +17,7 @@ import { acquired, getTicketID } from "@/utils/tickets.ts";
import { EventRegisterError } from "@/utils/event/register.ts";
import { RegisterErrors } from "../components/registerErrors.tsx";
import SelectShowTime from "./selectShowTime.tsx";
import { router } from "$fresh/src/server/router.ts";
import { isUUID } from "@/utils/db/misc.ts";

export default function EventRegister({
eventID,
Expand Down Expand Up @@ -155,13 +154,20 @@ export default function EventRegister({
lastName: string;
email: string;
}) => {
const formStates: { id: string, value: unknown }[] = [];

for (const [key, value] of [...Object.entries(toggles.value), ...Object.entries(formState)]) {
if (isUUID(key)) {
formStates.push({ id: key, value });
}
}

const fullTicket = {
...formState,
...toggles.value,
tickets: tickets.value,
showtimeID: showTime.value,
eventID,
fieldData: [],
fieldData: formStates,
};
error.value = undefined;
ticketID.value = "loading";
Expand Down
14 changes: 9 additions & 5 deletions routes/api/auth/regen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@ export const handler: Handlers = {
const user = await getUser(req);

if (user == undefined) {
deleteCookie(req.headers, "authToken");
return new Response(JSON.stringify({ error: "User not found" }), {
const resp = new Response(JSON.stringify({ error: "User not found" }), {
status: 400,
});

deleteCookie(resp.headers, "authToken");
return resp;
}

await generateAuthToken(user.email, true);

deleteCookie(req.headers, "authToken");

return new Response(JSON.stringify({ status: 200 }), {
const resp = new Response(JSON.stringify({ status: 200 }), {
status: 200,
});

deleteCookie(resp.headers, "authToken");

return resp;
},
};
4 changes: 3 additions & 1 deletion routes/api/events/scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ export const handler: Handlers = {

const isUsed = ticket.value.hasBeenUsed;

if (!isUsed) {
if (!isUsed || ticket.value.tickets > 0) {
ticket.value.hasBeenUsed = true;
ticket.value.uses += 1;

await kv.set(
[
"ticket",
Expand Down
136 changes: 132 additions & 4 deletions routes/api/events/ticket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,23 @@ import { EventRegisterError } from "@/utils/event/register.ts";

export const handler: Handlers = {
async POST(req) {
const { eventID, email, showtimeID, fieldData, firstName, lastName } =
await req.json();
const {
eventID,
email,
showtimeID,
fieldData,
firstName,
lastName,
tickets,
}: {
eventID: string;
email: string;
showtimeID: string;
fieldData: { id: string; value: string }[];
firstName: string;
lastName: string;
tickets: number;
} = await req.json();

const basicParamValidation = Yup.object({
eventID: Yup.string().uuid().required(),
Expand All @@ -23,6 +38,7 @@ export const handler: Handlers = {
firstName: Yup.string().required().min(1),
lastName: Yup.string().required().min(1),
fieldData: Yup.array().required(),
tickets: Yup.number().required().min(1).max(10),
});

try {
Expand All @@ -34,6 +50,7 @@ export const handler: Handlers = {
firstName,
lastName,
fieldData,
tickets,
},
{
strict: true,
Expand Down Expand Up @@ -66,6 +83,114 @@ export const handler: Handlers = {
);
}

if (
fieldData.length != event.value.additionalFields.length ||
fieldData
.map((f) => f.id)
.some(
(id) => !event.value.additionalFields.map((f) => f.id).includes(id),
)
) {
return new Response(
JSON.stringify({
error: {
message: "Invalid field data",
code: EventRegisterError.OTHER,
},
}),
{
status: 400,
},
);
}

for (const field of event.value.additionalFields) {
if (
fieldData.find((f) => f.id === field.id)?.value == undefined &&
(field.required ?? true) == true
) {
return new Response(
JSON.stringify({
error: {
message: "Missing required field",
code: EventRegisterError.OTHER,
},
}),
{
status: 400,
},
);
}

if (fieldData.find((f) => f.id === field.id)?.value == undefined) {
continue;
}

const value = fieldData.find((f) => f.id === field.id);

const defaultYupSchema = {
id: Yup.string().uuid().required(),
};

let schema = Yup.object({
...defaultYupSchema,
});

switch (field.type) {
case "text": {
schema = Yup.object({
...defaultYupSchema,
value: Yup.string().required(),
});

break;
}

case "toggle": {
schema = Yup.object({
...defaultYupSchema,
value: Yup.boolean().nonNullable(),
});

break;
}

case "email": {
schema = Yup.object({
...defaultYupSchema,
value: Yup.string().email().required(),
});

break;
}

case "number": {
schema = Yup.object({
...defaultYupSchema,
value: Yup.number().required(),
});

break;
}
}

try {
schema.validateSync(value, { strict: true });
} catch (e) {
return new Response(
JSON.stringify({
error: {
message: e.message,
code: EventRegisterError.OTHER,
},
}),
{
status: 400,
},
);
}
}

const showtime = event.value.showTimes.find((s) => s.id === showtimeID);

if (showtime == undefined) {
Expand Down Expand Up @@ -119,14 +244,15 @@ export const handler: Handlers = {
const user =
eventUser.value ??
({
onboarded: false,
tickets: [],
events: [],
plan: Plan.BASIC,
email,
joinedAt: Date.now().toString(),
authToken: "unregistered",
} satisfies User);

if (user.onboarded) {
if (user.authToken != "unregistered") {
const authToken = getUserAuthToken(req);

if (authToken != user.authToken) {
Expand Down Expand Up @@ -219,6 +345,8 @@ export const handler: Handlers = {
firstName,
lastName,
fieldData: fieldData,
tickets,
uses: 0,
})
.commit();

Expand Down
Loading

0 comments on commit 57076ae

Please sign in to comment.