From 896e7bd33c3f8961c496626412bcf3c343086ddb Mon Sep 17 00:00:00 2001 From: devleejb Date: Tue, 16 Jan 2024 15:23:14 +0900 Subject: [PATCH] Revert "Revert "Initialize supabase"" This reverts commit 112ec76b95ad89b6a7369e1d1342f65878729ee1. --- frontend/package-lock.json | 133 ++++++++++++++++ frontend/package.json | 1 + frontend/src/App.tsx | 20 ++- frontend/src/store/store.ts | 9 +- frontend/src/store/supabaseSlice.ts | 29 ++++ frontend/types/supabase.ts | 225 ++++++++++++++++++++++++++++ supabase/init.sql | 45 ++++++ 7 files changed, 457 insertions(+), 5 deletions(-) create mode 100644 frontend/src/store/supabaseSlice.ts create mode 100644 frontend/types/supabase.ts create mode 100644 supabase/init.sql diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 546c9e1b..2bcd577d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,6 +18,7 @@ "@mui/material": "^5.15.3", "@react-hook/window-size": "^3.1.1", "@reduxjs/toolkit": "^2.0.1", + "@supabase/supabase-js": "^2.39.3", "@swc/helpers": "^0.5.3", "@uiw/codemirror-theme-xcode": "^4.21.21", "@uiw/codemirror-themes": "^4.21.21", @@ -1902,6 +1903,73 @@ "win32" ] }, + "node_modules/@supabase/functions-js": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.1.5.tgz", + "integrity": "sha512-BNzC5XhCzzCaggJ8s53DP+WeHHGT/NfTsx2wUSSGKR2/ikLFQTBCDzMvGz/PxYMqRko/LwncQtKXGOYp1PkPaw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/gotrue-js": { + "version": "2.62.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.62.0.tgz", + "integrity": "sha512-4eBuZNXGOk7ewqJuHPYMnk8clCtEx6Hfnu6yHLjZlx7w18TqcojcTRUBZagErtpgwwdfzUwKbquexhbrpH/ysw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.9.2.tgz", + "integrity": "sha512-I6yHo8CC9cxhOo6DouDMy9uOfW7hjdsnCxZiaJuIVZm1dBGTFiQPgfMa9zXCamEWzNyWRjZvupAUuX+tqcl5Sw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.9.3.tgz", + "integrity": "sha512-lAp50s2n3FhGJFq+wTSXLNIDPw5Y0Wxrgt44eM5nLSA3jZNUUP3Oq2Ccd1CbZdVntPCWLZvJaU//pAd2NE+QnQ==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.14.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.5.tgz", + "integrity": "sha512-OpLoDRjFwClwc2cjTJZG8XviTiQH4Ik8sCiMK5v7et0MDu2QlXjCAW3ljxJB5+z/KazdMOTnySi+hysxWUPu3w==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.39.3", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.39.3.tgz", + "integrity": "sha512-NoltJSaJNKDJNutO5sJPAAi5RIWrn1z2XH+ig1+cHDojT6BTN7TvZPNa3Kq3gFQWfO5H1N9El/bCTZJ3iFW2kQ==", + "dependencies": { + "@supabase/functions-js": "^2.1.5", + "@supabase/gotrue-js": "^2.60.0", + "@supabase/node-fetch": "^2.6.14", + "@supabase/postgrest-js": "^1.9.0", + "@supabase/realtime-js": "^2.9.3", + "@supabase/storage-js": "^2.5.4" + } + }, "node_modules/@swc/helpers": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", @@ -2033,11 +2101,24 @@ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, + "node_modules/@types/node": { + "version": "20.11.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.3.tgz", + "integrity": "sha512-nrlmbvGPNGaj84IJZXMPhQuCMEVTT/hXZMJJG/aIqVL9fKxqk814sGGtJA4GI6hpJSLQjpi6cn0Qx9eOf9SDVg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, + "node_modules/@types/phoenix": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz", + "integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==" + }, "node_modules/@types/prismjs": { "version": "1.26.3", "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", @@ -2102,6 +2183,14 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.18.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", @@ -6114,6 +6203,11 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6186,6 +6280,11 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unified": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", @@ -6446,6 +6545,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6467,6 +6580,26 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6c8fab3e..f03dbd5f 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@mui/material": "^5.15.3", "@react-hook/window-size": "^3.1.1", "@reduxjs/toolkit": "^2.0.1", + "@supabase/supabase-js": "^2.39.3", "@swc/helpers": "^0.5.3", "@uiw/codemirror-theme-xcode": "^4.21.21", "@uiw/codemirror-themes": "^4.21.21", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 870f0898..6fb8b1a3 100755 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,12 +4,14 @@ import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; import "./App.css"; import { Box, CssBaseline, ThemeProvider, createTheme, useMediaQuery } from "@mui/material"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { RouterProvider, createBrowserRouter } from "react-router-dom"; import EditorLayout from "./components/layouts/EditorLayout"; import EditorIndex from "./pages/editor/Index"; -import { useMemo } from "react"; +import { useEffect, useMemo } from "react"; import { selectConfig } from "./store/configSlice"; +import { createClient } from "@supabase/supabase-js"; +import { setClient } from "./store/supabaseSlice"; const router = createBrowserRouter([ { @@ -25,6 +27,7 @@ const router = createBrowserRouter([ ]); function App() { + const dispatch = useDispatch(); const config = useSelector(selectConfig); const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); const theme = useMemo(() => { @@ -37,6 +40,19 @@ function App() { }); }, [config.theme, prefersDarkMode]); + useEffect(() => { + const supabase = createClient( + import.meta.env.VITE_SUPABASE_URL, + import.meta.env.VITE_SUPABASE_ANON + ); + + dispatch(setClient(supabase)); + + return () => { + dispatch(setClient(null)); + }; + }, [dispatch]); + return ( diff --git a/frontend/src/store/store.ts b/frontend/src/store/store.ts index a5c91b86..39f4c161 100644 --- a/frontend/src/store/store.ts +++ b/frontend/src/store/store.ts @@ -1,20 +1,23 @@ import { configureStore } from "@reduxjs/toolkit"; import editorSlice from "./editorSlice"; import configSlice from "./configSlice"; +import supabaseSlice from "./supabaseSlice"; export const store = configureStore({ reducer: { editor: editorSlice, config: configSlice, + supabase: supabaseSlice, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { - ignoredActions: ["editor/setDoc", "editor/setClient"], - ignoredPaths: ["editor.doc", "editor.client"], + ignoredActions: ["editor/setDoc", "editor/setClient", "supabase/setClient"], + ignoredPaths: ["editor.doc", "editor.client", "supabase.client"], }, immutableCheck: { - ignoredPaths: ["editor.doc", "editor.client"], + ignoredActions: ["editor/setDoc", "editor/setClient", "supabase/setClient"], + ignoredPaths: ["editor.doc", "editor.client", "supabase.client"], }, }), }); diff --git a/frontend/src/store/supabaseSlice.ts b/frontend/src/store/supabaseSlice.ts new file mode 100644 index 00000000..35182c4d --- /dev/null +++ b/frontend/src/store/supabaseSlice.ts @@ -0,0 +1,29 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; +import { RootState } from "./store"; +import { SupabaseClient } from "@supabase/supabase-js"; +import { Database } from "../../types/supabase"; + +export interface SupabaseState { + client: SupabaseClient | null; +} + +const initialState: SupabaseState = { + client: null, +}; + +export const supabaseSlice = createSlice({ + name: "supabase", + initialState, + reducers: { + setClient: (state, action: PayloadAction | null>) => { + state.client = action.payload; + }, + }, +}); + +export const { setClient } = supabaseSlice.actions; + +export const selectSupabase = (state: RootState) => state.supabase; + +export default supabaseSlice.reducer; diff --git a/frontend/types/supabase.ts b/frontend/types/supabase.ts new file mode 100644 index 00000000..7f7d3b0a --- /dev/null +++ b/frontend/types/supabase.ts @@ -0,0 +1,225 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json | undefined } + | Json[] + +export interface Database { + public: { + Tables: { + Document: { + Row: { + content: string | null + createdAt: string + id: string + title: string | null + updatedAt: string | null + workspaceId: string + yorkieDocumentId: string | null + } + Insert: { + content?: string | null + createdAt?: string + id?: string + title?: string | null + updatedAt?: string | null + workspaceId: string + yorkieDocumentId?: string | null + } + Update: { + content?: string | null + createdAt?: string + id?: string + title?: string | null + updatedAt?: string | null + workspaceId?: string + yorkieDocumentId?: string | null + } + Relationships: [ + { + foreignKeyName: "Document_workspaceId_fkey" + columns: ["workspaceId"] + isOneToOne: false + referencedRelation: "Workspace" + referencedColumns: ["id"] + } + ] + } + User: { + Row: { + createdAt: string + id: string + nickname: string | null + updatedAt: string | null + } + Insert: { + createdAt?: string + id?: string + nickname?: string | null + updatedAt?: string | null + } + Update: { + createdAt?: string + id?: string + nickname?: string | null + updatedAt?: string | null + } + Relationships: [] + } + UserWorkspace: { + Row: { + createdAt: string + id: string + updatedAt: string | null + userId: string + workspaceId: string + } + Insert: { + createdAt?: string + id?: string + updatedAt?: string | null + userId: string + workspaceId: string + } + Update: { + createdAt?: string + id?: string + updatedAt?: string | null + userId?: string + workspaceId?: string + } + Relationships: [ + { + foreignKeyName: "UserWorkspace_userId_fkey" + columns: ["userId"] + isOneToOne: false + referencedRelation: "User" + referencedColumns: ["id"] + }, + { + foreignKeyName: "UserWorkspace_workspaceId_fkey" + columns: ["workspaceId"] + isOneToOne: false + referencedRelation: "Workspace" + referencedColumns: ["id"] + } + ] + } + Workspace: { + Row: { + createdAt: string + id: string + title: string | null + updatedAt: string | null + } + Insert: { + createdAt?: string + id?: string + title?: string | null + updatedAt?: string | null + } + Update: { + createdAt?: string + id?: string + title?: string | null + updatedAt?: string | null + } + Relationships: [] + } + } + Views: { + [_ in never]: never + } + Functions: { + [_ in never]: never + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + +export type Tables< + PublicTableNameOrOptions extends + | keyof (Database["public"]["Tables"] & Database["public"]["Views"]) + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"]) + : never = never +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & + Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { + Row: infer R + } + ? R + : never + : PublicTableNameOrOptions extends keyof (Database["public"]["Tables"] & + Database["public"]["Views"]) + ? (Database["public"]["Tables"] & + Database["public"]["Views"])[PublicTableNameOrOptions] extends { + Row: infer R + } + ? R + : never + : never + +export type TablesInsert< + PublicTableNameOrOptions extends + | keyof Database["public"]["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Insert: infer I + } + ? I + : never + : PublicTableNameOrOptions extends keyof Database["public"]["Tables"] + ? Database["public"]["Tables"][PublicTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never + : never + +export type TablesUpdate< + PublicTableNameOrOptions extends + | keyof Database["public"]["Tables"] + | { schema: keyof Database }, + TableName extends PublicTableNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] + : never = never +> = PublicTableNameOrOptions extends { schema: keyof Database } + ? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends { + Update: infer U + } + ? U + : never + : PublicTableNameOrOptions extends keyof Database["public"]["Tables"] + ? Database["public"]["Tables"][PublicTableNameOrOptions] extends { + Update: infer U + } + ? U + : never + : never + +export type Enums< + PublicEnumNameOrOptions extends + | keyof Database["public"]["Enums"] + | { schema: keyof Database }, + EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } + ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] + : never = never +> = PublicEnumNameOrOptions extends { schema: keyof Database } + ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] + : PublicEnumNameOrOptions extends keyof Database["public"]["Enums"] + ? Database["public"]["Enums"][PublicEnumNameOrOptions] + : never diff --git a/supabase/init.sql b/supabase/init.sql new file mode 100644 index 00000000..e231d4d6 --- /dev/null +++ b/supabase/init.sql @@ -0,0 +1,45 @@ +CREATE TABLE "User" ( + "id" UUID NOT NULL REFERENCES auth.users ON DELETE CASCADE, + "nickname" VARCHAR(255) NULL, + "createdAt" timestamp NOT NULL DEFAULT now(), + "updatedAt" timestamp NULL +); + +CREATE TABLE "Workspace" ( + "id" UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + "title" VARCHAR(255) NULL, + "createdAt" timestamp NOT NULL DEFAULT now(), + "updatedAt" timestamp NULL +); + +CREATE TABLE "UserWorkspace" ( + "id" UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + "userId" UUID NOT NULL REFERENCES "User" ("id") ON DELETE CASCADE, + "workspaceId" UUID NOT NULL REFERENCES "Workspace" ("id") ON DELETE CASCADE, + "createdAt" timestamp NOT NULL DEFAULT now(), + "updatedAt" timestamp NULL +); + +CREATE TABLE "Document" ( + "id" UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + "workspaceId" UUID NOT NULL REFERENCES "Workspace" ("id") ON DELETE CASCADE, + "yorkieDocumentId" VARCHAR(255) NULL, + "title" VARCHAR(255) NULL, + "content" VARCHAR(255) NULL, + "createdAt" timestamp NOT NULL DEFAULT now(), + "updatedAt" timestamp NULL +); + +ALTER TABLE public."User" ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public profiles are viewable by everyone." + ON "User" for SELECT + using ( true ); + +CREATE POLICY "Users can insert their own profile." + ON "User" for INSERT + with check ( auth.uid() = id ); + +CREATE POLICY "Users can update own profile." + ON "User" for UPDATE + using ( auth.uid() = id );