Skip to content

Commit

Permalink
feat: upload files in shared workspaces and create new workspaces in …
Browse files Browse the repository at this point in the history
…those
  • Loading branch information
Vexcited committed Aug 14, 2023
1 parent 744ede0 commit 9cd1f5f
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 59 deletions.
21 changes: 11 additions & 10 deletions src/routes/api/workspace/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import { type APIEvent, json } from "solid-start";
import { supabase, getUserProfile } from "@/supabase/server";
import { supabase, getUserProfile, getPermissionForWorkspace } from "@/supabase/server";
import type { WorkspaceMeta } from "@/types/api";

/**
Expand Down Expand Up @@ -34,27 +34,28 @@ export const POST = async ({ request }: APIEvent) => {
.select()
.eq("id", parent_workspace_id)
.limit(1)
.single();
.single<WorkspaceMeta>();

const isCreatorOfWorkspace = workspace_data.creator === user_profile.user_id;

// Check if we're the owner of the file.
if (!isCreatorOfWorkspace) return json({
if (!getPermissionForWorkspace(workspace_data, user_profile)) return json({
success: false,
message: "You're not allowed to access this file."
}, { status: 403 });

const isCreatorOfCurrentWorkspace = workspace_data?.creator === user_profile.user_id;

const { data: created_workspace } = await supabase
.from("workspaces")
.insert([{
name: workspace_name,
parent_workspace_id,
creator: user_profile.user_id
}])
.select();
creator: user_profile.user_id,
shared_with: !isCreatorOfCurrentWorkspace ? [workspace_data?.creator] : undefined
} as WorkspaceMeta])
.select()
.single<WorkspaceMeta>();

return json({
success: true,
data: created_workspace![0] as WorkspaceMeta
data: created_workspace
});
};
13 changes: 3 additions & 10 deletions src/routes/api/workspace/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type APIEvent, json } from "solid-start";
import { supabase, getUserProfile } from "@/supabase/server";
import type { UploadedFile, UserProfile, WorkspaceMeta, WorkspaceContent } from "@/types/api";
import { getPermissionForWorkspace } from "@/supabase/server/utils";

export const GET = async ({ request }: APIEvent): Promise<Response> => {
const api_token = request.headers.get("authorization");
Expand All @@ -20,16 +21,8 @@ export const GET = async ({ request }: APIEvent): Promise<Response> => {
.eq("id", workspace_id)
.limit(1)
.single<WorkspaceMeta>();

const getPermissionForWorkspace = (data: WorkspaceMeta | undefined) => {
if (!data || !user_profile) return false;
const isCreator = data.creator === user_profile.user_id;
const isSharedWith = data.shared_with.includes(user_profile.user_id);
if (!isCreator && !isSharedWith) return false;
return true;
};

if (!getPermissionForWorkspace(workspace_data!)) return json({
if (!getPermissionForWorkspace(workspace_data, user_profile)) return json({
success: false,
message: "Not allowed to get that workspace."
}, { status: 403 });
Expand Down Expand Up @@ -69,7 +62,7 @@ export const GET = async ({ request }: APIEvent): Promise<Response> => {
.single<WorkspaceMeta>();

// If it's not the root folder, then show a back workspace.
if (parent_workspace_data && getPermissionForWorkspace(parent_workspace_data)) {
if (parent_workspace_data && getPermissionForWorkspace(parent_workspace_data, user_profile)) {
content.push({
type: "workspace",
data: {
Expand Down
20 changes: 0 additions & 20 deletions src/supabase/server.ts

This file was deleted.

14 changes: 14 additions & 0 deletions src/supabase/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(
import.meta.env.VITE_SUPABASE_PROJECT_URL as string,
process.env.SUPABASE_SERVICE_ROLE_KEY as string,
{
auth: { persistSession: false },
}
);

import { getUserProfile as _getUserProfile } from "./utils";
export const getUserProfile = (token: string) => _getUserProfile(supabase, token);

export { getPermissionForWorkspace } from "./utils";
20 changes: 20 additions & 0 deletions src/supabase/server/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { WorkspaceMeta, UserProfile } from "@/types/api";
import type { SupabaseClient } from "@supabase/supabase-js";

export const getUserProfile = async (supabase: SupabaseClient, token: string) => {
const { data: user_profile } = await supabase.from("profiles")
.select()
.eq("api_token", token)
.limit(1)
.single();

return user_profile as UserProfile;
};

export const getPermissionForWorkspace = (workspace_data: WorkspaceMeta | undefined | null, user_profile: UserProfile | undefined) => {
if (!workspace_data || !user_profile) return false;
const isCreator = workspace_data.creator === user_profile.user_id;
const isSharedWith = workspace_data.shared_with.includes(user_profile.user_id);
if (!isCreator && !isSharedWith) return false;
return true;
};
20 changes: 7 additions & 13 deletions supabase/functions/_shared/supabase.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { type UserProfile } from "../../../src/types/api.ts";
import { createClient } from "https://esm.sh/@supabase/[email protected]";
import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(
Deno.env.get('SUPABASE_URL') as string,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') as string,
Deno.env.get("SUPABASE_URL") as string,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") as string,
{
auth: { persistSession: false },
auth: { persistSession: false }
}
);

export const getUserProfile = async (token: string) => {
const { data: user_profile } = await supabase.from("profiles")
.select()
.eq("api_token", token)
.limit(1)
.single();
import { getUserProfile as _getUserProfile } from "../../../src/supabase/server/utils.ts";
export const getUserProfile = (token: string) => _getUserProfile(supabase, token);

return user_profile as UserProfile;
};
export { getPermissionForWorkspace } from "../../../src/supabase/server/utils.ts";
5 changes: 5 additions & 0 deletions supabase/functions/import_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"imports": {
"@supabase/supabase-js": "https://esm.sh/@supabase/[email protected]"
}
}
26 changes: 20 additions & 6 deletions supabase/functions/upload-file/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { serve } from "https://deno.land/[email protected]/http/server.ts";

import type { UploadedFile, UserProfile } from "../../../src/types/api.ts";
import { supabase, getUserProfile } from "../_shared/supabase.ts";
import type { UploadedFile, UserProfile, WorkspaceMeta } from "../../../src/types/api.ts";
import { supabase, getUserProfile, getPermissionForWorkspace } from "../_shared/supabase.ts";

const corsHeaders = {
"Access-Control-Allow-Origin": "*",
Expand All @@ -17,6 +17,7 @@ const json = <T>(data: T, options?: { status: number }) => new Response(
/**
* PUT / - Body should be FormData.
*
* `api_token?: string` to authenticate the user.
* `files: File[]` contains all the files to upload.
* `workspace_id: string` is the workspace where we should upload the files.
* `private?: "0" | "1"` is the accessibility of the file, where `0` means `public` and `1` means private.
Expand All @@ -30,8 +31,21 @@ serve(async (req: Request) => {
let user_profile: UserProfile | undefined;
if (formDataApiToken) (user_profile = await getUserProfile(formDataApiToken));

const workspaceId = (formData.get("workspace_id") as string | undefined) ?? user_profile?.root_workspace_id ?? undefined;

const workspace_id = (formData.get("workspace_id") as string | undefined) ?? user_profile?.root_workspace_id ?? undefined;
if (workspace_id) {
const { data: workspace_data } = await supabase
.from("workspaces")
.select()
.eq("id", workspace_id)
.limit(1)
.single<WorkspaceMeta>();

if (!getPermissionForWorkspace(workspace_data, user_profile)) return json({
success: false,
message: "Not allowed to upload in this directory."
}, { status: 403 });
}

const isPrivate = parseInt((formData.get("private") as string | null) ?? "1");
const newUploadsInDatabase: UploadedFile[] = [];

Expand All @@ -45,7 +59,7 @@ serve(async (req: Request) => {
// Always public for anonymous uploads.
private: user_profile ? Boolean(isPrivate) : false,
shared_with: [],
workspace_id: user_profile ? workspaceId : null,
workspace_id: user_profile ? workspace_id : null,
name: file.name
})
.select();
Expand All @@ -70,5 +84,5 @@ serve(async (req: Request) => {
return json({
success: false,
message: "Unknown method."
});
}, { status: 404 });
});

0 comments on commit 9cd1f5f

Please sign in to comment.