@capgo/capacitor-supabase 8.0.4
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.
- package/CapgoCapacitorSupabase.podspec +18 -0
- package/LICENSE +373 -0
- package/Package.swift +30 -0
- package/README.md +801 -0
- package/android/build.gradle +84 -0
- package/android/src/main/java/ee/forgr/plugin/capacitor_supabase/CapacitorSupabasePlugin.kt +751 -0
- package/dist/docs.json +1718 -0
- package/dist/esm/definitions.d.ts +754 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +33 -0
- package/dist/esm/web.js +68 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +82 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +85 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapacitorSupabasePlugin/CapacitorSupabasePlugin.swift +628 -0
- package/ios/Tests/CapacitorSupabasePluginTests/CapacitorSupabasePluginTests.swift +9 -0
- package/package.json +90 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
package ee.forgr.plugin.capacitor_supabase
|
|
2
|
+
|
|
3
|
+
import android.content.Intent
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import com.getcapacitor.JSArray
|
|
6
|
+
import com.getcapacitor.JSObject
|
|
7
|
+
import com.getcapacitor.Plugin
|
|
8
|
+
import com.getcapacitor.PluginCall
|
|
9
|
+
import com.getcapacitor.PluginMethod
|
|
10
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
11
|
+
import io.github.jan.supabase.SupabaseClient
|
|
12
|
+
import io.github.jan.supabase.auth.Auth
|
|
13
|
+
import io.github.jan.supabase.auth.auth
|
|
14
|
+
import io.github.jan.supabase.auth.providers.builtin.Email
|
|
15
|
+
import io.github.jan.supabase.auth.providers.builtin.OTP
|
|
16
|
+
import io.github.jan.supabase.auth.status.SessionStatus
|
|
17
|
+
import io.github.jan.supabase.auth.user.UserSession
|
|
18
|
+
import io.github.jan.supabase.auth.user.UserInfo
|
|
19
|
+
import io.github.jan.supabase.createSupabaseClient
|
|
20
|
+
import io.github.jan.supabase.postgrest.Postgrest
|
|
21
|
+
import io.github.jan.supabase.postgrest.postgrest
|
|
22
|
+
import io.github.jan.supabase.postgrest.query.Columns
|
|
23
|
+
import io.github.jan.supabase.postgrest.query.Order
|
|
24
|
+
import kotlinx.coroutines.CoroutineScope
|
|
25
|
+
import kotlinx.coroutines.Dispatchers
|
|
26
|
+
import kotlinx.coroutines.Job
|
|
27
|
+
import kotlinx.coroutines.cancel
|
|
28
|
+
import kotlinx.coroutines.launch
|
|
29
|
+
import kotlinx.serialization.json.Json
|
|
30
|
+
import kotlinx.serialization.json.JsonElement
|
|
31
|
+
import kotlinx.serialization.json.JsonObject
|
|
32
|
+
import kotlinx.serialization.json.JsonPrimitive
|
|
33
|
+
import kotlinx.serialization.json.buildJsonObject
|
|
34
|
+
import org.json.JSONArray
|
|
35
|
+
import org.json.JSONObject
|
|
36
|
+
|
|
37
|
+
@CapacitorPlugin(name = "CapacitorSupabase")
|
|
38
|
+
class CapacitorSupabasePlugin : Plugin() {
|
|
39
|
+
private val pluginVersion = "7.0.0"
|
|
40
|
+
private var supabaseClient: SupabaseClient? = null
|
|
41
|
+
private val scope = CoroutineScope(Dispatchers.Main + Job())
|
|
42
|
+
private var authStateJob: Job? = null
|
|
43
|
+
|
|
44
|
+
override fun handleOnDestroy() {
|
|
45
|
+
authStateJob?.cancel()
|
|
46
|
+
scope.cancel()
|
|
47
|
+
super.handleOnDestroy()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@PluginMethod
|
|
51
|
+
fun initialize(call: PluginCall) {
|
|
52
|
+
val supabaseUrl = call.getString("supabaseUrl")
|
|
53
|
+
val supabaseKey = call.getString("supabaseKey")
|
|
54
|
+
|
|
55
|
+
if (supabaseUrl.isNullOrEmpty() || supabaseKey.isNullOrEmpty()) {
|
|
56
|
+
call.reject("Missing supabaseUrl or supabaseKey")
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
supabaseClient = createSupabaseClient(
|
|
62
|
+
supabaseUrl = supabaseUrl,
|
|
63
|
+
supabaseKey = supabaseKey
|
|
64
|
+
) {
|
|
65
|
+
install(Auth)
|
|
66
|
+
install(Postgrest)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setupAuthStateListener()
|
|
70
|
+
call.resolve()
|
|
71
|
+
} catch (e: Exception) {
|
|
72
|
+
call.reject("Failed to initialize Supabase client: ${e.message}")
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private fun setupAuthStateListener() {
|
|
77
|
+
authStateJob?.cancel()
|
|
78
|
+
authStateJob = scope.launch {
|
|
79
|
+
supabaseClient?.auth?.sessionStatus?.collect { status ->
|
|
80
|
+
val eventName = when (status) {
|
|
81
|
+
is SessionStatus.Authenticated -> "SIGNED_IN"
|
|
82
|
+
is SessionStatus.NotAuthenticated -> "SIGNED_OUT"
|
|
83
|
+
is SessionStatus.RefreshFailure -> "TOKEN_REFRESHED"
|
|
84
|
+
SessionStatus.Initializing -> "INITIAL_SESSION"
|
|
85
|
+
else -> return@collect
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
val data = JSObject()
|
|
89
|
+
data.put("event", eventName)
|
|
90
|
+
|
|
91
|
+
when (status) {
|
|
92
|
+
is SessionStatus.Authenticated -> {
|
|
93
|
+
data.put("session", sessionToJSObject(status.session))
|
|
94
|
+
}
|
|
95
|
+
else -> {
|
|
96
|
+
data.put("session", JSONObject.NULL)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
notifyListeners("authStateChange", data)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@PluginMethod
|
|
106
|
+
fun signInWithPassword(call: PluginCall) {
|
|
107
|
+
val client = supabaseClient
|
|
108
|
+
if (client == null) {
|
|
109
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
val emailStr = call.getString("email")
|
|
114
|
+
val passwordStr = call.getString("password")
|
|
115
|
+
|
|
116
|
+
if (emailStr.isNullOrEmpty() || passwordStr.isNullOrEmpty()) {
|
|
117
|
+
call.reject("Missing email or password")
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
scope.launch {
|
|
122
|
+
try {
|
|
123
|
+
client.auth.signInWith(Email) {
|
|
124
|
+
email = emailStr
|
|
125
|
+
password = passwordStr
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
val session = client.auth.currentSessionOrNull()
|
|
129
|
+
val user = client.auth.currentUserOrNull()
|
|
130
|
+
|
|
131
|
+
val result = JSObject()
|
|
132
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
133
|
+
result.put("user", user?.let { userToJSObject(it) } ?: JSONObject.NULL)
|
|
134
|
+
call.resolve(result)
|
|
135
|
+
} catch (e: Exception) {
|
|
136
|
+
call.reject("Sign in failed: ${e.message}")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@PluginMethod
|
|
142
|
+
fun signUp(call: PluginCall) {
|
|
143
|
+
val client = supabaseClient
|
|
144
|
+
if (client == null) {
|
|
145
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
val emailStr = call.getString("email")
|
|
150
|
+
val passwordStr = call.getString("password")
|
|
151
|
+
val userData = call.getObject("data")
|
|
152
|
+
|
|
153
|
+
if (emailStr.isNullOrEmpty() || passwordStr.isNullOrEmpty()) {
|
|
154
|
+
call.reject("Missing email or password")
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
scope.launch {
|
|
159
|
+
try {
|
|
160
|
+
val jsonData = userData?.let { jsObjectToJsonObject(it) }
|
|
161
|
+
|
|
162
|
+
client.auth.signUpWith(Email) {
|
|
163
|
+
email = emailStr
|
|
164
|
+
password = passwordStr
|
|
165
|
+
if (jsonData != null) {
|
|
166
|
+
data = jsonData
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
val session = client.auth.currentSessionOrNull()
|
|
171
|
+
val user = client.auth.currentUserOrNull()
|
|
172
|
+
|
|
173
|
+
val result = JSObject()
|
|
174
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
175
|
+
result.put("user", user?.let { userToJSObject(it) } ?: JSONObject.NULL)
|
|
176
|
+
call.resolve(result)
|
|
177
|
+
} catch (e: Exception) {
|
|
178
|
+
call.reject("Sign up failed: ${e.message}")
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@PluginMethod
|
|
184
|
+
fun signInWithOAuth(call: PluginCall) {
|
|
185
|
+
val client = supabaseClient
|
|
186
|
+
if (client == null) {
|
|
187
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
val providerString = call.getString("provider")
|
|
192
|
+
val redirectTo = call.getString("redirectTo")
|
|
193
|
+
|
|
194
|
+
if (providerString.isNullOrEmpty()) {
|
|
195
|
+
call.reject("Missing provider")
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
scope.launch {
|
|
200
|
+
try {
|
|
201
|
+
val provider = getOAuthProvider(providerString)
|
|
202
|
+
if (provider == null) {
|
|
203
|
+
call.reject("Invalid OAuth provider: $providerString")
|
|
204
|
+
return@launch
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
val url = client.auth.getOAuthUrl(provider)
|
|
208
|
+
|
|
209
|
+
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
210
|
+
activity.startActivity(intent)
|
|
211
|
+
call.resolve()
|
|
212
|
+
} catch (e: Exception) {
|
|
213
|
+
call.reject("OAuth sign in failed: ${e.message}")
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
@PluginMethod
|
|
219
|
+
fun signInWithOtp(call: PluginCall) {
|
|
220
|
+
val client = supabaseClient
|
|
221
|
+
if (client == null) {
|
|
222
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
val emailStr = call.getString("email")
|
|
227
|
+
val phoneStr = call.getString("phone")
|
|
228
|
+
|
|
229
|
+
if (emailStr.isNullOrEmpty() && phoneStr.isNullOrEmpty()) {
|
|
230
|
+
call.reject("Either email or phone is required")
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
scope.launch {
|
|
235
|
+
try {
|
|
236
|
+
if (!emailStr.isNullOrEmpty()) {
|
|
237
|
+
client.auth.signInWith(OTP) {
|
|
238
|
+
email = emailStr
|
|
239
|
+
}
|
|
240
|
+
} else if (!phoneStr.isNullOrEmpty()) {
|
|
241
|
+
client.auth.signInWith(OTP) {
|
|
242
|
+
phone = phoneStr
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
call.resolve()
|
|
246
|
+
} catch (e: Exception) {
|
|
247
|
+
call.reject("OTP sign in failed: ${e.message}")
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@PluginMethod
|
|
253
|
+
fun verifyOtp(call: PluginCall) {
|
|
254
|
+
val client = supabaseClient
|
|
255
|
+
if (client == null) {
|
|
256
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
val emailStr = call.getString("email")
|
|
261
|
+
val phoneStr = call.getString("phone")
|
|
262
|
+
val token = call.getString("token")
|
|
263
|
+
val type = call.getString("type")
|
|
264
|
+
|
|
265
|
+
if (token.isNullOrEmpty()) {
|
|
266
|
+
call.reject("Missing token")
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (emailStr.isNullOrEmpty() && phoneStr.isNullOrEmpty()) {
|
|
271
|
+
call.reject("Either email or phone is required")
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
scope.launch {
|
|
276
|
+
try {
|
|
277
|
+
if (!emailStr.isNullOrEmpty()) {
|
|
278
|
+
client.auth.verifyEmailOtp(
|
|
279
|
+
type = getEmailOtpType(type ?: "email"),
|
|
280
|
+
email = emailStr,
|
|
281
|
+
token = token
|
|
282
|
+
)
|
|
283
|
+
} else if (!phoneStr.isNullOrEmpty()) {
|
|
284
|
+
client.auth.verifyPhoneOtp(
|
|
285
|
+
type = getPhoneOtpType(type ?: "sms"),
|
|
286
|
+
phone = phoneStr,
|
|
287
|
+
token = token
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
val session = client.auth.currentSessionOrNull()
|
|
292
|
+
val user = client.auth.currentUserOrNull()
|
|
293
|
+
|
|
294
|
+
val result = JSObject()
|
|
295
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
296
|
+
result.put("user", user?.let { userToJSObject(it) } ?: JSONObject.NULL)
|
|
297
|
+
call.resolve(result)
|
|
298
|
+
} catch (e: Exception) {
|
|
299
|
+
call.reject("OTP verification failed: ${e.message}")
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@PluginMethod
|
|
305
|
+
fun signOut(call: PluginCall) {
|
|
306
|
+
val client = supabaseClient
|
|
307
|
+
if (client == null) {
|
|
308
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
scope.launch {
|
|
313
|
+
try {
|
|
314
|
+
client.auth.signOut()
|
|
315
|
+
call.resolve()
|
|
316
|
+
} catch (e: Exception) {
|
|
317
|
+
call.reject("Sign out failed: ${e.message}")
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@PluginMethod
|
|
323
|
+
fun getSession(call: PluginCall) {
|
|
324
|
+
val client = supabaseClient
|
|
325
|
+
if (client == null) {
|
|
326
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
scope.launch {
|
|
331
|
+
try {
|
|
332
|
+
val session = client.auth.currentSessionOrNull()
|
|
333
|
+
val result = JSObject()
|
|
334
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
335
|
+
call.resolve(result)
|
|
336
|
+
} catch (e: Exception) {
|
|
337
|
+
val result = JSObject()
|
|
338
|
+
result.put("session", JSONObject.NULL)
|
|
339
|
+
call.resolve(result)
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@PluginMethod
|
|
345
|
+
fun refreshSession(call: PluginCall) {
|
|
346
|
+
val client = supabaseClient
|
|
347
|
+
if (client == null) {
|
|
348
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
scope.launch {
|
|
353
|
+
try {
|
|
354
|
+
client.auth.refreshCurrentSession()
|
|
355
|
+
val session = client.auth.currentSessionOrNull()
|
|
356
|
+
val result = JSObject()
|
|
357
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
358
|
+
call.resolve(result)
|
|
359
|
+
} catch (e: Exception) {
|
|
360
|
+
call.reject("Session refresh failed: ${e.message}")
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@PluginMethod
|
|
366
|
+
fun getUser(call: PluginCall) {
|
|
367
|
+
val client = supabaseClient
|
|
368
|
+
if (client == null) {
|
|
369
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
scope.launch {
|
|
374
|
+
try {
|
|
375
|
+
val user = client.auth.currentUserOrNull()
|
|
376
|
+
val result = JSObject()
|
|
377
|
+
result.put("user", user?.let { userToJSObject(it) } ?: JSONObject.NULL)
|
|
378
|
+
call.resolve(result)
|
|
379
|
+
} catch (e: Exception) {
|
|
380
|
+
val result = JSObject()
|
|
381
|
+
result.put("user", JSONObject.NULL)
|
|
382
|
+
call.resolve(result)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@PluginMethod
|
|
388
|
+
fun setSession(call: PluginCall) {
|
|
389
|
+
val client = supabaseClient
|
|
390
|
+
if (client == null) {
|
|
391
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
392
|
+
return
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
val accessToken = call.getString("accessToken")
|
|
396
|
+
val refreshToken = call.getString("refreshToken")
|
|
397
|
+
|
|
398
|
+
if (accessToken.isNullOrEmpty() || refreshToken.isNullOrEmpty()) {
|
|
399
|
+
call.reject("Missing accessToken or refreshToken")
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
scope.launch {
|
|
404
|
+
try {
|
|
405
|
+
client.auth.importSession(
|
|
406
|
+
UserSession(
|
|
407
|
+
accessToken = accessToken,
|
|
408
|
+
refreshToken = refreshToken,
|
|
409
|
+
expiresIn = 3600,
|
|
410
|
+
tokenType = "bearer",
|
|
411
|
+
user = null
|
|
412
|
+
)
|
|
413
|
+
)
|
|
414
|
+
val session = client.auth.currentSessionOrNull()
|
|
415
|
+
val result = JSObject()
|
|
416
|
+
result.put("session", session?.let { sessionToJSObject(it) } ?: JSONObject.NULL)
|
|
417
|
+
call.resolve(result)
|
|
418
|
+
} catch (e: Exception) {
|
|
419
|
+
call.reject("Set session failed: ${e.message}")
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Database Operations
|
|
425
|
+
|
|
426
|
+
@PluginMethod
|
|
427
|
+
fun select(call: PluginCall) {
|
|
428
|
+
val client = supabaseClient
|
|
429
|
+
if (client == null) {
|
|
430
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
val table = call.getString("table")
|
|
435
|
+
if (table.isNullOrEmpty()) {
|
|
436
|
+
call.reject("Missing table name")
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
val columns = call.getString("columns") ?: "*"
|
|
441
|
+
val filter = call.getObject("filter")
|
|
442
|
+
val limit = call.getInt("limit")
|
|
443
|
+
val offset = call.getInt("offset")
|
|
444
|
+
val orderBy = call.getString("orderBy")
|
|
445
|
+
val ascending = call.getBoolean("ascending", true) ?: true
|
|
446
|
+
val single = call.getBoolean("single", false) ?: false
|
|
447
|
+
|
|
448
|
+
scope.launch {
|
|
449
|
+
try {
|
|
450
|
+
val result = client.postgrest.from(table).select(Columns.raw(columns)) {
|
|
451
|
+
filter?.let { f ->
|
|
452
|
+
f.keys().forEach { key ->
|
|
453
|
+
val value = f.get(key)
|
|
454
|
+
filter { eq(key, value) }
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
orderBy?.let {
|
|
459
|
+
order(it, if (ascending) Order.ASCENDING else Order.DESCENDING)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
limit?.let {
|
|
463
|
+
limit(it.toLong())
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
offset?.let { off ->
|
|
467
|
+
val lim = limit ?: 1000
|
|
468
|
+
range(off.toLong(), (off + lim - 1).toLong())
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (single) {
|
|
472
|
+
single()
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
val jsonString = result.data
|
|
477
|
+
val response = JSObject()
|
|
478
|
+
try {
|
|
479
|
+
if (single) {
|
|
480
|
+
response.put("data", JSObject(jsonString))
|
|
481
|
+
} else {
|
|
482
|
+
response.put("data", JSArray(jsonString))
|
|
483
|
+
}
|
|
484
|
+
} catch (e: Exception) {
|
|
485
|
+
response.put("data", jsonString)
|
|
486
|
+
}
|
|
487
|
+
response.put("error", JSONObject.NULL)
|
|
488
|
+
call.resolve(response)
|
|
489
|
+
} catch (e: Exception) {
|
|
490
|
+
val response = JSObject()
|
|
491
|
+
response.put("data", JSONObject.NULL)
|
|
492
|
+
response.put("error", e.message)
|
|
493
|
+
call.resolve(response)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
@PluginMethod
|
|
499
|
+
fun insert(call: PluginCall) {
|
|
500
|
+
val client = supabaseClient
|
|
501
|
+
if (client == null) {
|
|
502
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
503
|
+
return
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
val table = call.getString("table")
|
|
507
|
+
if (table.isNullOrEmpty()) {
|
|
508
|
+
call.reject("Missing table name")
|
|
509
|
+
return
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
val values = call.getObject("values")
|
|
513
|
+
if (values == null) {
|
|
514
|
+
call.reject("Missing values to insert")
|
|
515
|
+
return
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
scope.launch {
|
|
519
|
+
try {
|
|
520
|
+
val jsonObject = jsObjectToJsonObject(values)
|
|
521
|
+
val result = client.postgrest.from(table).insert(jsonObject) {
|
|
522
|
+
select()
|
|
523
|
+
single()
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
val jsonString = result.data
|
|
527
|
+
val response = JSObject()
|
|
528
|
+
try {
|
|
529
|
+
response.put("data", JSObject(jsonString))
|
|
530
|
+
} catch (e: Exception) {
|
|
531
|
+
response.put("data", jsonString)
|
|
532
|
+
}
|
|
533
|
+
response.put("error", JSONObject.NULL)
|
|
534
|
+
call.resolve(response)
|
|
535
|
+
} catch (e: Exception) {
|
|
536
|
+
val response = JSObject()
|
|
537
|
+
response.put("data", JSONObject.NULL)
|
|
538
|
+
response.put("error", e.message)
|
|
539
|
+
call.resolve(response)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
@PluginMethod
|
|
545
|
+
fun update(call: PluginCall) {
|
|
546
|
+
val client = supabaseClient
|
|
547
|
+
if (client == null) {
|
|
548
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
549
|
+
return
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
val table = call.getString("table")
|
|
553
|
+
if (table.isNullOrEmpty()) {
|
|
554
|
+
call.reject("Missing table name")
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
val values = call.getObject("values")
|
|
559
|
+
if (values == null) {
|
|
560
|
+
call.reject("Missing values to update")
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
val filter = call.getObject("filter")
|
|
565
|
+
if (filter == null || filter.length() == 0) {
|
|
566
|
+
call.reject("Missing filter for update operation")
|
|
567
|
+
return
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
scope.launch {
|
|
571
|
+
try {
|
|
572
|
+
val jsonObject = jsObjectToJsonObject(values)
|
|
573
|
+
val result = client.postgrest.from(table).update(jsonObject) {
|
|
574
|
+
filter.keys().forEach { key ->
|
|
575
|
+
val value = filter.get(key)
|
|
576
|
+
filter { eq(key, value) }
|
|
577
|
+
}
|
|
578
|
+
select()
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
val jsonString = result.data
|
|
582
|
+
val response = JSObject()
|
|
583
|
+
try {
|
|
584
|
+
response.put("data", JSArray(jsonString))
|
|
585
|
+
} catch (e: Exception) {
|
|
586
|
+
response.put("data", jsonString)
|
|
587
|
+
}
|
|
588
|
+
response.put("error", JSONObject.NULL)
|
|
589
|
+
call.resolve(response)
|
|
590
|
+
} catch (e: Exception) {
|
|
591
|
+
val response = JSObject()
|
|
592
|
+
response.put("data", JSONObject.NULL)
|
|
593
|
+
response.put("error", e.message)
|
|
594
|
+
call.resolve(response)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@PluginMethod
|
|
600
|
+
fun delete(call: PluginCall) {
|
|
601
|
+
val client = supabaseClient
|
|
602
|
+
if (client == null) {
|
|
603
|
+
call.reject("Supabase client not initialized. Call initialize() first.")
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
val table = call.getString("table")
|
|
608
|
+
if (table.isNullOrEmpty()) {
|
|
609
|
+
call.reject("Missing table name")
|
|
610
|
+
return
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
val filter = call.getObject("filter")
|
|
614
|
+
if (filter == null || filter.length() == 0) {
|
|
615
|
+
call.reject("Missing filter for delete operation")
|
|
616
|
+
return
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
scope.launch {
|
|
620
|
+
try {
|
|
621
|
+
val result = client.postgrest.from(table).delete {
|
|
622
|
+
filter.keys().forEach { key ->
|
|
623
|
+
val value = filter.get(key)
|
|
624
|
+
filter { eq(key, value) }
|
|
625
|
+
}
|
|
626
|
+
select()
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
val jsonString = result.data
|
|
630
|
+
val response = JSObject()
|
|
631
|
+
try {
|
|
632
|
+
response.put("data", JSArray(jsonString))
|
|
633
|
+
} catch (e: Exception) {
|
|
634
|
+
response.put("data", jsonString)
|
|
635
|
+
}
|
|
636
|
+
response.put("error", JSONObject.NULL)
|
|
637
|
+
call.resolve(response)
|
|
638
|
+
} catch (e: Exception) {
|
|
639
|
+
val response = JSObject()
|
|
640
|
+
response.put("data", JSONObject.NULL)
|
|
641
|
+
response.put("error", e.message)
|
|
642
|
+
call.resolve(response)
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
@PluginMethod
|
|
648
|
+
fun getPluginVersion(call: PluginCall) {
|
|
649
|
+
val result = JSObject()
|
|
650
|
+
result.put("version", pluginVersion)
|
|
651
|
+
call.resolve(result)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Helper Methods
|
|
655
|
+
|
|
656
|
+
private fun sessionToJSObject(session: UserSession): JSObject {
|
|
657
|
+
val obj = JSObject()
|
|
658
|
+
obj.put("accessToken", session.accessToken)
|
|
659
|
+
obj.put("refreshToken", session.refreshToken)
|
|
660
|
+
obj.put("tokenType", session.tokenType)
|
|
661
|
+
obj.put("expiresIn", session.expiresIn)
|
|
662
|
+
obj.put("expiresAt", session.expiresAt?.epochSeconds ?: 0)
|
|
663
|
+
session.user?.let { obj.put("user", userToJSObject(it)) }
|
|
664
|
+
return obj
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
private fun userToJSObject(user: UserInfo): JSObject {
|
|
668
|
+
val obj = JSObject()
|
|
669
|
+
obj.put("id", user.id)
|
|
670
|
+
user.email?.let { obj.put("email", it) }
|
|
671
|
+
user.phone?.let { obj.put("phone", it) }
|
|
672
|
+
user.createdAt?.let { obj.put("createdAt", it.toString()) }
|
|
673
|
+
user.lastSignInAt?.let { obj.put("lastSignInAt", it.toString()) }
|
|
674
|
+
user.userMetadata?.let { metadata ->
|
|
675
|
+
try {
|
|
676
|
+
val jsonString = Json.encodeToString(JsonObject.serializer(), metadata)
|
|
677
|
+
obj.put("userMetadata", JSObject(jsonString))
|
|
678
|
+
} catch (e: Exception) {
|
|
679
|
+
// Ignore serialization errors
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
user.appMetadata?.let { metadata ->
|
|
683
|
+
try {
|
|
684
|
+
val jsonString = Json.encodeToString(JsonObject.serializer(), metadata)
|
|
685
|
+
obj.put("appMetadata", JSObject(jsonString))
|
|
686
|
+
} catch (e: Exception) {
|
|
687
|
+
// Ignore serialization errors
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return obj
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private fun jsObjectToJsonObject(jsObject: JSObject): JsonObject {
|
|
694
|
+
return buildJsonObject {
|
|
695
|
+
jsObject.keys().forEach { key ->
|
|
696
|
+
val value = jsObject.get(key)
|
|
697
|
+
when (value) {
|
|
698
|
+
is String -> put(key, JsonPrimitive(value))
|
|
699
|
+
is Number -> put(key, JsonPrimitive(value))
|
|
700
|
+
is Boolean -> put(key, JsonPrimitive(value))
|
|
701
|
+
is JSObject -> put(key, jsObjectToJsonObject(value))
|
|
702
|
+
null -> put(key, kotlinx.serialization.json.JsonNull)
|
|
703
|
+
else -> put(key, JsonPrimitive(value.toString()))
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
private fun getOAuthProvider(provider: String): io.github.jan.supabase.auth.providers.OAuthProvider? {
|
|
710
|
+
return when (provider.lowercase()) {
|
|
711
|
+
"apple" -> io.github.jan.supabase.auth.providers.Apple
|
|
712
|
+
"azure" -> io.github.jan.supabase.auth.providers.Azure
|
|
713
|
+
"bitbucket" -> io.github.jan.supabase.auth.providers.Bitbucket
|
|
714
|
+
"discord" -> io.github.jan.supabase.auth.providers.Discord
|
|
715
|
+
"facebook" -> io.github.jan.supabase.auth.providers.Facebook
|
|
716
|
+
"figma" -> io.github.jan.supabase.auth.providers.Figma
|
|
717
|
+
"github" -> io.github.jan.supabase.auth.providers.Github
|
|
718
|
+
"gitlab" -> io.github.jan.supabase.auth.providers.Gitlab
|
|
719
|
+
"google" -> io.github.jan.supabase.auth.providers.Google
|
|
720
|
+
"kakao" -> io.github.jan.supabase.auth.providers.Kakao
|
|
721
|
+
"keycloak" -> io.github.jan.supabase.auth.providers.Keycloak
|
|
722
|
+
"linkedin" -> io.github.jan.supabase.auth.providers.LinkedIn
|
|
723
|
+
"linkedin_oidc" -> io.github.jan.supabase.auth.providers.LinkedInOIDC
|
|
724
|
+
"notion" -> io.github.jan.supabase.auth.providers.Notion
|
|
725
|
+
"slack" -> io.github.jan.supabase.auth.providers.Slack
|
|
726
|
+
"slack_oidc" -> io.github.jan.supabase.auth.providers.SlackOIDC
|
|
727
|
+
"spotify" -> io.github.jan.supabase.auth.providers.Spotify
|
|
728
|
+
"twitch" -> io.github.jan.supabase.auth.providers.Twitch
|
|
729
|
+
"twitter" -> io.github.jan.supabase.auth.providers.Twitter
|
|
730
|
+
"zoom" -> io.github.jan.supabase.auth.providers.Zoom
|
|
731
|
+
else -> null
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private fun getEmailOtpType(type: String): io.github.jan.supabase.auth.OtpType.Email {
|
|
736
|
+
return when (type.lowercase()) {
|
|
737
|
+
"signup" -> io.github.jan.supabase.auth.OtpType.Email.SIGNUP
|
|
738
|
+
"magiclink" -> io.github.jan.supabase.auth.OtpType.Email.MAGIC_LINK
|
|
739
|
+
"recovery" -> io.github.jan.supabase.auth.OtpType.Email.RECOVERY
|
|
740
|
+
"email" -> io.github.jan.supabase.auth.OtpType.Email.EMAIL
|
|
741
|
+
else -> io.github.jan.supabase.auth.OtpType.Email.EMAIL
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
private fun getPhoneOtpType(type: String): io.github.jan.supabase.auth.OtpType.Phone {
|
|
746
|
+
return when (type.lowercase()) {
|
|
747
|
+
"sms" -> io.github.jan.supabase.auth.OtpType.Phone.SMS
|
|
748
|
+
else -> io.github.jan.supabase.auth.OtpType.Phone.SMS
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|