From 9d303b0c7a0a17b482d10bec687d4b19453372bd Mon Sep 17 00:00:00 2001 From: Lucas Santana Date: Fri, 7 Feb 2025 09:37:15 -0300 Subject: [PATCH] =?UTF-8?q?refactor:=20normaliza=20JSON=20Schema=20da=20an?= =?UTF-8?q?=C3=A1lise=20de=20reda=C3=A7=C3=B5es=20-=20Reordena=20campos=20?= =?UTF-8?q?para=20corresponder=20=C3=A0=20estrutura=20do=20banco=20de=20da?= =?UTF-8?q?dos=20-=20Ajusta=20descri=C3=A7=C3=B5es=20dos=20campos=20para?= =?UTF-8?q?=20maior=20clareza=20-=20Alinha=20com=20as=20tabelas:=20essay?= =?UTF-8?q?=5Fanalyses=20e=20relacionadas=20-=20Melhora=20valida=C3=A7?= =?UTF-8?q?=C3=A3o=20dos=20dados=20com=20JSON=20Schema=20mais=20preciso?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 + supabase/contexts/constraints.md | 77 ++++ supabase/contexts/dependencies.md | 38 ++ supabase/contexts/full_schema.md | 386 ++++++++++-------- supabase/contexts/functions.md | 116 +++--- supabase/contexts/policies.md | 144 ++++--- supabase/functions/analyze-essay/index.ts | 199 +++++++-- .../20240326000001_create_essay_system.sql | 68 ++- ...326000001_create_essay_system_rollback.sql | 12 +- ...0240327000001_normalize_essay_analyses.sql | 165 ++++++++ ...0001_normalize_essay_analyses_rollback.sql | 89 ++++ 11 files changed, 966 insertions(+), 335 deletions(-) create mode 100644 supabase/migrations/20240327000001_normalize_essay_analyses.sql create mode 100644 supabase/migrations/20240327000001_normalize_essay_analyses_rollback.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index 7106f4c..4b022b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -250,3 +250,10 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/). - Configuração centralizada de métricas - Suporte a tooltips e ícones personalizados - Responsividade e acessibilidade melhoradas + +### Técnico +- Normalização do JSON Schema da análise de redações para corresponder à estrutura do banco de dados + - Reordenação dos campos para corresponder à estrutura das tabelas + - Ajuste nas descrições dos campos para maior clareza + - Alinhamento com as tabelas: essay_analyses, essay_analysis_feedback, essay_analysis_strengths e essay_analysis_improvements + - Melhoria na validação dos dados com JSON Schema mais preciso diff --git a/supabase/contexts/constraints.md b/supabase/contexts/constraints.md index 202a53a..2bf463d 100644 --- a/supabase/contexts/constraints.md +++ b/supabase/contexts/constraints.md @@ -88,6 +88,8 @@ | public | interests | item | UNIQUE | | public | interests | student_id | FOREIGN KEY | | public | languages | code | UNIQUE | +| public | essay_analysis_scores | id | PRIMARY KEY | +| public | essay_analysis_scores | analysis_id | FOREIGN KEY | | supabase_migrations | schema_migrations | version | PRIMARY KEY | | supabase_migrations | seed_files | path | PRIMARY KEY | | public | phonics_categories | id | PRIMARY KEY | @@ -108,10 +110,19 @@ | public | story_characters | slug | UNIQUE | | public | story_settings | id | PRIMARY KEY | | public | story_settings | slug | UNIQUE | +| public | essay_types | id | PRIMARY KEY | +| public | essay_types | slug | UNIQUE | | public | stories | theme_id | FOREIGN KEY | | public | stories | subject_id | FOREIGN KEY | | public | stories | character_id | FOREIGN KEY | | public | stories | setting_id | FOREIGN KEY | +| public | essay_genres | id | PRIMARY KEY | +| public | essay_genres | slug | UNIQUE | +| public | essay_genres | type_id | FOREIGN KEY | +| public | student_essays | id | PRIMARY KEY | +| public | student_essays | student_id | FOREIGN KEY | +| public | student_essays | type_id | FOREIGN KEY | +| public | student_essays | genre_id | FOREIGN KEY | | auth | identities | user_id | FOREIGN KEY | | auth | refresh_tokens | token | UNIQUE | | auth | sessions | id | PRIMARY KEY | @@ -150,12 +161,20 @@ | storage | s3_multipart_uploads_parts | bucket_id | FOREIGN KEY | | realtime | schema_migrations | version | PRIMARY KEY | | realtime | subscription | id | PRIMARY KEY | +| public | essay_analyses | id | PRIMARY KEY | +| public | essay_analyses | essay_id | FOREIGN KEY | | public | stories | theme_id | FOREIGN KEY | | public | stories | subject_id | FOREIGN KEY | | public | stories | character_id | FOREIGN KEY | | public | stories | setting_id | FOREIGN KEY | +| public | essay_analysis_feedback | id | PRIMARY KEY | | realtime | messages | id | PRIMARY KEY | | realtime | messages | inserted_at | PRIMARY KEY | +| public | essay_analysis_feedback | analysis_id | FOREIGN KEY | +| public | essay_analysis_strengths | id | PRIMARY KEY | +| public | essay_analysis_strengths | analysis_id | FOREIGN KEY | +| public | essay_analysis_improvements | id | PRIMARY KEY | +| public | essay_analysis_improvements | analysis_id | FOREIGN KEY | | public | story_subjects | null | CHECK | | public | story_recordings | null | CHECK | | public | story_characters | null | CHECK | @@ -174,9 +193,11 @@ | public | students | null | CHECK | | public | phonics_word_audio | null | CHECK | | public | languages | null | CHECK | +| public | student_essays | null | CHECK | | pgsodium | key | null | CHECK | | public | phonics_word_audio | null | CHECK | | realtime | messages | null | CHECK | +| public | essay_analysis_strengths | null | CHECK | | public | achievements | null | CHECK | | public | phonics_exercise_types | null | CHECK | | public | teacher_invites | null | CHECK | @@ -186,6 +207,7 @@ | auth | refresh_tokens | null | CHECK | | pgsodium | key | null | CHECK | | public | student_phonics_attempt_answers | null | CHECK | +| public | essay_types | null | CHECK | | auth | identities | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | | auth | flow_state | null | CHECK | @@ -194,6 +216,7 @@ | public | stories | null | CHECK | | public | story_subjects | null | CHECK | | auth | flow_state | null | CHECK | +| public | essay_analyses | null | CHECK | | public | phonics_words | null | CHECK | | realtime | subscription | null | CHECK | | auth | sso_domains | null | CHECK | @@ -201,25 +224,36 @@ | auth | users | null | CHECK | | public | phonics_categories | null | CHECK | | public | teacher_invites | null | CHECK | +| public | essay_genres | null | CHECK | | public | stories | null | CHECK | | public | story_themes | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | +| public | student_essays | null | CHECK | | public | teachers | null | CHECK | | public | teachers | null | CHECK | | public | story_themes | null | CHECK | +| public | essay_genres | null | CHECK | | public | phonics_exercise_words | null | CHECK | | public | students | null | CHECK | | realtime | subscription | null | CHECK | | auth | mfa_amr_claims | null | CHECK | | public | teacher_classes | null | CHECK | +| public | essay_genres | null | CHECK | | auth | flow_state | null | CHECK | +| public | essay_analysis_scores | null | CHECK | +| public | student_essays | null | CHECK | +| public | essay_types | null | CHECK | | public | students | null | CHECK | | public | story_characters | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | auth | mfa_factors | null | CHECK | +| public | student_essays | null | CHECK | | realtime | schema_migrations | null | CHECK | | public | languages | null | CHECK | | public | stories | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | public | phonics_word_audio | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | pgsodium | key | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | | auth | saml_providers | null | CHECK | @@ -232,7 +266,9 @@ | vault | secrets | null | CHECK | | public | students | null | CHECK | | vault | secrets | null | CHECK | +| public | essay_genres | null | CHECK | | auth | users | null | CHECK | +| public | student_essays | null | CHECK | | public | story_themes | null | CHECK | | public | classes | null | CHECK | | auth | mfa_amr_claims | null | CHECK | @@ -246,13 +282,19 @@ | public | teachers | null | CHECK | | auth | mfa_amr_claims | null | CHECK | | public | teacher_invites | null | CHECK | +| public | student_essays | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | | public | teachers | null | CHECK | +| public | student_essays | null | CHECK | +| public | essay_analysis_scores | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | public | phonics_categories | null | CHECK | | public | story_settings | null | CHECK | | public | schools | null | CHECK | | realtime | subscription | null | CHECK | | public | story_recordings | null | CHECK | +| public | essay_genres | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | public | stories | null | CHECK | | public | story_pages | null | CHECK | | auth | identities | null | CHECK | @@ -260,34 +302,44 @@ | auth | flow_state | null | CHECK | | auth | one_time_tokens | null | CHECK | | public | teacher_invites | null | CHECK | +| public | essay_genres | null | CHECK | | auth | schema_migrations | null | CHECK | | auth | mfa_challenges | null | CHECK | | public | student_phonics_attempts | null | CHECK | | public | story_settings | null | CHECK | | public | teachers | null | CHECK | | pgsodium | key | null | CHECK | +| public | essay_types | null | CHECK | | public | interests | null | CHECK | | auth | instances | null | CHECK | | public | schools | null | CHECK | | public | student_phonics_achievements | null | CHECK | | storage | migrations | null | CHECK | | public | classes | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | public | story_exercise_words | null | CHECK | | public | phonics_word_audio | null | CHECK | | public | media_types | null | CHECK | | public | languages | null | CHECK | | public | story_themes | null | CHECK | +| public | essay_analysis_strengths | null | CHECK | | auth | one_time_tokens | null | CHECK | +| public | essay_analyses | null | CHECK | | auth | one_time_tokens | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | realtime | messages | null | CHECK | | realtime | messages | null | CHECK | | public | story_characters | null | CHECK | | public | story_subjects | null | CHECK | +| public | essay_analysis_scores | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | | auth | sso_domains | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | +| public | essay_genres | null | CHECK | | public | teacher_invites | null | CHECK | | public | students | null | CHECK | +| public | essay_analysis_improvements | null | CHECK | | auth | saml_relay_states | null | CHECK | | public | stories | null | CHECK | | public | story_settings | null | CHECK | @@ -301,17 +353,22 @@ | public | phonics_achievements | null | CHECK | | public | phonics_exercises | null | CHECK | | public | story_pages | null | CHECK | +| public | essay_types | null | CHECK | | public | student_phonics_progress | null | CHECK | | auth | saml_providers | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | realtime | subscription | null | CHECK | | public | story_subjects | null | CHECK | +| public | essay_analyses | null | CHECK | | auth | sso_providers | null | CHECK | +| public | student_essays | null | CHECK | | public | media_types | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | | auth | one_time_tokens | null | CHECK | | public | story_generations | null | CHECK | | auth | saml_relay_states | null | CHECK | | public | story_themes | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | storage | buckets | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | | vault | secrets | null | CHECK | @@ -330,35 +387,44 @@ | public | student_phonics_attempts | null | CHECK | | public | story_generations | null | CHECK | | public | phonics_exercises | null | CHECK | +| public | student_essays | null | CHECK | | auth | sso_domains | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | +| public | essay_genres | null | CHECK | | public | schools | null | CHECK | | auth | users | null | CHECK | | public | interests | null | CHECK | | realtime | messages | null | CHECK | +| public | essay_types | null | CHECK | | public | phonics_exercise_types | null | CHECK | | public | story_exercise_words | null | CHECK | | auth | audit_log_entries | null | CHECK | | public | story_characters | null | CHECK | | public | student_achievements_old | null | CHECK | +| public | essay_types | null | CHECK | | net | http_request_queue | null | CHECK | | auth | saml_providers | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | +| public | essay_analysis_strengths | null | CHECK | | supabase_migrations | seed_files | null | CHECK | | public | phonics_achievements | null | CHECK | | public | story_pages | null | CHECK | | public | phonics_exercises | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | storage | objects | null | CHECK | +| public | essay_analysis_improvements | null | CHECK | | public | student_phonics_attempt_answers | null | CHECK | | storage | migrations | null | CHECK | | auth | sso_domains | null | CHECK | | public | story_recordings | null | CHECK | +| public | essay_types | null | CHECK | | public | classes | null | CHECK | | net | _http_response | null | CHECK | | realtime | subscription | null | CHECK | | public | teacher_invites | null | CHECK | | auth | sessions | null | CHECK | | public | phonics_exercise_media | null | CHECK | +| public | essay_analysis_improvements | null | CHECK | | public | schools | null | CHECK | | net | http_request_queue | null | CHECK | | public | classes | null | CHECK | @@ -373,12 +439,16 @@ | auth | identities | null | CHECK | | storage | s3_multipart_uploads | null | CHECK | | public | story_themes | null | CHECK | +| public | essay_analyses | null | CHECK | | auth | saml_relay_states | null | CHECK | +| public | essay_analyses | null | CHECK | | public | teacher_invites | null | CHECK | +| public | essay_analysis_feedback | null | CHECK | | auth | mfa_factors | null | CHECK | | auth | identities | null | CHECK | | public | stories | null | CHECK | | public | story_pages | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | vault | secrets | null | CHECK | | vault | secrets | null | CHECK | | public | achievement_types | null | CHECK | @@ -388,6 +458,7 @@ | supabase_migrations | schema_migrations | null | CHECK | | public | stories | null | CHECK | | public | phonics_exercise_media | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | auth | mfa_challenges | null | CHECK | | public | story_characters | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | @@ -401,17 +472,23 @@ | auth | sessions | null | CHECK | | public | story_pages | null | CHECK | | net | http_request_queue | null | CHECK | +| public | essay_analysis_strengths | null | CHECK | | auth | saml_providers | null | CHECK | | public | teacher_classes | null | CHECK | | public | story_generations | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | auth | flow_state | null | CHECK | | public | story_characters | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | | public | student_achievements | null | CHECK | +| public | student_essays | null | CHECK | +| public | essay_analysis_scores | null | CHECK | | public | students | null | CHECK | | public | story_generations | null | CHECK | | auth | one_time_tokens | null | CHECK | | net | http_request_queue | null | CHECK | | auth | mfa_factors | null | CHECK | +| public | essay_analysis_improvements | null | CHECK | +| public | essay_genres | null | CHECK | | storage | s3_multipart_uploads_parts | null | CHECK | | public | teacher_classes | null | CHECK | \ No newline at end of file diff --git a/supabase/contexts/dependencies.md b/supabase/contexts/dependencies.md index 4395e99..4084c83 100644 --- a/supabase/contexts/dependencies.md +++ b/supabase/contexts/dependencies.md @@ -617,14 +617,52 @@ | 29638 | schools | | 29639 | students | | 29639 | schools | +| 113605 | essay_types_pkey | +| 113605 | essay_types | +| 113617 | student_essays | +| 113622 | students_pkey | +| 113622 | students | +| 113627 | essay_types_pkey | +| 113627 | essay_types | +| 113632 | essay_genres_pkey | +| 113632 | essay_genres | +| 113642 | essay_analyses | +| 113647 | student_essays_pkey | +| 113647 | student_essays | +| 113661 | essay_analyses_pkey | +| 113661 | essay_analyses | +| 113675 | essay_analyses_pkey | +| 113675 | essay_analyses | +| 113689 | essay_analyses_pkey | +| 113689 | essay_analyses | | 29709 | students_pkey | | 29709 | students | | 29716 | storage.objects | | 29717 | storage.objects | +| 113699 | essay_analysis_scores | +| 113700 | essay_analysis_scores | +| 113701 | essay_analysis_scores | +| 113702 | essay_analysis_scores | +| 113703 | essay_analysis_scores | +| 113706 | essay_analyses_pkey | +| 113706 | essay_analyses | | 29748 | story_recordings | | 29749 | story_recordings | | 74045 | storage.objects | | 34119 | net.http_request_queue_id_seq | +| 113764 | essay_types | +| 113765 | essay_genres | +| 113766 | student_essays | +| 113767 | student_essays | +| 113768 | student_essays | +| 113768 | student_essays | +| 113769 | student_essays | +| 113770 | essay_analyses | +| 113770 | student_essays | +| 113770 | student_essays | +| 113771 | essay_analyses | +| 113771 | student_essays | +| 113771 | student_essays | | 29823 | auth.users | | 53921 | story_exercise_words | | 53928 | stories_pkey | diff --git a/supabase/contexts/full_schema.md b/supabase/contexts/full_schema.md index fd6e615..e2ddc6d 100644 --- a/supabase/contexts/full_schema.md +++ b/supabase/contexts/full_schema.md @@ -1,6 +1,6 @@ | table_schema | table_name | column_name | data_type | is_nullable | column_default | | ------------------- | ------------------------------- | --------------------------- | --------------------------- | ----------- | -------------------------------------------------- | -| storage | s3_multipart_uploads | user_metadata | jsonb | YES | null | +| realtime | schema_migrations | version | bigint | NO | null | | realtime | schema_migrations | inserted_at | timestamp without time zone | YES | null | | extensions | pg_stat_statements_info | dealloc | bigint | YES | null | | extensions | pg_stat_statements_info | stats_reset | timestamp with time zone | YES | null | @@ -8,6 +8,23 @@ | extensions | pg_stat_statements | dbid | oid | YES | null | | extensions | pg_stat_statements | toplevel | boolean | YES | null | | extensions | pg_stat_statements | queryid | bigint | YES | null | +| public | teachers | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | teachers | class_ids | ARRAY | YES | null | +| public | classes | id | uuid | NO | uuid_generate_v4() | +| public | classes | school_id | uuid | NO | null | +| public | classes | year | integer | NO | null | +| public | classes | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | classes | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | classes | teacher_id | uuid | YES | null | +| public | essay_analysis_scores | id | uuid | NO | uuid_generate_v4() | +| public | essay_analysis_scores | analysis_id | uuid | NO | null | +| public | essay_analysis_scores | adequacy | integer | NO | null | +| public | essay_analysis_scores | coherence | integer | NO | null | +| public | essay_analysis_scores | cohesion | integer | NO | null | +| public | essay_analysis_scores | vocabulary | integer | NO | null | +| public | essay_analysis_scores | grammar | integer | NO | null | +| public | essay_analysis_scores | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | teacher_classes | id | uuid | NO | uuid_generate_v4() | | public | teacher_classes | teacher_id | uuid | NO | null | | public | teacher_classes | class_id | uuid | NO | null | | public | teacher_classes | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | @@ -233,7 +250,7 @@ | storage | s3_multipart_uploads_parts | created_at | timestamp with time zone | NO | now() | | storage | s3_multipart_uploads | in_progress_size | bigint | NO | 0 | | storage | s3_multipart_uploads | created_at | timestamp with time zone | NO | now() | -| realtime | schema_migrations | version | bigint | NO | null | +| storage | s3_multipart_uploads | user_metadata | jsonb | YES | null | | extensions | pg_stat_statements | plans | bigint | YES | null | | extensions | pg_stat_statements | total_plan_time | double precision | YES | null | | extensions | pg_stat_statements | min_plan_time | double precision | YES | null | @@ -338,7 +355,17 @@ | storage | objects | user_metadata | jsonb | YES | null | | storage | migrations | id | integer | NO | null | | storage | migrations | executed_at | timestamp without time zone | YES | CURRENT_TIMESTAMP | +| public | essay_types | id | uuid | NO | uuid_generate_v4() | +| public | essay_types | active | boolean | YES | true | +| public | essay_types | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_types | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | pgsodium | mask_columns | attrelid | oid | YES | null | +| public | essay_genres | id | uuid | NO | uuid_generate_v4() | +| public | essay_genres | type_id | uuid | NO | null | +| public | essay_genres | requirements | jsonb | NO | '{}'::jsonb | +| public | essay_genres | active | boolean | YES | true | +| public | essay_genres | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_genres | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | pgsodium | valid_key | id | uuid | YES | null | | pgsodium | valid_key | status | USER-DEFINED | YES | null | | pgsodium | valid_key | key_type | USER-DEFINED | YES | null | @@ -346,6 +373,12 @@ | pgsodium | valid_key | key_context | bytea | YES | null | | pgsodium | valid_key | created | timestamp with time zone | YES | null | | pgsodium | valid_key | expires | timestamp with time zone | YES | null | +| public | student_essays | id | uuid | NO | uuid_generate_v4() | +| public | student_essays | student_id | uuid | NO | null | +| public | student_essays | type_id | uuid | NO | null | +| public | student_essays | genre_id | uuid | NO | null | +| public | student_essays | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | student_essays | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | pgsodium | decrypted_key | id | uuid | YES | null | | pgsodium | decrypted_key | status | USER-DEFINED | YES | null | | pgsodium | decrypted_key | created | timestamp with time zone | YES | null | @@ -404,71 +437,89 @@ | public | story_pages | story_id | uuid | YES | null | | public | story_pages | page_number | integer | NO | null | | public | story_pages | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_analyses | id | uuid | NO | uuid_generate_v4() | +| public | essay_analyses | essay_id | uuid | NO | null | +| public | essay_analyses | overall_score | integer | NO | null | +| public | essay_analyses | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_analysis_feedback | id | uuid | NO | uuid_generate_v4() | +| public | essay_analysis_feedback | analysis_id | uuid | NO | null | +| public | essay_analysis_feedback | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | public | schools | id | uuid | NO | uuid_generate_v4() | | public | schools | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | public | schools | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_analysis_strengths | id | uuid | NO | uuid_generate_v4() | +| public | essay_analysis_strengths | analysis_id | uuid | NO | null | +| public | essay_analysis_strengths | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | +| public | essay_analysis_improvements | id | uuid | NO | uuid_generate_v4() | +| public | essay_analysis_improvements | analysis_id | uuid | NO | null | +| public | essay_analysis_improvements | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | | public | teachers | id | uuid | NO | uuid_generate_v4() | | public | teachers | school_id | uuid | NO | null | | public | teachers | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | -| public | teachers | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | -| public | teachers | class_ids | ARRAY | YES | null | -| public | classes | id | uuid | NO | uuid_generate_v4() | -| public | classes | school_id | uuid | NO | null | -| public | classes | year | integer | NO | null | -| public | classes | created_at | timestamp with time zone | NO | timezone('utc'::text, now()) | -| public | classes | updated_at | timestamp with time zone | NO | timezone('utc'::text, now()) | -| public | classes | teacher_id | uuid | YES | null | -| public | teacher_classes | id | uuid | NO | uuid_generate_v4() | +| vault | secrets | secret | text | NO | null | +| public | story_themes | icon | text | NO | null | +| public | story_recordings | improvements | ARRAY | YES | null | +| public | achievement_types | name | character varying | NO | null | +| public | achievement_types | description | text | YES | null | +| public | story_recordings | suggestions | text | YES | null | +| vault | decrypted_secrets | name | text | YES | null | +| vault | decrypted_secrets | description | text | YES | null | +| vault | decrypted_secrets | secret | text | YES | null | +| pgsodium | key | associated_data | text | YES | 'associated'::text | +| storage | s3_multipart_uploads_parts | bucket_id | text | NO | null | +| auth | sso_domains | domain | text | NO | null | +| auth | flow_state | authentication_method | text | NO | null | +| pgsodium | key | comment | text | YES | null | +| pgsodium | key | user_data | text | YES | null | +| public | stories | context | text | YES | null | +| public | student_phonics_attempt_answers | answer_text | text | YES | null | +| public | story_recordings | strengths | ARRAY | YES | null | +| pgsodium | masking_rule | format_type | text | YES | null | +| public | story_themes | slug | text | NO | null | +| public | story_themes | title | text | NO | null | +| public | story_themes | description | text | NO | null | +| vault | secrets | name | text | YES | null | +| vault | secrets | description | text | NO | ''::text | +| extensions | pg_stat_statements | query | text | YES | null | +| public | story_subjects | slug | text | NO | null | +| public | story_subjects | title | text | NO | null | +| public | story_subjects | description | text | NO | null | +| public | story_subjects | icon | text | NO | null | +| realtime | messages | topic | text | NO | null | +| realtime | messages | extension | text | NO | null | +| realtime | messages | event | text | YES | null | +| auth | mfa_factors | secret | text | YES | null | +| public | interests | category | text | NO | null | +| public | interests | item | text | NO | null | +| public | story_characters | slug | text | NO | null | +| public | story_characters | title | text | NO | null | +| public | story_characters | description | text | NO | null | +| public | story_generations | original_prompt | text | NO | null | +| public | story_generations | ai_response | text | NO | null | +| public | story_generations | model_used | text | NO | null | +| public | story_characters | icon | text | NO | null | +| auth | mfa_factors | phone | text | YES | null | +| auth | identities | provider_id | text | NO | null | +| net | http_request_queue | method | text | NO | null | +| net | http_request_queue | url | text | NO | null | +| storage | s3_multipart_uploads_parts | etag | text | NO | null | +| public | achievements | name | text | YES | null | +| public | story_settings | slug | text | NO | null | +| public | story_settings | title | text | NO | null | +| public | story_settings | description | text | NO | null | +| net | _http_response | content_type | text | YES | null | +| public | story_settings | icon | text | NO | null | +| net | _http_response | content | text | YES | null | +| public | achievements | description | text | YES | null | +| public | story_details | title | text | YES | null | +| net | _http_response | error_msg | text | YES | null | +| public | story_details | status | text | YES | null | | storage | s3_multipart_uploads_parts | owner_id | text | YES | null | | auth | mfa_amr_claims | authentication_method | text | NO | null | | auth | identities | provider | text | NO | null | | storage | s3_multipart_uploads_parts | version | text | NO | null | -| realtime | messages | topic | text | NO | null | -| realtime | messages | extension | text | NO | null | -| public | teacher_invites | email | text | NO | null | -| realtime | messages | event | text | YES | null | -| public | teacher_invites | name | text | NO | null | -| public | teacher_invites | subject | text | YES | null | -| public | teacher_invites | message | text | YES | null | -| public | teacher_invites | status | text | YES | 'pending'::text | -| public | teacher_invites | token | text | NO | null | -| storage | s3_multipart_uploads | version | text | NO | null | -| public | story_generations | original_prompt | text | NO | null | -| public | story_generations | ai_response | text | NO | null | -| public | story_generations | model_used | text | NO | null | -| auth | saml_providers | entity_id | text | NO | null | -| auth | mfa_challenges | otp_code | text | YES | null | -| auth | saml_providers | metadata_xml | text | NO | null | -| net | http_request_queue | method | text | NO | null | -| net | http_request_queue | url | text | NO | null | -| auth | saml_providers | metadata_url | text | YES | null | -| public | phonics_exercises | title | character varying | NO | null | -| public | phonics_exercises | description | text | YES | null | -| public | phonics_achievements | name | character varying | NO | null | -| public | phonics_achievements | description | text | YES | null | -| net | _http_response | content_type | text | YES | null | -| public | phonics_exercises | instructions | text | NO | null | -| net | _http_response | content | text | YES | null | -| storage | s3_multipart_uploads | id | text | NO | null | -| net | _http_response | error_msg | text | YES | null | -| public | phonics_achievements | icon_url | text | YES | null | -| auth | identities | email | text | YES | null | -| storage | s3_multipart_uploads | owner_id | text | YES | null | -| auth | saml_relay_states | request_id | text | NO | null | | public | story_pages | text | text | NO | null | | public | story_pages | image_url | text | NO | null | -| auth | saml_relay_states | for_email | text | YES | null | -| public | story_pages | image_path | text | YES | null | -| public | story_pages | image_url_thumb | text | YES | null | -| public | story_details | title | text | YES | null | -| public | story_pages | image_url_medium | text | YES | null | -| public | story_details | status | text | YES | null | -| public | story_pages | image_url_large | text | YES | null | -| public | story_pages | image_path_thumb | text | YES | null | -| public | story_pages | image_path_medium | text | YES | null | -| public | story_pages | image_path_large | text | YES | null | -| public | story_pages | text_syllables | text | YES | null | -| public | phonics_exercise_types | name | character varying | NO | null | | public | story_details | context | text | YES | null | | public | story_details | theme_title | text | YES | null | | public | story_details | theme_icon | text | YES | null | @@ -478,70 +529,70 @@ | public | story_details | character_icon | text | YES | null | | public | story_details | setting_title | text | YES | null | | public | story_details | setting_icon | text | YES | null | -| public | schools | name | text | NO | null | -| public | schools | address | text | YES | null | +| public | teacher_invites | email | text | NO | null | +| public | story_pages | image_path | text | YES | null | | public | story_exercise_words | word | text | NO | null | | public | story_exercise_words | exercise_type | text | NO | null | | public | story_exercise_words | phonemes | ARRAY | YES | null | | public | story_exercise_words | syllable_pattern | text | YES | null | -| public | schools | phone | text | YES | null | -| public | schools | email | text | YES | null | -| public | phonics_exercise_types | description | text | YES | null | -| auth | saml_relay_states | redirect_to | text | YES | null | -| public | schools | director_name | text | NO | 'Não informado'::text | -| auth | audit_log_entries | ip_address | character varying | NO | ''::character varying | -| public | phonics_words | word | character varying | NO | null | -| public | teachers | name | text | NO | null | +| public | story_pages | image_url_thumb | text | YES | null | +| public | story_pages | image_url_medium | text | YES | null | +| public | story_pages | image_url_large | text | YES | null | +| public | story_pages | image_path_thumb | text | YES | null | +| public | story_pages | image_path_medium | text | YES | null | +| public | story_pages | image_path_large | text | YES | null | +| public | story_pages | text_syllables | text | YES | null | +| public | teacher_invites | name | text | NO | null | | auth | schema_migrations | version | character varying | NO | null | -| public | teachers | email | text | NO | null | -| public | teachers | phone | text | YES | null | +| public | teacher_invites | subject | text | YES | null | +| public | teacher_invites | message | text | YES | null | | auth | instances | raw_base_config | text | YES | null | -| public | teachers | subject | text | YES | null | -| public | phonics_words | phonetic_transcription | character varying | YES | null | -| auth | saml_providers | name_id_format | text | YES | null | -| public | teachers | status | text | YES | 'pending'::text | +| public | essay_analyses | suggestions | text | YES | null | +| public | teacher_invites | status | text | YES | 'pending'::text | +| public | teacher_invites | token | text | NO | null | +| storage | s3_multipart_uploads | version | text | NO | null | | auth | users | aud | character varying | YES | null | | auth | users | role | character varying | YES | null | | auth | users | email | character varying | YES | null | | auth | users | encrypted_password | character varying | YES | null | -| public | phonics_word_audio | word | text | NO | null | -| public | phonics_word_audio | audio_url | text | NO | null | +| public | essay_analysis_feedback | structure_feedback | text | NO | null | +| public | essay_analysis_feedback | content_feedback | text | NO | null | | auth | users | confirmation_token | character varying | YES | null | -| public | phonics_word_audio | audio_path | text | NO | null | +| public | essay_analysis_feedback | language_feedback | text | NO | null | | auth | users | recovery_token | character varying | YES | null | -| public | classes | name | text | NO | null | +| auth | saml_providers | entity_id | text | NO | null | | auth | users | email_change_token_new | character varying | YES | null | | auth | users | email_change | character varying | YES | null | -| public | classes | grade | text | NO | null | -| storage | s3_multipart_uploads | upload_signature | text | NO | null | -| public | classes | period | text | YES | null | -| storage | s3_multipart_uploads_parts | upload_id | text | NO | null | -| public | languages | name | character varying | NO | null | -| public | languages | code | character varying | NO | null | -| public | languages | instructions | text | YES | null | +| auth | mfa_challenges | otp_code | text | YES | null | +| public | schools | name | text | NO | null | +| public | schools | address | text | YES | null | +| public | schools | phone | text | YES | null | +| public | schools | email | text | YES | null | +| auth | saml_providers | metadata_xml | text | NO | null | +| auth | saml_providers | metadata_url | text | YES | null | | auth | users | phone | text | YES | NULL::character varying | -| public | phonics_categories | name | character varying | NO | null | +| public | schools | director_name | text | NO | 'Não informado'::text | | auth | users | phone_change | text | YES | ''::character varying | | auth | users | phone_change_token | character varying | YES | ''::character varying | -| public | phonics_categories | description | text | YES | null | -| public | languages | flag_icon | character varying | YES | null | +| public | phonics_exercises | title | character varying | NO | null | +| public | phonics_exercises | description | text | YES | null | | auth | users | email_change_token_current | character varying | YES | ''::character varying | -| auth | sso_providers | resource_id | text | YES | null | -| auth | flow_state | auth_code | text | NO | null | +| public | essay_analysis_strengths | strength | text | NO | null | +| public | phonics_achievements | name | character varying | NO | null | | auth | users | reauthentication_token | character varying | YES | ''::character varying | -| public | students | name | text | NO | null | -| public | students | email | text | NO | null | -| storage | s3_multipart_uploads | bucket_id | text | NO | null | -| public | students | guardian_name | text | YES | null | -| public | students | guardian_phone | text | YES | null | -| public | students | guardian_email | text | YES | null | +| public | phonics_achievements | description | text | YES | null | +| public | phonics_exercises | instructions | text | NO | null | +| public | essay_analysis_improvements | improvement | text | NO | null | +| storage | s3_multipart_uploads | id | text | NO | null | +| public | phonics_achievements | icon_url | text | YES | null | +| auth | identities | email | text | YES | null | | auth | refresh_tokens | token | character varying | YES | null | | auth | refresh_tokens | user_id | character varying | YES | null | -| auth | flow_state | code_challenge | text | NO | null | -| auth | flow_state | provider_type | text | NO | null | -| public | students | status | text | NO | 'active'::text | +| public | teachers | name | text | NO | null | +| public | teachers | email | text | NO | null | +| public | teachers | phone | text | YES | null | | auth | refresh_tokens | parent | character varying | YES | null | -| public | story_recordings | audio_url | text | YES | null | +| public | teachers | subject | text | YES | null | | supabase_migrations | seed_files | path | text | NO | null | | supabase_migrations | seed_files | hash | text | NO | null | | supabase_migrations | schema_migrations | version | text | NO | null | @@ -549,99 +600,102 @@ | supabase_migrations | schema_migrations | name | text | YES | null | | storage | buckets | id | text | NO | null | | storage | buckets | name | text | NO | null | +| storage | s3_multipart_uploads | owner_id | text | YES | null | +| auth | saml_relay_states | request_id | text | NO | null | +| public | teachers | status | text | YES | 'pending'::text | +| auth | saml_relay_states | for_email | text | YES | null | +| public | phonics_exercise_types | name | character varying | NO | null | +| public | phonics_exercise_types | description | text | YES | null | +| storage | buckets | allowed_mime_types | ARRAY | YES | null | +| storage | buckets | owner_id | text | YES | null | +| public | classes | name | text | NO | null | +| storage | objects | bucket_id | text | YES | null | +| storage | objects | name | text | YES | null | +| public | classes | grade | text | NO | null | +| auth | saml_relay_states | redirect_to | text | YES | null | +| public | classes | period | text | YES | null | +| auth | audit_log_entries | ip_address | character varying | NO | ''::character varying | +| public | phonics_words | word | character varying | NO | null | +| storage | objects | path_tokens | ARRAY | YES | null | +| storage | objects | version | text | YES | null | +| storage | objects | owner_id | text | YES | null | +| public | phonics_words | phonetic_transcription | character varying | YES | null | +| auth | saml_providers | name_id_format | text | YES | null | +| storage | migrations | name | character varying | NO | null | +| storage | migrations | hash | character varying | NO | null | +| public | phonics_word_audio | word | text | NO | null | +| public | phonics_word_audio | audio_url | text | NO | null | +| public | essay_types | slug | text | NO | null | +| public | essay_types | title | text | NO | null | +| public | essay_types | description | text | NO | null | +| public | essay_types | icon | text | NO | null | +| public | phonics_word_audio | audio_path | text | NO | null | +| storage | s3_multipart_uploads | upload_signature | text | NO | null | +| storage | s3_multipart_uploads_parts | upload_id | text | NO | null | +| public | languages | name | character varying | NO | null | +| pgsodium | mask_columns | format_type | text | YES | null | +| public | languages | code | character varying | NO | null | +| public | languages | instructions | text | YES | null | +| public | essay_genres | slug | text | NO | null | +| public | essay_genres | title | text | NO | null | +| public | essay_genres | description | text | NO | null | +| public | essay_genres | icon | text | NO | null | +| public | phonics_categories | name | character varying | NO | null | +| public | phonics_categories | description | text | YES | null | +| public | languages | flag_icon | character varying | YES | null | +| auth | sso_providers | resource_id | text | YES | null | +| auth | flow_state | auth_code | text | NO | null | +| pgsodium | valid_key | name | text | YES | null | +| public | students | name | text | NO | null | +| public | students | email | text | NO | null | +| storage | s3_multipart_uploads | bucket_id | text | NO | null | +| public | students | guardian_name | text | YES | null | +| public | students | guardian_phone | text | YES | null | +| public | students | guardian_email | text | YES | null | +| pgsodium | valid_key | associated_data | text | YES | null | +| auth | flow_state | code_challenge | text | NO | null | +| auth | flow_state | provider_type | text | NO | null | +| public | students | status | text | NO | 'active'::text | +| public | story_recordings | audio_url | text | YES | null | +| public | student_essays | title | text | NO | null | +| public | student_essays | content | text | NO | null | +| public | student_essays | status | text | NO | 'draft'::text | | public | students | avatar_url | text | YES | null | | public | students | nickname | character varying | YES | null | | public | phonics_exercise_media | url | text | NO | null | | public | phonics_exercise_media | alt_text | text | YES | null | | public | students | preferred_themes | ARRAY | YES | null | | public | story_recordings | status | text | NO | 'pending_analysis'::text | -| storage | buckets | allowed_mime_types | ARRAY | YES | null | -| storage | buckets | owner_id | text | YES | null | | auth | flow_state | provider_access_token | text | YES | null | -| storage | objects | bucket_id | text | YES | null | -| storage | objects | name | text | YES | null | | auth | one_time_tokens | token_hash | text | NO | null | | public | media_types | name | character varying | NO | null | +| pgsodium | decrypted_key | name | text | YES | null | +| pgsodium | decrypted_key | associated_data | text | YES | null | | public | media_types | description | text | YES | null | | public | story_recordings | transcription | text | YES | null | | auth | one_time_tokens | relates_to | text | NO | null | -| storage | objects | path_tokens | ARRAY | YES | null | -| storage | objects | version | text | YES | null | -| storage | objects | owner_id | text | YES | null | | public | story_recordings | error_message | text | YES | null | +| pgsodium | decrypted_key | comment | text | YES | null | | auth | sessions | user_agent | text | YES | null | -| storage | migrations | name | character varying | NO | null | -| storage | migrations | hash | character varying | NO | null | | public | stories | title | text | NO | null | -| pgsodium | masking_rule | format_type | text | YES | null | -| auth | identities | provider_id | text | NO | null | -| storage | s3_multipart_uploads_parts | etag | text | NO | null | -| public | achievements | name | text | YES | null | -| vault | secrets | name | text | YES | null | -| vault | secrets | description | text | NO | ''::text | -| vault | secrets | secret | text | NO | null | -| public | story_settings | slug | text | NO | null | -| public | story_settings | title | text | NO | null | -| public | story_settings | description | text | NO | null | -| public | story_settings | icon | text | NO | null | -| public | achievements | description | text | YES | null | -| vault | decrypted_secrets | name | text | YES | null | -| vault | decrypted_secrets | description | text | YES | null | -| vault | decrypted_secrets | secret | text | YES | null | -| extensions | pg_stat_statements | query | text | YES | null | | auth | flow_state | provider_refresh_token | text | YES | null | -| pgsodium | mask_columns | format_type | text | YES | null | | public | stories | status | text | YES | 'draft'::text | -| pgsodium | valid_key | name | text | YES | null | | auth | sessions | tag | text | YES | null | | auth | mfa_factors | friendly_name | text | YES | null | -| storage | s3_multipart_uploads_parts | bucket_id | text | NO | null | -| auth | sso_domains | domain | text | NO | null | -| auth | flow_state | authentication_method | text | NO | null | -| pgsodium | valid_key | associated_data | text | YES | null | -| public | stories | context | text | YES | null | -| public | student_phonics_attempt_answers | answer_text | text | YES | null | -| public | story_recordings | strengths | ARRAY | YES | null | -| public | story_themes | slug | text | NO | null | -| public | story_themes | title | text | NO | null | -| public | story_themes | description | text | NO | null | -| public | story_themes | icon | text | NO | null | -| pgsodium | decrypted_key | name | text | YES | null | -| pgsodium | decrypted_key | associated_data | text | YES | null | -| public | story_recordings | improvements | ARRAY | YES | null | -| public | achievement_types | name | character varying | NO | null | -| public | achievement_types | description | text | YES | null | -| public | story_recordings | suggestions | text | YES | null | -| pgsodium | decrypted_key | comment | text | YES | null | -| public | story_subjects | slug | text | NO | null | -| public | story_subjects | title | text | NO | null | -| public | story_subjects | description | text | NO | null | -| public | story_subjects | icon | text | NO | null | -| auth | mfa_factors | secret | text | YES | null | -| public | interests | category | text | NO | null | | pgsodium | key | name | text | YES | null | -| pgsodium | key | associated_data | text | YES | 'associated'::text | -| public | interests | item | text | NO | null | -| public | story_characters | slug | text | NO | null | -| public | story_characters | title | text | NO | null | -| pgsodium | key | comment | text | YES | null | -| pgsodium | key | user_data | text | YES | null | -| public | story_characters | description | text | NO | null | -| public | story_characters | icon | text | NO | null | -| auth | mfa_factors | phone | text | YES | null | -| pgsodium | mask_columns | attname | name | YES | null | -| pgsodium | masking_rule | nonce_column | text | YES | null | -| pgsodium | mask_columns | key_id | text | YES | null | +| vault | decrypted_secrets | decrypted_secret | text | YES | null | | pgsodium | mask_columns | key_id_column | text | YES | null | | pgsodium | mask_columns | associated_columns | text | YES | null | | pgsodium | mask_columns | nonce_column | text | YES | null | | pgsodium | masking_rule | view_name | text | YES | null | -| pgsodium | masking_rule | relname | name | YES | null | -| pgsodium | masking_rule | attname | name | YES | null | -| vault | decrypted_secrets | decrypted_secret | text | YES | null | -| storage | s3_multipart_uploads | key | text | NO | null | | pgsodium | masking_rule | col_description | text | YES | null | | pgsodium | masking_rule | key_id_column | text | YES | null | -| pgsodium | masking_rule | key_id | text | YES | null | | storage | s3_multipart_uploads_parts | key | text | NO | null | -| pgsodium | masking_rule | associated_columns | text | YES | null | \ No newline at end of file +| storage | s3_multipart_uploads | key | text | NO | null | +| pgsodium | masking_rule | key_id | text | YES | null | +| pgsodium | masking_rule | associated_columns | text | YES | null | +| pgsodium | masking_rule | nonce_column | text | YES | null | +| pgsodium | masking_rule | relname | name | YES | null | +| pgsodium | mask_columns | attname | name | YES | null | +| pgsodium | masking_rule | attname | name | YES | null | +| pgsodium | mask_columns | key_id | text | YES | null | \ No newline at end of file diff --git a/supabase/contexts/functions.md b/supabase/contexts/functions.md index 9a3eaf6..188ccc5 100644 --- a/supabase/contexts/functions.md +++ b/supabase/contexts/functions.md @@ -63,18 +63,18 @@ WITH SELECT extensions.url_encode(extensions.hmac(signables, secret, alg.id)) FROM alg; $function$ | -| extensions | armor | CREATE OR REPLACE FUNCTION extensions.armor(bytea, text[], text[]) - RETURNS text - LANGUAGE c - IMMUTABLE PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pg_armor$function$ - | | extensions | armor | CREATE OR REPLACE FUNCTION extensions.armor(bytea) RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pg_armor$function$ | +| extensions | armor | CREATE OR REPLACE FUNCTION extensions.armor(bytea, text[], text[]) + RETURNS text + LANGUAGE c + IMMUTABLE PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pg_armor$function$ + | | extensions | bytea_to_text | CREATE OR REPLACE FUNCTION extensions.bytea_to_text(data bytea) RETURNS text LANGUAGE c @@ -296,16 +296,21 @@ AS '$libdir/pgcrypto', $function$pg_hmac$function$ LANGUAGE c AS '$libdir/http', $function$http_request$function$ | -| extensions | http_delete | CREATE OR REPLACE FUNCTION extensions.http_delete(uri character varying, content character varying, content_type character varying) - RETURNS http_response - LANGUAGE sql -AS $function$ SELECT extensions.http(('DELETE', $1, NULL, $3, $2)::extensions.http_request) $function$ - | | extensions | http_delete | CREATE OR REPLACE FUNCTION extensions.http_delete(uri character varying) RETURNS http_response LANGUAGE sql AS $function$ SELECT extensions.http(('DELETE', $1, NULL, NULL, NULL)::extensions.http_request) $function$ | +| extensions | http_delete | CREATE OR REPLACE FUNCTION extensions.http_delete(uri character varying, content character varying, content_type character varying) + RETURNS http_response + LANGUAGE sql +AS $function$ SELECT extensions.http(('DELETE', $1, NULL, $3, $2)::extensions.http_request) $function$ + | +| extensions | http_get | CREATE OR REPLACE FUNCTION extensions.http_get(uri character varying) + RETURNS http_response + LANGUAGE sql +AS $function$ SELECT extensions.http(('GET', $1, NULL, NULL, NULL)::extensions.http_request) $function$ + | | extensions | http_get | CREATE OR REPLACE FUNCTION extensions.http_get(uri character varying, data jsonb) RETURNS http_response LANGUAGE sql @@ -313,11 +318,6 @@ AS $function$ SELECT extensions.http(('GET', $1 || '?' || extensions.urlencode($2), NULL, NULL, NULL)::extensions.http_request) $function$ | -| extensions | http_get | CREATE OR REPLACE FUNCTION extensions.http_get(uri character varying) - RETURNS http_response - LANGUAGE sql -AS $function$ SELECT extensions.http(('GET', $1, NULL, NULL, NULL)::extensions.http_request) $function$ - | | extensions | http_head | CREATE OR REPLACE FUNCTION extensions.http_head(uri character varying) RETURNS http_response LANGUAGE sql @@ -338,11 +338,6 @@ AS '$libdir/http', $function$http_list_curlopt$function$ LANGUAGE sql AS $function$ SELECT extensions.http(('PATCH', $1, NULL, $3, $2)::extensions.http_request) $function$ | -| extensions | http_post | CREATE OR REPLACE FUNCTION extensions.http_post(uri character varying, content character varying, content_type character varying) - RETURNS http_response - LANGUAGE sql -AS $function$ SELECT extensions.http(('POST', $1, NULL, $3, $2)::extensions.http_request) $function$ - | | extensions | http_post | CREATE OR REPLACE FUNCTION extensions.http_post(uri character varying, data jsonb) RETURNS http_response LANGUAGE sql @@ -350,6 +345,11 @@ AS $function$ SELECT extensions.http(('POST', $1, NULL, 'application/x-www-form-urlencoded', extensions.urlencode($2))::extensions.http_request) $function$ | +| extensions | http_post | CREATE OR REPLACE FUNCTION extensions.http_post(uri character varying, content character varying, content_type character varying) + RETURNS http_response + LANGUAGE sql +AS $function$ SELECT extensions.http(('POST', $1, NULL, $3, $2)::extensions.http_request) $function$ + | | extensions | http_put | CREATE OR REPLACE FUNCTION extensions.http_put(uri character varying, content character varying, content_type character varying) RETURNS http_response LANGUAGE sql @@ -395,12 +395,6 @@ AS '$libdir/pgcrypto', $function$pgp_armor_headers$function$ IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_key_id_w$function$ | -| extensions | pgp_pub_decrypt | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) - RETURNS text - LANGUAGE c - IMMUTABLE PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_text$function$ - | | extensions | pgp_pub_decrypt | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt(bytea, bytea) RETURNS text LANGUAGE c @@ -413,24 +407,30 @@ AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_text$function$ IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_text$function$ | +| extensions | pgp_pub_decrypt | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt(bytea, bytea, text, text) + RETURNS text + LANGUAGE c + IMMUTABLE PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_text$function$ + | | extensions | pgp_pub_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea) RETURNS bytea LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_bytea$function$ | -| extensions | pgp_pub_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) - RETURNS bytea - LANGUAGE c - IMMUTABLE PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_bytea$function$ - | | extensions | pgp_pub_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text) RETURNS bytea LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_bytea$function$ | +| extensions | pgp_pub_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_decrypt_bytea(bytea, bytea, text, text) + RETURNS bytea + LANGUAGE c + IMMUTABLE PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pgp_pub_decrypt_bytea$function$ + | | extensions | pgp_pub_encrypt | CREATE OR REPLACE FUNCTION extensions.pgp_pub_encrypt(text, bytea) RETURNS bytea LANGUAGE c @@ -443,18 +443,18 @@ AS '$libdir/pgcrypto', $function$pgp_pub_encrypt_text$function$ PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_pub_encrypt_text$function$ | -| extensions | pgp_pub_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) - RETURNS bytea - LANGUAGE c - PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_pub_encrypt_bytea$function$ - | | extensions | pgp_pub_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea) RETURNS bytea LANGUAGE c PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_pub_encrypt_bytea$function$ | +| extensions | pgp_pub_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_pub_encrypt_bytea(bytea, bytea, text) + RETURNS bytea + LANGUAGE c + PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pgp_pub_encrypt_bytea$function$ + | | extensions | pgp_sym_decrypt | CREATE OR REPLACE FUNCTION extensions.pgp_sym_decrypt(bytea, text) RETURNS text LANGUAGE c @@ -467,42 +467,42 @@ AS '$libdir/pgcrypto', $function$pgp_sym_decrypt_text$function$ IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_sym_decrypt_text$function$ | -| extensions | pgp_sym_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) - RETURNS bytea - LANGUAGE c - IMMUTABLE PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_sym_decrypt_bytea$function$ - | | extensions | pgp_sym_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text) RETURNS bytea LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_sym_decrypt_bytea$function$ | -| extensions | pgp_sym_encrypt | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt(text, text, text) +| extensions | pgp_sym_decrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_decrypt_bytea(bytea, text, text) RETURNS bytea LANGUAGE c - PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_text$function$ - | + IMMUTABLE PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pgp_sym_decrypt_bytea$function$ + | | extensions | pgp_sym_encrypt | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt(text, text) RETURNS bytea LANGUAGE c PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_text$function$ | -| extensions | pgp_sym_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) +| extensions | pgp_sym_encrypt | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt(text, text, text) RETURNS bytea LANGUAGE c PARALLEL SAFE STRICT -AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_bytea$function$ - | +AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_text$function$ + | | extensions | pgp_sym_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text) RETURNS bytea LANGUAGE c PARALLEL SAFE STRICT AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_bytea$function$ | +| extensions | pgp_sym_encrypt_bytea | CREATE OR REPLACE FUNCTION extensions.pgp_sym_encrypt_bytea(bytea, text, text) + RETURNS bytea + LANGUAGE c + PARALLEL SAFE STRICT +AS '$libdir/pgcrypto', $function$pgp_sym_encrypt_bytea$function$ + | | extensions | pgrst_ddl_watch | CREATE OR REPLACE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger LANGUAGE plpgsql @@ -675,6 +675,12 @@ AS $function$ SELECT translate(encode(data, 'base64'), E'+/=\n', '-_'); $function$ | +| extensions | urlencode | CREATE OR REPLACE FUNCTION extensions.urlencode(string bytea) + RETURNS text + LANGUAGE c + IMMUTABLE STRICT +AS '$libdir/http', $function$urlencode$function$ + | | extensions | urlencode | CREATE OR REPLACE FUNCTION extensions.urlencode(string character varying) RETURNS text LANGUAGE c @@ -687,12 +693,6 @@ AS '$libdir/http', $function$urlencode$function$ IMMUTABLE STRICT AS '$libdir/http', $function$urlencode_jsonb$function$ | -| extensions | urlencode | CREATE OR REPLACE FUNCTION extensions.urlencode(string bytea) - RETURNS text - LANGUAGE c - IMMUTABLE STRICT -AS '$libdir/http', $function$urlencode$function$ - | | extensions | uuid_generate_v1 | CREATE OR REPLACE FUNCTION extensions.uuid_generate_v1() RETURNS uuid LANGUAGE c diff --git a/supabase/contexts/policies.md b/supabase/contexts/policies.md index bd0a459..7010b0e 100644 --- a/supabase/contexts/policies.md +++ b/supabase/contexts/policies.md @@ -1,31 +1,39 @@ -| policy_id | schema_name | table_name | policy_name | command | policy_using | policy_check | -| --------- | ----------- | ---------------------- | ------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| 29823 | auth | users | Validate user metadata | * | (((raw_user_meta_data ->> 'role'::text))::user_role IS NOT NULL) | null | -| 29615 | public | classes | Schools can create classes | a | null | (school_id = auth.uid()) | -| 29616 | public | classes | Schools can update their classes | w | (school_id = auth.uid()) | (school_id = auth.uid()) | -| 29614 | public | classes | Schools can view their classes | r | (school_id = auth.uid()) | null | -| 29301 | public | classes | Turmas visíveis para usuários autenticados | r | true | null | -| 65878 | public | interests | Students can delete their own interests | d | (auth.uid() = student_id) | null | -| 65876 | public | interests | Students can insert their own interests | a | null | (auth.uid() = student_id) | -| 65877 | public | interests | Students can update their own interests | w | (auth.uid() = student_id) | (auth.uid() = student_id) | -| 65875 | public | interests | Students can view their own interests | r | (auth.uid() = student_id) | null | -| 104599 | public | languages | Allow insert/update for admins only | * | ((auth.jwt() ->> 'role'::text) = 'admin'::text) | ((auth.jwt() ->> 'role'::text) = 'admin'::text) | -| 104598 | public | languages | Allow read access for all authenticated users | r | true | null | -| 79931 | public | phonics_categories | Permitir leitura de categorias fonéticas para usuários autent | r | true | null | -| 79932 | public | phonics_exercise_types | Permitir leitura de tipos de exercícios fonéticos para usuár | r | true | null | -| 79934 | public | phonics_exercise_words | Permitir leitura de relações exercício-palavra para usuário | r | true | null | -| 79930 | public | phonics_exercises | Permitir leitura de exercícios fonéticos para usuários auten | r | true | null | -| 79933 | public | phonics_words | Permitir leitura de palavras fonéticas para usuários autentic | r | true | null | -| 29440 | public | schools | Enable insert for registration | a | null | true | -| 29299 | public | schools | Escolas visíveis para usuários autenticados | r | true | null | -| 29442 | public | schools | Schools can update own data | w | (auth.uid() = id) | (auth.uid() = id) | -| 29441 | public | schools | Schools can view own data | r | (auth.uid() = id) | null | +| policy_id | schema_name | table_name | policy_name | command | policy_using | policy_check | +| --------- | ----------- | ---------------------- | ------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 29823 | auth | users | Validate user metadata | * | (((raw_user_meta_data ->> 'role'::text))::user_role IS NOT NULL) | null | +| 29615 | public | classes | Schools can create classes | a | null | (school_id = auth.uid()) | +| 29616 | public | classes | Schools can update their classes | w | (school_id = auth.uid()) | (school_id = auth.uid()) | +| 29614 | public | classes | Schools can view their classes | r | (school_id = auth.uid()) | null | +| 29301 | public | classes | Turmas visíveis para usuários autenticados | r | true | null | +| 113771 | public | essay_analyses | Alunos podem ver análises de suas próprias redações | r | (EXISTS ( SELECT 1 + FROM student_essays + WHERE ((student_essays.id = essay_analyses.essay_id) AND (student_essays.student_id = auth.uid())))) | null | +| 113770 | public | essay_analyses | Edge Function pode inserir análises | a | null | (((auth.jwt() ->> 'role'::text) = 'service_role'::text) OR (EXISTS ( SELECT 1 + FROM student_essays + WHERE ((student_essays.id = essay_analyses.essay_id) AND (student_essays.student_id = auth.uid()))))) | +| 113765 | public | essay_genres | Gêneros textuais visíveis para todos | r | (active = true) | null | +| 113764 | public | essay_types | Tipos de redação visíveis para todos | r | (active = true) | null | +| 65878 | public | interests | Students can delete their own interests | d | (auth.uid() = student_id) | null | +| 65876 | public | interests | Students can insert their own interests | a | null | (auth.uid() = student_id) | +| 65877 | public | interests | Students can update their own interests | w | (auth.uid() = student_id) | (auth.uid() = student_id) | +| 65875 | public | interests | Students can view their own interests | r | (auth.uid() = student_id) | null | +| 104599 | public | languages | Allow insert/update for admins only | * | ((auth.jwt() ->> 'role'::text) = 'admin'::text) | ((auth.jwt() ->> 'role'::text) = 'admin'::text) | +| 104598 | public | languages | Allow read access for all authenticated users | r | true | null | +| 79931 | public | phonics_categories | Permitir leitura de categorias fonéticas para usuários autent | r | true | null | +| 79932 | public | phonics_exercise_types | Permitir leitura de tipos de exercícios fonéticos para usuár | r | true | null | +| 79934 | public | phonics_exercise_words | Permitir leitura de relações exercício-palavra para usuário | r | true | null | +| 79930 | public | phonics_exercises | Permitir leitura de exercícios fonéticos para usuários auten | r | true | null | +| 79933 | public | phonics_words | Permitir leitura de palavras fonéticas para usuários autentic | r | true | null | +| 29440 | public | schools | Enable insert for registration | a | null | true | +| 29299 | public | schools | Escolas visíveis para usuários autenticados | r | true | null | +| 29442 | public | schools | Schools can update own data | w | (auth.uid() = id) | (auth.uid() = id) | +| 29441 | public | schools | Schools can view own data | r | (auth.uid() = id) | null | | 29347 | public | stories | Alunos podem atualizar suas próprias histórias | w | (student_id IN ( SELECT students.id FROM students - WHERE (students.email = auth.email()))) | null | + WHERE (students.email = auth.email()))) | null | | 29346 | public | stories | Alunos podem criar suas próprias histórias | a | null | (student_id IN ( SELECT students.id FROM students - WHERE (students.email = auth.email()))) | + WHERE (students.email = auth.email()))) | | 36241 | public | stories | Estudantes podem ver suas próprias histórias | r | ((auth.uid() = student_id) AND (EXISTS ( SELECT 1 FROM story_themes WHERE ((story_themes.id = stories.theme_id) AND (story_themes.active = true)))) AND (EXISTS ( SELECT 1 @@ -34,60 +42,64 @@ FROM story_characters WHERE ((story_characters.id = stories.character_id) AND (story_characters.active = true)))) AND (EXISTS ( SELECT 1 FROM story_settings - WHERE ((story_settings.id = stories.setting_id) AND (story_settings.active = true))))) | null | -| 29345 | public | stories | Histórias visíveis para usuários autenticados | r | true | null | -| 53384 | public | stories | Permitir deleção pelo dono | d | (auth.uid() = student_id) | null | -| 34952 | public | story_characters | Permitir leitura pública dos personagens | r | (active = true) | null | -| 53955 | public | story_exercise_words | Apenas sistema pode inserir | a | null | (auth.role() = 'service_role'::text) | -| 53954 | public | story_exercise_words | Leitura pública das palavras | r | true | null | -| 37664 | public | story_generations | Apenas service_role pode inserir metadados | a | null | true | -| 37663 | public | story_generations | Metadados são visíveis para todos | r | true | null | -| 37662 | public | story_pages | Apenas service_role pode inserir páginas | a | null | true | -| 37661 | public | story_pages | Páginas são visíveis para todos | r | true | null | + WHERE ((story_settings.id = stories.setting_id) AND (story_settings.active = true))))) | null | +| 29345 | public | stories | Histórias visíveis para usuários autenticados | r | true | null | +| 53384 | public | stories | Permitir deleção pelo dono | d | (auth.uid() = student_id) | null | +| 34952 | public | story_characters | Permitir leitura pública dos personagens | r | (active = true) | null | +| 53955 | public | story_exercise_words | Apenas sistema pode inserir | a | null | (auth.role() = 'service_role'::text) | +| 53954 | public | story_exercise_words | Leitura pública das palavras | r | true | null | +| 37664 | public | story_generations | Apenas service_role pode inserir metadados | a | null | true | +| 37663 | public | story_generations | Metadados são visíveis para todos | r | true | null | +| 37662 | public | story_pages | Apenas service_role pode inserir páginas | a | null | true | +| 37661 | public | story_pages | Páginas são visíveis para todos | r | true | null | | 31560 | public | story_recordings | Escolas podem ver todas as gravações | r | (EXISTS ( SELECT 1 FROM students s - WHERE ((s.id = story_recordings.student_id) AND (s.school_id = auth.uid())))) | null | -| 30092 | public | story_recordings | Estudantes podem gravar áudios | a | null | (auth.uid() = student_id) | -| 31511 | public | story_recordings | Estudantes podem ver suas próprias gravações | r | (auth.uid() = student_id) | null | + WHERE ((s.id = story_recordings.student_id) AND (s.school_id = auth.uid())))) | null | +| 30092 | public | story_recordings | Estudantes podem gravar áudios | a | null | (auth.uid() = student_id) | +| 31511 | public | story_recordings | Estudantes podem ver suas próprias gravações | r | (auth.uid() = student_id) | null | | 31558 | public | story_recordings | Professores podem ver gravações de seus alunos | r | (EXISTS ( SELECT 1 FROM (classes c JOIN students s ON ((s.class_id = c.id))) - WHERE ((s.id = story_recordings.student_id) AND (c.teacher_id = auth.uid())))) | null | -| 29748 | public | story_recordings | Students can insert their own recordings | a | null | (auth.uid() = student_id) | -| 29749 | public | story_recordings | Students can view their own recordings | r | (auth.uid() = student_id) | null | -| 34953 | public | story_settings | Permitir leitura pública dos cenários | r | (active = true) | null | -| 34951 | public | story_subjects | Permitir leitura pública das disciplinas | r | (active = true) | null | -| 34950 | public | story_themes | Permitir leitura pública das categorias | r | (active = true) | null | -| 29302 | public | students | Alunos visíveis para usuários autenticados | r | true | null | + WHERE ((s.id = story_recordings.student_id) AND (c.teacher_id = auth.uid())))) | null | +| 29748 | public | story_recordings | Students can insert their own recordings | a | null | (auth.uid() = student_id) | +| 29749 | public | story_recordings | Students can view their own recordings | r | (auth.uid() = student_id) | null | +| 34953 | public | story_settings | Permitir leitura pública dos cenários | r | (active = true) | null | +| 34951 | public | story_subjects | Permitir leitura pública das disciplinas | r | (active = true) | null | +| 34950 | public | story_themes | Permitir leitura pública das categorias | r | (active = true) | null | +| 113768 | public | student_essays | Alunos podem atualizar suas próprias redações | w | (student_id = auth.uid()) | (student_id = auth.uid()) | +| 113767 | public | student_essays | Alunos podem criar suas próprias redações | a | null | (student_id = auth.uid()) | +| 113769 | public | student_essays | Alunos podem deletar suas próprias redações | d | (student_id = auth.uid()) | null | +| 113766 | public | student_essays | Alunos podem ver suas próprias redações | r | (student_id = auth.uid()) | null | +| 29302 | public | students | Alunos visíveis para usuários autenticados | r | true | null | | 29638 | public | students | Escolas podem inserir seus próprios alunos | a | null | (auth.uid() IN ( SELECT schools.id FROM schools - WHERE (schools.id = students.school_id))) | + WHERE (schools.id = students.school_id))) | | 29639 | public | students | Escolas podem ver seus próprios alunos | r | (auth.uid() IN ( SELECT schools.id FROM schools - WHERE (schools.id = students.school_id))) | null | -| 29584 | public | students | Schools can view their students | r | (school_id = auth.uid()) | null | + WHERE (schools.id = students.school_id))) | null | +| 29584 | public | students | Schools can view their students | r | (school_id = auth.uid()) | null | | 29511 | public | teacher_invites | Schools can invite teachers | a | null | (school_id IN ( SELECT schools.id FROM schools - WHERE (schools.id = auth.uid()))) | -| 29300 | public | teachers | Professores visíveis para usuários autenticados | r | true | null | + WHERE (schools.id = auth.uid()))) | +| 29300 | public | teachers | Professores visíveis para usuários autenticados | r | true | null | | 29510 | public | teachers | Schools can view their teachers | r | (school_id IN ( SELECT schools.id FROM schools - WHERE (schools.id = auth.uid()))) | null | -| 29509 | public | teachers | Teachers can view own data | r | (auth.uid() = id) | null | -| 29717 | storage | objects | Anyone can read recordings | r | (bucket_id = 'recordings'::text) | null | -| 30136 | storage | objects | Estudantes podem fazer upload de áudios | a | null | ((bucket_id = 'recordings'::text) AND ((auth.uid())::text = (storage.foldername(name))[1])) | -| 75352 | storage | objects | Imagens são publicamente acessíveis | r | (bucket_id = 'story-images'::text) | null | -| 43940 | storage | objects | Permitir acesso da Edge Function | r | ((bucket_id = 'recordings'::text) AND ((auth.jwt() ->> 'role'::text) = 'service_role'::text)) | null | -| 52098 | storage | objects | Permitir acesso público para leitura | r | (bucket_id = 'recordings'::text) | null | -| 37570 | storage | objects | Permitir acesso público para leitura de imagens de histórias | r | (bucket_id = 'story-images'::text) | null | -| 37573 | storage | objects | Permitir delete pela edge function | d | (bucket_id = 'story-images'::text) | null | + WHERE (schools.id = auth.uid()))) | null | +| 29509 | public | teachers | Teachers can view own data | r | (auth.uid() = id) | null | +| 29717 | storage | objects | Anyone can read recordings | r | (bucket_id = 'recordings'::text) | null | +| 30136 | storage | objects | Estudantes podem fazer upload de áudios | a | null | ((bucket_id = 'recordings'::text) AND ((auth.uid())::text = (storage.foldername(name))[1])) | +| 75352 | storage | objects | Imagens são publicamente acessíveis | r | (bucket_id = 'story-images'::text) | null | +| 43940 | storage | objects | Permitir acesso da Edge Function | r | ((bucket_id = 'recordings'::text) AND ((auth.jwt() ->> 'role'::text) = 'service_role'::text)) | null | +| 52098 | storage | objects | Permitir acesso público para leitura | r | (bucket_id = 'recordings'::text) | null | +| 37570 | storage | objects | Permitir acesso público para leitura de imagens de histórias | r | (bucket_id = 'story-images'::text) | null | +| 37573 | storage | objects | Permitir delete pela edge function | d | (bucket_id = 'story-images'::text) | null | | 53468 | storage | objects | Permitir deleção de imagens pelo dono da história | d | ((bucket_id = 'story-images'::text) AND (EXISTS ( SELECT 1 FROM stories s - WHERE ((s.id = ((storage.foldername(objects.name))[1])::uuid) AND (s.student_id = auth.uid()))))) | null | -| 53426 | storage | objects | Permitir deleção pelo dono do arquivo | d | ((bucket_id = 'recordings'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text)) | null | -| 52099 | storage | objects | Permitir download público | r | (bucket_id = 'recordings'::text) | null | -| 37572 | storage | objects | Permitir update pela edge function | w | (bucket_id = 'story-images'::text) | null | -| 43939 | storage | objects | Permitir upload de áudios autenticado | a | null | ((bucket_id = 'recordings'::text) AND (auth.role() = 'authenticated'::text)) | -| 37571 | storage | objects | Permitir upload pela edge function | a | null | (bucket_id = 'story-images'::text) | -| 29716 | storage | objects | Students can upload their recordings | a | null | ((bucket_id = 'recordings'::text) AND (auth.role() = 'student'::text)) | -| 74045 | storage | objects | Áudios públicos | r | (bucket_id = 'phonics-audio'::text) | null | \ No newline at end of file + WHERE ((s.id = ((storage.foldername(objects.name))[1])::uuid) AND (s.student_id = auth.uid()))))) | null | +| 53426 | storage | objects | Permitir deleção pelo dono do arquivo | d | ((bucket_id = 'recordings'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text)) | null | +| 52099 | storage | objects | Permitir download público | r | (bucket_id = 'recordings'::text) | null | +| 37572 | storage | objects | Permitir update pela edge function | w | (bucket_id = 'story-images'::text) | null | +| 43939 | storage | objects | Permitir upload de áudios autenticado | a | null | ((bucket_id = 'recordings'::text) AND (auth.role() = 'authenticated'::text)) | +| 37571 | storage | objects | Permitir upload pela edge function | a | null | (bucket_id = 'story-images'::text) | +| 29716 | storage | objects | Students can upload their recordings | a | null | ((bucket_id = 'recordings'::text) AND (auth.role() = 'student'::text)) | +| 74045 | storage | objects | Áudios públicos | r | (bucket_id = 'phonics-audio'::text) | null | \ No newline at end of file diff --git a/supabase/functions/analyze-essay/index.ts b/supabase/functions/analyze-essay/index.ts index 132435d..633d3c9 100644 --- a/supabase/functions/analyze-essay/index.ts +++ b/supabase/functions/analyze-essay/index.ts @@ -12,6 +12,7 @@ interface EssayAnalysisRequest { interface EssayAnalysisResponse { overall_score: number; + suggestions: string; feedback: { structure: string; content: string; @@ -19,13 +20,12 @@ interface EssayAnalysisResponse { }; strengths: string[]; improvements: string[]; - suggestions: string; criteria_scores: { - adequacy: number; // Adequação ao tema/gênero - coherence: number; // Coerência textual - cohesion: number; // Coesão - vocabulary: number; // Vocabulário - grammar: number; // Gramática/ortografia + adequacy: number; + coherence: number; + cohesion: number; + vocabulary: number; + grammar: number; }; } @@ -62,13 +62,18 @@ serve(async (req) => { // Criar cliente Supabase console.log('Inicializando cliente Supabase...') const supabaseUrl = Deno.env.get('SUPABASE_URL') - const supabaseKey = Deno.env.get('SUPABASE_ANON_KEY') + const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') - if (!supabaseUrl || !supabaseKey) { + if (!supabaseUrl || !supabaseServiceKey) { throw new Error('Variáveis de ambiente do Supabase não configuradas') } - const supabaseClient = createClient(supabaseUrl, supabaseKey) + const supabaseClient = createClient(supabaseUrl, supabaseServiceKey, { + auth: { + persistSession: false, + autoRefreshToken: false, + } + }) // Criar cliente OpenAI console.log('Inicializando cliente OpenAI...') @@ -173,6 +178,7 @@ Forneça uma análise detalhada considerando: Responda em formato JSON seguindo exatamente esta estrutura: { "overall_score": number, // 0 a 100 + "suggestions": string, // Sugestões específicas "feedback": { "structure": string, // Feedback sobre estrutura e organização "content": string, // Feedback sobre conteúdo e ideias @@ -180,13 +186,12 @@ Responda em formato JSON seguindo exatamente esta estrutura: }, "strengths": string[], // Lista de pontos fortes "improvements": string[], // Lista de pontos a melhorar - "suggestions": string, // Sugestões específicas "criteria_scores": { "adequacy": number, // 0 a 100 - Adequação ao tema/gênero "coherence": number, // 0 a 100 - Coerência textual "cohesion": number, // 0 a 100 - Coesão "vocabulary": number, // 0 a 100 - Vocabulário - "grammar": number // 0 a 100 - Gramática/ortografia + "grammar": number // 0 a 100 - Gramática e ortografia } }` @@ -207,51 +212,89 @@ Responda em formato JSON seguindo exatamente esta estrutura: ], response_format: { type: "json_schema", - schema: { + json_schema: { type: "object", - required: ["overall_score", "feedback", "strengths", "improvements", "suggestions", "criteria_scores"], + required: ["overall_score", "suggestions", "feedback", "strengths", "improvements", "criteria_scores"], properties: { overall_score: { type: "number", minimum: 0, - maximum: 100 + maximum: 100, + description: "Pontuação geral da redação (0-100)" + }, + suggestions: { + type: "string", + description: "Sugestões específicas para aprimoramento" }, feedback: { type: "object", required: ["structure", "content", "language"], properties: { - structure: { type: "string" }, - content: { type: "string" }, - language: { type: "string" } + structure: { + type: "string", + description: "Feedback sobre estrutura e organização" + }, + content: { + type: "string", + description: "Feedback sobre conteúdo e ideias" + }, + language: { + type: "string", + description: "Feedback sobre linguagem e gramática" + } } }, strengths: { type: "array", - items: { type: "string" } + items: { + type: "string", + description: "Ponto forte da redação" + }, + minItems: 1, + description: "Lista de pontos fortes da redação" }, improvements: { - type: "array", - items: { type: "string" } + type: "array", + items: { + type: "string", + description: "Ponto a melhorar na redação" + }, + minItems: 1, + description: "Lista de pontos a melhorar na redação" }, - suggestions: { type: "string" }, criteria_scores: { type: "object", required: ["adequacy", "coherence", "cohesion", "vocabulary", "grammar"], properties: { adequacy: { - type: "number" + type: "number", + minimum: 0, + maximum: 100, + description: "Adequação ao tema/gênero (0-100)" }, coherence: { - type: "number" + type: "number", + minimum: 0, + maximum: 100, + description: "Coerência textual (0-100)" }, cohesion: { - type: "number" + type: "number", + minimum: 0, + maximum: 100, + description: "Coesão textual (0-100)" }, vocabulary: { - type: "number" + type: "number", + minimum: 0, + maximum: 100, + description: "Vocabulário (0-100)" }, grammar: { - type: "number" + type: "number", + minimum: 0, + maximum: 100, + description: "Gramática e ortografia (0-100)" } } } @@ -266,24 +309,110 @@ Responda em formato JSON seguindo exatamente esta estrutura: // Salvar análise no banco console.log('Salvando análise no banco...') - const { error: saveError } = await supabaseClient + + // Primeiro, criar a análise principal + const { data: analysisData, error: analysisError } = await supabaseClient .from('essay_analyses') .insert({ essay_id, overall_score: analysis.overall_score, - feedback: analysis.feedback, - strengths: analysis.strengths, - improvements: analysis.improvements, - suggestions: analysis.suggestions, - criteria_scores: analysis.criteria_scores + suggestions: analysis.suggestions }) + .select() + .single() - if (saveError) { - console.error('Erro ao salvar análise:', saveError) + if (analysisError) { + console.error('Erro ao salvar análise principal:', analysisError) return new Response( JSON.stringify({ error: 'Erro ao salvar análise', - details: saveError.message + details: analysisError.message + }), + { status: 500, headers: { ...getCorsHeaders(req), 'Content-Type': 'application/json' } } + ) + } + + // Salvar feedback + const { error: feedbackError } = await supabaseClient + .from('essay_analysis_feedback') + .insert({ + analysis_id: analysisData.id, + structure_feedback: analysis.feedback.structure, + content_feedback: analysis.feedback.content, + language_feedback: analysis.feedback.language + }) + + if (feedbackError) { + console.error('Erro ao salvar feedback:', feedbackError) + return new Response( + JSON.stringify({ + error: 'Erro ao salvar feedback', + details: feedbackError.message + }), + { status: 500, headers: { ...getCorsHeaders(req), 'Content-Type': 'application/json' } } + ) + } + + // Salvar pontos fortes + const strengths = analysis.strengths.map(strength => ({ + analysis_id: analysisData.id, + strength + })) + + const { error: strengthsError } = await supabaseClient + .from('essay_analysis_strengths') + .insert(strengths) + + if (strengthsError) { + console.error('Erro ao salvar pontos fortes:', strengthsError) + return new Response( + JSON.stringify({ + error: 'Erro ao salvar pontos fortes', + details: strengthsError.message + }), + { status: 500, headers: { ...getCorsHeaders(req), 'Content-Type': 'application/json' } } + ) + } + + // Salvar pontos a melhorar + const improvements = analysis.improvements.map(improvement => ({ + analysis_id: analysisData.id, + improvement + })) + + const { error: improvementsError } = await supabaseClient + .from('essay_analysis_improvements') + .insert(improvements) + + if (improvementsError) { + console.error('Erro ao salvar pontos a melhorar:', improvementsError) + return new Response( + JSON.stringify({ + error: 'Erro ao salvar pontos a melhorar', + details: improvementsError.message + }), + { status: 500, headers: { ...getCorsHeaders(req), 'Content-Type': 'application/json' } } + ) + } + + // Salvar notas por critério + const { error: scoresError } = await supabaseClient + .from('essay_analysis_scores') + .insert({ + analysis_id: analysisData.id, + adequacy: analysis.criteria_scores.adequacy, + coherence: analysis.criteria_scores.coherence, + cohesion: analysis.criteria_scores.cohesion, + vocabulary: analysis.criteria_scores.vocabulary, + grammar: analysis.criteria_scores.grammar + }) + + if (scoresError) { + console.error('Erro ao salvar notas:', scoresError) + return new Response( + JSON.stringify({ + error: 'Erro ao salvar notas', + details: scoresError.message }), { status: 500, headers: { ...getCorsHeaders(req), 'Content-Type': 'application/json' } } ) diff --git a/supabase/migrations/20240326000001_create_essay_system.sql b/supabase/migrations/20240326000001_create_essay_system.sql index e8c0b19..a434df6 100644 --- a/supabase/migrations/20240326000001_create_essay_system.sql +++ b/supabase/migrations/20240326000001_create_essay_system.sql @@ -44,11 +44,45 @@ CREATE TABLE public.essay_analyses ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, essay_id UUID NOT NULL REFERENCES public.student_essays(id), overall_score INTEGER NOT NULL CHECK (overall_score >= 0 AND overall_score <= 100), - feedback JSONB NOT NULL DEFAULT '{}', - strengths TEXT[] DEFAULT ARRAY[]::TEXT[], - improvements TEXT[] DEFAULT ARRAY[]::TEXT[], suggestions TEXT, - criteria_scores JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Feedback das análises +CREATE TABLE public.essay_analysis_feedback ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL REFERENCES public.essay_analyses(id) ON DELETE CASCADE, + structure_feedback TEXT NOT NULL, + content_feedback TEXT NOT NULL, + language_feedback TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Pontos fortes das análises +CREATE TABLE public.essay_analysis_strengths ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL REFERENCES public.essay_analyses(id) ON DELETE CASCADE, + strength TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Pontos a melhorar das análises +CREATE TABLE public.essay_analysis_improvements ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL REFERENCES public.essay_analyses(id) ON DELETE CASCADE, + improvement TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Notas por critério das análises +CREATE TABLE public.essay_analysis_scores ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL REFERENCES public.essay_analyses(id) ON DELETE CASCADE, + adequacy INTEGER NOT NULL CHECK (adequacy >= 0 AND adequacy <= 100), + coherence INTEGER NOT NULL CHECK (coherence >= 0 AND coherence <= 100), + cohesion INTEGER NOT NULL CHECK (cohesion >= 0 AND cohesion <= 100), + vocabulary INTEGER NOT NULL CHECK (vocabulary >= 0 AND vocabulary <= 100), + grammar INTEGER NOT NULL CHECK (grammar >= 0 AND grammar <= 100), created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); @@ -57,6 +91,10 @@ CREATE INDEX idx_student_essays_student_id ON public.student_essays(student_id); CREATE INDEX idx_student_essays_status ON public.student_essays(status); CREATE INDEX idx_essay_genres_type_id ON public.essay_genres(type_id); CREATE INDEX idx_essay_analyses_essay_id ON public.essay_analyses(essay_id); +CREATE INDEX idx_essay_analysis_feedback_analysis_id ON public.essay_analysis_feedback(analysis_id); +CREATE INDEX idx_essay_analysis_strengths_analysis_id ON public.essay_analysis_strengths(analysis_id); +CREATE INDEX idx_essay_analysis_improvements_analysis_id ON public.essay_analysis_improvements(analysis_id); +CREATE INDEX idx_essay_analysis_scores_analysis_id ON public.essay_analysis_scores(analysis_id); -- Triggers para updated_at CREATE OR REPLACE FUNCTION public.handle_updated_at() @@ -117,12 +155,28 @@ CREATE POLICY "Alunos podem deletar suas próprias redações" USING (student_id = auth.uid()); -- Políticas para essay_analyses +CREATE POLICY "Edge Function pode inserir análises" + ON public.essay_analyses FOR INSERT + WITH CHECK ( + -- A função de análise roda com a service_role, então permitimos + (auth.jwt() ->> 'role' = 'service_role') OR + -- Permitir inserção se o essay_id pertence ao usuário atual + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ) + ); + CREATE POLICY "Alunos podem ver análises de suas próprias redações" ON public.essay_analyses FOR SELECT USING ( - essay_id IN ( - SELECT id FROM public.student_essays - WHERE student_id = auth.uid() + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() ) ); diff --git a/supabase/migrations/20240326000001_create_essay_system_rollback.sql b/supabase/migrations/20240326000001_create_essay_system_rollback.sql index e0c358c..331aa16 100644 --- a/supabase/migrations/20240326000001_create_essay_system_rollback.sql +++ b/supabase/migrations/20240326000001_create_essay_system_rollback.sql @@ -1,4 +1,5 @@ -- Remover políticas +DROP POLICY IF EXISTS "Edge Function pode inserir análises" ON public.essay_analyses; DROP POLICY IF EXISTS "Alunos podem ver análises de suas próprias redações" ON public.essay_analyses; DROP POLICY IF EXISTS "Alunos podem deletar suas próprias redações" ON public.student_essays; DROP POLICY IF EXISTS "Alunos podem atualizar suas próprias redações" ON public.student_essays; @@ -15,16 +16,21 @@ DROP TRIGGER IF EXISTS student_essays_updated_at ON public.student_essays; DROP TRIGGER IF EXISTS essay_genres_updated_at ON public.essay_genres; DROP TRIGGER IF EXISTS essay_types_updated_at ON public.essay_types; --- Remover função do trigger -DROP FUNCTION IF EXISTS public.handle_updated_at(); - -- Remover índices +DROP INDEX IF EXISTS public.idx_essay_analysis_scores_analysis_id; +DROP INDEX IF EXISTS public.idx_essay_analysis_improvements_analysis_id; +DROP INDEX IF EXISTS public.idx_essay_analysis_strengths_analysis_id; +DROP INDEX IF EXISTS public.idx_essay_analysis_feedback_analysis_id; DROP INDEX IF EXISTS public.idx_essay_analyses_essay_id; DROP INDEX IF EXISTS public.idx_essay_genres_type_id; DROP INDEX IF EXISTS public.idx_student_essays_status; DROP INDEX IF EXISTS public.idx_student_essays_student_id; -- Remover tabelas +DROP TABLE IF EXISTS public.essay_analysis_scores; +DROP TABLE IF EXISTS public.essay_analysis_improvements; +DROP TABLE IF EXISTS public.essay_analysis_strengths; +DROP TABLE IF EXISTS public.essay_analysis_feedback; DROP TABLE IF EXISTS public.essay_analyses; DROP TABLE IF EXISTS public.student_essays; DROP TABLE IF EXISTS public.essay_genres; diff --git a/supabase/migrations/20240327000001_normalize_essay_analyses.sql b/supabase/migrations/20240327000001_normalize_essay_analyses.sql new file mode 100644 index 0000000..b646dc6 --- /dev/null +++ b/supabase/migrations/20240327000001_normalize_essay_analyses.sql @@ -0,0 +1,165 @@ +-- Primeiro, vamos criar as novas tabelas normalizadas +CREATE TABLE public.essay_analysis_feedback ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL, + structure_feedback TEXT NOT NULL, + content_feedback TEXT NOT NULL, + language_feedback TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +CREATE TABLE public.essay_analysis_strengths ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL, + strength TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +CREATE TABLE public.essay_analysis_improvements ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL, + improvement TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +CREATE TABLE public.essay_analysis_scores ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + analysis_id UUID NOT NULL, + adequacy INTEGER NOT NULL CHECK (adequacy >= 0 AND adequacy <= 100), + coherence INTEGER NOT NULL CHECK (coherence >= 0 AND coherence <= 100), + cohesion INTEGER NOT NULL CHECK (cohesion >= 0 AND cohesion <= 100), + vocabulary INTEGER NOT NULL CHECK (vocabulary >= 0 AND vocabulary <= 100), + grammar INTEGER NOT NULL CHECK (grammar >= 0 AND grammar <= 100), + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Criar nova versão da tabela essay_analyses sem os campos JSONB e arrays +CREATE TABLE public.essay_analyses_new ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + essay_id UUID NOT NULL REFERENCES public.student_essays(id), + overall_score INTEGER NOT NULL CHECK (overall_score >= 0 AND overall_score <= 100), + suggestions TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Migrar dados da tabela antiga para as novas tabelas +INSERT INTO public.essay_analyses_new (id, essay_id, overall_score, suggestions, created_at) +SELECT id, essay_id, overall_score, suggestions, created_at +FROM public.essay_analyses; + +-- Adicionar chaves estrangeiras nas tabelas de normalização +ALTER TABLE public.essay_analysis_feedback + ADD CONSTRAINT fk_analysis_feedback + FOREIGN KEY (analysis_id) + REFERENCES public.essay_analyses_new(id) + ON DELETE CASCADE; + +ALTER TABLE public.essay_analysis_strengths + ADD CONSTRAINT fk_analysis_strengths + FOREIGN KEY (analysis_id) + REFERENCES public.essay_analyses_new(id) + ON DELETE CASCADE; + +ALTER TABLE public.essay_analysis_improvements + ADD CONSTRAINT fk_analysis_improvements + FOREIGN KEY (analysis_id) + REFERENCES public.essay_analyses_new(id) + ON DELETE CASCADE; + +ALTER TABLE public.essay_analysis_scores + ADD CONSTRAINT fk_analysis_scores + FOREIGN KEY (analysis_id) + REFERENCES public.essay_analyses_new(id) + ON DELETE CASCADE; + +-- Criar índices para melhor performance +CREATE INDEX idx_essay_analysis_feedback_analysis_id ON public.essay_analysis_feedback(analysis_id); +CREATE INDEX idx_essay_analysis_strengths_analysis_id ON public.essay_analysis_strengths(analysis_id); +CREATE INDEX idx_essay_analysis_improvements_analysis_id ON public.essay_analysis_improvements(analysis_id); +CREATE INDEX idx_essay_analysis_scores_analysis_id ON public.essay_analysis_scores(analysis_id); + +-- Aplicar políticas RLS nas novas tabelas +ALTER TABLE public.essay_analyses_new ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_analysis_feedback ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_analysis_strengths ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_analysis_improvements ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.essay_analysis_scores ENABLE ROW LEVEL SECURITY; + +-- Políticas para essay_analyses_new +CREATE POLICY "Edge Function pode inserir análises" + ON public.essay_analyses_new FOR INSERT + WITH CHECK ( + (auth.jwt() ->> 'role' = 'service_role') OR + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ) + ); + +CREATE POLICY "Alunos podem ver análises de suas próprias redações" + ON public.essay_analyses_new FOR SELECT + USING ( + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ) + ); + +-- Políticas para tabelas relacionadas +CREATE POLICY "Acesso vinculado à análise principal - feedback" + ON public.essay_analysis_feedback FOR ALL + USING ( + EXISTS ( + SELECT 1 + FROM public.essay_analyses_new a + JOIN public.student_essays e ON e.id = a.essay_id + WHERE a.id = analysis_id + AND e.student_id = auth.uid() + ) + ); + +CREATE POLICY "Acesso vinculado à análise principal - strengths" + ON public.essay_analysis_strengths FOR ALL + USING ( + EXISTS ( + SELECT 1 + FROM public.essay_analyses_new a + JOIN public.student_essays e ON e.id = a.essay_id + WHERE a.id = analysis_id + AND e.student_id = auth.uid() + ) + ); + +CREATE POLICY "Acesso vinculado à análise principal - improvements" + ON public.essay_analysis_improvements FOR ALL + USING ( + EXISTS ( + SELECT 1 + FROM public.essay_analyses_new a + JOIN public.student_essays e ON e.id = a.essay_id + WHERE a.id = analysis_id + AND e.student_id = auth.uid() + ) + ); + +CREATE POLICY "Acesso vinculado à análise principal - scores" + ON public.essay_analysis_scores FOR ALL + USING ( + EXISTS ( + SELECT 1 + FROM public.essay_analyses_new a + JOIN public.student_essays e ON e.id = a.essay_id + WHERE a.id = analysis_id + AND e.student_id = auth.uid() + ) + ); + +-- Dropar a tabela antiga +DROP TABLE public.essay_analyses; + +-- Renomear a nova tabela +ALTER TABLE public.essay_analyses_new RENAME TO essay_analyses; \ No newline at end of file diff --git a/supabase/migrations/20240327000001_normalize_essay_analyses_rollback.sql b/supabase/migrations/20240327000001_normalize_essay_analyses_rollback.sql new file mode 100644 index 0000000..94da410 --- /dev/null +++ b/supabase/migrations/20240327000001_normalize_essay_analyses_rollback.sql @@ -0,0 +1,89 @@ +-- Recriar a tabela original com os campos JSONB e arrays +CREATE TABLE public.essay_analyses_old ( + id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, + essay_id UUID NOT NULL REFERENCES public.student_essays(id), + overall_score INTEGER NOT NULL CHECK (overall_score >= 0 AND overall_score <= 100), + feedback JSONB NOT NULL DEFAULT '{}', + strengths TEXT[] DEFAULT ARRAY[]::TEXT[], + improvements TEXT[] DEFAULT ARRAY[]::TEXT[], + suggestions TEXT, + criteria_scores JSONB NOT NULL DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Migrar dados das tabelas normalizadas para a tabela original +INSERT INTO public.essay_analyses_old ( + id, + essay_id, + overall_score, + feedback, + strengths, + improvements, + suggestions, + criteria_scores, + created_at +) +SELECT + a.id, + a.essay_id, + a.overall_score, + jsonb_build_object( + 'structure', f.structure_feedback, + 'content', f.content_feedback, + 'language', f.language_feedback + ) as feedback, + array_agg(DISTINCT s.strength) as strengths, + array_agg(DISTINCT i.improvement) as improvements, + a.suggestions, + jsonb_build_object( + 'adequacy', sc.adequacy, + 'coherence', sc.coherence, + 'cohesion', sc.cohesion, + 'vocabulary', sc.vocabulary, + 'grammar', sc.grammar + ) as criteria_scores, + a.created_at +FROM public.essay_analyses a +LEFT JOIN public.essay_analysis_feedback f ON f.analysis_id = a.id +LEFT JOIN public.essay_analysis_strengths s ON s.analysis_id = a.id +LEFT JOIN public.essay_analysis_improvements i ON i.analysis_id = a.id +LEFT JOIN public.essay_analysis_scores sc ON sc.analysis_id = a.id +GROUP BY a.id, a.essay_id, a.overall_score, a.suggestions, a.created_at, + f.structure_feedback, f.content_feedback, f.language_feedback, + sc.adequacy, sc.coherence, sc.cohesion, sc.vocabulary, sc.grammar; + +-- Dropar as tabelas normalizadas +DROP TABLE IF EXISTS public.essay_analysis_scores; +DROP TABLE IF EXISTS public.essay_analysis_improvements; +DROP TABLE IF EXISTS public.essay_analysis_strengths; +DROP TABLE IF EXISTS public.essay_analysis_feedback; +DROP TABLE IF EXISTS public.essay_analyses; + +-- Renomear a tabela antiga +ALTER TABLE public.essay_analyses_old RENAME TO essay_analyses; + +-- Recriar as políticas originais +ALTER TABLE public.essay_analyses ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Edge Function pode inserir análises" + ON public.essay_analyses FOR INSERT + WITH CHECK ( + (auth.jwt() ->> 'role' = 'service_role') OR + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ) + ); + +CREATE POLICY "Alunos podem ver análises de suas próprias redações" + ON public.essay_analyses FOR SELECT + USING ( + EXISTS ( + SELECT 1 + FROM public.student_essays + WHERE id = essay_id + AND student_id = auth.uid() + ) + ); \ No newline at end of file