Skip to content

Commit

Permalink
Deck one time access (#34)
Browse files Browse the repository at this point in the history
* Deck one time access
  • Loading branch information
kubk authored Jan 5, 2024
1 parent d83f35b commit 7748914
Show file tree
Hide file tree
Showing 63 changed files with 1,315 additions and 155 deletions.
3 changes: 1 addition & 2 deletions functions/add-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ export const onRequestPost = handleError(async ({ request, env }) => {
const canEdit = await getDeckByIdAndAuthorId(
envSafe,
input.data.deckId,
user.id,
user.is_admin,
user,
);
if (!canEdit) {
return createForbiddenRequestResponse();
Expand Down
53 changes: 53 additions & 0 deletions functions/add-deck-access.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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 { createBadRequestResponse } from "./lib/json-response/create-bad-request-response.ts";
import { z } from "zod";
import { getDeckByIdAndAuthorId } from "./db/deck/get-deck-by-id-and-author-id.ts";
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";

const requestSchema = z.object({
deckId: z.number(),
durationDays: z.number().nullable(),
});

const responseSchema = z.object({
share_id: z.string(),
});

export type AddDeckAccessRequest = z.infer<typeof requestSchema>;
export type AddDeckAccessResponse = z.infer<typeof responseSchema>;

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 canEdit = await getDeckByIdAndAuthorId(
envSafe,
input.data.deckId,
user,
);
if (!canEdit) {
return createForbiddenRequestResponse();
}

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

return createJsonResponse<AddDeckAccessResponse>(
responseSchema.parse(createDeckAccessResult),
200,
);
});
162 changes: 162 additions & 0 deletions functions/db/databaseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,58 @@ export interface Database {
}
]
}
deck_access: {
Row: {
author_id: number
created_at: string
deck_id: number
duration_days: number | null
id: number
share_id: string
usage_started_at: string | null
used_by: number | null
}
Insert: {
author_id: number
created_at?: string
deck_id: number
duration_days?: number | null
id?: number
share_id: string
usage_started_at?: string | null
used_by?: number | null
}
Update: {
author_id?: number
created_at?: string
deck_id?: number
duration_days?: number | null
id?: number
share_id?: string
usage_started_at?: string | null
used_by?: number | null
}
Relationships: [
{
foreignKeyName: "deck_access_author_id_fkey"
columns: ["author_id"]
referencedRelation: "user"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_access_deck_id_fkey"
columns: ["deck_id"]
referencedRelation: "deck"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_access_used_by_fkey"
columns: ["used_by"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
deck_card: {
Row: {
back: string
Expand Down Expand Up @@ -162,6 +214,65 @@ export interface Database {
}
Relationships: []
}
deck_folder: {
Row: {
created_at: string
deck_id: number
folder_id: number
}
Insert: {
created_at?: string
deck_id: number
folder_id: number
}
Update: {
created_at?: string
deck_id?: number
folder_id?: number
}
Relationships: [
{
foreignKeyName: "deck_folder_deck_id_fkey"
columns: ["deck_id"]
referencedRelation: "deck"
referencedColumns: ["id"]
},
{
foreignKeyName: "deck_folder_folder_id_fkey"
columns: ["folder_id"]
referencedRelation: "folder"
referencedColumns: ["id"]
}
]
}
folder: {
Row: {
author_id: number
created_at: string
id: number
title: string
}
Insert: {
author_id: number
created_at?: string
id?: number
title: string
}
Update: {
author_id?: number
created_at?: string
id?: number
title?: string
}
Relationships: [
{
foreignKeyName: "folder_author_id_fkey"
columns: ["author_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
notification: {
Row: {
created_at: string
Expand Down Expand Up @@ -259,6 +370,37 @@ export interface Database {
}
]
}
user_folder: {
Row: {
created_at: string
folder_id: number
user_id: number
}
Insert: {
created_at?: string
folder_id: number
user_id: number
}
Update: {
created_at?: string
folder_id?: number
user_id?: number
}
Relationships: [
{
foreignKeyName: "user_folder_folder_id_fkey"
columns: ["folder_id"]
referencedRelation: "folder"
referencedColumns: ["id"]
},
{
foreignKeyName: "user_folder_user_id_fkey"
columns: ["user_id"]
referencedRelation: "user"
referencedColumns: ["id"]
}
]
}
}
Views: {
[_ in never]: never
Expand Down Expand Up @@ -292,6 +434,16 @@ export interface Database {
type: string
}[]
}
get_folder_with_decks: {
Args: {
usr_id: number
}
Returns: {
folder_id: number
folder_title: string
deck_id: number
}[]
}
get_unadded_public_decks: {
Args: {
user_id: number
Expand Down Expand Up @@ -347,6 +499,16 @@ export interface Database {
is_admin: boolean
}[]
}
get_users_with_review_to_notify2: {
Args: Record<PropertyKey, never>
Returns: {
user_id: number
review_count: number
last_reminded_date: string
is_admin: boolean
language_code: string
}[]
}
}
Enums: {
[_ in never]: never
Expand Down
30 changes: 30 additions & 0 deletions functions/db/deck-access/create-deck-access-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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";

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

const createDeckAccessResult = await db
.from("deck_access")
.insert({
deck_id: deckId,
author_id: userId,
share_id: shortUniqueId(),
duration_days: durationDays,
})
.select()
.single();

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

return createDeckAccessResult.data;
};
34 changes: 34 additions & 0 deletions functions/db/deck-access/get-deck-access-by-share-id-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DatabaseException } from "../database-exception.ts";
import { getDatabase } from "../get-database.ts";
import { EnvSafe } from "../../env/env-schema.ts";
import { z } from "zod";

const resultSchema = z.object({
deck_id: z.number(),
author_id: z.number(),
used_by: z.number().nullable(),
processed_at: z.string().nullable(),
});

type GetDeckAccessByShareIdDbResultType = z.infer<typeof resultSchema>;

export const getDeckAccessByShareIdDb = async (
envSafe: EnvSafe,
shareId: string,
): Promise<GetDeckAccessByShareIdDbResultType | null> => {
const db = getDatabase(envSafe);

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

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

return oneTimeShareLinkResult.data
? resultSchema.parse(oneTimeShareLinkResult.data)
: null;
};
46 changes: 46 additions & 0 deletions functions/db/deck-access/get-last-deck-accesses-for-deck-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { DatabaseException } from "../database-exception.ts";
import { getDatabase } from "../get-database.ts";
import { EnvSafe } from "../../env/env-schema.ts";
import { z } from "zod";

const responseSchema = z.array(
z.object({
used_by: z.number().nullable(),
user: z
.object({
id: z.number(),
username: z.string().nullable(),
first_name: z.string().nullable(),
last_name: z.string().nullable(),
})
.nullable(),
share_id: z.string(),
id: z.number(),
created_at: z.string(),
duration_days: z.number().nullable(),
}),
);

export type DeckAccessesForDeckTypeDb = z.infer<typeof responseSchema>;

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

const { data, error } = await 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 (error) {
throw new DatabaseException(error);
}

return responseSchema.parse(data);
};
24 changes: 24 additions & 0 deletions functions/db/deck-access/start-using-deck-access-db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getDatabase } from "../get-database.ts";
import { EnvSafe } from "../../env/env-schema.ts";
import { DatabaseException } from "../database-exception.ts";

export const startUsingDeckAccessDb = async (
envSafe: EnvSafe,
userId: number,
shareId: string,
) => {
const db = getDatabase(envSafe);

const updateResult = await db
.from("deck_access")
.update({
used_by: userId,
usage_started_at: new Date().toISOString(),
})
.eq("share_id", shareId)
.single();

if (updateResult.error) {
throw new DatabaseException(updateResult.error);
}
};
Loading

0 comments on commit 7748914

Please sign in to comment.