From 5b10e46e62d2e6f9093bd2d6c44c96be541b85fd Mon Sep 17 00:00:00 2001 From: "joao.herculano" Date: Thu, 6 Aug 2026 11:53:09 -0300 Subject: [PATCH] =?UTF-8?q?adi=C3=A7=C3=A3o=20de=20recupera=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20senha=20e=20limpeza=20de=20remanescentes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 6 - bun.lock | 15 -- package-lock.json | 102 -------- package.json | 1 - src/integrations/supabase/auth-attacher.ts | 15 -- src/integrations/supabase/auth-middleware.ts | 109 --------- src/integrations/supabase/client.server.ts | 69 ------ src/integrations/supabase/client.ts | 68 ------ src/integrations/supabase/types.ts | 231 ------------------ src/lib/auth.ts | 44 ++++ src/lib/coletas.ts | 16 +- src/routes/_authenticated/coleta.tsx | 23 +- src/routes/auth.tsx | 131 ++++++++-- supabase/config.toml | 1 - ...4_b9f169f0-e015-4cd8-ad93-52d659270785.sql | 20 -- ...0_59493172-5f03-4832-94e2-87daaecf9a83.sql | 141 ----------- ...0_e9284991-f847-4754-b9d2-8707e7caa31e.sql | 8 - ...0_9973b16d-ccc8-4a27-ac49-bc821c7f8600.sql | 46 ---- ...2_81889677-83e4-4f69-9bc7-879355dd9edd.sql | 4 - ...4_c3bea5a2-7c9a-413f-b471-94a01200da30.sql | 58 ----- 20 files changed, 183 insertions(+), 925 deletions(-) delete mode 100644 src/integrations/supabase/auth-attacher.ts delete mode 100644 src/integrations/supabase/auth-middleware.ts delete mode 100644 src/integrations/supabase/client.server.ts delete mode 100644 src/integrations/supabase/client.ts delete mode 100644 src/integrations/supabase/types.ts delete mode 100644 supabase/config.toml delete mode 100644 supabase/migrations/20260731131104_b9f169f0-e015-4cd8-ad93-52d659270785.sql delete mode 100644 supabase/migrations/20260803175010_59493172-5f03-4832-94e2-87daaecf9a83.sql delete mode 100644 supabase/migrations/20260803175040_e9284991-f847-4754-b9d2-8707e7caa31e.sql delete mode 100644 supabase/migrations/20260803181430_9973b16d-ccc8-4a27-ac49-bc821c7f8600.sql delete mode 100644 supabase/migrations/20260803182902_81889677-83e4-4f69-9bc7-879355dd9edd.sql delete mode 100644 supabase/migrations/20260803183004_c3bea5a2-7c9a-413f-b471-94a01200da30.sql diff --git a/.env b/.env index 2304848..b3351d0 100644 --- a/.env +++ b/.env @@ -1,9 +1,3 @@ -SUPABASE_PROJECT_ID="iiknzpjckdzlpglimmtq" -SUPABASE_PUBLISHABLE_KEY="sb_publishable_K0u7m8HzX7LZ39cEhDoD-A_v4KDxHce" -SUPABASE_URL="https://iiknzpjckdzlpglimmtq.supabase.co" -VITE_SUPABASE_PROJECT_ID="iiknzpjckdzlpglimmtq" -VITE_SUPABASE_PUBLISHABLE_KEY="sb_publishable_K0u7m8HzX7LZ39cEhDoD-A_v4KDxHce" -VITE_SUPABASE_URL="https://iiknzpjckdzlpglimmtq.supabase.co" DB_HOST="10.77.77.10" DB_PORT="1433" DB_NAME="GINSENG" diff --git a/bun.lock b/bun.lock index d43c70f..5d9c46f 100644 --- a/bun.lock +++ b/bun.lock @@ -32,7 +32,6 @@ "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", - "@supabase/supabase-js": "^2.111.0", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-query": "^5.101.1", "@tanstack/react-router": "^1.170.18", @@ -379,20 +378,6 @@ "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], - "@supabase/auth-js": ["@supabase/auth-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/auth-js/-/auth-js-2.111.0.tgz", { "dependencies": { "tslib": "2.8.1" } }, "sha512-hbRLgyQZEX0SDyF4LYXpv94qOIQyFATfpT5SIs2V0SisHnisVRkYgiPQWzv80l4S2mO3qof78rmoH9j5zoFsYQ=="], - - "@supabase/functions-js": ["@supabase/functions-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/functions-js/-/functions-js-2.111.0.tgz", { "dependencies": { "tslib": "2.8.1" } }, "sha512-RW/OCsd6MO592zU8ifzP8/f8XzxxIdpb+Up5XaOtE26Fw+3zTp475WX7+GuuktiD1WF8pFUDe6khUPbMp77RCw=="], - - "@supabase/phoenix": ["@supabase/phoenix@0.4.5", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/phoenix/-/phoenix-0.4.5.tgz", {}, "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw=="], - - "@supabase/postgrest-js": ["@supabase/postgrest-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/postgrest-js/-/postgrest-js-2.111.0.tgz", { "dependencies": { "tslib": "2.8.1" } }, "sha512-pcqeDsnWP0lx9GawduYxNZJHeuTm53O7L0SC8RF8tniV3GWIPY6me6OTdnwzdwNUmNy1dzUVtSyIfE6+OflzPQ=="], - - "@supabase/realtime-js": ["@supabase/realtime-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/realtime-js/-/realtime-js-2.111.0.tgz", { "dependencies": { "@supabase/phoenix": "0.4.5", "tslib": "2.8.1" } }, "sha512-6oRf/vZyRwg8f8GbFSJkrD2w4HAu/yTvyMViHXHS+H5hNJzdXCrUR7cP5oW7daT3YlRnzRPY9LcGSJKZAmfMSg=="], - - "@supabase/storage-js": ["@supabase/storage-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/storage-js/-/storage-js-2.111.0.tgz", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-UEViNmTzVOxE8dqUA81wls+n9xgmlvSFfhfwo6QxrO4kQOytCYyw3ciYFoi4XoD4Jl95NJ3jnndHN5iIudWzqw=="], - - "@supabase/supabase-js": ["@supabase/supabase-js@2.111.0", "https://europe-west1-npm.pkg.dev/lovable-core-prod/sandbox-npm-cache/@supabase/supabase-js/-/supabase-js-2.111.0.tgz", { "dependencies": { "@supabase/auth-js": "2.111.0", "@supabase/functions-js": "2.111.0", "@supabase/postgrest-js": "2.111.0", "@supabase/realtime-js": "2.111.0", "@supabase/storage-js": "2.111.0" } }, "sha512-9q0/AULthQnWeiDh1vGyjoJZbSY04bu6qHcWit70pqEYn5Kv/dkCPY62Ja1123jEnJbB9Vd2pjY7Kvk/lK3peA=="], - "@tabby_ai/hijri-converter": ["@tabby_ai/hijri-converter@1.0.5", "", {}, "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ=="], "@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="], diff --git a/package-lock.json b/package-lock.json index c5dcc9f..062a719 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,6 @@ "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", - "@supabase/supabase-js": "^2.111.0", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-query": "^5.101.1", "@tanstack/react-router": "^1.170.18", @@ -3151,98 +3150,6 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, - "node_modules/@supabase/auth-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.112.0.tgz", - "integrity": "sha512-8qAdObNQHKbSeVBLmf2WLNT7+bCE8zBofpCFiNUqGBAl5qw9VagSvcmPXlh78McAU7iHrGik1oeJxiB2A6B29w==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.112.0.tgz", - "integrity": "sha512-2DdaEZs0vq86orMIZBO+eM5w5/UxZb1EZyg2JraBKS4W9BzfFO+fOFD6YStmZ7iAOWvxNTGPjPNItsofpGgTCA==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/phoenix": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz", - "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==", - "license": "MIT" - }, - "node_modules/@supabase/postgrest-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.112.0.tgz", - "integrity": "sha512-4HKCVq32Jlk/wS8Ud8QgaAuQ4u6w1hZfw/gS5IcIN7wYddElMj4kfiCZpHMPfncomMnYzAKBUhwBZPpRcTH2Yw==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/realtime-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.112.0.tgz", - "integrity": "sha512-McFFP+ivFDMTaCEh8JDpG+sPEmv5IjKvrP0uTH3Lbsriai4KbxB8ycY8TqPQnbjhOVjEifQm1q0y2tL1CUw9Zg==", - "license": "MIT", - "dependencies": { - "@supabase/phoenix": "0.4.5", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/storage-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.112.0.tgz", - "integrity": "sha512-X44Bl045X/e5e2tJqWsY+JmQvgtm04BJuijiWprIWYLn0mDvGnu8hLdzPOPcTVji7wlqbt2ZUHttsv+rGKQYXw==", - "license": "MIT", - "dependencies": { - "iceberg-js": "^0.8.1", - "tslib": "2.8.1" - }, - "engines": { - "node": ">=22.0.0" - } - }, - "node_modules/@supabase/supabase-js": { - "version": "2.112.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.112.0.tgz", - "integrity": "sha512-dHVOgog58GOagtrZuPxJYg/R45ZV2U0qqgXffH+lMlt1OS+267Pw4g7bw3iXCGxN85OufiE0nI1baxDXlgEyfQ==", - "license": "MIT", - "dependencies": { - "@supabase/auth-js": "2.112.0", - "@supabase/functions-js": "2.112.0", - "@supabase/postgrest-js": "2.112.0", - "@supabase/realtime-js": "2.112.0", - "@supabase/storage-js": "2.112.0" - }, - "engines": { - "node": ">=22.0.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - } - } - }, "node_modules/@tabby_ai/hijri-converter": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", @@ -6346,15 +6253,6 @@ "dev": true, "license": "MIT" }, - "node_modules/iceberg-js": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", - "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", diff --git a/package.json b/package.json index e99a9f0..7c7b791 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.2.8", - "@supabase/supabase-js": "^2.111.0", "@tailwindcss/vite": "^4.2.1", "@tanstack/react-query": "^5.101.1", "@tanstack/react-router": "^1.170.18", diff --git a/src/integrations/supabase/auth-attacher.ts b/src/integrations/supabase/auth-attacher.ts deleted file mode 100644 index 5bc57b7..0000000 --- a/src/integrations/supabase/auth-attacher.ts +++ /dev/null @@ -1,15 +0,0 @@ -// This file is automatically generated. Do not edit it directly. -import { createMiddleware } from '@tanstack/react-start' -import { supabase } from './client' - -// Must be registered as a global `functionMiddleware` in `src/start.ts`; otherwise -// the browser never attaches the bearer token to serverFn RPCs. -export const attachSupabaseAuth = createMiddleware({ type: 'function' }).client( - async ({ next }) => { - const { data } = await supabase.auth.getSession() - const token = data.session?.access_token - return next({ - headers: token ? { Authorization: `Bearer ${token}` } : {}, - }) - }, -) diff --git a/src/integrations/supabase/auth-middleware.ts b/src/integrations/supabase/auth-middleware.ts deleted file mode 100644 index a6bc029..0000000 --- a/src/integrations/supabase/auth-middleware.ts +++ /dev/null @@ -1,109 +0,0 @@ -// This file is automatically generated. Do not edit it directly. -import { createMiddleware } from '@tanstack/react-start' -import { getRequest } from '@tanstack/react-start/server' -import { createClient } from '@supabase/supabase-js' -import type { Database } from './types' - - - -function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); -} - -function createSupabaseFetch(supabaseKey: string): typeof fetch { - return (input, init) => { - const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, - ); - - if (init?.headers) { - new Headers(init.headers).forEach((value, key) => headers.set(key, value)); - } - - // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); - } - - headers.set('apikey', supabaseKey); - return fetch(input, { ...init, headers }); - }; -} - -export const requireSupabaseAuth = createMiddleware({ type: 'function' }).server( - async ({ next }) => { - - const SUPABASE_URL = process.env['SUPABASE_URL']; - const SUPABASE_PUBLISHABLE_KEY = process.env['SUPABASE_PUBLISHABLE_KEY']; - - if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) { - const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_PUBLISHABLE_KEY ? ['SUPABASE_PUBLISHABLE_KEY'] : []), - ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; - console.error(`[Supabase] ${message}`); - throw new Error(message); - } - - const request = getRequest(); - - if (!request?.headers) { - throw new Error('Unauthorized: No request headers available'); - } - - const authHeader = request.headers.get('authorization'); - - if (!authHeader) { - throw new Error('Unauthorized: No authorization header provided'); - } - - if (!authHeader.startsWith('Bearer ')) { - throw new Error('Unauthorized: Only Bearer tokens are supported'); - } - - const token = authHeader.replace('Bearer ', ''); - if (!token) { - throw new Error('Unauthorized: No token provided'); - } - - if (token.split('.').length !== 3) { - throw new Error('Unauthorized: Invalid token'); - } - - const supabase = createClient( - SUPABASE_URL!, - SUPABASE_PUBLISHABLE_KEY!, - { - global: { - fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY!), - headers: { - Authorization: `Bearer ${token}`, - }, - }, - auth: { - storage: undefined, - persistSession: false, - autoRefreshToken: false, - }, - } - ); - - const { data, error } = await supabase.auth.getClaims(token); - if (error || !data?.claims) { - throw new Error('Unauthorized: Invalid token'); - } - - if (!data.claims.sub) { - throw new Error('Unauthorized: No user ID found in token'); - } - - return next({ - context: { - supabase, - userId: data.claims.sub, - claims: data.claims, - }, - }); - }, -); diff --git a/src/integrations/supabase/client.server.ts b/src/integrations/supabase/client.server.ts deleted file mode 100644 index 45a04b9..0000000 --- a/src/integrations/supabase/client.server.ts +++ /dev/null @@ -1,69 +0,0 @@ -// This file is automatically generated. Do not edit it directly. -// Server-side Supabase client with service role key - bypasses RLS. -// Use this for admin operations in server functions and server routes only. -// For user-authenticated queries (with RLS), use the auth middleware instead. -import { createClient } from '@supabase/supabase-js'; -import type { Database } from './types'; - -function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); -} - -function createSupabaseFetch(supabaseKey: string): typeof fetch { - return (input, init) => { - const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, - ); - - if (init?.headers) { - new Headers(init.headers).forEach((value, key) => headers.set(key, value)); - } - - // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); - } - - headers.set('apikey', supabaseKey); - return fetch(input, { ...init, headers }); - }; -} - -function createSupabaseAdminClient() { - const SUPABASE_URL = process.env['SUPABASE_URL']; - const SUPABASE_SERVICE_ROLE_KEY = process.env['SUPABASE_SERVICE_ROLE_KEY']; - - if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { - const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_SERVICE_ROLE_KEY ? ['SUPABASE_SERVICE_ROLE_KEY'] : []), - ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; - console.error(`[Supabase] ${message}`); - throw new Error(message); - } - - return createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, { - global: { - fetch: createSupabaseFetch(SUPABASE_SERVICE_ROLE_KEY), - }, - auth: { - storage: undefined, - persistSession: false, - autoRefreshToken: false, - } - }); -} - -let _supabaseAdmin: ReturnType | undefined; - -// Server-side Supabase client with service role - bypasses RLS -// SECURITY: Only use this for trusted server-side operations, never expose to client code -// Load inside server handlers: const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); -// Top-level import is safe only in other .server.ts modules - route files and *.functions.ts ship to the client bundle. -export const supabaseAdmin = new Proxy({} as ReturnType, { - get(_, prop, receiver) { - if (!_supabaseAdmin) _supabaseAdmin = createSupabaseAdminClient(); - return Reflect.get(_supabaseAdmin, prop, receiver); - }, -}); diff --git a/src/integrations/supabase/client.ts b/src/integrations/supabase/client.ts deleted file mode 100644 index 0034e50..0000000 --- a/src/integrations/supabase/client.ts +++ /dev/null @@ -1,68 +0,0 @@ -// This file is automatically generated. Do not edit it directly. -import { createClient } from '@supabase/supabase-js'; -import type { Database } from './types'; - -function isNewSupabaseApiKey(value: string): boolean { - return value.startsWith('sb_publishable_') || value.startsWith('sb_secret_'); -} - -function createSupabaseFetch(supabaseKey: string): typeof fetch { - return (input, init) => { - const headers = new Headers( - typeof Request !== 'undefined' && input instanceof Request ? input.headers : undefined, - ); - - if (init?.headers) { - new Headers(init.headers).forEach((value, key) => headers.set(key, value)); - } - - // New Supabase API keys are opaque strings, not bearer JWTs. - if (isNewSupabaseApiKey(supabaseKey) && headers.get('Authorization') === `Bearer ${supabaseKey}`) { - headers.delete('Authorization'); - } - - headers.set('apikey', supabaseKey); - return fetch(input, { ...init, headers }); - }; -} - - -function createSupabaseClient() { - // Use import.meta.env for client-side (Vite build-time replacement) - // Fall back to process.env for SSR (server-side rendering) - const SUPABASE_URL = import.meta.env['VITE_SUPABASE_URL'] || process.env['SUPABASE_URL']; - const SUPABASE_PUBLISHABLE_KEY = import.meta.env['VITE_SUPABASE_PUBLISHABLE_KEY'] || process.env['SUPABASE_PUBLISHABLE_KEY']; - - if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) { - const missing = [ - ...(!SUPABASE_URL ? ['SUPABASE_URL'] : []), - ...(!SUPABASE_PUBLISHABLE_KEY ? ['SUPABASE_PUBLISHABLE_KEY'] : []), - ]; - const message = `Missing Supabase environment variable(s): ${missing.join(', ')}. Connect Supabase in Lovable Cloud.`; - console.error(`[Supabase] ${message}`); - throw new Error(message); - } - - return createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, { - global: { - fetch: createSupabaseFetch(SUPABASE_PUBLISHABLE_KEY), - }, - auth: { - storage: typeof window !== 'undefined' ? localStorage : undefined, - persistSession: true, - autoRefreshToken: true, - } - }); -} - -let _supabase: ReturnType | undefined; - -// Import the supabase client like this: -// import { supabase } from "@/integrations/supabase/client"; -export const supabase = new Proxy({} as ReturnType, { - get(_, prop, receiver) { - if (!_supabase) _supabase = createSupabaseClient(); - return Reflect.get(_supabase, prop, receiver); - }, -}); - diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts deleted file mode 100644 index d917497..0000000 --- a/src/integrations/supabase/types.ts +++ /dev/null @@ -1,231 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json | undefined } - | Json[] - -export type Database = { - // Allows to automatically instantiate createClient with right options - // instead of createClient(URL, KEY) - __InternalSupabase: { - PostgrestVersion: "14.15" - } - public: { - Tables: { - coletas: { - Row: { - codigo_barras: string - created_at: string - fim_coleta: string | null - id: number - inicio_coleta: string - usuario: string - } - Insert: { - codigo_barras: string - created_at?: string - fim_coleta?: string | null - id?: never - inicio_coleta?: string - usuario: string - } - Update: { - codigo_barras?: string - created_at?: string - fim_coleta?: string | null - id?: never - inicio_coleta?: string - usuario?: string - } - Relationships: [] - } - papeis_usuario: { - Row: { - created_at: string - id: string - role: Database["public"]["Enums"]["app_role"] - user_id: string - } - Insert: { - created_at?: string - id?: string - role: Database["public"]["Enums"]["app_role"] - user_id: string - } - Update: { - created_at?: string - id?: string - role?: Database["public"]["Enums"]["app_role"] - user_id?: string - } - Relationships: [] - } - perfis: { - Row: { - aprovado: boolean - created_at: string - email: string - id: string - nome: string - updated_at: string - } - Insert: { - aprovado?: boolean - created_at?: string - email: string - id: string - nome: string - updated_at?: string - } - Update: { - aprovado?: boolean - created_at?: string - email?: string - id?: string - nome?: string - updated_at?: string - } - Relationships: [] - } - } - Views: { - [_ in never]: never - } - Functions: { - [_ in never]: never - } - Enums: { - app_role: "admin" | "user" - } - CompositeTypes: { - [_ in never]: never - } - } -} - -type DatabaseWithoutInternals = Omit - -type DefaultSchema = DatabaseWithoutInternals[Extract] - -export type Tables< - DefaultSchemaTableNameOrOptions extends - | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals - } - ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) - : never = never, -> = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals -} - ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { - Row: infer R - } - ? R - : never - : DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & - DefaultSchema["Views"]) - ? (DefaultSchema["Tables"] & - DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends { - Row: infer R - } - ? R - : never - : never - -export type TablesInsert< - DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema["Tables"] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals - } - ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] - : never = never, -> = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals -} - ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Insert: infer I - } - ? I - : never - : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] - ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Insert: infer I - } - ? I - : never - : never - -export type TablesUpdate< - DefaultSchemaTableNameOrOptions extends - | keyof DefaultSchema["Tables"] - | { schema: keyof DatabaseWithoutInternals }, - TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals - } - ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] - : never = never, -> = DefaultSchemaTableNameOrOptions extends { - schema: keyof DatabaseWithoutInternals -} - ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { - Update: infer U - } - ? U - : never - : DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"] - ? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends { - Update: infer U - } - ? U - : never - : never - -export type Enums< - DefaultSchemaEnumNameOrOptions extends - | keyof DefaultSchema["Enums"] - | { schema: keyof DatabaseWithoutInternals }, - EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals - } - ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] - : never = never, -> = DefaultSchemaEnumNameOrOptions extends { - schema: keyof DatabaseWithoutInternals -} - ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] - : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] - ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] - : never - -export type CompositeTypes< - PublicCompositeTypeNameOrOptions extends - | keyof DefaultSchema["CompositeTypes"] - | { schema: keyof DatabaseWithoutInternals }, - CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals - } - ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] - : never = never, -> = PublicCompositeTypeNameOrOptions extends { - schema: keyof DatabaseWithoutInternals -} - ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] - : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] - ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] - : never - -export const Constants = { - public: { - Enums: { - app_role: ["admin", "user"], - }, - }, -} as const diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 0bfa4e9..68c749f 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -255,6 +255,50 @@ export const criarConta = createServerFn({ method: "POST" }) }; }); +export const recuperarSenha = createServerFn({ method: "POST" }) + .validator(loginSchema) + .handler(async ({ data }) => { + const nome = normalizarNome(data.nome); + const [{ getPool, sql }, bcrypt] = await Promise.all([ + import("@/lib/db.server"), + import("bcryptjs"), + ]); + const pool = await getPool(); + + const existente = await pool + .request() + .input("nome", sql.NVarChar(150), nome) + .query<{ id: string; ativo: boolean }>(` + SELECT TOP 1 id, ativo + FROM slalog.usuarios + WHERE LOWER(nome) = LOWER(@nome) + `); + + const usuario = existente.recordset[0]; + if (!usuario || !usuario.ativo) { + throw new Error("Nome de usuário não encontrado."); + } + + const senhaHash = await bcrypt.hash(data.senha, 10); + + await pool + .request() + .input("id", sql.UniqueIdentifier, usuario.id) + .input("senhaHash", sql.NVarChar(255), senhaHash) + .query(` + UPDATE slalog.usuarios + SET senha_hash = @senhaHash, + aprovado = 0, + updated_at = SYSDATETIME() + WHERE id = @id + `); + + const { clearAuthSession } = await import("@/lib/auth.server"); + await clearAuthSession(); + + return { success: true }; + }); + export const sair = createServerFn({ method: "POST" }).handler(async () => { const { clearAuthSession } = await import("@/lib/auth.server"); await clearAuthSession(); diff --git a/src/lib/coletas.ts b/src/lib/coletas.ts index 1d4ec1f..c05fd34 100644 --- a/src/lib/coletas.ts +++ b/src/lib/coletas.ts @@ -35,6 +35,7 @@ export const iniciarColeta = createServerFn({ method: "POST" }) const sessao = await obterSessaoAprovada(); const [{ getPool, sql }] = await Promise.all([import("@/lib/db.server")]); const pool = await getPool(); + const codigo = data.codigo.trim(); const dataAberta = await pool .request() @@ -48,10 +49,23 @@ export const iniciarColeta = createServerFn({ method: "POST" }) if (dataAberta.recordset[0]) throw new Error("Já existe uma coleta em andamento."); + const duplicado = await pool + .request() + .input("codigo", sql.NVarChar(100), codigo) + .query<{ id: number }>(` + SELECT TOP 1 id + FROM slalog.coletas + WHERE codigo_barras = @codigo + `); + + if (duplicado.recordset[0]) { + throw new Error("Este número de caixa já foi registrado anteriormente."); + } + const result = await pool .request() .input("usuario", sql.NVarChar(150), sessao.nome) - .input("codigo", sql.NVarChar(100), data.codigo) + .input("codigo", sql.NVarChar(100), codigo) .query(` INSERT INTO slalog.coletas (usuario, codigo_barras, inicio_coleta, created_at) OUTPUT diff --git a/src/routes/_authenticated/coleta.tsx b/src/routes/_authenticated/coleta.tsx index 85a2a72..41ac47b 100644 --- a/src/routes/_authenticated/coleta.tsx +++ b/src/routes/_authenticated/coleta.tsx @@ -42,13 +42,13 @@ export const Route = createFileRoute("/_authenticated/coleta")({ { name: "description", content: - "Registre o início e o fim da coleta de caixas informando o código do pedido, com cronômetro em tempo real.", + "Registre o início e o fim da coleta de caixas informando o número de caixa, com cronômetro em tempo real.", }, { property: "og:title", content: "Coleta de Caixas | Registro de pedidos" }, { property: "og:description", content: - "Registre o início e o fim da coleta de caixas informando o código do pedido, com cronômetro em tempo real.", + "Registre o início e o fim da coleta de caixas informando o número de caixa, com cronômetro em tempo real.", }, { property: "og:type", content: "website" }, { name: "twitter:card", content: "summary" }, @@ -129,7 +129,7 @@ function ColetaScreen() { async function handleIniciar() { const valor = codigo.trim(); if (!valor) { - toast.error("Digite o código do pedido antes de iniciar."); + toast.error("Digite o número de caixa antes de iniciar."); return; } if (coletaAtiva) { @@ -146,7 +146,14 @@ function ColetaScreen() { toast.success("Coleta iniciada."); } catch (error) { console.error(error); - toast.error("Erro ao iniciar a coleta. Tente novamente."); + const msg = (error as { message?: string })?.message ?? ""; + toast.error( + msg.includes("já foi registrado") + ? "Este número de caixa já foi contabilizado." + : msg.includes("coleta em andamento") + ? "Já existe uma coleta em andamento." + : "Erro ao iniciar a coleta. Tente novamente.", + ); } finally { setCarregando(false); } @@ -162,7 +169,7 @@ function ColetaScreen() { inicioRef.current = null; setConfirmarFim(false); toast.success( - adicionarOutro ? "Coleta finalizada. Digite o próximo pedido." : "Coleta finalizada.", + adicionarOutro ? "Coleta finalizada. Digite o próximo número de caixa." : "Coleta finalizada.", ); if (adicionarOutro) window.setTimeout(() => inputRef.current?.focus(), 50); } catch (error) { @@ -242,7 +249,7 @@ function ColetaScreen() {
@@ -325,7 +332,7 @@ function ColetaScreen() { Deseja realmente finalizar esta coleta? - O horário de término será registrado e o campo ficará pronto para o próximo pedido. + O horário de término será registrado e o campo ficará pronto para o próximo número de caixa. diff --git a/src/routes/auth.tsx b/src/routes/auth.tsx index 4cb2bdf..1031bc0 100644 --- a/src/routes/auth.tsx +++ b/src/routes/auth.tsx @@ -1,12 +1,12 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useEffect, useState } from "react"; import { toast } from "sonner"; -import { Boxes, Loader2, LogIn, UserPlus } from "lucide-react"; +import { Boxes, Eye, EyeOff, KeyRound, Loader2, LogIn, UserPlus } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { criarConta, entrar, obterSessaoAtual, normalizarNome } from "@/lib/auth"; +import { criarConta, entrar, obterSessaoAtual, normalizarNome, recuperarSenha } from "@/lib/auth"; export const Route = createFileRoute("/auth")({ head: () => ({ @@ -32,11 +32,14 @@ export const Route = createFileRoute("/auth")({ function AuthPage() { const navigate = useNavigate(); - const [modo, setModo] = useState<"entrar" | "criar">("entrar"); + const [modo, setModo] = useState<"entrar" | "criar" | "recuperar">("entrar"); const [nome, setNome] = useState(""); const [senha, setSenha] = useState(""); + const [confirmarSenha, setConfirmarSenha] = useState(""); + const [mostrarSenha, setMostrarSenha] = useState(false); const [carregando, setCarregando] = useState(false); const [verificando, setVerificando] = useState(true); + const precisaConfirmarSenha = modo !== "entrar"; useEffect(() => { let cancelado = false; @@ -57,6 +60,10 @@ function AuthPage() { toast.error("Informe um nome com pelo menos 3 caracteres e senha com 6 ou mais."); return; } + if (precisaConfirmarSenha && senha !== confirmarSenha) { + toast.error("As senhas digitadas não coincidem."); + return; + } setCarregando(true); try { if (modo === "criar") { @@ -66,6 +73,10 @@ function AuthPage() { ? "Conta criada e liberada. Faça login." : "Conta criada! Aguarde a liberação de um administrador.", ); + } else if (modo === "recuperar") { + await recuperarSenha({ data: { nome: usuario, senha } }); + toast.success("Senha redefinida. Aguarde a aprovação de um administrador."); + setModo("entrar"); } else { await entrar({ data: { nome: usuario, senha } }); void navigate({ to: "/coleta", replace: true }); @@ -76,6 +87,8 @@ function AuthPage() { toast.error( msg.includes("incorretos") ? "Nome de usuário ou senha incorretos." + : msg.includes("não encontrado") + ? "Nome de usuário não encontrado." : msg.includes("já existe") ? "Este nome de usuário já existe. Faça login." : "Não foi possível concluir. Tente novamente.", @@ -104,7 +117,9 @@ function AuthPage() {

{modo === "entrar" ? "Entre com seu nome de usuário e senha." - : "Crie sua conta — o acesso é liberado por um administrador."} + : modo === "criar" + ? "Crie sua conta — o acesso é liberado por um administrador." + : "Defina uma nova senha. Depois disso, a conta volta para aprovação do administrador."}

@@ -124,17 +139,52 @@ function AuthPage() {
- setSenha(e.target.value)} - placeholder="Mínimo de 6 caracteres" - className="h-12 rounded-xl" - /> +
+ setSenha(e.target.value)} + placeholder="Mínimo de 6 caracteres" + className="h-12 rounded-xl pr-12" + /> + +
+ {precisaConfirmarSenha ? ( +
+ +
+ setConfirmarSenha(e.target.value)} + placeholder="Repita a senha" + className="h-12 rounded-xl pr-12" + /> + +
+
+ ) : null} + - +
+ {modo !== "entrar" ? ( + + ) : null} + {modo !== "criar" ? ( + + ) : null} + {modo !== "recuperar" ? ( + + ) : null} +
); diff --git a/supabase/config.toml b/supabase/config.toml deleted file mode 100644 index ed46980..0000000 --- a/supabase/config.toml +++ /dev/null @@ -1 +0,0 @@ -project_id = "iiknzpjckdzlpglimmtq" \ No newline at end of file diff --git a/supabase/migrations/20260731131104_b9f169f0-e015-4cd8-ad93-52d659270785.sql b/supabase/migrations/20260731131104_b9f169f0-e015-4cd8-ad93-52d659270785.sql deleted file mode 100644 index fc5ac5b..0000000 --- a/supabase/migrations/20260731131104_b9f169f0-e015-4cd8-ad93-52d659270785.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE TABLE public.coletas ( - id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - usuario TEXT NOT NULL, - codigo_barras TEXT NOT NULL, - inicio_coleta TIMESTAMPTZ NOT NULL DEFAULT now(), - fim_coleta TIMESTAMPTZ, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() -); - -GRANT SELECT, INSERT, UPDATE ON public.coletas TO anon; -GRANT SELECT, INSERT, UPDATE, DELETE ON public.coletas TO authenticated; -GRANT ALL ON public.coletas TO service_role; - -ALTER TABLE public.coletas ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Coletas sao visiveis publicamente" ON public.coletas FOR SELECT TO anon, authenticated USING (true); -CREATE POLICY "Qualquer um pode criar coleta" ON public.coletas FOR INSERT TO anon, authenticated WITH CHECK (true); -CREATE POLICY "Qualquer um pode atualizar coleta" ON public.coletas FOR UPDATE TO anon, authenticated USING (true) WITH CHECK (true); - -CREATE INDEX idx_coletas_usuario_abertas ON public.coletas (usuario) WHERE fim_coleta IS NULL; \ No newline at end of file diff --git a/supabase/migrations/20260803175010_59493172-5f03-4832-94e2-87daaecf9a83.sql b/supabase/migrations/20260803175010_59493172-5f03-4832-94e2-87daaecf9a83.sql deleted file mode 100644 index 0bd3810..0000000 --- a/supabase/migrations/20260803175010_59493172-5f03-4832-94e2-87daaecf9a83.sql +++ /dev/null @@ -1,141 +0,0 @@ --- Perfis -CREATE TABLE public.perfis ( - id uuid PRIMARY KEY, - email text NOT NULL, - aprovado boolean NOT NULL DEFAULT false, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now() -); - -GRANT SELECT, INSERT, UPDATE ON public.perfis TO authenticated; -GRANT ALL ON public.perfis TO service_role; -ALTER TABLE public.perfis ENABLE ROW LEVEL SECURITY; - --- Papeis -CREATE TYPE public.app_role AS ENUM ('admin', 'user'); - -CREATE TABLE public.papeis_usuario ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL, - role public.app_role NOT NULL, - created_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (user_id, role) -); - -GRANT SELECT ON public.papeis_usuario TO authenticated; -GRANT ALL ON public.papeis_usuario TO service_role; -ALTER TABLE public.papeis_usuario ENABLE ROW LEVEL SECURITY; - -CREATE OR REPLACE FUNCTION public.has_role(_user_id uuid, _role public.app_role) -RETURNS boolean -LANGUAGE sql -STABLE -SECURITY DEFINER -SET search_path = public -AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.papeis_usuario WHERE user_id = _user_id AND role = _role - ) -$$; - -CREATE OR REPLACE FUNCTION public.is_aprovado(_user_id uuid) -RETURNS boolean -LANGUAGE sql -STABLE -SECURITY DEFINER -SET search_path = public -AS $$ - SELECT EXISTS ( - SELECT 1 FROM public.perfis WHERE id = _user_id AND aprovado = true - ) -$$; - --- Policies perfis -CREATE POLICY "Ver proprio perfil" ON public.perfis - FOR SELECT TO authenticated USING (id = auth.uid()); -CREATE POLICY "Admins veem todos os perfis" ON public.perfis - FOR SELECT TO authenticated USING (public.has_role(auth.uid(), 'admin')); -CREATE POLICY "Criar proprio perfil" ON public.perfis - FOR INSERT TO authenticated WITH CHECK (id = auth.uid()); -CREATE POLICY "Admins atualizam perfis" ON public.perfis - FOR UPDATE TO authenticated - USING (public.has_role(auth.uid(), 'admin')) - WITH CHECK (public.has_role(auth.uid(), 'admin')); - --- Policies papeis -CREATE POLICY "Ver proprios papeis" ON public.papeis_usuario - FOR SELECT TO authenticated USING (user_id = auth.uid()); -CREATE POLICY "Admins veem todos os papeis" ON public.papeis_usuario - FOR SELECT TO authenticated USING (public.has_role(auth.uid(), 'admin')); - --- Registro de perfil: primeiro usuario vira admin aprovado -CREATE OR REPLACE FUNCTION public.registrar_perfil() -RETURNS public.perfis -LANGUAGE plpgsql -SECURITY DEFINER -SET search_path = public -AS $$ -DECLARE - v_uid uuid := auth.uid(); - v_email text; - v_primeiro boolean; - v_perfil public.perfis; -BEGIN - IF v_uid IS NULL THEN - RAISE EXCEPTION 'Nao autenticado'; - END IF; - - SELECT * INTO v_perfil FROM public.perfis WHERE id = v_uid; - IF FOUND THEN - RETURN v_perfil; - END IF; - - SELECT email INTO v_email FROM auth.users WHERE id = v_uid; - SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro; - - INSERT INTO public.perfis (id, email, aprovado) - VALUES (v_uid, coalesce(v_email, ''), v_primeiro) - RETURNING * INTO v_perfil; - - IF v_primeiro THEN - INSERT INTO public.papeis_usuario (user_id, role) VALUES (v_uid, 'admin') - ON CONFLICT DO NOTHING; - END IF; - - RETURN v_perfil; -END; -$$; - -GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated; - -CREATE OR REPLACE FUNCTION public.set_updated_at() -RETURNS TRIGGER -LANGUAGE plpgsql -SET search_path = public -AS $$ -BEGIN - NEW.updated_at = now(); - RETURN NEW; -END; -$$; - -CREATE TRIGGER perfis_updated_at BEFORE UPDATE ON public.perfis -FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); - --- Coletas: apenas usuarios logados e aprovados -DROP POLICY IF EXISTS "Coletas sao visiveis publicamente" ON public.coletas; -DROP POLICY IF EXISTS "Qualquer um pode atualizar coleta" ON public.coletas; -DROP POLICY IF EXISTS "Qualquer um pode criar coleta" ON public.coletas; - -REVOKE ALL ON public.coletas FROM anon; -GRANT SELECT, INSERT, UPDATE ON public.coletas TO authenticated; -GRANT ALL ON public.coletas TO service_role; - -CREATE POLICY "Aprovados veem coletas" ON public.coletas - FOR SELECT TO authenticated USING (public.is_aprovado(auth.uid())); -CREATE POLICY "Aprovados criam coletas" ON public.coletas - FOR INSERT TO authenticated WITH CHECK (public.is_aprovado(auth.uid())); -CREATE POLICY "Aprovados atualizam coletas" ON public.coletas - FOR UPDATE TO authenticated - USING (public.is_aprovado(auth.uid())) - WITH CHECK (public.is_aprovado(auth.uid())); \ No newline at end of file diff --git a/supabase/migrations/20260803175040_e9284991-f847-4754-b9d2-8707e7caa31e.sql b/supabase/migrations/20260803175040_e9284991-f847-4754-b9d2-8707e7caa31e.sql deleted file mode 100644 index fc65157..0000000 --- a/supabase/migrations/20260803175040_e9284991-f847-4754-b9d2-8707e7caa31e.sql +++ /dev/null @@ -1,8 +0,0 @@ -REVOKE ALL ON FUNCTION public.has_role(uuid, public.app_role) FROM PUBLIC, anon; -REVOKE ALL ON FUNCTION public.is_aprovado(uuid) FROM PUBLIC, anon; -REVOKE ALL ON FUNCTION public.registrar_perfil() FROM PUBLIC, anon; -REVOKE ALL ON FUNCTION public.set_updated_at() FROM PUBLIC, anon, authenticated; - -GRANT EXECUTE ON FUNCTION public.has_role(uuid, public.app_role) TO authenticated; -GRANT EXECUTE ON FUNCTION public.is_aprovado(uuid) TO authenticated; -GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated; \ No newline at end of file diff --git a/supabase/migrations/20260803181430_9973b16d-ccc8-4a27-ac49-bc821c7f8600.sql b/supabase/migrations/20260803181430_9973b16d-ccc8-4a27-ac49-bc821c7f8600.sql deleted file mode 100644 index 6da0793..0000000 --- a/supabase/migrations/20260803181430_9973b16d-ccc8-4a27-ac49-bc821c7f8600.sql +++ /dev/null @@ -1,46 +0,0 @@ -ALTER TABLE public.perfis ADD COLUMN IF NOT EXISTS nome text; - -UPDATE public.perfis SET nome = split_part(email, '@', 1) WHERE nome IS NULL; - -ALTER TABLE public.perfis ALTER COLUMN nome SET NOT NULL; - -CREATE UNIQUE INDEX IF NOT EXISTS perfis_nome_unico ON public.perfis (lower(nome)); - -CREATE OR REPLACE FUNCTION public.registrar_perfil() - RETURNS perfis - LANGUAGE plpgsql - SECURITY DEFINER - SET search_path TO 'public' -AS $function$ -DECLARE - v_uid uuid := auth.uid(); - v_email text; - v_nome text; - v_primeiro boolean; - v_perfil public.perfis; -BEGIN - IF v_uid IS NULL THEN - RAISE EXCEPTION 'Nao autenticado'; - END IF; - - SELECT * INTO v_perfil FROM public.perfis WHERE id = v_uid; - IF FOUND THEN - RETURN v_perfil; - END IF; - - SELECT email INTO v_email FROM auth.users WHERE id = v_uid; - v_nome := split_part(coalesce(v_email, ''), '@', 1); - SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro; - - INSERT INTO public.perfis (id, email, nome, aprovado) - VALUES (v_uid, coalesce(v_email, ''), v_nome, v_primeiro) - RETURNING * INTO v_perfil; - - IF v_primeiro THEN - INSERT INTO public.papeis_usuario (user_id, role) VALUES (v_uid, 'admin') - ON CONFLICT DO NOTHING; - END IF; - - RETURN v_perfil; -END; -$function$; \ No newline at end of file diff --git a/supabase/migrations/20260803182902_81889677-83e4-4f69-9bc7-879355dd9edd.sql b/supabase/migrations/20260803182902_81889677-83e4-4f69-9bc7-879355dd9edd.sql deleted file mode 100644 index 9148da3..0000000 --- a/supabase/migrations/20260803182902_81889677-83e4-4f69-9bc7-879355dd9edd.sql +++ /dev/null @@ -1,4 +0,0 @@ -REVOKE ALL ON FUNCTION public.has_role(uuid, public.app_role) FROM PUBLIC, anon, authenticated; -REVOKE ALL ON FUNCTION public.is_aprovado(uuid) FROM PUBLIC, anon, authenticated; -REVOKE ALL ON FUNCTION public.registrar_perfil() FROM PUBLIC, anon; -GRANT EXECUTE ON FUNCTION public.registrar_perfil() TO authenticated; \ No newline at end of file diff --git a/supabase/migrations/20260803183004_c3bea5a2-7c9a-413f-b471-94a01200da30.sql b/supabase/migrations/20260803183004_c3bea5a2-7c9a-413f-b471-94a01200da30.sql deleted file mode 100644 index 44501c4..0000000 --- a/supabase/migrations/20260803183004_c3bea5a2-7c9a-413f-b471-94a01200da30.sql +++ /dev/null @@ -1,58 +0,0 @@ -CREATE SCHEMA IF NOT EXISTS private; -REVOKE ALL ON SCHEMA private FROM PUBLIC, anon, authenticated; -GRANT USAGE ON SCHEMA private TO postgres, service_role; - -CREATE OR REPLACE FUNCTION private.has_role(_user_id uuid, _role public.app_role) -RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' -AS $$ SELECT EXISTS (SELECT 1 FROM public.papeis_usuario WHERE user_id = _user_id AND role = _role) $$; - -CREATE OR REPLACE FUNCTION private.is_aprovado(_user_id uuid) -RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path TO 'public' -AS $$ SELECT EXISTS (SELECT 1 FROM public.perfis WHERE id = _user_id AND aprovado = true) $$; - -DROP POLICY "Admins veem todos os papeis" ON public.papeis_usuario; -CREATE POLICY "Admins veem todos os papeis" ON public.papeis_usuario FOR SELECT TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role)); - -DROP POLICY "Admins atualizam perfis" ON public.perfis; -CREATE POLICY "Admins atualizam perfis" ON public.perfis FOR UPDATE TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role)) WITH CHECK (private.has_role(auth.uid(), 'admin'::public.app_role)); - -DROP POLICY "Admins veem todos os perfis" ON public.perfis; -CREATE POLICY "Admins veem todos os perfis" ON public.perfis FOR SELECT TO authenticated USING (private.has_role(auth.uid(), 'admin'::public.app_role)); - -DROP POLICY "Aprovados atualizam coletas" ON public.coletas; -CREATE POLICY "Aprovados atualizam coletas" ON public.coletas FOR UPDATE TO authenticated USING (private.is_aprovado(auth.uid())) WITH CHECK (private.is_aprovado(auth.uid())); - -DROP POLICY "Aprovados criam coletas" ON public.coletas; -CREATE POLICY "Aprovados criam coletas" ON public.coletas FOR INSERT TO authenticated WITH CHECK (private.is_aprovado(auth.uid())); - -DROP POLICY "Aprovados veem coletas" ON public.coletas; -CREATE POLICY "Aprovados veem coletas" ON public.coletas FOR SELECT TO authenticated USING (private.is_aprovado(auth.uid())); - -DROP FUNCTION IF EXISTS public.has_role(uuid, public.app_role); -DROP FUNCTION IF EXISTS public.is_aprovado(uuid); - -CREATE OR REPLACE FUNCTION private.registrar_perfil_para(_uid uuid, _email text) -RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' -AS $$ -DECLARE v_primeiro boolean; -BEGIN - IF EXISTS (SELECT 1 FROM public.perfis WHERE id = _uid) THEN RETURN; END IF; - SELECT NOT EXISTS (SELECT 1 FROM public.perfis) INTO v_primeiro; - INSERT INTO public.perfis (id, email, nome, aprovado) - VALUES (_uid, coalesce(_email, ''), split_part(coalesce(_email, ''), '@', 1), v_primeiro); - IF v_primeiro THEN - INSERT INTO public.papeis_usuario (user_id, role) VALUES (_uid, 'admin') ON CONFLICT DO NOTHING; - END IF; -END; -$$; - -CREATE OR REPLACE FUNCTION private.on_auth_user_created() -RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public' -AS $$ -BEGIN - PERFORM private.registrar_perfil_para(NEW.id, NEW.email); - RETURN NEW; -END; -$$; - -DROP FUNCTION IF EXISTS public.registrar_perfil(); \ No newline at end of file