Skip to content

Commit

Permalink
Merge branch 'main' of github.com:kubk/memo-card
Browse files Browse the repository at this point in the history
  • Loading branch information
kubk committed Jan 7, 2024
2 parents 09d5281 + 3ef3cc8 commit d56838f
Show file tree
Hide file tree
Showing 52 changed files with 1,528 additions and 277 deletions.
36 changes: 36 additions & 0 deletions functions/db/databaseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface Database {
deck_id: number
duration_days: number | null
id: number
processed_at: string | null
share_id: string
usage_started_at: string | null
used_by: number | null
Expand All @@ -121,6 +122,7 @@ export interface Database {
deck_id: number
duration_days?: number | null
id?: number
processed_at?: string | null
share_id: string
usage_started_at?: string | null
used_by?: number | null
Expand All @@ -131,6 +133,7 @@ export interface Database {
deck_id?: number
duration_days?: number | null
id?: number
processed_at?: string | null
share_id?: string
usage_started_at?: string | null
used_by?: number | null
Expand Down Expand Up @@ -249,18 +252,21 @@ export interface Database {
Row: {
author_id: number
created_at: string
description: string | null
id: number
title: string
}
Insert: {
author_id: number
created_at?: string
description?: string | null
id?: number
title: string
}
Update: {
author_id?: number
created_at?: string
description?: string | null
id?: number
title?: string
}
Expand Down Expand Up @@ -370,6 +376,34 @@ export interface Database {
}
]
}
user_features: {
Row: {
advanced_share: boolean
created_at: string
id: number
user_id: number
}
Insert: {
advanced_share?: boolean
created_at?: string
id?: number
user_id: number
}
Update: {
advanced_share?: boolean
created_at?: string
id?: number
user_id?: number
}
Relationships: [
{
foreignKeyName: "user_features_user_id_fkey"
columns: ["user_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
user_folder: {
Row: {
created_at: string
Expand Down Expand Up @@ -441,6 +475,8 @@ export interface Database {
Returns: {
folder_id: number
folder_title: string
folder_description: string
folder_author_id: number
deck_id: number
}[]
}
Expand Down
19 changes: 19 additions & 0 deletions functions/db/folder/delete-folder-by-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";

export const deleteFolderById = async (
env: EnvSafe,
folderId: number,
): Promise<void> => {
const db = getDatabase(env);
const deleteFolderResult = await db
.from("folder")
.delete()
.eq("id", folderId)
.single();

if (deleteFolderResult.error) {
throw new DatabaseException(deleteFolderResult.error);
}
};
23 changes: 23 additions & 0 deletions functions/db/folder/get-folder-by-id-and-author-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";

export const getFolderByIdAndAuthorId = async (
envSafe: EnvSafe,
folderId: number,
user: { id: number; is_admin: boolean },
) => {
const db = getDatabase(envSafe);

let query = db.from("folder").select().eq("id", folderId);
if (!user.is_admin) {
query = query.eq("author_id", user.id);
}

const canEditResult = await query.single();
if (canEditResult.error) {
throw new DatabaseException(canEditResult.error);
}

return canEditResult.data ?? null;
};
28 changes: 28 additions & 0 deletions functions/db/folder/get-folders-with-decks-db.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";
import { z } from "zod";

const userFoldersSchema = z.object({
folder_id: z.number(),
folder_title: z.string(),
folder_description: z.string().nullable(),
folder_author_id: z.number(),
deck_id: z.number().nullable(),
});

export type UserFoldersDbType = z.infer<typeof userFoldersSchema>;

export const getFoldersWithDecksDb = async (env: EnvSafe, userId: number) => {
const db = getDatabase(env);

const result = await db.rpc("get_folder_with_decks", {
usr_id: userId,
});

if (result.error) {
throw new DatabaseException(result.error);
}

return z.array(userFoldersSchema).parse(result.data);
};
21 changes: 21 additions & 0 deletions functions/decks-mine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { handleError } from "./lib/handle-error/handle-error.ts";
import { getUser } from "./services/get-user.ts";
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
import { envSchema } from "./env/env-schema.ts";
import { getDecksCreatedByMe } from "./db/deck/get-decks-created-by-me.ts";
import { DeckWithoutCardsDbType } from "./db/deck/decks-with-cards-schema.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";

export type DecksMineResponse = {
decks: DeckWithoutCardsDbType[];
};

export const onRequest = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();
const envSafe = envSchema.parse(env);

const decks = await getDecksCreatedByMe(envSafe, user.id);

return createJsonResponse<DecksMineResponse>({ decks: decks });
});
35 changes: 35 additions & 0 deletions functions/delete-folder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { handleError } from "./lib/handle-error/handle-error.ts";
import { getUser } from "./services/get-user.ts";
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
import { envSchema } from "./env/env-schema.ts";
import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts";
import { getFolderByIdAndAuthorId } from "./db/folder/get-folder-by-id-and-author-id.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
import { deleteFolderById } from "./db/folder/delete-folder-by-id.ts";

export const onRequestPost = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) {
return createAuthFailedResponse();
}
const envSafe = envSchema.parse(env);

const url = new URL(request.url);
const folderId = url.searchParams.get("folder_id");
if (!folderId) {
return createBadRequestResponse();
}

const canEdit = await getFolderByIdAndAuthorId(
envSafe,
parseInt(folderId),
user,
);
if (!canEdit) {
return createBadRequestResponse();
}

await deleteFolderById(envSafe, parseInt(folderId));

return createJsonResponse(null);
});
9 changes: 8 additions & 1 deletion functions/my-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,36 @@ import {
getCardsToReviewDb,
} from "./db/deck/get-cards-to-review-db.ts";
import { getUnAddedPublicDecksDb } from "./db/deck/get-un-added-public-decks-db.ts";
import {
getFoldersWithDecksDb,
UserFoldersDbType,
} from "./db/folder/get-folders-with-decks-db.tsx";

export type MyInfoResponse = {
user: UserDbType;
myDecks: DeckWithCardsDbType[];
publicDecks: DeckWithCardsDbType[];
cardsToReview: CardToReviewDbType[];
folders: UserFoldersDbType[];
};

export const onRequest = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();
const envSafe = envSchema.parse(env);

const [publicDecks, myDecks, cardsToReview] = await Promise.all([
const [publicDecks, myDecks, cardsToReview, folders] = await Promise.all([
await getUnAddedPublicDecksDb(envSafe, user.id),
await getMyDecksWithCardsDb(envSafe, user.id),
await getCardsToReviewDb(envSafe, user.id),
await getFoldersWithDecksDb(envSafe, user.id),
]);

return createJsonResponse<MyInfoResponse>({
user,
publicDecks,
myDecks,
cardsToReview,
folders,
});
});
92 changes: 92 additions & 0 deletions functions/upsert-folder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { handleError } from "./lib/handle-error/handle-error.ts";
import { getUser } from "./services/get-user.ts";
import { createAuthFailedResponse } from "./lib/json-response/create-auth-failed-response.ts";
import { z } from "zod";
import { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts";
import { getDatabase } from "./db/get-database.ts";
import { envSchema } from "./env/env-schema.ts";
import { DatabaseException } from "./db/database-exception.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
import {
getFoldersWithDecksDb,
UserFoldersDbType,
} from "./db/folder/get-folders-with-decks-db.tsx";
import { getFolderByIdAndAuthorId } from "./db/folder/get-folder-by-id-and-author-id.ts";

const requestSchema = z.object({
id: z.number().optional(),
title: z.string(),
description: z.string().nullable(),
deckIds: z.array(z.number()),
});

export type AddFolderRequest = z.infer<typeof requestSchema>;
export type AddFolderResponse = {
folder: {
id: number;
}
folders: UserFoldersDbType[];
};

export const onRequestPost = handleError(async ({ request, env }) => {
const user = await getUser(request, env);
if (!user) return createAuthFailedResponse();

const input = requestSchema.safeParse(await request.json());
if (!input.success) {
return createBadRequestResponse();
}

const envSafe = envSchema.parse(env);

const { data } = input;
if (data.id) {
const canEdit = await getFolderByIdAndAuthorId(envSafe, data.id, user);
if (!canEdit) {
return createBadRequestResponse();
}
}

const db = getDatabase(envSafe);

const upsertFolderResult = await db
.from("folder")
.upsert({
id: data.id,
title: data.title,
description: data.description,
author_id: user.id,
})
.select()
.single();

if (upsertFolderResult.error) {
throw new DatabaseException(upsertFolderResult.error);
}

const folderId = upsertFolderResult.data.id;

const oldDeckFolderResult = await db.from("deck_folder").delete().match({
folder_id: folderId,
});

if (oldDeckFolderResult.error) {
throw new DatabaseException(oldDeckFolderResult.error);
}

const upsertDeckFolderResult = await db.from("deck_folder").upsert(
data.deckIds.map((deckId) => ({
deck_id: deckId,
folder_id: folderId,
})),
);

if (upsertDeckFolderResult.error) {
throw new DatabaseException(upsertDeckFolderResult.error);
}

return createJsonResponse<AddFolderResponse>({
folder: upsertFolderResult.data,
folders: await getFoldersWithDecksDb(envSafe, user.id),
});
});
Loading

0 comments on commit d56838f

Please sign in to comment.