Skip to content

Commit

Permalink
Folder sharing
Browse files Browse the repository at this point in the history
  • Loading branch information
kubk committed Jan 10, 2024
1 parent cca50a9 commit fed588b
Show file tree
Hide file tree
Showing 40 changed files with 559 additions and 179 deletions.
32 changes: 22 additions & 10 deletions functions/add-deck-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import { envSchema } from "./env/env-schema.ts";
import { createForbiddenRequestResponse } from "./lib/json-response/create-forbidden-request-response.ts";
import { createJsonResponse } from "./lib/json-response/create-json-response.ts";
import { createDeckAccessDb } from "./db/deck-access/create-deck-access-db.ts";
import { getFolderByIdAndAuthorId } from "./db/folder/get-folder-by-id-and-author-id.ts";

const requestSchema = z.object({
deckId: z.number(),
deckId: z.number().nullable(),
folderId: z.number().nullable(),
durationDays: z.number().nullable(),
type: z.enum(["deck", "folder"]),
});

const responseSchema = z.object({
Expand All @@ -30,20 +33,29 @@ export const onRequestPost = handleError(async ({ request, env }) => {
}

const envSafe = envSchema.parse(env);
const canEdit = await getDeckByIdAndAuthorId(
envSafe,
input.data.deckId,
user,
);
if (!canEdit) {
return createForbiddenRequestResponse();
if (input.data.deckId) {
if (!(await getDeckByIdAndAuthorId(envSafe, input.data.deckId, user))) {
return createForbiddenRequestResponse();
}
}
if (input.data.folderId) {
if (!(await getFolderByIdAndAuthorId(envSafe, input.data.folderId, user))) {
return createForbiddenRequestResponse();
}
}

// Only 1 option from deckId or folderId should be provided
if (
(input.data.folderId && input.data.deckId) ||
(!input.data.folderId && !input.data.deckId)
) {
return createBadRequestResponse();
}

const createDeckAccessResult = await createDeckAccessDb(
envSafe,
user.id,
input.data.deckId,
input.data.durationDays,
input.data,
);

return createJsonResponse<AddDeckAccessResponse>(
Expand Down
3 changes: 3 additions & 0 deletions functions/db/custom-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Database } from "./databaseTypes.ts";

export type DeckAccessType = Database["public"]["Enums"]["deck_access_type"];
24 changes: 20 additions & 4 deletions functions/db/databaseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,39 @@ export interface Database {
Row: {
author_id: number
created_at: string
deck_id: number
deck_id: number | null
duration_days: number | null
folder_id: number | null
id: number
processed_at: string | null
share_id: string
type: Database["public"]["Enums"]["deck_access_type"]
usage_started_at: string | null
used_by: number | null
}
Insert: {
author_id: number
created_at?: string
deck_id: number
deck_id?: number | null
duration_days?: number | null
folder_id?: number | null
id?: number
processed_at?: string | null
share_id: string
type?: Database["public"]["Enums"]["deck_access_type"]
usage_started_at?: string | null
used_by?: number | null
}
Update: {
author_id?: number
created_at?: string
deck_id?: number
deck_id?: number | null
duration_days?: number | null
folder_id?: number | null
id?: number
processed_at?: string | null
share_id?: string
type?: Database["public"]["Enums"]["deck_access_type"]
usage_started_at?: string | null
used_by?: number | null
}
Expand All @@ -151,6 +157,12 @@ export interface Database {
referencedRelation: "deck"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_access_folder_id_fkey"
columns: ["folder_id"]
referencedRelation: "folder"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_access_used_by_fkey"
columns: ["used_by"]
Expand Down Expand Up @@ -254,20 +266,23 @@ export interface Database {
created_at: string
description: string | null
id: number
share_id: string
title: string
}
Insert: {
author_id: number
created_at?: string
description?: string | null
id?: number
share_id: string
title: string
}
Update: {
author_id?: number
created_at?: string
description?: string | null
id?: number
share_id?: string
title?: string
}
Relationships: [
Expand Down Expand Up @@ -476,6 +491,7 @@ export interface Database {
folder_id: number
folder_title: string
folder_description: string
folder_share_id: string
folder_author_id: number
deck_id: number
}[]
Expand Down Expand Up @@ -547,7 +563,7 @@ export interface Database {
}
}
Enums: {
[_ in never]: never
deck_access_type: "deck" | "folder"
}
CompositeTypes: {
[_ in never]: never
Expand Down
15 changes: 11 additions & 4 deletions functions/db/deck-access/create-deck-access-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,29 @@ import { EnvSafe } from "../../env/env-schema.ts";
import { shortUniqueId } from "../../lib/short-unique-id/short-unique-id.ts";
import { DatabaseException } from "../database-exception.ts";
import { getDatabase } from "../get-database.ts";
import { DeckAccessType } from "../custom-types.ts";

export const createDeckAccessDb = async (
envSafe: EnvSafe,
userId: number,
deckId: number,
durationDays: number | null,
body: {
deckId: number | null;
folderId: number | null;
durationDays: number | null;
type: DeckAccessType;
},
) => {
const db = getDatabase(envSafe);

const createDeckAccessResult = await db
.from("deck_access")
.insert({
deck_id: deckId,
deck_id: body.deckId,
folder_id: body.folderId,
author_id: userId,
share_id: shortUniqueId(),
duration_days: durationDays,
duration_days: body.durationDays,
type: body.type,
})
.select()
.single();
Expand Down
6 changes: 4 additions & 2 deletions functions/db/deck-access/get-deck-access-by-share-id-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { EnvSafe } from "../../env/env-schema.ts";
import { z } from "zod";

const resultSchema = z.object({
deck_id: z.number(),
deck_id: z.number().nullable(),
folder_id: z.number().nullable(),
author_id: z.number(),
used_by: z.number().nullable(),
processed_at: z.string().nullable(),
type: z.enum(["folder", "deck"]),
});

type GetDeckAccessByShareIdDbResultType = z.infer<typeof resultSchema>;
Expand All @@ -20,7 +22,7 @@ export const getDeckAccessByShareIdDb = async (

const oneTimeShareLinkResult = await db
.from("deck_access")
.select("deck_id, author_id, used_by, processed_at")
.select("deck_id, author_id, used_by, processed_at, folder_id, type")
.eq("share_id", shareId)
.maybeSingle();

Expand Down
13 changes: 10 additions & 3 deletions functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,26 @@ export type DeckAccessesForDeckTypeDb = z.infer<typeof responseSchema>;

export const getLastDeckAccessesForDeckDb = async (
envSafe: EnvSafe,
deckId: number,
filters: { deckId: number } | { folderId: number },
): Promise<DeckAccessesForDeckTypeDb> => {
const db = getDatabase(envSafe);

const { data, error } = await db
const query = db
.from("deck_access")
.select(
"deck_id, author_id, used_by, share_id, id, created_at, duration_days, user:used_by (id, username, first_name, last_name)",
)
.eq("deck_id", deckId)
.order("created_at", { ascending: false })
.limit(100);

if ("deckId" in filters) {
query.eq("deck_id", filters.deckId);
}
if ("folderId" in filters) {
query.eq("folder_id", filters.folderId);
}

const { data, error } = await query;
if (error) {
throw new DatabaseException(error);
}
Expand Down
2 changes: 1 addition & 1 deletion functions/db/deck/get-deck-by-id-and-author-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getDeckByIdAndAuthorId = async (
query = query.eq("author_id", user.id);
}

const canEditDeckResult = await query.single();
const canEditDeckResult = await query.maybeSingle();
if (canEditDeckResult.error) {
throw new DatabaseException(canEditDeckResult.error);
}
Expand Down
11 changes: 7 additions & 4 deletions functions/db/deck/get-deck-with-cards-by-share-id-db.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { DatabaseException } from "../database-exception.ts";
import { deckWithCardsSchema } from "./decks-with-cards-schema.ts";
import {
DeckWithCardsDbType,
deckWithCardsSchema,
} from "./decks-with-cards-schema.ts";
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";

export const getDeckWithCardsByShareIdDb = async (
env: EnvSafe,
shareId: string,
) => {
): Promise<DeckWithCardsDbType | null> => {
const db = getDatabase(env);

const { data, error } = await db
.from("deck")
.select("*, deck_card!deck_card_deck_id_fkey(*)")
.eq("share_id", shareId)
.limit(1)
.single();
.maybeSingle();

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

return deckWithCardsSchema.parse(data);
return data ? deckWithCardsSchema.parse(data) : null;
};
26 changes: 26 additions & 0 deletions functions/db/deck/get-many-decks-with-cards-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DatabaseException } from "../database-exception.ts";
import {
decksWithCardsSchema,
DeckWithCardsDbType,
} from "./decks-with-cards-schema.ts";
import { getDatabase } from "../get-database.ts";
import { EnvSafe } from "../../env/env-schema.ts";

export const getManyDecksWithCardsDb = async (
env: EnvSafe,
deckIds: number[],
): Promise<DeckWithCardsDbType[]> => {
const db = getDatabase(env);

const { data, error } = await db
.from("deck")
.select("*, deck_card!deck_card_deck_id_fkey(*)")
.in("id", deckIds)
.order("id", { ascending: false });

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

return decksWithCardsSchema.parse(data);
};
24 changes: 4 additions & 20 deletions functions/db/deck/get-my-decks-with-cards-db.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { EnvSafe } from "../../env/env-schema.ts";
import { getDatabase } from "../get-database.ts";
import { DatabaseException } from "../database-exception.ts";
import {
decksWithCardsSchema,
DeckWithCardsDbType,
} from "./decks-with-cards-schema.ts";
import { DeckWithCardsDbType } from "./decks-with-cards-schema.ts";
import { z } from "zod";
import { getManyDecksWithCardsDb } from "./get-many-decks-with-cards-db.ts";

export const getMyDecksWithCardsDb = async (
env: EnvSafe,
Expand All @@ -22,23 +20,9 @@ export const getMyDecksWithCardsDb = async (
}

const deckIds = z
.array(
z.object({
id: z.number(),
}),
)
.array(z.object({ id: z.number() }))
.transform((list) => list.map((item) => item.id))
.parse(getUserDeckIdsResult.data);

const { data, error } = await db
.from("deck")
.select("*, deck_card!deck_card_deck_id_fkey(*)")
.in("id", deckIds)
.order("id", { ascending: false });

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

return decksWithCardsSchema.parse(data);
return getManyDecksWithCardsDb(env, deckIds);
};
8 changes: 6 additions & 2 deletions functions/db/deck/is-user-deck-exists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ export const isUserDeckExists = async (
) => {
const db = getDatabase(envSafe);

const userDeck = await db.from("user_deck").select().match(body);
const userDeck = await db
.from("user_deck")
.select()
.match(body)
.maybeSingle();

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

return userDeck.data.length ? userDeck.data[0] : null;
return userDeck.data ? userDeck.data : null;
};
Loading

0 comments on commit fed588b

Please sign in to comment.