realtimex-crm 0.5.7 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,20 +1,29 @@
1
+ # For detailed configuration reference documentation, visit:
2
+ # https://supabase.com/docs/guides/local-development/cli/config
1
3
  # A string used to distinguish different Supabase projects on the same host. Defaults to the
2
4
  # working directory name when running `supabase init`.
3
- project_id = "atomic-crm-demo"
5
+ project_id = "atomic-crm"
4
6
 
5
7
  [api]
6
8
  enabled = true
7
9
  # Port to use for the API URL.
8
10
  port = 54321
9
11
  # Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
10
- # endpoints. public and storage are always included.
11
- schemas = ["public", "storage", "graphql_public"]
12
- # Extra schemas to add to the search_path of every request. public is always included.
12
+ # endpoints. `public` and `graphql_public` schemas are included by default.
13
+ schemas = ["public", "graphql_public"]
14
+ # Extra schemas to add to the search_path of every request.
13
15
  extra_search_path = ["public", "extensions"]
14
16
  # The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
15
17
  # for accidental or malicious requests.
16
18
  max_rows = 1000
17
19
 
20
+ [api.tls]
21
+ # Enable HTTPS endpoints locally using a self-signed certificate.
22
+ enabled = false
23
+ # Paths to self-signed certificate pair.
24
+ # cert_path = "../certs/my-cert.pem"
25
+ # key_path = "../certs/my-key.pem"
26
+
18
27
  [db]
19
28
  # Port to use for the local database URL.
20
29
  port = 54322
@@ -22,7 +31,7 @@ port = 54322
22
31
  shadow_port = 54320
23
32
  # The database major version to use. This has to be the same as your remote database's. Run `SHOW
24
33
  # server_version;` on the remote database to check.
25
- major_version = 15
34
+ major_version = 17
26
35
 
27
36
  [db.pooler]
28
37
  enabled = false
@@ -36,9 +45,36 @@ default_pool_size = 20
36
45
  # Maximum number of client connections allowed.
37
46
  max_client_conn = 100
38
47
 
48
+ # [db.vault]
49
+ # secret_key = "env(SECRET_VALUE)"
50
+
51
+ [db.migrations]
52
+ # If disabled, migrations will be skipped during a db push or reset.
53
+ enabled = true
54
+ # Specifies an ordered list of schema files that describe your database.
55
+ # Supports glob patterns relative to supabase directory: "./schemas/*.sql"
56
+ schema_paths = []
57
+
58
+ [db.seed]
59
+ # If enabled, seeds the database after migrations during a db reset.
60
+ enabled = true
61
+ # Specifies an ordered list of seed files to load during db reset.
62
+ # Supports glob patterns relative to supabase directory: "./seeds/*.sql"
63
+ sql_paths = ["./seed.sql"]
64
+
65
+ [db.network_restrictions]
66
+ # Enable management of network restrictions.
67
+ enabled = false
68
+ # List of IPv4 CIDR blocks allowed to connect to the database.
69
+ # Defaults to allow all IPv4 connections. Set empty array to block all IPs.
70
+ allowed_cidrs = ["0.0.0.0/0"]
71
+ # List of IPv6 CIDR blocks allowed to connect to the database.
72
+ # Defaults to allow all IPv6 connections. Set empty array to block all IPs.
73
+ allowed_cidrs_v6 = ["::/0"]
74
+
39
75
  [realtime]
40
76
  enabled = true
41
- # Bind realtime via either IPv4 or IPv6. (default: IPv6)
77
+ # Bind realtime via either IPv4 or IPv6. (default: IPv4)
42
78
  # ip_version = "IPv6"
43
79
  # The maximum length in bytes of HTTP request headers. (default: 4096)
44
80
  # max_header_length = 4096
@@ -49,6 +85,8 @@ enabled = true
49
85
  port = 54323
50
86
  # External URL of the API server that frontend connects to.
51
87
  api_url = "http://127.0.0.1"
88
+ # OpenAI API Key to use for Supabase AI in the Supabase Studio.
89
+ openai_api_key = "env(OPENAI_API_KEY)"
52
90
 
53
91
  # Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
54
92
  # are monitored, and you can view the emails that would have been sent from the web interface.
@@ -59,21 +97,63 @@ port = 54324
59
97
  # Uncomment to expose additional ports for testing user applications that send emails.
60
98
  # smtp_port = 54325
61
99
  # pop3_port = 54326
100
+ # admin_email = "admin@email.com"
101
+ # sender_name = "Admin"
62
102
 
63
103
  [storage]
64
104
  enabled = true
65
105
  # The maximum file size allowed (e.g. "5MB", "500KB").
66
106
  file_size_limit = "50MiB"
67
107
 
108
+ # Uncomment to configure local storage buckets
109
+ # [storage.buckets.images]
110
+ # public = false
111
+ # file_size_limit = "50MiB"
112
+ # allowed_mime_types = ["image/png", "image/jpeg"]
113
+ # objects_path = "./images"
114
+
115
+ # Allow connections via S3 compatible clients
116
+ [storage.s3_protocol]
117
+ enabled = true
118
+
119
+ # Image transformation API is available to Supabase Pro plan.
120
+ # [storage.image_transformation]
121
+ # enabled = true
122
+
123
+ # Store analytical data in S3 for running ETL jobs over Iceberg Catalog
124
+ # This feature is only available on the hosted platform.
125
+ [storage.analytics]
126
+ enabled = false
127
+ max_namespaces = 5
128
+ max_tables = 10
129
+ max_catalogs = 2
130
+
131
+ # Analytics Buckets is available to Supabase Pro plan.
132
+ # [storage.analytics.buckets.my-warehouse]
133
+
134
+ # Store vector embeddings in S3 for large and durable datasets
135
+ # This feature is only available on the hosted platform.
136
+ [storage.vector]
137
+ enabled = false
138
+ max_buckets = 10
139
+ max_indexes = 5
140
+
141
+ # Vector Buckets is available to Supabase Pro plan.
142
+ # [storage.vector.buckets.documents-openai]
143
+
68
144
  [auth]
69
145
  enabled = true
70
146
  # The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
71
147
  # in emails.
72
- site_url = "http://localhost:5173/"
148
+ site_url = "http://127.0.0.1:3000"
73
149
  # A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
74
- additional_redirect_urls = ["https://localhost:5173/auth-callback.html"]
150
+ additional_redirect_urls = ["https://127.0.0.1:3000"]
75
151
  # How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week).
76
152
  jwt_expiry = 3600
153
+ # JWT issuer URL. If not set, defaults to the local API URL (http://127.0.0.1:<port>/auth/v1).
154
+ # jwt_issuer = ""
155
+ # Path to JWT signing key. DO NOT commit your signing keys file to git.
156
+ # signing_keys_path = "./signing_keys.json"
77
157
  # If disabled, the refresh token will never expire.
78
158
  enable_refresh_token_rotation = true
79
159
  # Allows refresh tokens to be reused after expiry, up to the specified interval in seconds.
@@ -81,6 +161,37 @@ enable_refresh_token_rotation = true
81
161
  refresh_token_reuse_interval = 10
82
162
  # Allow/disallow new user signups to your project.
83
163
  enable_signup = true
164
+ # Allow/disallow anonymous sign-ins to your project.
165
+ enable_anonymous_sign_ins = false
166
+ # Allow/disallow testing manual linking of accounts
167
+ enable_manual_linking = false
168
+ # Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more.
169
+ minimum_password_length = 6
170
+ # Passwords that do not meet the following requirements will be rejected as weak. Supported values
171
+ # are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols`
172
+ password_requirements = ""
173
+
174
+ [auth.rate_limit]
175
+ # Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled.
176
+ email_sent = 2
177
+ # Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled.
178
+ sms_sent = 30
179
+ # Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true.
180
+ anonymous_users = 30
181
+ # Number of sessions that can be refreshed in a 5 minute interval per IP address.
182
+ token_refresh = 150
183
+ # Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users).
184
+ sign_in_sign_ups = 30
185
+ # Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address.
186
+ token_verifications = 30
187
+ # Number of Web3 logins that can be made in a 5 minute interval per IP address.
188
+ web3 = 30
189
+
190
+ # Configure one of the supported captcha providers: `hcaptcha`, `turnstile`.
191
+ # [auth.captcha]
192
+ # enabled = true
193
+ # provider = "hcaptcha"
194
+ # secret = ""
84
195
 
85
196
  [auth.email]
86
197
  # Allow/disallow new user signups via email to your project.
@@ -90,27 +201,67 @@ enable_signup = true
90
201
  double_confirm_changes = true
91
202
  # If enabled, users need to confirm their email address before signing in.
92
203
  enable_confirmations = false
204
+ # If enabled, users will need to reauthenticate or have logged in recently to change their password.
205
+ secure_password_change = false
206
+ # Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email.
207
+ max_frequency = "1s"
208
+ # Number of characters used in the email OTP.
209
+ otp_length = 6
210
+ # Number of seconds before the email OTP expires (defaults to 1 hour).
211
+ otp_expiry = 3600
93
212
 
94
- [auth.email.template.invite]
95
- subject = "You've been invited to Atomic CRM"
96
- content_path = "./supabase/templates/invite.html"
213
+ # Use a production-ready SMTP server
214
+ # [auth.email.smtp]
215
+ # enabled = true
216
+ # host = "smtp.sendgrid.net"
217
+ # port = 587
218
+ # user = "apikey"
219
+ # pass = "env(SENDGRID_API_KEY)"
220
+ # admin_email = "admin@email.com"
221
+ # sender_name = "Admin"
97
222
 
98
- [auth.email.template.recovery]
99
- subject = "Reset your Atomic CRM Password"
100
- content_path = "./supabase/templates/recovery.html"
223
+ # Uncomment to customize email template
224
+ # [auth.email.template.invite]
225
+ # subject = "You have been invited"
226
+ # content_path = "./supabase/templates/invite.html"
227
+
228
+ # Uncomment to customize notification email template
229
+ # [auth.email.notification.password_changed]
230
+ # enabled = true
231
+ # subject = "Your password has been changed"
232
+ # content_path = "./templates/password_changed_notification.html"
101
233
 
102
234
  [auth.sms]
103
235
  # Allow/disallow new user signups via SMS to your project.
104
- enable_signup = true
236
+ enable_signup = false
105
237
  # If enabled, users need to confirm their phone number before signing in.
106
238
  enable_confirmations = false
107
239
  # Template for sending OTP to users
108
- template = "Your code is {{ .Code }} ."
240
+ template = "Your code is {{ .Code }}"
241
+ # Controls the minimum amount of time that must pass before sending another sms otp.
242
+ max_frequency = "5s"
109
243
 
110
244
  # Use pre-defined map of phone number to OTP for testing.
111
- [auth.sms.test_otp]
245
+ # [auth.sms.test_otp]
112
246
  # 4152127777 = "123456"
113
247
 
248
+ # Configure logged in session timeouts.
249
+ # [auth.sessions]
250
+ # Force log out after the specified duration.
251
+ # timebox = "24h"
252
+ # Force log out if the user has been inactive longer than the specified duration.
253
+ # inactivity_timeout = "8h"
254
+
255
+ # This hook runs before a new user is created and allows developers to reject the request based on the incoming user object.
256
+ # [auth.hook.before_user_created]
257
+ # enabled = true
258
+ # uri = "pg-functions://postgres/auth/before-user-created-hook"
259
+
260
+ # This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
261
+ # [auth.hook.custom_access_token]
262
+ # enabled = true
263
+ # uri = "pg-functions://<database>/<schema>/<hook_name>"
264
+
114
265
  # Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`.
115
266
  [auth.sms.twilio]
116
267
  enabled = false
@@ -119,8 +270,31 @@ message_service_sid = ""
119
270
  # DO NOT commit your Twilio auth token to git. Use environment variable substitution instead:
120
271
  auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)"
121
272
 
273
+ # Multi-factor-authentication is available to Supabase Pro plan.
274
+ [auth.mfa]
275
+ # Control how many MFA factors can be enrolled at once per user.
276
+ max_enrolled_factors = 10
277
+
278
+ # Control MFA via App Authenticator (TOTP)
279
+ [auth.mfa.totp]
280
+ enroll_enabled = false
281
+ verify_enabled = false
282
+
283
+ # Configure MFA via Phone Messaging
284
+ [auth.mfa.phone]
285
+ enroll_enabled = false
286
+ verify_enabled = false
287
+ otp_length = 6
288
+ template = "Your code is {{ .Code }}"
289
+ max_frequency = "5s"
290
+
291
+ # Configure MFA via WebAuthn
292
+ # [auth.mfa.web_authn]
293
+ # enroll_enabled = true
294
+ # verify_enabled = true
295
+
122
296
  # Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
123
- # `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`,
297
+ # `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`,
124
298
  # `twitter`, `slack`, `spotify`, `workos`, `zoom`.
125
299
  [auth.external.apple]
126
300
  enabled = false
@@ -132,11 +306,65 @@ redirect_uri = ""
132
306
  # Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
133
307
  # or any other third-party OIDC providers.
134
308
  url = ""
309
+ # If enabled, the nonce check will be skipped. Required for local sign in with Google auth.
310
+ skip_nonce_check = false
311
+ # If enabled, it will allow the user to successfully authenticate when the provider does not return an email address.
312
+ email_optional = false
135
313
 
136
- [analytics]
314
+ # Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard.
315
+ # You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting.
316
+ [auth.web3.solana]
317
+ enabled = false
318
+
319
+ # Use Firebase Auth as a third-party provider alongside Supabase Auth.
320
+ [auth.third_party.firebase]
321
+ enabled = false
322
+ # project_id = "my-firebase-project"
323
+
324
+ # Use Auth0 as a third-party provider alongside Supabase Auth.
325
+ [auth.third_party.auth0]
326
+ enabled = false
327
+ # tenant = "my-auth0-tenant"
328
+ # tenant_region = "us"
329
+
330
+ # Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth.
331
+ [auth.third_party.aws_cognito]
332
+ enabled = false
333
+ # user_pool_id = "my-user-pool-id"
334
+ # user_pool_region = "us-east-1"
335
+
336
+ # Use Clerk as a third-party provider alongside Supabase Auth.
337
+ [auth.third_party.clerk]
137
338
  enabled = false
339
+ # Obtain from https://clerk.com/setup/supabase
340
+ # domain = "example.clerk.accounts.dev"
341
+
342
+ # OAuth server configuration
343
+ [auth.oauth_server]
344
+ # Enable OAuth server functionality
345
+ enabled = false
346
+ # Path for OAuth consent flow UI
347
+ authorization_url_path = "/oauth/consent"
348
+ # Allow dynamic client registration
349
+ allow_dynamic_registration = false
350
+
351
+ [edge_runtime]
352
+ enabled = true
353
+ # Supported request policies: `oneshot`, `per_worker`.
354
+ # `per_worker` (default) — enables hot reload during local development.
355
+ # `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks).
356
+ policy = "per_worker"
357
+ # Port to attach the Chrome inspector for debugging edge functions.
358
+ inspector_port = 8083
359
+ # The Deno major version to use.
360
+ deno_version = 2
361
+
362
+ # [edge_runtime.secrets]
363
+ # secret_key = "env(SECRET_VALUE)"
364
+
365
+ [analytics]
366
+ enabled = true
138
367
  port = 54327
139
- vector_port = 54328
140
368
  # Configure one of the supported backends: `postgres`, `bigquery`.
141
369
  backend = "postgres"
142
370
 
@@ -152,6 +380,3 @@ s3_region = "env(S3_REGION)"
152
380
  s3_access_key = "env(S3_ACCESS_KEY)"
153
381
  # Configures AWS_SECRET_ACCESS_KEY for S3 bucket
154
382
  s3_secret_key = "env(S3_SECRET_KEY)"
155
-
156
- [functions.postmark]
157
- verify_jwt = false
@@ -2,7 +2,7 @@ export const corsHeaders = {
2
2
  "Access-Control-Allow-Origin": "*",
3
3
  "Access-Control-Allow-Headers":
4
4
  "authorization, x-client-info, apikey, content-type",
5
- "Access-Control-Allow-Methods": "POST, PATCH, DELETE",
5
+ "Access-Control-Allow-Methods": "POST, PATCH, PUT, DELETE",
6
6
  };
7
7
 
8
8
  export function createErrorResponse(status: number, message: string) {
@@ -0,0 +1,58 @@
1
+ import "jsr:@supabase/functions-js/edge-runtime.d.ts";
2
+ import { supabaseAdmin } from "../_shared/supabaseAdmin.ts";
3
+ import { corsHeaders, createErrorResponse } from "../_shared/utils.ts";
4
+
5
+ async function createFirstUser(req: Request) {
6
+ const { email, password, first_name, last_name } = await req.json();
7
+
8
+ // Check if any users exist
9
+ const { count } = await supabaseAdmin
10
+ .from("sales")
11
+ .select("*", { count: "exact", head: true });
12
+
13
+ if (count && count > 0) {
14
+ return createErrorResponse(403, "First user already exists");
15
+ }
16
+
17
+ // Create user with admin API (bypasses signup restrictions)
18
+ const { data, error: userError } = await supabaseAdmin.auth.admin.createUser({
19
+ email,
20
+ password,
21
+ email_confirm: true, // Auto-confirm first user
22
+ user_metadata: { first_name, last_name },
23
+ });
24
+
25
+ if (!data?.user || userError) {
26
+ console.error("Error creating first user:", userError);
27
+ return createErrorResponse(500, "Failed to create first user");
28
+ }
29
+
30
+ // The database trigger will create the sales record and set administrator = true for first user
31
+
32
+ return new Response(
33
+ JSON.stringify({
34
+ data: {
35
+ id: data.user.id,
36
+ email: data.user.email,
37
+ },
38
+ }),
39
+ {
40
+ headers: { "Content-Type": "application/json", ...corsHeaders },
41
+ },
42
+ );
43
+ }
44
+
45
+ Deno.serve(async (req: Request) => {
46
+ if (req.method === "OPTIONS") {
47
+ return new Response(null, {
48
+ status: 204,
49
+ headers: corsHeaders,
50
+ });
51
+ }
52
+
53
+ if (req.method === "POST") {
54
+ return createFirstUser(req);
55
+ }
56
+
57
+ return createErrorResponse(405, "Method Not Allowed");
58
+ });
@@ -42,32 +42,35 @@ async function updateSaleAvatar(user_id: string, avatar: string) {
42
42
  }
43
43
 
44
44
  async function inviteUser(req: Request, currentUserSale: any) {
45
- const { email, password, first_name, last_name, disabled, administrator } =
45
+ const { email, first_name, last_name, disabled, administrator } =
46
46
  await req.json();
47
47
 
48
48
  if (!currentUserSale.administrator) {
49
49
  return createErrorResponse(401, "Not Authorized");
50
50
  }
51
51
 
52
- const { data, error: userError } = await supabaseAdmin.auth.admin.createUser({
53
- email,
54
- password,
55
- user_metadata: { first_name, last_name },
56
- });
52
+ // Create user using inviteUserByEmail which:
53
+ // 1. Creates the user account
54
+ // 2. Sends the "Invite user" email template (not "Confirm signup")
55
+ // 3. User will use OTP to log in (not the invite link)
56
+ console.log(`[inviteUser] Creating user and sending welcome email to ${email}`);
57
57
 
58
- const { error: emailError } =
59
- await supabaseAdmin.auth.admin.inviteUserByEmail(email);
58
+ const { data, error: inviteError } = await supabaseAdmin.auth.admin.inviteUserByEmail(email, {
59
+ data: { first_name, last_name }, // User metadata
60
+ });
60
61
 
61
- if (!data?.user || userError) {
62
- console.error(`Error inviting user: user_error=${userError}`);
63
- return createErrorResponse(500, "Internal Server Error");
62
+ if (inviteError) {
63
+ console.error(`[inviteUser] Error: ${inviteError.message}`);
64
+ return createErrorResponse(500, `Failed to invite user: ${inviteError.message}`);
64
65
  }
65
66
 
66
- if (!data?.user || userError || emailError) {
67
- console.error(`Error inviting user, email_error=${emailError}`);
68
- return createErrorResponse(500, "Failed to send invitation mail");
67
+ if (!data?.user) {
68
+ console.error(`[inviteUser] No user returned from invite`);
69
+ return createErrorResponse(500, "Failed to create user");
69
70
  }
70
71
 
72
+ console.log(`[inviteUser] User created and welcome email sent to ${email}`);
73
+
71
74
  try {
72
75
  await updateSaleDisabled(data.user.id, disabled);
73
76
  const sale = await updateSaleAdministrator(data.user.id, administrator);
@@ -86,6 +89,57 @@ async function inviteUser(req: Request, currentUserSale: any) {
86
89
  }
87
90
  }
88
91
 
92
+ async function resendInvite(req: Request, currentUserSale: any) {
93
+ try {
94
+ const { sales_id, action } = await req.json();
95
+ console.log("[resendInvite] Request:", { sales_id, action });
96
+
97
+ if (!currentUserSale.administrator) {
98
+ return createErrorResponse(401, "Not Authorized");
99
+ }
100
+
101
+ const { data: sale } = await supabaseAdmin
102
+ .from("sales")
103
+ .select("*")
104
+ .eq("id", sales_id)
105
+ .single();
106
+
107
+ if (!sale) {
108
+ return createErrorResponse(404, "User not found");
109
+ }
110
+
111
+ // Get user from auth
112
+ const { data: authUser, error: getUserError } =
113
+ await supabaseAdmin.auth.admin.getUserById(sale.user_id);
114
+
115
+ if (!authUser?.user || getUserError) {
116
+ console.error("Error getting user:", getUserError);
117
+ return createErrorResponse(404, "User not found");
118
+ }
119
+
120
+ console.log("[resendInvite] User email:", authUser.user.email, "Action:", action);
121
+
122
+ // NOTE: With OTP authentication, users don't need invite/reset emails
123
+ // Instead, instruct them to:
124
+ // - For new users: Use "Login with email code (OTP)" to set up their account
125
+ // - For password reset: Use "Forgot Password?" which sends OTP code
126
+
127
+ // Return success - admin should manually notify user to use OTP login
128
+ console.log("[resendInvite] OTP-based flow - no email sent");
129
+ return new Response(
130
+ JSON.stringify({
131
+ data: sale,
132
+ }),
133
+ {
134
+ headers: { "Content-Type": "application/json", ...corsHeaders },
135
+ },
136
+ );
137
+ } catch (err) {
138
+ console.error("[resendInvite] Unexpected error:", err);
139
+ return createErrorResponse(500, `Internal error: ${err instanceof Error ? err.message : "Unknown error"}`);
140
+ }
141
+ }
142
+
89
143
  async function patchUser(req: Request, currentUserSale: any) {
90
144
  const {
91
145
  sales_id,
@@ -202,5 +256,9 @@ Deno.serve(async (req: Request) => {
202
256
  return patchUser(req, currentUserSale.data);
203
257
  }
204
258
 
259
+ if (req.method === "PUT") {
260
+ return resendInvite(req, currentUserSale.data);
261
+ }
262
+
205
263
  return createErrorResponse(405, "Method Not Allowed");
206
264
  });
@@ -0,0 +1,52 @@
1
+ -- Add email_confirmed_at column to sales table
2
+ ALTER TABLE sales ADD COLUMN email_confirmed_at timestamp with time zone;
3
+
4
+ -- Update existing records with current confirmation status
5
+ UPDATE sales
6
+ SET email_confirmed_at = auth.users.email_confirmed_at
7
+ FROM auth.users
8
+ WHERE sales.user_id = auth.users.id;
9
+
10
+ -- Update the handle_new_user trigger to include email_confirmed_at
11
+ CREATE OR REPLACE FUNCTION handle_new_user()
12
+ RETURNS trigger
13
+ LANGUAGE plpgsql
14
+ SECURITY DEFINER SET search_path = ''
15
+ AS $$
16
+ DECLARE
17
+ sales_count integer;
18
+ BEGIN
19
+ -- Count existing sales records
20
+ SELECT COUNT(*) INTO sales_count FROM public.sales;
21
+
22
+ INSERT INTO public.sales (user_id, email, first_name, last_name, administrator, email_confirmed_at)
23
+ VALUES (
24
+ new.id,
25
+ new.email,
26
+ COALESCE(new.raw_user_meta_data->>'first_name', ''),
27
+ COALESCE(new.raw_user_meta_data->>'last_name', ''),
28
+ -- First user is administrator, others are not
29
+ CASE WHEN sales_count = 0 THEN true ELSE false END,
30
+ new.email_confirmed_at
31
+ );
32
+ RETURN new;
33
+ END;
34
+ $$;
35
+
36
+ -- Update the handle_update_user trigger to include email_confirmed_at
37
+ CREATE OR REPLACE FUNCTION handle_update_user()
38
+ RETURNS trigger
39
+ LANGUAGE plpgsql
40
+ SECURITY DEFINER SET search_path = ''
41
+ AS $$
42
+ BEGIN
43
+ UPDATE public.sales
44
+ SET
45
+ email = new.email,
46
+ first_name = COALESCE(new.raw_user_meta_data->>'first_name', first_name),
47
+ last_name = COALESCE(new.raw_user_meta_data->>'last_name', last_name),
48
+ email_confirmed_at = new.email_confirmed_at
49
+ WHERE user_id = new.id;
50
+ RETURN new;
51
+ END;
52
+ $$;