diff --git a/packages/dapp/.env.example b/packages/dapp/.env.example index 75576437f..b732617f7 100644 --- a/packages/dapp/.env.example +++ b/packages/dapp/.env.example @@ -10,14 +10,14 @@ SEPOLIA_RPC_URL= IPFS_GATEWAY= POLYGON_RPC_URL= POLYGON_MUMBAI_RPC_URL= -= + # Masca version NEXT_PUBLIC_MASCA_VERSION=v1.1.0 # SupaBase Public -NEXT_PUBLIC_SUPABASE_URL= -NEXT_PUBLIC_SUPABASE_ANON_KEY= +NEXT_PUBLIC_SUPABASE_URL=https://nqgexdszsnrliuzuobul.supabase.co +NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5xZ2V4ZHN6c25ybGl1enVvYnVsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTA3NjQ5NDcsImV4cCI6MjAyNjM0MDk0N30.GtmyqLhsptRv-w-BNMtzTJX1Jquwym61qMagpNs7Hso # SupaBase Private SUPABASE_SECRET_KEY= diff --git a/packages/dapp/next.config.js b/packages/dapp/next.config.js index d6ab18724..8ef5dfb6f 100644 --- a/packages/dapp/next.config.js +++ b/packages/dapp/next.config.js @@ -49,6 +49,12 @@ const nextConfig = { }, env: { USE_LOCAL: process.env.USE_LOCAL || 'false', + NEXT_PUBLIC_SUPABASE_URL: + process.env.NEXT_PUBLIC_SUPABASE_URL || + 'https://nqgexdszsnrliuzuobul.supabase.co', + NEXT_PUBLIC_SUPABASE_ANON_KEY: + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5xZ2V4ZHN6c25ybGl1enVvYnVsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTA3NjQ5NDcsImV4cCI6MjAyNjM0MDk0N30.GtmyqLhsptRv-w-BNMtzTJX1Jquwym61qMagpNs7Hso', }, typescript: { // We can ignore build errors because we are using tsc to type check diff --git a/packages/dapp/package.json b/packages/dapp/package.json index 5f4b20c1e..8a8793d34 100644 --- a/packages/dapp/package.json +++ b/packages/dapp/package.json @@ -18,7 +18,7 @@ "lint:stylelint": "stylelint \"src/**/*.{css,scss}\"", "lint:tsc": "tsc -p tsconfig.json --noEmit --incremental false", "start": "next start", - "supabase:generate": "supabase gen types typescript --project-id vfxyvzkprpeegheyapzg --schema public > src/utils/supabase/database.types.ts" + "supabase:generate": "supabase gen types typescript --project-id nqgexdszsnrliuzuobul --schema public > src/utils/supabase/database.types.ts" }, "dependencies": { "@blockchain-lab-um/did-provider-key": "1.0.8-beta.0", @@ -29,7 +29,7 @@ "@nextui-org/react": "^2.2.9", "@radix-ui/react-toast": "^1.1.5", "@react-oauth/google": "^0.11.1", - "@supabase/supabase-js": "^2.38.5", + "@supabase/supabase-js": "^2.40.0", "@tanstack/react-query": "^5.17.15", "@tanstack/react-table": "^8.10.7", "@types/js-cookie": "^3.0.6", @@ -94,7 +94,7 @@ "stylelint-config-standard-scss": "^11.0.0", "stylelint-prettier": "^4.0.2", "stylelint-webpack-plugin": "^4.1.1", - "supabase": "^1.113.3", + "supabase": "^1.150.0", "tailwindcss": "^3.3.5" }, "nx": { diff --git a/packages/dapp/src/app/api/encrypted-session/route.ts b/packages/dapp/src/app/api/encrypted-session/route.ts index 2bbc5b35a..637678aa6 100644 --- a/packages/dapp/src/app/api/encrypted-session/route.ts +++ b/packages/dapp/src/app/api/encrypted-session/route.ts @@ -1,118 +1,118 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { createClient } from '@supabase/supabase-js'; -import jwt from 'jsonwebtoken'; +import { type NextRequest, NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; +import jwt from "jsonwebtoken"; -import { Database } from '@/utils/supabase/database.types'; +import type { Database } from "@/utils/supabase/database.types"; const CORS_HEADERS = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type', + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", }; export async function GET(request: NextRequest) { - try { - const token = request.headers.get('Authorization')?.replace('Bearer ', ''); + try { + const token = request.headers.get("Authorization")?.replace("Bearer ", ""); - if (!token) { - return new NextResponse('Unauthorized', { - status: 401, - headers: { - ...CORS_HEADERS, - }, - }); - } + if (!token) { + return new NextResponse("Unauthorized", { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } - const user = jwt.verify(token, process.env.SUPABASE_JWT_SECRET!) as { - sub: string; - address: string; - aud: string; - role: string; - iat: number; - exp: number; - }; + const user = jwt.verify(token, process.env.SUPABASE_JWT_SECRET!) as { + sub: string; + address: string; + aud: string; + role: string; + iat: number; + exp: number; + }; - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.SUPABASE_SECRET_KEY! - ); + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.SUPABASE_SECRET_KEY!, + ); - const { data: selectData, error: selectError } = await supabase - .from('encrypted_sessions') - .select('id') - .eq('user_id', user.sub); + const { data: selectData, error: selectError } = await supabase + .from("sessions") + .select("id") + .eq("user_id", user.sub); - if (selectError) { - return new NextResponse('Internal Server Error', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } + if (selectError) { + return new NextResponse("Internal Server Error", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } - // If session is found delete it - if (selectData.length !== 0) { - const { error: deleteError } = await supabase - .from('encrypted_sessions') - .delete() - .eq('user_id', user.sub); + // If session is found delete it + if (selectData.length !== 0) { + const { error: deleteError } = await supabase + .from("sessions") + .delete() + .eq("user_id", user.sub); - if (deleteError) { - return new NextResponse('Internal Server Error', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } - } + if (deleteError) { + return new NextResponse("Internal Server Error", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } + } - // Create a new session - const { data: insertData, error: insertError } = await supabase - .from('encrypted_sessions') - .insert({ - user_id: user.sub, - }) - .select() - .limit(1) - .single(); + // Create a new session + const { data: insertData, error: insertError } = await supabase + .from("sessions") + .insert({ + user_id: user.sub, + }) + .select() + .limit(1) + .single(); - if (insertError || !insertData) { - return new NextResponse('Internal Server Error', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } + if (insertError || !insertData) { + return new NextResponse("Internal Server Error", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } - return NextResponse.json( - { - sessionId: insertData.id, - }, - { - status: 201, - headers: { - ...CORS_HEADERS, - }, - } - ); - } catch (error) { - return new NextResponse('Internal Server Error', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } + return NextResponse.json( + { + sessionId: insertData.id, + }, + { + status: 201, + headers: { + ...CORS_HEADERS, + }, + }, + ); + } catch (error) { + return new NextResponse("Internal Server Error", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } } export async function OPTIONS() { - return new NextResponse(null, { - status: 200, - headers: { - ...CORS_HEADERS, - }, - }); + return new NextResponse(null, { + status: 200, + headers: { + ...CORS_HEADERS, + }, + }); } diff --git a/packages/dapp/src/app/api/siwe/nonce/route.ts b/packages/dapp/src/app/api/siwe/nonce/route.ts index f3ba16e67..c9337b9db 100644 --- a/packages/dapp/src/app/api/siwe/nonce/route.ts +++ b/packages/dapp/src/app/api/siwe/nonce/route.ts @@ -1,65 +1,65 @@ -import { NextResponse } from 'next/server'; -import { createClient } from '@supabase/supabase-js'; -import { add, format } from 'date-fns'; +import { NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; +import { add, format } from "date-fns"; -import { Database } from '@/utils/supabase/database.types'; +import type { Database } from "@/utils/supabase/database.types"; const CORS_HEADERS = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type', + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", }; export async function GET() { - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.SUPABASE_SECRET_KEY! - ); + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.SUPABASE_SECRET_KEY!, + ); - // Insert a new nonce and select 1 row - const { data, error } = await supabase - .from('authorization') - .insert({ - // Expires in 5 minutes (ISO String) - expires_at: format( - add(new Date(), { minutes: 5 }), - "yyyy-MM-dd'T'HH:mm:ss.SSSxxx" - ), - }) - .select() - .limit(1) - .single(); + // Insert a new nonce and select 1 row + const { data, error } = await supabase + .from("siwe") + .insert({ + // Expires in 5 minutes (ISO String) + expires_at: format( + add(new Date(), { minutes: 5 }), + "yyyy-MM-dd'T'HH:mm:ss.SSSxxx", + ), + }) + .select() + .limit(1) + .single(); - if (error || !data) { - return new NextResponse('Internal server error', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } + if (error || !data) { + return new NextResponse("Internal server error", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } - return NextResponse.json( - { - nonce: data.nonce, - expiresAt: data.expires_at, - createdAt: data.created_at, - }, - { - headers: { - ...CORS_HEADERS, - 'Set-Cookie': `verify.session=${data.id}; Path=/; HttpOnly; Secure; SameSite=Strict;`, - }, - status: 200, - } - ); + return NextResponse.json( + { + nonce: data.nonce, + expiresAt: data.expires_at, + createdAt: data.created_at, + }, + { + headers: { + ...CORS_HEADERS, + "Set-Cookie": `verify.session=${data.id}; Path=/; HttpOnly; Secure; SameSite=Strict;`, + }, + status: 200, + }, + ); } export async function OPTIONS() { - return new NextResponse(null, { - status: 200, - headers: { - ...CORS_HEADERS, - }, - }); + return new NextResponse(null, { + status: 200, + headers: { + ...CORS_HEADERS, + }, + }); } diff --git a/packages/dapp/src/app/api/siwe/verify/route.ts b/packages/dapp/src/app/api/siwe/verify/route.ts index ada920ab8..0843304d5 100644 --- a/packages/dapp/src/app/api/siwe/verify/route.ts +++ b/packages/dapp/src/app/api/siwe/verify/route.ts @@ -1,167 +1,167 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { createClient } from '@supabase/supabase-js'; -import jwt from 'jsonwebtoken'; -import { SiweMessage } from 'siwe'; +import { type NextRequest, NextResponse } from "next/server"; +import { createClient } from "@supabase/supabase-js"; +import jwt from "jsonwebtoken"; +import { SiweMessage } from "siwe"; -import { Database } from '@/utils/supabase/database.types'; +import type { Database } from "@/utils/supabase/database.types"; const CORS_HEADERS = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'POST OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type', + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", }; export async function POST(request: NextRequest) { - try { - // Get session id from cookie - const sessionId = request.cookies.get('verify.session')?.value; - - if (!sessionId) { - return new NextResponse('Unauthorized', { - status: 401, - headers: { - ...CORS_HEADERS, - }, - }); - } - - const supabase = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.SUPABASE_SECRET_KEY! - ); - - const { data: authorizationQueryData } = await supabase - .from('authorization') - .select() - .eq('id', sessionId) - .limit(1); - - if (!authorizationQueryData || authorizationQueryData.length === 0) { - return new NextResponse('Unauthorized', { - status: 401, - headers: { - ...CORS_HEADERS, - }, - }); - } - - const [authorizationData] = authorizationQueryData; - - // Check if expired - if (new Date(authorizationData.expires_at) < new Date()) { - return new NextResponse('Unauthorized', { - status: 401, - headers: { - ...CORS_HEADERS, - }, - }); - } - - const { message, signature } = await request.json(); - - if (!message || !signature) { - return new NextResponse('Unauthorized', { - status: 401, - headers: { - ...CORS_HEADERS, - }, - }); - } - - const siweObject = new SiweMessage(message); - - const { data } = await siweObject.verify({ - signature, - nonce: authorizationData.nonce.replaceAll('-', ''), - }); - - const { address } = data; - - let userData; - - // Find user by address - const { data: userQueryData, error: userQueryError } = await supabase - .from('users') - .select() - .eq('address', address.toLowerCase()) - .limit(1); - - if (userQueryError) { - return new NextResponse('Failed to query user by address', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } - - // If user does not exist, create a new one - if (!userQueryData || userQueryData.length === 0) { - const { data: userCreateData, error: userCreateError } = await supabase - .from('users') - .insert({ - address: address.toLowerCase(), - }) - .select() - .limit(1) - .single(); - - if (userCreateError || !userCreateData) { - return new NextResponse('Failed to create user', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } - - userData = userCreateData; - } else { - [userData] = userQueryData; - } - - // Create a access token - const token = jwt.sign( - { - sub: userData.id, - address, - // Neded for Supabase - aud: 'authenticated', - role: 'authenticated', - }, - process.env.SUPABASE_JWT_SECRET!, - { - expiresIn: '12h', - } - ); - - // Return response and set cookie - return NextResponse.json( - { - jwt: token, - }, - { - status: 200, - headers: { - ...CORS_HEADERS, - }, - } - ); - } catch (error) { - return new NextResponse('An unexpected error occurred.', { - status: 500, - headers: { - ...CORS_HEADERS, - }, - }); - } + try { + // Get session id from cookie + const sessionId = request.cookies.get("verify.session")?.value; + + if (!sessionId) { + return new NextResponse("Unauthorized", { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } + + const supabase = createClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.SUPABASE_SECRET_KEY!, + ); + + const { data: authorizationQueryData } = await supabase + .from("siwe") + .select() + .eq("id", sessionId) + .limit(1); + + if (!authorizationQueryData || authorizationQueryData.length === 0) { + return new NextResponse("Unauthorized", { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } + + const [authorizationData] = authorizationQueryData; + + // Check if expired + if (new Date(authorizationData.expires_at) < new Date()) { + return new NextResponse("Unauthorized", { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } + + const { message, signature } = await request.json(); + + if (!message || !signature) { + return new NextResponse("Unauthorized", { + status: 401, + headers: { + ...CORS_HEADERS, + }, + }); + } + + const siweObject = new SiweMessage(message); + + const { data } = await siweObject.verify({ + signature, + nonce: authorizationData.nonce.replaceAll("-", ""), + }); + + const { address } = data; + + let userData; + + // Find user by address + const { data: userQueryData, error: userQueryError } = await supabase + .from("users") + .select() + .eq("address", address.toLowerCase()) + .limit(1); + + if (userQueryError) { + return new NextResponse("Failed to query user by address", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } + + // If user does not exist, create a new one + if (!userQueryData || userQueryData.length === 0) { + const { data: userCreateData, error: userCreateError } = await supabase + .from("users") + .insert({ + address: address.toLowerCase(), + }) + .select() + .limit(1) + .single(); + + if (userCreateError || !userCreateData) { + return new NextResponse("Failed to create user", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } + + userData = userCreateData; + } else { + [userData] = userQueryData; + } + + // Create a access token + const token = jwt.sign( + { + sub: userData.id, + address, + // Neded for Supabase + aud: "authenticated", + role: "authenticated", + }, + process.env.SUPABASE_JWT_SECRET!, + { + expiresIn: "12h", + }, + ); + + // Return response and set cookie + return NextResponse.json( + { + jwt: token, + }, + { + status: 200, + headers: { + ...CORS_HEADERS, + }, + }, + ); + } catch (error) { + return new NextResponse("An unexpected error occurred.", { + status: 500, + headers: { + ...CORS_HEADERS, + }, + }); + } } export async function OPTIONS() { - return new NextResponse(null, { - status: 200, - headers: { - ...CORS_HEADERS, - }, - }); + return new NextResponse(null, { + status: 200, + headers: { + ...CORS_HEADERS, + }, + }); } diff --git a/packages/dapp/src/components/EncryptedSessionDisplay/ConnectDeviceView.tsx b/packages/dapp/src/components/EncryptedSessionDisplay/ConnectDeviceView.tsx index 4d1fc438c..379bdb1f5 100644 --- a/packages/dapp/src/components/EncryptedSessionDisplay/ConnectDeviceView.tsx +++ b/packages/dapp/src/components/EncryptedSessionDisplay/ConnectDeviceView.tsx @@ -1,185 +1,185 @@ -import React, { useEffect, useState } from 'react'; -import { createClient as createSupbaseClient } from '@supabase/supabase-js'; -import { useTranslations } from 'next-intl'; -import { useAccount } from 'wagmi'; - -import Button from '@/components/Button'; -import CreateConnectionModal from '@/components/ConnectionModal/CreateConnectionModal'; -import ScanQRCodeModal from '@/components/ScanQRCodeModal/ScanQRCodeModal'; -import { Database } from '@/utils/supabase/database.types'; -import { useEncryptedSessionStore, useToastStore } from '@/stores'; -import { useAuthStore } from '@/stores/authStore'; +import React, { useEffect, useState } from "react"; +import { createClient as createSupbaseClient } from "@supabase/supabase-js"; +import { useTranslations } from "next-intl"; +import { useAccount } from "wagmi"; + +import Button from "@/components/Button"; +import CreateConnectionModal from "@/components/ConnectionModal/CreateConnectionModal"; +import ScanQRCodeModal from "@/components/ScanQRCodeModal/ScanQRCodeModal"; +import type { Database } from "@/utils/supabase/database.types"; +import { useEncryptedSessionStore, useToastStore } from "@/stores"; +import { useAuthStore } from "@/stores/authStore"; export const ConnectDeviceView = () => { - const t = useTranslations('ConnectDeviceView'); - - // Local state - const [isModalOpen, setIsModalOpen] = useState(false); - const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); - - // Global state - const { isSignedIn, changeIsSignInModalOpen } = useAuthStore((state) => ({ - isSignedIn: state.isSignedIn, - changeIsSignInModalOpen: state.changeIsSignInModalOpen, - })); - - const { isConnected } = useAccount(); - const { - connected, - deviceType, - hasCamera, - changeSession, - changeConnected, - changeSessionId, - } = useEncryptedSessionStore((state) => ({ - session: state.session, - connected: state.connected, - deviceType: state.deviceType, - hasCamera: state.hasCamera, - changeSession: state.changeSession, - changeConnected: state.changeConnected, - changeSessionId: state.changeSessionId, - })); - - useEffect(() => { - // Close connect QR modal if connection is established - if (connected && isModalOpen) { - setIsModalOpen(false); - } - }, [connected]); - - const onScanSuccessConnectionQRCode = async (decodedText: string, _: any) => { - if (isConnectionModalOpen) { - setIsConnectionModalOpen(false); - } - // Close if already connected - if (connected) return; - - try { - const data = JSON.parse(decodedText); - - if (!data.sessionId || !data.keyData || !data.exp) throw new Error(); - - const decryptionKey = await crypto.subtle.importKey( - 'jwk', - data.keyData, - { name: 'AES-GCM', length: 256 }, - true, - ['encrypt', 'decrypt'] - ); - - // Send data - const client = createSupbaseClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! - ); - - const { error } = await client - .from('encrypted_sessions') - .update({ - connected: true, - }) - .eq('id', data.sessionId); - - if (error) throw new Error('Failed to send data'); - - changeSession({ - key: decryptionKey, - exp: data.exp, - }); - changeConnected(true); - changeSessionId(data.sessionId); - - setTimeout(() => { - useToastStore.setState({ - open: true, - title: t('success'), - type: 'success', - loading: false, - link: null, - }); - }, 200); - } catch (e) { - console.log(e); - setTimeout(() => { - useToastStore.setState({ - open: true, - title: t('invalid'), - type: 'error', - loading: false, - link: null, - }); - }, 200); - } - }; - - return ( -
- {deviceType === 'primary' && !hasCamera && ( - <> - {isConnected && ( - <> -
-
{t('start-primary')}
-
- {`Press the 'Create Connection' button below and Scan the QR + const t = useTranslations("ConnectDeviceView"); + + // Local state + const [isModalOpen, setIsModalOpen] = useState(false); + const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); + + // Global state + const { isSignedIn, changeIsSignInModalOpen } = useAuthStore((state) => ({ + isSignedIn: state.isSignedIn, + changeIsSignInModalOpen: state.changeIsSignInModalOpen, + })); + + const { isConnected } = useAccount(); + const { + connected, + deviceType, + hasCamera, + changeSession, + changeConnected, + changeSessionId, + } = useEncryptedSessionStore((state) => ({ + session: state.session, + connected: state.connected, + deviceType: state.deviceType, + hasCamera: state.hasCamera, + changeSession: state.changeSession, + changeConnected: state.changeConnected, + changeSessionId: state.changeSessionId, + })); + + useEffect(() => { + // Close connect QR modal if connection is established + if (connected && isModalOpen) { + setIsModalOpen(false); + } + }, [connected]); + + const onScanSuccessConnectionQRCode = async (decodedText: string, _: any) => { + if (isConnectionModalOpen) { + setIsConnectionModalOpen(false); + } + // Close if already connected + if (connected) return; + + try { + const data = JSON.parse(decodedText); + + if (!data.sessionId || !data.keyData || !data.exp) throw new Error(); + + const decryptionKey = await crypto.subtle.importKey( + "jwk", + data.keyData, + { name: "AES-GCM", length: 256 }, + true, + ["encrypt", "decrypt"], + ); + + // Send data + const client = createSupbaseClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + ); + + const { error } = await client + .from("sessions") + .update({ + connected: true, + }) + .eq("id", data.sessionId); + + if (error) throw new Error("Failed to send data"); + + changeSession({ + key: decryptionKey, + exp: data.exp, + }); + changeConnected(true); + changeSessionId(data.sessionId); + + setTimeout(() => { + useToastStore.setState({ + open: true, + title: t("success"), + type: "success", + loading: false, + link: null, + }); + }, 200); + } catch (e) { + console.log(e); + setTimeout(() => { + useToastStore.setState({ + open: true, + title: t("invalid"), + type: "error", + loading: false, + link: null, + }); + }, 200); + } + }; + + return ( +
+ {deviceType === "primary" && !hasCamera && ( + <> + {isConnected && ( + <> +
+
{t("start-primary")}
+
+ {`Press the 'Create Connection' button below and Scan the QR code on your secondary device`} -
-
-
- -
- - )} - {!isConnected &&
{t('connect')}
} - - )} - {hasCamera && ( - <> - {deviceType === 'secondary' && ( - <> -
-
{t('start-secondary')}
-
-
- -
- - )} - - )} - - -
- ); +
+
+
+ +
+ + )} + {!isConnected &&
{t("connect")}
} + + )} + {hasCamera && ( + <> + {deviceType === "secondary" && ( + <> +
+
{t("start-secondary")}
+
+
+ +
+ + )} + + )} + + +
+ ); }; diff --git a/packages/dapp/src/components/EncryptedSessionDisplay/ScanQRCodeView.tsx b/packages/dapp/src/components/EncryptedSessionDisplay/ScanQRCodeView.tsx index a70454187..1b487fa4e 100644 --- a/packages/dapp/src/components/EncryptedSessionDisplay/ScanQRCodeView.tsx +++ b/packages/dapp/src/components/EncryptedSessionDisplay/ScanQRCodeView.tsx @@ -8,7 +8,7 @@ import { useAccount } from 'wagmi'; import Button from '@/components/Button'; import ScanQRCodeModal from '@/components/ScanQRCodeModal/ScanQRCodeModal'; import UploadButton from '@/components/UploadButton'; -import { Database } from '@/utils/supabase/database.types'; +import type { Database } from '@/utils/supabase/database.types'; import { useToastStore } from '@/stores'; import { useEncryptedSessionStore } from '@/stores/encryptedSessionStore'; import { useQRCodeStore } from '@/stores/qrCodeStore'; @@ -36,6 +36,7 @@ export const ScanQRCodeView = ({ onQRCodeScanned }: ScanQRCodeViewProps) => { // Same device if (isConnected && deviceType === 'primary') { changeRequestData(decodedText); + onQRCodeScanned(); return; } @@ -96,7 +97,7 @@ export const ScanQRCodeView = ({ onQRCodeScanned }: ScanQRCodeViewProps) => { ); const { error } = await client - .from('encrypted_sessions') + .from('sessions') .update({ data: uint8ArrayToHex(encryptedData), iv: uint8ArrayToHex(iv), @@ -104,6 +105,8 @@ export const ScanQRCodeView = ({ onQRCodeScanned }: ScanQRCodeViewProps) => { .eq('id', sessionId); if (error) throw new Error('Failed to send data'); + + onQRCodeScanned(); } catch (e) { setTimeout(() => { useToastStore.setState({ diff --git a/packages/dapp/src/components/EncryptedSessionProvider/index.tsx b/packages/dapp/src/components/EncryptedSessionProvider/index.tsx index ede1b146e..f80a716e6 100644 --- a/packages/dapp/src/components/EncryptedSessionProvider/index.tsx +++ b/packages/dapp/src/components/EncryptedSessionProvider/index.tsx @@ -1,11 +1,13 @@ 'use client'; -import { useEffect, useMemo } from 'react'; +import { useEffect, useState } from 'react'; import { hexToUint8Array } from '@blockchain-lab-um/masca-connector'; +import type { SupabaseClient } from '@supabase/supabase-js'; import { useTranslations } from 'next-intl'; import { useAccount } from 'wagmi'; import { createClient } from '@/utils/supabase/client'; +import type { Database } from '@/utils/supabase/database.types'; import { useMascaStore, useToastStore } from '@/stores'; import { useAuthStore } from '@/stores/authStore'; import { useEncryptedSessionStore } from '@/stores/encryptedSessionStore'; @@ -13,6 +15,7 @@ import { useEncryptedSessionStore } from '@/stores/encryptedSessionStore'; export const EncryptedSessionProvider = () => { const t = useTranslations('EncryptedSessionProvider'); const token = useAuthStore((state) => state.token); + const [client, setClient] = useState>(null); const { address } = useAccount(); @@ -36,8 +39,6 @@ export const EncryptedSessionProvider = () => { const api = useMascaStore((state) => state.mascaApi); - const client = useMemo(() => createClient(token ?? ''), [token]); - // Decrypt data const decryptData = async ({ iv, @@ -136,15 +137,16 @@ export const EncryptedSessionProvider = () => { }; useEffect(() => { + if (!client) return; if (sessionId && deviceType === 'primary') { client - .channel('realtime encrypted_sessions') + .channel('realtime sessions') .on( 'postgres_changes', - { event: 'UPDATE', schema: 'public', table: 'encrypted_sessions' }, + { event: 'UPDATE', schema: 'public', table: 'sessions' }, async () => { const { data, error } = await client - .from('encrypted_sessions') + .from('sessions') .select() .eq('id', sessionId) .single(); @@ -198,5 +200,10 @@ export const EncryptedSessionProvider = () => { }); }, [address]); + useEffect(() => { + if (!token) return; + setClient(createClient(token)); + }, [token]); + return null; }; diff --git a/packages/dapp/src/utils/supabase/database.types.ts b/packages/dapp/src/utils/supabase/database.types.ts index f535b41da..11f1c84f5 100644 --- a/packages/dapp/src/utils/supabase/database.types.ts +++ b/packages/dapp/src/utils/supabase/database.types.ts @@ -38,142 +38,201 @@ export type Database = { isOneToOne: true referencedRelation: "users" referencedColumns: ["id"] - } + }, ] } - authorization: { + campaigns: { Row: { + additional_constraints: Json[] | null + claimed: number created_at: string - expires_at: string + credential_subject: Json + description: string | null + end_date: string | null id: string - nonce: string + image_url: string + production: boolean + rewards: string + schema_context_url: string | null + schema_url: string | null + start_date: string | null + title: string | null + total: number | null + type: string } Insert: { + additional_constraints?: Json[] | null + claimed?: number created_at?: string - expires_at: string + credential_subject: Json + description?: string | null + end_date?: string | null id?: string - nonce?: string + image_url: string + production?: boolean + rewards: string + schema_context_url?: string | null + schema_url?: string | null + start_date?: string | null + title?: string | null + total?: number | null + type?: string } Update: { + additional_constraints?: Json[] | null + claimed?: number created_at?: string - expires_at?: string + credential_subject?: Json + description?: string | null + end_date?: string | null id?: string - nonce?: string + image_url?: string + production?: boolean + rewards?: string + schema_context_url?: string | null + schema_url?: string | null + start_date?: string | null + title?: string | null + total?: number | null + type?: string } Relationships: [] } - campaign_claims: { + campaigns_requirements: { + Row: { + campaign_id: string + requirement_id: string + } + Insert: { + campaign_id: string + requirement_id: string + } + Update: { + campaign_id?: string + requirement_id?: string + } + Relationships: [ + { + foreignKeyName: "public_campaigns_requirements_campaign_id_fkey" + columns: ["campaign_id"] + isOneToOne: false + referencedRelation: "campaigns" + referencedColumns: ["id"] + }, + { + foreignKeyName: "public_campaigns_requirements_requirement_id_fkey" + columns: ["requirement_id"] + isOneToOne: false + referencedRelation: "requirements" + referencedColumns: ["id"] + }, + ] + } + claims: { Row: { campaign_id: string claimed_at: string + credential_id: number | null id: number user_id: string } Insert: { campaign_id: string claimed_at?: string + credential_id?: number | null id?: number user_id: string } Update: { campaign_id?: string claimed_at?: string + credential_id?: number | null id?: number user_id?: string } Relationships: [ { - foreignKeyName: "public_campaign_claims_campaign_id_fkey" + foreignKeyName: "public_claims_campaign_id_fkey" columns: ["campaign_id"] isOneToOne: false referencedRelation: "campaigns" referencedColumns: ["id"] }, { - foreignKeyName: "public_campaign_claims_user_id_fkey" + foreignKeyName: "public_claims_user_id_fkey" columns: ["user_id"] isOneToOne: false referencedRelation: "users" referencedColumns: ["id"] - } + }, ] } - campaign_requirements: { + presentations: { Row: { - action_link: string | null created_at: string + expires_at: string | null id: string - issuer: string | null - jsonld_schema_url: string | null - name: string | null - types: string[] | null + presentation: Json + title: string + user_id: string + views: number } Insert: { - action_link?: string | null created_at?: string + expires_at?: string | null id?: string - issuer?: string | null - jsonld_schema_url?: string | null - name?: string | null - types?: string[] | null + presentation: Json + title: string + user_id?: string + views?: number } Update: { - action_link?: string | null created_at?: string + expires_at?: string | null id?: string - issuer?: string | null - jsonld_schema_url?: string | null - name?: string | null - types?: string[] | null + presentation?: Json + title?: string + user_id?: string + views?: number } - Relationships: [] + Relationships: [ + { + foreignKeyName: "public_presentations_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] + }, + ] } - campaigns: { + requirements: { Row: { - additional_constraints: Json[] | null - claimed: number | null + action_link: string | null created_at: string - description: string | null - end_date: string | null id: string - image_url: string | null - production: boolean - schema_url: string | null - start_date: string | null - title: string | null - total: number | null + issuer: string | null + name: string | null + types: string[] | null } Insert: { - additional_constraints?: Json[] | null - claimed?: number | null + action_link?: string | null created_at?: string - description?: string | null - end_date?: string | null id?: string - image_url?: string | null - production?: boolean - schema_url?: string | null - start_date?: string | null - title?: string | null - total?: number | null + issuer?: string | null + name?: string | null + types?: string[] | null } Update: { - additional_constraints?: Json[] | null - claimed?: number | null + action_link?: string | null created_at?: string - description?: string | null - end_date?: string | null id?: string - image_url?: string | null - production?: boolean - schema_url?: string | null - start_date?: string | null - title?: string | null - total?: number | null + issuer?: string | null + name?: string | null + types?: string[] | null } Relationships: [] } - encrypted_sessions: { + sessions: { Row: { connected: boolean data: string | null @@ -197,81 +256,34 @@ export type Database = { } Relationships: [ { - foreignKeyName: "public_encrypted-sessions_user_id_fkey" + foreignKeyName: "public_sessions_user_id_fkey" columns: ["user_id"] isOneToOne: false referencedRelation: "users" referencedColumns: ["id"] - } + }, ] } - presentations: { + siwe: { Row: { created_at: string - expires_at: string | null + expires_at: string id: string - presentation: Json - title: string - user_id: string - views: number + nonce: string } Insert: { created_at?: string - expires_at?: string | null + expires_at: string id?: string - presentation: Json - title: string - user_id?: string - views?: number + nonce?: string } Update: { created_at?: string - expires_at?: string | null + expires_at?: string id?: string - presentation?: Json - title?: string - user_id?: string - views?: number - } - Relationships: [ - { - foreignKeyName: "presentations_user_id_fkey" - columns: ["user_id"] - isOneToOne: false - referencedRelation: "users" - referencedColumns: ["id"] - } - ] - } - requirement_campaign_rel: { - Row: { - campaign_id: string - requirement_id: string - } - Insert: { - campaign_id: string - requirement_id: string - } - Update: { - campaign_id?: string - requirement_id?: string + nonce?: string } - Relationships: [ - { - foreignKeyName: "public_requirement_campaign_rel_campaign_id_fkey" - columns: ["campaign_id"] - isOneToOne: false - referencedRelation: "campaigns" - referencedColumns: ["id"] - }, - { - foreignKeyName: "public_requirement_campaign_rel_requirement_id_fkey" - columns: ["requirement_id"] - isOneToOne: false - referencedRelation: "campaign_requirements" - referencedColumns: ["id"] - } - ] + Relationships: [] } users: { Row: { @@ -291,12 +303,54 @@ export type Database = { } Relationships: [] } + users_requirements: { + Row: { + created_at: string + requirement_id: string + user_id: string + } + Insert: { + created_at?: string + requirement_id?: string + user_id?: string + } + Update: { + created_at?: string + requirement_id?: string + user_id?: string + } + Relationships: [ + { + foreignKeyName: "public_users_requirements_requirement_id_fkey" + columns: ["requirement_id"] + isOneToOne: false + referencedRelation: "requirements" + referencedColumns: ["id"] + }, + { + foreignKeyName: "public_users_requirements_user_id_fkey" + columns: ["user_id"] + isOneToOne: false + referencedRelation: "users" + referencedColumns: ["id"] + }, + ] + } } Views: { [_ in never]: never } Functions: { - [_ in never]: never + increment_presentation_views: { + Args: { + presentation_id: string + } + Returns: undefined + } + requesting_user_id: { + Args: Record + Returns: string + } } Enums: { [_ in never]: never @@ -307,14 +361,16 @@ export type Database = { } } +type PublicSchema = Database[Extract] + export type Tables< PublicTableNameOrOptions extends - | keyof (Database["public"]["Tables"] & Database["public"]["Views"]) + | keyof (PublicSchema["Tables"] & PublicSchema["Views"]) | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] & Database[PublicTableNameOrOptions["schema"]]["Views"]) - : never = never + : never = never, > = PublicTableNameOrOptions extends { schema: keyof Database } ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] & Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends { @@ -322,67 +378,67 @@ export type Tables< } ? R : never - : PublicTableNameOrOptions extends keyof (Database["public"]["Tables"] & - Database["public"]["Views"]) - ? (Database["public"]["Tables"] & - Database["public"]["Views"])[PublicTableNameOrOptions] extends { - Row: infer R - } - ? R + : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] & + PublicSchema["Views"]) + ? (PublicSchema["Tables"] & + PublicSchema["Views"])[PublicTableNameOrOptions] extends { + Row: infer R + } + ? R + : never : never - : never export type TablesInsert< PublicTableNameOrOptions extends - | keyof Database["public"]["Tables"] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] - : never = never + : 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 + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Insert: infer I + } + ? I + : never : never - : never export type TablesUpdate< PublicTableNameOrOptions extends - | keyof Database["public"]["Tables"] + | keyof PublicSchema["Tables"] | { schema: keyof Database }, TableName extends PublicTableNameOrOptions extends { schema: keyof Database } ? keyof Database[PublicTableNameOrOptions["schema"]]["Tables"] - : never = never + : 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 + : PublicTableNameOrOptions extends keyof PublicSchema["Tables"] + ? PublicSchema["Tables"][PublicTableNameOrOptions] extends { + Update: infer U + } + ? U + : never : never - : never export type Enums< PublicEnumNameOrOptions extends - | keyof Database["public"]["Enums"] + | keyof PublicSchema["Enums"] | { schema: keyof Database }, EnumName extends PublicEnumNameOrOptions extends { schema: keyof Database } ? keyof Database[PublicEnumNameOrOptions["schema"]]["Enums"] - : never = never + : never = never, > = PublicEnumNameOrOptions extends { schema: keyof Database } ? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName] - : PublicEnumNameOrOptions extends keyof Database["public"]["Enums"] - ? Database["public"]["Enums"][PublicEnumNameOrOptions] - : never + : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] + ? PublicSchema["Enums"][PublicEnumNameOrOptions] + : never diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c19394163..034e83f41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -414,8 +414,8 @@ importers: specifier: ^0.11.1 version: 0.11.1(react-dom@18.2.0)(react@18.2.0) '@supabase/supabase-js': - specifier: ^2.38.5 - version: 2.39.0 + specifier: ^2.40.0 + version: 2.40.0 '@tanstack/react-query': specifier: ^5.17.15 version: 5.17.19(react@18.2.0) @@ -604,8 +604,8 @@ importers: specifier: ^4.1.1 version: 4.1.1(stylelint@15.11.0)(webpack@5.89.0) supabase: - specifier: ^1.113.3 - version: 1.115.4 + specifier: ^1.150.0 + version: 1.150.0 tailwindcss: specifier: ^3.3.5 version: 3.3.5(ts-node@10.9.1) @@ -10975,8 +10975,8 @@ packages: '@supabase/node-fetch': 2.6.15 dev: false - /@supabase/gotrue-js@2.57.0: - resolution: {integrity: sha512-/CcAW40aPKgp9/w9WgXVUQFg1AOdvFR687ONOMjASPBuC6FsNbKlcXp4pc+rwKNtxyxDkBbR+x7zj/8g00r/Og==} + /@supabase/gotrue-js@2.62.2: + resolution: {integrity: sha512-AP6e6W9rQXFTEJ7sTTNYQrNf0LCcnt1hUW+RIgUK+Uh3jbWvcIST7wAlYyNZiMlS9+PYyymWQ+Ykz/rOYSO0+A==} dependencies: '@supabase/node-fetch': 2.6.15 dev: false @@ -10988,21 +10988,22 @@ packages: whatwg-url: 5.0.0 dev: false - /@supabase/postgrest-js@1.9.0: - resolution: {integrity: sha512-axP6cU69jDrLbfihJKQ6vU27tklD0gzb9idkMN363MtTXeJVt5DQNT3JnJ58JVNBdL74hgm26rAsFNvHk+tnSw==} + /@supabase/postgrest-js@1.9.2: + resolution: {integrity: sha512-I6yHo8CC9cxhOo6DouDMy9uOfW7hjdsnCxZiaJuIVZm1dBGTFiQPgfMa9zXCamEWzNyWRjZvupAUuX+tqcl5Sw==} dependencies: '@supabase/node-fetch': 2.6.15 dev: false - /@supabase/realtime-js@2.8.4: - resolution: {integrity: sha512-5C9slLTGikHnYmAnIBOaPogAgbcNY68vnIyE6GpqIKjHElVb6LIi4clwNcjHSj4z6szuvvzj8T/+ePEgGEGekw==} + /@supabase/realtime-js@2.9.3: + resolution: {integrity: sha512-lAp50s2n3FhGJFq+wTSXLNIDPw5Y0Wxrgt44eM5nLSA3jZNUUP3Oq2Ccd1CbZdVntPCWLZvJaU//pAd2NE+QnQ==} dependencies: '@supabase/node-fetch': 2.6.15 '@types/phoenix': 1.6.4 - '@types/websocket': 1.0.10 - websocket: 1.0.34 + '@types/ws': 8.5.10 + ws: 8.14.2 transitivePeerDependencies: - - supports-color + - bufferutil + - utf-8-validate dev: false /@supabase/storage-js@2.5.5: @@ -11011,17 +11012,18 @@ packages: '@supabase/node-fetch': 2.6.15 dev: false - /@supabase/supabase-js@2.39.0: - resolution: {integrity: sha512-cYfnwWRW5rYBbPT/BNIejtRT9ULdD9PnIExQV28PZpqcqm3PLwS4f3pY7WGB01Da63VYdvktZPKuYvreqsj/Zg==} + /@supabase/supabase-js@2.40.0: + resolution: {integrity: sha512-XF8OrsA13DYBL074sHH4M0NhXJCWhQ0R5JbVeVUytZ0coPMS9krRdzxl+0c4z4LLjqbm/Wdz0UYlTYM9MgnDag==} dependencies: '@supabase/functions-js': 2.1.5 - '@supabase/gotrue-js': 2.57.0 + '@supabase/gotrue-js': 2.62.2 '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 1.9.0 - '@supabase/realtime-js': 2.8.4 + '@supabase/postgrest-js': 1.9.2 + '@supabase/realtime-js': 2.9.3 '@supabase/storage-js': 2.5.5 transitivePeerDependencies: - - supports-color + - bufferutil + - utf-8-validate dev: false /@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.23.2): @@ -12120,8 +12122,8 @@ packages: resolution: {integrity: sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==} dev: true - /@types/websocket@1.0.10: - resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==} + /@types/ws@8.5.10: + resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: '@types/node': 18.18.7 dev: false @@ -16375,13 +16377,6 @@ packages: stream-transform: 2.1.3 dev: false - /d@1.0.1: - resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} - dependencies: - es5-ext: 0.10.62 - type: 1.2.0 - dev: false - /dag-jose-utils@3.0.0: resolution: {integrity: sha512-gu+XutOTy3kD8fDcA1SMjZ2U0mUOb/hxoRVZaMCizXN7Ssbc5dKOzeXQ4GquV4BdQzs3w5Y7irOpn2plFPIJfg==} dependencies: @@ -17361,24 +17356,6 @@ packages: is-date-object: 1.0.5 is-symbol: 1.0.4 - /es5-ext@0.10.62: - resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==} - engines: {node: '>=0.10'} - requiresBuild: true - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.3 - next-tick: 1.1.0 - dev: false - - /es6-iterator@2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - dependencies: - d: 1.0.1 - es5-ext: 0.10.62 - es6-symbol: 3.1.3 - dev: false - /es6-object-assign@1.1.0: resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==} dev: true @@ -17387,13 +17364,6 @@ packages: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} dev: false - /es6-symbol@3.1.3: - resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} - dependencies: - d: 1.0.1 - ext: 1.7.0 - dev: false - /esbuild-loader@4.0.2(webpack@5.89.0): resolution: {integrity: sha512-kj88m0yrtTEJDeUEF+3TZsq7t9VPzQQj7UmXAzUbIaipoYSrd0UxKAcg4l9CBgP8uVoploiw+nKr8DIv6Y9gXw==} peerDependencies: @@ -18501,12 +18471,6 @@ packages: transitivePeerDependencies: - supports-color - /ext@1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} - dependencies: - type: 2.7.2 - dev: false - /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -23375,10 +23339,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false - /next-tick@1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - dev: false - /next@13.5.6(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==} engines: {node: '>=16.14.0'} @@ -27816,8 +27776,8 @@ packages: /sudo-prompt@9.2.1: resolution: {integrity: sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==} - /supabase@1.115.4: - resolution: {integrity: sha512-VXfz9riWHKEmbzv8ITJuqVCVl/92DLhKvUMiU9tVwCBylMWMqg0hOoh89VSZETektZOtxXk24jCQ9zrBqodzKQ==} + /supabase@1.150.0: + resolution: {integrity: sha512-hjyXukIww6Jc37Wq9urUldS61a7lZdw6J8Ky/TaScGFqTsO4DbPXUoOxHHaWYSM2ucV+4ejNZ5jYpAGmorApkA==} engines: {npm: '>=8'} hasBin: true requiresBuild: true @@ -28665,14 +28625,6 @@ packages: media-typer: 0.3.0 mime-types: 2.1.35 - /type@1.2.0: - resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} - dev: false - - /type@2.7.2: - resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} - dev: false - /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} engines: {node: '>= 0.4'} @@ -29309,14 +29261,6 @@ packages: dependencies: react: 18.2.0 - /utf-8-validate@5.0.10: - resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} - engines: {node: '>=6.14.2'} - requiresBuild: true - dependencies: - node-gyp-build: 4.6.1 - dev: false - /utf-8-validate@6.0.3: resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==} engines: {node: '>=6.14.2'} @@ -30181,20 +30125,6 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} - /websocket@1.0.34: - resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==} - engines: {node: '>=4.0.0'} - dependencies: - bufferutil: 4.0.8 - debug: 2.6.9 - es5-ext: 0.10.62 - typedarray-to-buffer: 3.1.5 - utf-8-validate: 5.0.10 - yaeti: 0.0.6 - transitivePeerDependencies: - - supports-color - dev: false - /whatwg-fetch@3.6.19: resolution: {integrity: sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==} @@ -30532,11 +30462,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - /yaeti@0.0.6: - resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} - engines: {node: '>=0.10.32'} - dev: false - /yallist@2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: false