let debouncedSaveTimer: NodeJS.Timeout
let unsaved = false
@@ -17,7 +16,7 @@
async function save() {
try {
- await update_dictionary({ id: dictionary_id, [field]: value })
+ await update_dictionary({ [field]: value })
unsaved = false
} catch (err) {
alert(err)
diff --git a/packages/site/src/routes/admin/dictionaries/DictionaryRow.svelte b/packages/site/src/routes/admin/dictionaries/DictionaryRow.svelte
index 400a16221..6a5b11bbb 100644
--- a/packages/site/src/routes/admin/dictionaries/DictionaryRow.svelte
+++ b/packages/site/src/routes/admin/dictionaries/DictionaryRow.svelte
@@ -1,26 +1,31 @@
window.open(`/${dictionary.id}`)} class="absolute top-0 left-0 text-xs text-gray-400 cursor-pointer">{index + 1}
-
+
|
|
-
- {#each $invites || [] as invite}
+ {
+ await add_editor({ role: 'manager', user_id, dictionary_id: dictionary.id })
+ await load_data()
+ }}
+ remove_editor={async (user_id) => {
+ await remove_editor({ user_id, dictionary_id: dictionary.id })
+ await load_data()
+ }}
+ invite_editor={async () => {
+ await inviteHelper('manager', dictionary.id)
+ await load_data()
+ }}
+ {users} />
+ {#each dictionary.invites as invite}
{#if invite.role === 'manager'}
- updateOnline(`dictionaries/${dictionary.id}/invites/${invite.id}`, {
- status: 'cancelled',
- })}>
+ on_delete_invite={async () => {
+ const { error } = await supabase.from('invites').update({ status: 'cancelled' }).eq('id', invite.id)
+ if (error) {
+ alert(error.message)
+ console.error(error)
+ } else {
+ await load_data()
+ }
+ }}>
@@ -64,39 +87,52 @@
|
-
- {#each $invites || [] as invite}
+ {
+ await add_editor({ role: 'contributor', user_id, dictionary_id: dictionary.id })
+ await load_data()
+ }}
+ remove_editor={async (user_id) => {
+ await remove_editor({ user_id, dictionary_id: dictionary.id })
+ await load_data()
+ }}
+ invite_editor={async () => {
+ await inviteHelper('contributor', dictionary.id)
+ await load_data()
+ }}
+ {users} />
+ {#each dictionary.invites as invite}
{#if invite.role === 'contributor'}
- updateOnline(`dictionaries/${dictionary.id}/invites/${invite.id}`, {
- status: 'cancelled',
- })}>
+ on_delete_invite={async () => {
+ const { error } = await supabase.from('invites').update({ status: 'cancelled' }).eq('id', invite.id)
+ if (error) {
+ alert(error.message)
+ console.error(error)
+ } else {
+ await load_data()
+ }
+ }}>
{/if}
{/each}
|
-
-
-
- |
|
|
@@ -116,7 +152,6 @@
on:update={({ detail: { lat, lng } }) => {
const [, ...rest] = dictionary.coordinates?.points || []
update_dictionary({
- id: dictionary.id,
coordinates: {
points: [{ coordinates: { latitude: lat, longitude: lng } }, ...rest],
regions: dictionary.coordinates?.regions,
@@ -126,7 +161,6 @@
on:remove={() => {
const [, ...rest] = dictionary.coordinates?.points || []
update_dictionary({
- id: dictionary.id,
coordinates: {
points: rest,
regions: dictionary.coordinates?.regions,
@@ -142,7 +176,6 @@
|
@@ -158,14 +191,12 @@
const name = prompt('Enter alternate name:')
if (name) {
update_dictionary({
- id: dictionary.id,
alternate_names: [...(dictionary.alternate_names || []), name],
})
}
}}
on:itemremoved={({ detail: { value } }) => {
update_dictionary({
- id: dictionary.id,
alternate_names: dictionary.alternate_names.filter(name => name !== value),
})
}} />
@@ -193,7 +224,6 @@
onclick={() => {
if (confirm('Toggle con lang status?')) {
update_dictionary({
- id: dictionary.id,
con_language_description: !dictionary.con_language_description ? 'YES' : null,
})
}
diff --git a/packages/site/src/routes/admin/dictionaries/RolesManagment.svelte b/packages/site/src/routes/admin/dictionaries/RolesManagment.svelte
index 64e5ff691..44cbfd9e7 100644
--- a/packages/site/src/routes/admin/dictionaries/RolesManagment.svelte
+++ b/packages/site/src/routes/admin/dictionaries/RolesManagment.svelte
@@ -1,47 +1,26 @@
-
+
h.name)}
+ strings={editors.map(({ full_name, email }) => full_name || email)}
canEdit
addMessage="Add"
- on:itemclicked={e => alert(helpers[e.detail.index].id)}
- on:itemremoved={async e => await remove(helpers[e.detail.index], dictionary.id, role)}
- on:additem={role === 'writeInCollaborator' ? addWriteInCollaborator : toggleSelectUserModal} />
+ on:itemclicked={e => alert(editors[e.detail.index].id)}
+ on:itemremoved={async e => await remove_editor(editors[e.detail.index].id)}
+ on:additem={toggle} />
- {#if show && role !== 'writeInCollaborator'}
- {#await import('../users/SelectUserModal.svelte') then { default: SelectUserModal }}
-
+ {#if show}
+ {#await import('./SelectUserModal.svelte') then { default: SelectUserModal }}
+
{/await}
{/if}
diff --git a/packages/site/src/routes/admin/dictionaries/SelectDictionaryModal.svelte b/packages/site/src/routes/admin/dictionaries/SelectDictionaryModal.svelte
deleted file mode 100644
index 09879118f..000000000
--- a/packages/site/src/routes/admin/dictionaries/SelectDictionaryModal.svelte
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
- Select Dictionary to let {user.displayName} be a {role}
-
-
-
- {#each filteredDictionaries as dictionary}
-
- {/each}
-
-
-
-
-
diff --git a/packages/site/src/routes/admin/dictionaries/SelectUserModal.svelte b/packages/site/src/routes/admin/dictionaries/SelectUserModal.svelte
new file mode 100644
index 000000000..af5e272cc
--- /dev/null
+++ b/packages/site/src/routes/admin/dictionaries/SelectUserModal.svelte
@@ -0,0 +1,38 @@
+
+
+
+ Select a user to add role to
+
+ {#each filteredUsers as user}
+
+ {:else}
+
+ {/each}
+
+
+
+
diff --git a/packages/site/src/routes/admin/dictionaries/SortDictionaries.svelte b/packages/site/src/routes/admin/dictionaries/SortDictionaries.svelte
index b75b6367d..fed0ef6be 100644
--- a/packages/site/src/routes/admin/dictionaries/SortDictionaries.svelte
+++ b/packages/site/src/routes/admin/dictionaries/SortDictionaries.svelte
@@ -1,52 +1,32 @@
- {#each userFields as field}
+ {#each dictionary_fields as field}
setSortSettings(field.key)}
diff --git a/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.d.ts b/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.d.ts
deleted file mode 100644
index 2341abacc..000000000
--- a/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.d.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { Readable } from 'svelte/store'
-import type { DictionaryView, IHelper, IInvite } from '@living-dictionaries/types'
-
-type DictionaryWithHelperStores = DictionaryView & {
- managers: Readable
- contributors: Readable
- writeInCollaborators: Readable
- invites: Readable
- getManagers: Promise
- getContributors: Promise
- getWriteInCollaborators: Promise
- getInvites: Promise
-}
-
-type DictionaryWithHelpers = DictionaryView & {
- managers: IHelper[]
- contributors: IHelper[]
- writeInCollaborators: IHelper[]
- invites: IInvite[]
-}
diff --git a/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.types.ts b/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.types.ts
new file mode 100644
index 000000000..1f1900f64
--- /dev/null
+++ b/packages/site/src/routes/admin/dictionaries/dictionaryWithHelpers.types.ts
@@ -0,0 +1,7 @@
+import type { DictionaryView, Tables } from '@living-dictionaries/types'
+import type { UserWithDictionaryRoles } from '@living-dictionaries/types/supabase/users.types'
+
+export type DictionaryWithHelpers = DictionaryView & {
+ editors: UserWithDictionaryRoles[]
+ invites: Tables<'invites'>[]
+}
diff --git a/packages/site/src/routes/admin/dictionaries/export.ts b/packages/site/src/routes/admin/dictionaries/export.ts
index e8df3f754..403510cd4 100644
--- a/packages/site/src/routes/admin/dictionaries/export.ts
+++ b/packages/site/src/routes/admin/dictionaries/export.ts
@@ -1,6 +1,7 @@
-import type { DictionaryWithHelperStores, DictionaryWithHelpers } from './dictionaryWithHelpers'
+import type { DictionaryWithHelpers } from './dictionaryWithHelpers.types'
import { downloadObjectsAsCSV } from '$lib/export/csv'
-import { type StandardDictionaryForCSV, prepareDictionaryForCsv, dictionary_headers as standard_headers, timestamp_to_string_date } from '$lib/export/prepareDictionariesForCsv'
+import { type StandardDictionaryForCSV, prepareDictionaryForCsv, dictionary_headers as standard_headers } from '$lib/export/prepareDictionariesForCsv'
+import { supabase_date_to_friendly } from '$lib/helpers/time'
enum AdminDictionaryCSVFields {
entryCount = 'Entries',
@@ -14,7 +15,6 @@ enum AdminDictionaryCSVFields {
conLangDescription = 'Conlang Description',
managers = 'Managers',
contributors = 'Contributors',
- writeInCollaborators = 'Write-In Collaborators',
invites = 'Pending Invites',
}
@@ -25,9 +25,7 @@ type AdminDictionaryForCSV = {
const admin_headers: AdminDictionaryForCSV = { ...AdminDictionaryCSVFields }
-export async function exportAdminDictionariesAsCSV(dictionariesAndHelpers: DictionaryWithHelperStores[], section: 'public' | 'private' | 'other') {
- const dictionaries = await getAllDictionariesAndHelpers(dictionariesAndHelpers)
-
+export function exportAdminDictionariesAsCSV(dictionaries: DictionaryWithHelpers[], section: 'public' | 'private' | 'other') {
const formatted_dictionaries: (StandardDictionaryForCSV & AdminDictionaryForCSV)[] = dictionaries.map((dictionary) => {
const standard_dictionary = prepareDictionaryForCsv(dictionary)
const admin_dictionary: AdminDictionaryForCSV = {
@@ -43,12 +41,10 @@ export async function exportAdminDictionariesAsCSV(dictionariesAndHelpers: Dicti
alternate_orthographies: dictionary.orthographies?.map(({ name }) => name.default)?.join(', '),
created_at: dictionary.created_at,
- // data from subcollections
- managers: dictionary.managers.map(({ name }) => name).join(', '),
- contributors: dictionary.contributors.map(({ name }) => name).join(', '),
- writeInCollaborators: dictionary.writeInCollaborators.map(({ name }) => name).join(', '),
+ managers: dictionary.editors.filter(({ dictionary_roles }) => dictionary_roles.some(({ role }) => role === 'manager')).map(({ full_name, email }) => full_name || email).join(', '),
+ contributors: dictionary.editors.filter(({ dictionary_roles }) => dictionary_roles.some(({ role }) => role === 'contributor')).map(({ full_name, email }) => full_name || email).join(', '),
invites: dictionary.invites.map((invite) => {
- return `${invite.inviterName} (${invite.inviterEmail}) invited ${invite.targetEmail} as ${invite.role} on ${timestamp_to_string_date(invite.createdAt)}`
+ return `${invite.inviter_email} invited ${invite.target_email} as ${invite.role} on ${supabase_date_to_friendly(invite.created_at)}`
}).join(', '),
}
@@ -61,19 +57,3 @@ export async function exportAdminDictionariesAsCSV(dictionariesAndHelpers: Dicti
`living-dictionaries-${section}`,
)
}
-
-async function getAllDictionariesAndHelpers(
- dictionariesAndHelpers: DictionaryWithHelperStores[],
-): Promise {
- const dictionaries: DictionaryWithHelpers[] = []
- for (const dictionary of dictionariesAndHelpers) {
- dictionaries.push({
- ...dictionary,
- managers: await dictionary.getManagers,
- contributors: await dictionary.getContributors,
- writeInCollaborators: await dictionary.getWriteInCollaborators,
- invites: await dictionary.getInvites,
- })
- }
- return dictionaries
-}
diff --git a/packages/site/src/routes/admin/users/+page.svelte b/packages/site/src/routes/admin/users/+page.svelte
index 7db99b480..6e6017999 100644
--- a/packages/site/src/routes/admin/users/+page.svelte
+++ b/packages/site/src/routes/admin/users/+page.svelte
@@ -1,15 +1,33 @@
-
-
-
-
-
-
-
-
-
- {#each sortedUsers as user (user.uid)}
-
- {/each}
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {#each sortedUsers as user (user.id)}
+
+ {/each}
+
+
+
+
diff --git a/packages/site/src/routes/admin/users/DictionariesHelping.svelte b/packages/site/src/routes/admin/users/DictionariesHelping.svelte
new file mode 100644
index 000000000..f50c977bb
--- /dev/null
+++ b/packages/site/src/routes/admin/users/DictionariesHelping.svelte
@@ -0,0 +1,24 @@
+
+
+
+ window.open(`/${e.detail.value}`, '_blank')}
+ on:itemremoved={async e => await remove_dictionary(e.detail.value)}
+ on:additem={toggle} />
+ {#if show}
+ {#await import('./SelectDictionaryModal.svelte') then { default: SelectDictionaryModal }}
+
+ {/await}
+ {/if}
+
diff --git a/packages/site/src/routes/admin/users/SelectDictionaryModal.svelte b/packages/site/src/routes/admin/users/SelectDictionaryModal.svelte
new file mode 100644
index 000000000..dc1e3e486
--- /dev/null
+++ b/packages/site/src/routes/admin/users/SelectDictionaryModal.svelte
@@ -0,0 +1,34 @@
+
+
+
+
+ Select Dictionary
+
+
+ {#each filteredDictionaries as dictionary}
+
+ {/each}
+
+
+
+
diff --git a/packages/site/src/routes/admin/users/SelectUserModal.svelte b/packages/site/src/routes/admin/users/SelectUserModal.svelte
deleted file mode 100644
index f4404febc..000000000
--- a/packages/site/src/routes/admin/users/SelectUserModal.svelte
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
- Select a user to add {role} role to
-
-
- {#each filteredUsers as user}
-
- {:else}
-
- {/each}
-
-
-
-
-
diff --git a/packages/site/src/routes/admin/users/SortUsers.svelte b/packages/site/src/routes/admin/users/SortUsers.svelte
index 696c27fa8..5ea1a45a2 100644
--- a/packages/site/src/routes/admin/users/SortUsers.svelte
+++ b/packages/site/src/routes/admin/users/SortUsers.svelte
@@ -1,66 +1,71 @@
diff --git a/packages/site/src/routes/admin/users/UserRow.svelte b/packages/site/src/routes/admin/users/UserRow.svelte
index 12cbac9cf..5b200ef11 100644
--- a/packages/site/src/routes/admin/users/UserRow.svelte
+++ b/packages/site/src/routes/admin/users/UserRow.svelte
@@ -1,63 +1,88 @@
1 && JSON.stringify(user, null, 1)}>
- {user.displayName}
+ {user.email}
|
- {user.email}
+ {user.full_name || ''}
|
-
-
- {#if intersecting}
-
- {/if}
- |
+ {
+ await remove_editor({ dictionary_id, user_id: user.id })
+ await load_data()
+ }}
+ add_dictionary={async (dictionary_id) => {
+ await add_editor({ dictionary_id, user_id: user.id, role: 'manager' })
+ await load_data()
+ }} />
+
-
-
- {#if intersecting}
-
- {/if}
- |
+ {
+ await remove_editor({ dictionary_id, user_id: user.id })
+ await load_data()
+ }}
+ add_dictionary={async (dictionary_id) => {
+ await add_editor({ dictionary_id, user_id: user.id, role: 'contributor' })
+ await load_data()
+ }} />
+
- {#if user.lastVisit}{printDate(user.lastVisit.toDate())}{/if}
+ {#if user.last_sign_in_at}{supabase_date_to_friendly(user.last_sign_in_at)}{/if}
|
- {#if user.createdAt}{printDate(user.createdAt.toDate())}{/if}
+ {#if user.created_at}{supabase_date_to_friendly(user.created_at)}{/if}
|
- {#if user.unsubscribe}
+ {#if user.unsubscribed_from_emails}
+ }}>{supabase_date_to_friendly(user.unsubscribed_from_emails)}
{:else}
{/if}
|
diff --git a/packages/site/src/routes/api/db/check-permission.ts b/packages/site/src/routes/api/db/check-permission.ts
index bd6f8431d..a3031b300 100644
--- a/packages/site/src/routes/api/db/check-permission.ts
+++ b/packages/site/src/routes/api/db/check-permission.ts
@@ -1,38 +1,10 @@
-import type { IUser } from '@living-dictionaries/types'
-import { getDb } from '$lib/server/firebase-admin'
+import type { Supabase } from '$lib/supabase'
-let db: FirebaseFirestore.Firestore
+export async function check_can_edit(supabase: Supabase, dictionary_id: string) {
+ const { data: roles, error } = await supabase.from('dictionary_roles').select()
+ if (error) throw new Error(error.message)
-export async function check_can_edit(user_id: string, dictionary_id: string) {
- if (!db)
- db = getDb()
-
- const dictionaryManagers = await db.collection(`dictionaries/${dictionary_id}/managers`).get()
- const isDictionaryManager = dictionaryManagers.docs.some(({ id }) => id === user_id)
- if (isDictionaryManager) return true
-
- const dictionaryContributors = await db.collection(`dictionaries/${dictionary_id}/contributors`).get()
- const isDictionaryContributor = dictionaryContributors.docs.some(({ id }) => id === user_id)
- if (isDictionaryContributor) return true
-
- const userSnap = await db.doc(`users/${user_id}`).get()
- const { roles } = userSnap.data() as IUser
- if (roles?.admin) return true
-
- throw new Error('Is not authorized to make changes to this dictionary.')
-}
-
-export async function check_manager(user_id: string, dictionary_id: string) {
- if (!db)
- db = getDb()
-
- const dictionaryManagers = await db.collection(`dictionaries/${dictionary_id}/managers`).get()
- const isDictionaryManager = dictionaryManagers.docs.some(({ id }) => id === user_id)
- if (isDictionaryManager) return true
-
- const userSnap = await db.doc(`users/${user_id}`).get()
- const { roles } = userSnap.data() as IUser
- if (roles?.admin) return true
+ if (roles.some(({ dictionary_id: _dictionary_id, role }) => _dictionary_id === dictionary_id && (role === 'manager' || role === 'contributor'))) return true
throw new Error('Is not authorized to make changes to this dictionary.')
}
diff --git a/packages/site/src/routes/api/db/content-update/+server.ts b/packages/site/src/routes/api/db/content-update/+server.ts
index 69802329f..4352d5759 100644
--- a/packages/site/src/routes/api/db/content-update/+server.ts
+++ b/packages/site/src/routes/api/db/content-update/+server.ts
@@ -3,49 +3,29 @@ import { json, error as kit_error } from '@sveltejs/kit'
import type { ContentUpdateRequestBody, TablesInsert } from '@living-dictionaries/types'
import { check_can_edit } from '../check-permission'
import type { RequestHandler } from './$types'
-import { decodeToken } from '$lib/server/firebase-admin'
import { getAdminSupabaseClient } from '$lib/supabase/admin'
import { ResponseCodes } from '$lib/constants'
-import { dev } from '$app/environment'
export type ContentUpdateResponseBody = TablesInsert<'content_updates'>
-export const POST: RequestHandler = async ({ request }) => {
+export const POST: RequestHandler = async ({ request, locals: { getSession } }) => {
try {
const body = await request.json() as ContentUpdateRequestBody
- const { update_id, import_meta, auth_token, dictionary_id, import_id, type, data } = body
+ const { update_id, dictionary_id, import_id, type, data } = body
const admin_supabase = getAdminSupabaseClient()
- let user_id: string
+ const { data: session_data, error: _error, supabase } = await getSession()
+ if (_error || !session_data?.user)
+ kit_error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
+ const user_id = session_data.user.id
- if (dev)
- // @ts-expect-error
- user_id = import_meta?.user_id || data?.updated_by || data?.created_by
-
- if (auth_token) {
- const decoded_token = await decodeToken(auth_token)
- if (!decoded_token?.uid)
- throw new Error('No user id found in token')
-
- await check_can_edit(decoded_token.uid, dictionary_id)
-
- const { data } = await admin_supabase.from('user_emails')
- .select('id')
- .eq('email', decoded_token.email!)
- .single()
- if (!data?.id)
- throw new Error('No user id found in database')
- user_id = data.id
+ if (!session_data.user.app_metadata.admin) {
+ await check_can_edit(supabase, dictionary_id)
}
- if (!user_id)
- throw new Error('No user id found. Pass it into the user_id_from_local field or use a valid auth_token.')
-
- const timestamp = dev
// @ts-expect-error
- ? import_meta?.timestamp || data?.updated_at || data?.created_at || new Date().toISOString()
- : new Date().toISOString()
+ const timestamp = data?.updated_at || data?.created_at || new Date().toISOString()
const c_u_meta = {
created_by: user_id,
diff --git a/packages/site/src/routes/api/db/create-dictionary/+server.ts b/packages/site/src/routes/api/db/create-dictionary/+server.ts
index b91490043..27c9133e3 100644
--- a/packages/site/src/routes/api/db/create-dictionary/+server.ts
+++ b/packages/site/src/routes/api/db/create-dictionary/+server.ts
@@ -1,48 +1,28 @@
import { json, error as kit_error } from '@sveltejs/kit'
-import type { IUser, TablesInsert } from '@living-dictionaries/types'
+import type { TablesInsert } from '@living-dictionaries/types'
import type { RequestHandler } from './$types'
-import { decodeToken } from '$lib/server/firebase-admin'
-import { getAdminSupabaseClient } from '$lib/supabase/admin'
import { ResponseCodes } from '$lib/constants'
import { send_dictionary_emails } from '$api/email/new_dictionary/dictionary-emails'
export interface CreateDictionaryRequestBody {
- auth_token: string
- dictionary: Omit, 'created_by' | 'updated_by'>
- fb_user: IUser
+ dictionary: TablesInsert<'dictionaries'>
}
-export const POST: RequestHandler = async ({ request }) => {
- try {
- const { auth_token, dictionary, fb_user } = await request.json() as CreateDictionaryRequestBody
-
- const admin_supabase = getAdminSupabaseClient()
-
- if (!auth_token)
- throw new Error('missing auth_token')
+export const POST: RequestHandler = async ({ request, locals: { getSession } }) => {
+ const { data: session_data, error: _error, supabase } = await getSession()
+ if (_error || !session_data?.user)
+ kit_error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
- const decoded_token = await decodeToken(auth_token)
- if (!decoded_token?.uid)
- throw new Error('No user id found in token')
-
- const { data: user } = await admin_supabase.from('user_emails')
- .select('id')
- .eq('email', decoded_token.email!)
- .single()
- if (!user?.id)
- throw new Error('No user id found in database')
+ try {
+ const { dictionary } = await request.json() as CreateDictionaryRequestBody
- const { data: saved_dictionary, error } = await admin_supabase.from('dictionaries').insert({
- ...dictionary,
- created_by: user.id,
- updated_by: user.id,
- }).select().single()
+ const { data: saved_dictionary, error } = await supabase.from('dictionaries').insert(dictionary).select().single()
if (error) {
console.error({ error })
throw new Error(error.message)
}
- await send_dictionary_emails(saved_dictionary, fb_user)
+ await send_dictionary_emails(saved_dictionary, session_data.user.email)
return json(null)
} catch (err) {
diff --git a/packages/site/src/routes/api/db/create-dictionary/_call.ts b/packages/site/src/routes/api/db/create-dictionary/_call.ts
index 015b605c0..ff271b4a2 100644
--- a/packages/site/src/routes/api/db/create-dictionary/_call.ts
+++ b/packages/site/src/routes/api/db/create-dictionary/_call.ts
@@ -1,10 +1,6 @@
-import { authState } from 'sveltefirets'
-import { get } from 'svelte/store'
import type { CreateDictionaryRequestBody } from './+server'
import { post_request } from '$lib/helpers/get-post-requests'
-export async function api_create_dictionary(body: Omit) {
- const auth_state_user = get(authState)
- const auth_token = await auth_state_user.getIdToken()
- return await post_request(`/api/db/create-dictionary`, { ...body, auth_token })
+export async function api_create_dictionary(body: CreateDictionaryRequestBody) {
+ return await post_request(`/api/db/create-dictionary`, body)
}
diff --git a/packages/site/src/routes/api/db/update-dev-admin-role/+server.ts b/packages/site/src/routes/api/db/update-dev-admin-role/+server.ts
index 3b70dd42e..f38778825 100644
--- a/packages/site/src/routes/api/db/update-dev-admin-role/+server.ts
+++ b/packages/site/src/routes/api/db/update-dev-admin-role/+server.ts
@@ -1,34 +1,35 @@
-import { firebaseConfig } from 'sveltefirets'
import { error, json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'
import { ResponseCodes } from '$lib/constants'
-import { decodeToken, getDb } from '$lib/server/firebase-admin'
+import { mode } from '$lib/supabase'
+import { getAdminSupabaseClient } from '$lib/supabase/admin'
export interface UpdateDevAdminRoleRequestBody {
- role: number
- auth_token: string
+ role_level: number
}
-export const POST: RequestHandler = async ({ request }) => {
- const { role, auth_token } = await request.json() as UpdateDevAdminRoleRequestBody
+export const POST: RequestHandler = async ({ request, locals: { getSession } }) => {
+ const { role_level } = await request.json() as UpdateDevAdminRoleRequestBody
- if (firebaseConfig.projectId !== 'talking-dictionaries-dev')
+ if (mode !== 'development')
error(ResponseCodes.BAD_REQUEST, `Only works on dev`)
- if (typeof role !== 'number')
+ if (typeof role_level !== 'number')
error(ResponseCodes.BAD_REQUEST, `Role must be a number`)
- console.info({ role })
- const db = getDb()
- const decodedToken = await decodeToken(auth_token)
- if (!decodedToken?.uid)
- throw new Error('No user id found in token')
-
- await db.doc(`users/${decodedToken.uid}`).update({
- roles: {
- admin: role,
- },
- })
+ const { data: session_data, error: _error } = await getSession()
+ if (_error || !session_data?.user)
+ error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
+
+ const admin_supabase = getAdminSupabaseClient()
+
+ const { error: update_role_error } = await admin_supabase.auth.admin.updateUserById(
+ session_data.user.id,
+ { app_metadata: { admin: role_level } },
+ )
+
+ if (update_role_error)
+ error(ResponseCodes.INTERNAL_SERVER_ERROR, { message: update_role_error.message })
return json('success')
}
diff --git a/packages/site/src/routes/api/db/update-dev-admin-role/_call.ts b/packages/site/src/routes/api/db/update-dev-admin-role/_call.ts
new file mode 100644
index 000000000..b34c7f842
--- /dev/null
+++ b/packages/site/src/routes/api/db/update-dev-admin-role/_call.ts
@@ -0,0 +1,6 @@
+import type { UpdateDevAdminRoleRequestBody } from './+server'
+import { post_request } from '$lib/helpers/get-post-requests'
+
+export async function api_update_dev_admin_role(body: UpdateDevAdminRoleRequestBody) {
+ return await post_request(`/api/db/update-dev-admin-role`, body)
+}
diff --git a/packages/site/src/routes/api/db/update-dictionary/+server.ts b/packages/site/src/routes/api/db/update-dictionary/+server.ts
deleted file mode 100644
index 09f1fa3d5..000000000
--- a/packages/site/src/routes/api/db/update-dictionary/+server.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { json, error as kit_error } from '@sveltejs/kit'
-import type { Tables, TablesUpdate } from '@living-dictionaries/types'
-import { check_manager } from '../check-permission'
-import type { RequestHandler } from './$types'
-import { decodeToken } from '$lib/server/firebase-admin'
-import { getAdminSupabaseClient } from '$lib/supabase/admin'
-import { ResponseCodes } from '$lib/constants'
-
-export interface UpdateDictionaryRequestBody {
- auth_token: string
- dictionary: TablesUpdate<'dictionaries'>
-}
-
-export interface UpdateDictionaryResponseBody {
- saved_dictionary: Tables<'dictionaries'>
-}
-
-export const POST: RequestHandler = async ({ request }) => {
- try {
- const { auth_token, dictionary } = await request.json() as UpdateDictionaryRequestBody
-
- const admin_supabase = getAdminSupabaseClient()
-
- if (!auth_token)
- throw new Error('missing auth_token')
-
- const decoded_token = await decodeToken(auth_token)
- if (!decoded_token?.uid)
- throw new Error('No user id found in token')
-
- await check_manager(decoded_token.uid, dictionary.id)
-
- const { data: user } = await admin_supabase.from('user_emails')
- .select('id')
- .eq('email', decoded_token.email!)
- .single()
- if (!user?.id)
- throw new Error('No user id found in database')
-
- const { data: saved_dictionary, error } = await admin_supabase.from('dictionaries').update({
- ...dictionary,
- updated_by: user.id,
- })
- .eq('id', dictionary.id)
- .select().single()
- if (error) {
- console.error({ error })
- throw new Error(error.message)
- }
-
- return json({ saved_dictionary } satisfies UpdateDictionaryResponseBody)
- } catch (err) {
- console.error(`Error creating dictionary: ${err.message}`)
- kit_error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error creating dictionary: ${err.message}`)
- }
-}
diff --git a/packages/site/src/routes/api/db/update-dictionary/_call.ts b/packages/site/src/routes/api/db/update-dictionary/_call.ts
deleted file mode 100644
index a78dd3a98..000000000
--- a/packages/site/src/routes/api/db/update-dictionary/_call.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { TablesUpdate } from '@living-dictionaries/types'
-import { authState } from 'sveltefirets'
-import { get } from 'svelte/store'
-import type { UpdateDictionaryRequestBody, UpdateDictionaryResponseBody } from './+server'
-import { post_request } from '$lib/helpers/get-post-requests'
-
-export async function api_update_dictionary(dictionary: TablesUpdate<'dictionaries'> & { id: string }) {
- const auth_state_user = get(authState)
- const auth_token = await auth_state_user.getIdToken()
- return await post_request(`/api/db/update-dictionary`, { auth_token, dictionary })
-}
diff --git a/packages/site/src/routes/api/email/addresses.ts b/packages/site/src/routes/api/email/addresses.ts
index bd8bffd17..54b8be39b 100644
--- a/packages/site/src/routes/api/email/addresses.ts
+++ b/packages/site/src/routes/api/email/addresses.ts
@@ -1,4 +1,3 @@
-import { firebaseConfig } from 'sveltefirets'
import type { Address } from './send-email'
import { dev } from '$app/environment'
@@ -23,7 +22,7 @@ export function getAdminRecipients(initiatorEmail: string): Address[] {
{ email: 'diego@livingtongues.org' },
]
- if (dev || firebaseConfig.projectId === 'talking-dictionaries-dev')
+ if (dev)
return recipients
return [
@@ -39,7 +38,7 @@ export function getSupportMessageRecipients({ dev }: { dev: boolean }): Address[
{ email: 'diego@livingtongues.org' },
]
- if (dev || firebaseConfig.projectId === 'talking-dictionaries-dev')
+ if (dev)
return recipients
return [
@@ -54,7 +53,7 @@ export function getLanguageLearningMaterialsRecipients({ dev }: { dev: boolean }
{ email: 'diego@livingtongues.org' },
]
- if (dev || firebaseConfig.projectId === 'talking-dictionaries-dev')
+ if (dev)
return recipients
return [
diff --git a/packages/site/src/routes/api/email/announcement/+server.ts b/packages/site/src/routes/api/email/announcement/+server.ts
index c70bfa996..c63ee795f 100644
--- a/packages/site/src/routes/api/email/announcement/+server.ts
+++ b/packages/site/src/routes/api/email/announcement/+server.ts
@@ -1,5 +1,4 @@
import { error, json } from '@sveltejs/kit'
-import type { IUser } from '@living-dictionaries/types'
import { send_email } from '../send-email'
import { render_component_to_html } from '../render-component-to-html'
import { jacobAddress, no_reply_address } from '../addresses'
@@ -7,7 +6,6 @@ import type { RequestHandler } from './$types'
import MigrationClosure from './MigrationClosure.svelte'
import { ResponseCodes } from '$lib/constants'
import { dev } from '$app/environment'
-import { getDb } from '$lib/server/firebase-admin'
const batchSize = 50
@@ -16,10 +14,8 @@ export const GET: RequestHandler = async () => {
error(ResponseCodes.INTERNAL_SERVER_ERROR, { message: 'Not allowed' })
try {
- const db = getDb()
- const userSnapshots = await db.collection('users').get()
- const users = userSnapshots.docs.map(doc => doc.data() as IUser)
- const user_emails = users.map(user => ({ email: user.email }))
+ // const user_emails = users.map(user => ({ email: user.email }))
+ const user_emails = [] // TODO: get from supabase next time using 'user_emails' view
// return json({ emails_to_send: users.map(user => user.email).splice(received) })
diff --git a/packages/site/src/routes/api/email/invite/+server.ts b/packages/site/src/routes/api/email/invite/+server.ts
index f015e7a95..03f615ed7 100644
--- a/packages/site/src/routes/api/email/invite/+server.ts
+++ b/packages/site/src/routes/api/email/invite/+server.ts
@@ -1,60 +1,56 @@
import { error, json } from '@sveltejs/kit'
-import type { IInvite, IUser } from '@living-dictionaries/types'
+import type { TablesInsert } from '@living-dictionaries/types'
import { getAdminRecipients } from '../addresses'
import { send_email } from '../send-email'
import type { RequestHandler } from './$types'
-import { decodeToken, getDb } from '$lib/server/firebase-admin'
import { ResponseCodes } from '$lib/constants'
-export interface InviteRequestBody {
- auth_token: string
- dictionaryId: string
- invite: IInvite
+export type InviteRequestBody = Pick, 'dictionary_id' | 'role' | 'target_email'> & {
+ origin: string
}
-export const POST: RequestHandler = async ({ request }) => {
- const { auth_token, dictionaryId, invite } = await request.json() as InviteRequestBody
- const { dictionaryName, inviterName, role, targetEmail, inviterEmail } = invite
+export const POST: RequestHandler = async ({ request, locals: { getSession } }) => {
+ const { data: session_data, error: _error, supabase } = await getSession()
+ if (_error || !session_data?.user)
+ error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
- const decodedToken = await decodeToken(auth_token)
- if (!decodedToken?.uid)
- error(ResponseCodes.UNAUTHORIZED, { message: 'Unauthorized' })
- if (inviterEmail !== decodedToken.email)
- error(ResponseCodes.BAD_REQUEST, { message: 'Requesting from incorrect account' })
-
- const db = getDb()
-
- const checkForPermission = async () => {
- const dictionaryManagers = await db.collection(`dictionaries/${dictionaryId}/managers`).get()
- const isDictionaryManager = dictionaryManagers.docs.some(({ id }) => id === decodedToken.uid)
- if (isDictionaryManager) return true
-
- const userSnap = await db.doc(`users/${decodedToken.uid}`).get()
- const { roles } = userSnap.data() as IUser
- if (roles?.admin) return true
-
- error(ResponseCodes.BAD_REQUEST, { message: 'Is not a manager of this dictionary.' })
- }
- await checkForPermission()
-
- const inviteRef = await db.collection(`dictionaries/${dictionaryId}/invites`).add(invite)
-
- const roleMessage
+ try {
+ const data = await request.json() as InviteRequestBody
+ const { role, dictionary_id, target_email, origin } = data
+
+ const inviter_email = session_data.user.email
+ const { data: invite, error: insert_invite_error } = await supabase.from('invites').insert({
+ dictionary_id,
+ role,
+ target_email,
+ inviter_email,
+ status: 'queued',
+ }).select().single()
+
+ if (insert_invite_error)
+ throw new Error(insert_invite_error.message)
+
+ const roleMessage
= role === 'manager'
? 'manager'
: 'contributor, which allows you to add and edit entries'
- try {
+ const { data: dictionary, error: dictionary_error } = await supabase.from('dictionaries').select('name').eq('id', dictionary_id).single()
+ if (dictionary_error)
+ throw new Error(dictionary_error.message)
+
+ const inviter_name_or_email = session_data.user.user_metadata.full_name || session_data.user.email
+
await send_email({
- to: [{ email: targetEmail }],
- reply_to: { email: inviterEmail },
- subject: `${inviterName} has invited you to contribute to the ${dictionaryName} Living Dictionary`,
+ to: [{ email: target_email }],
+ reply_to: { email: inviter_email },
+ subject: `${inviter_name_or_email} has invited you to contribute to the ${dictionary.name} Living Dictionary`,
type: 'text/plain',
body: `Hello,
-${inviterName} has invited you to work on the ${dictionaryName} Living Dictionary as a ${roleMessage}. If you would like to help with this dictionary, then open this link: https://livingdictionaries.app/${dictionaryId}/invite/${inviteRef.id} to access the dictionary.
+${inviter_name_or_email} has invited you to work on the ${dictionary.name} Living Dictionary as a ${roleMessage}. If you would like to help with this dictionary, then open this link: ${origin}/${dictionary_id}/invite/${invite.id} to access the dictionary.
-If you have any questions for ${inviterName}, send an email to ${inviterEmail} or just reply to this email.
+If you have any questions for ${inviter_name_or_email}, send an email to ${inviter_email} or just reply to this email.
Thank you,
Living Tongues Institute for Endangered Languages
@@ -63,28 +59,30 @@ https://livingtongues.org (Living Tongues Homepage)
https://livingdictionaries.app (Living Dictionaries website)`,
})
- const adminRecipients = getAdminRecipients(inviterEmail)
- if (!adminRecipients.find(({ email }) => email === inviterEmail)) {
+ const adminRecipients = getAdminRecipients(inviter_email)
+ if (!adminRecipients.find(({ email }) => email === inviter_email)) {
await send_email({
to: adminRecipients,
- reply_to: { email: inviterEmail },
- subject: `${inviterName} has invited ${targetEmail} to contribute to the ${dictionaryName} Living Dictionary`,
+ reply_to: { email: inviter_email },
+ subject: `${inviter_name_or_email} has invited ${target_email} to contribute to the ${dictionary.name} Living Dictionary`,
type: 'text/plain',
body: `Hello Admins,
-${inviterName} has invited ${targetEmail} to work on the ${dictionaryName} Living Dictionary as a ${roleMessage}.
+ ${inviter_name_or_email} has invited ${target_email} to work on the ${dictionary.name} Living Dictionary as a ${roleMessage}.
+
+ Dictionary URL: https://livingdictionaries.app/${dictionary_id}
-Dictionary URL: https://livingdictionaries.app/${dictionaryId}
-
-If you have any questions for ${inviterName}, just reply to this email.
+ If you have any questions for ${inviter_name_or_email}, just reply to this email.
-Thanks,
-Our automatic Vercel function
+ Thanks,
+ Our automatic Vercel function
-https://livingdictionaries.app`,
+ https://livingdictionaries.app`,
})
}
- await inviteRef.update({ status: 'sent' })
+ const { error: update_error } = await supabase.from('invites').update({ status: 'sent' }).eq('id', invite.id)
+ if (update_error)
+ throw new Error(update_error.message)
return json('success')
} catch (err) {
diff --git a/packages/site/src/routes/api/email/invite/_call.ts b/packages/site/src/routes/api/email/invite/_call.ts
new file mode 100644
index 000000000..ec0f627c8
--- /dev/null
+++ b/packages/site/src/routes/api/email/invite/_call.ts
@@ -0,0 +1,6 @@
+import type { InviteRequestBody } from './+server'
+import { post_request } from '$lib/helpers/get-post-requests'
+
+export async function api_dictionary_invite(body: InviteRequestBody) {
+ return await post_request(`/api/email/invite`, body)
+}
diff --git a/packages/site/src/routes/api/email/new_dictionary/composeMessages.test.ts b/packages/site/src/routes/api/email/new_dictionary/composeMessages.test.ts
index e06b184c1..969af7b69 100644
--- a/packages/site/src/routes/api/email/new_dictionary/composeMessages.test.ts
+++ b/packages/site/src/routes/api/email/new_dictionary/composeMessages.test.ts
@@ -1,4 +1,4 @@
-import type { IUser, Tables } from '@living-dictionaries/types'
+import type { Tables } from '@living-dictionaries/types'
import { notifyAdminsOnNewDictionary } from './composeMessages'
test('composeAdminNotice returns', () => {
@@ -14,14 +14,13 @@ test('composeAdminNotice returns', () => {
points: [{ coordinates: { latitude: 1, longitude: 2 } }],
},
} as Tables<'dictionaries'>
- const user: IUser = {
- displayName: 'James Johnson',
+ const user = {
email: 'jamesj@gmail.com',
}
- expect(notifyAdminsOnNewDictionary(dictionary, user)).toMatchInlineSnapshot(`
+ expect(notifyAdminsOnNewDictionary(dictionary, user.email)).toMatchInlineSnapshot(`
"Hey Admins,
- James Johnson created a new Living Dictionary named Test-Dictionary. Here's the details:
+ jamesj@gmail.com created a new Living Dictionary named Test-Dictionary. Here's the details:
URL: https://livingdictionaries.app/testID
@@ -40,7 +39,7 @@ test('composeAdminNotice returns', () => {
Author's Connection: "Something about how I know this community and more..."
ConLang Description: "undefined"
- We sent James Johnson an automatic dictionary-info email to jamesj@gmail.com, but you can also get in touch with them if needed.
+ We sent an automatic dictionary-info email to jamesj@gmail.com, but you can also get in touch with them if needed.
Thanks,
Our automatic Vercel Function
diff --git a/packages/site/src/routes/api/email/new_dictionary/composeMessages.ts b/packages/site/src/routes/api/email/new_dictionary/composeMessages.ts
index 94c6bf342..20a9d3efa 100644
--- a/packages/site/src/routes/api/email/new_dictionary/composeMessages.ts
+++ b/packages/site/src/routes/api/email/new_dictionary/composeMessages.ts
@@ -1,9 +1,9 @@
-import type { IUser, Tables } from '@living-dictionaries/types'
+import type { Tables } from '@living-dictionaries/types'
-export function notifyAdminsOnNewDictionary(dictionary: Tables<'dictionaries'>, user: IUser) {
+export function notifyAdminsOnNewDictionary(dictionary: Tables<'dictionaries'>, email: string) {
return `Hey Admins,
- ${user.displayName} created a new Living Dictionary named ${dictionary.name}. Here's the details:
+ ${email} created a new Living Dictionary named ${dictionary.name}. Here's the details:
URL: https://livingdictionaries.app/${dictionary.id}
@@ -26,9 +26,7 @@ export function notifyAdminsOnNewDictionary(dictionary: Tables<'dictionaries'>,
Author's Connection: "${dictionary.author_connection}"
ConLang Description: "${dictionary.con_language_description}"
- We sent ${user.displayName} an automatic dictionary-info email to ${
- user.email
-}, but you can also get in touch with them if needed.
+ We sent an automatic dictionary-info email to ${email}, but you can also get in touch with them if needed.
Thanks,
Our automatic Vercel Function
diff --git a/packages/site/src/routes/api/email/new_dictionary/dictionary-emails.ts b/packages/site/src/routes/api/email/new_dictionary/dictionary-emails.ts
index d912a4373..28847258e 100644
--- a/packages/site/src/routes/api/email/new_dictionary/dictionary-emails.ts
+++ b/packages/site/src/routes/api/email/new_dictionary/dictionary-emails.ts
@@ -1,23 +1,23 @@
-import type { IUser, Tables } from '@living-dictionaries/types'
+import type { Tables } from '@living-dictionaries/types'
import { getAdminRecipients } from '../addresses'
import newDictionary from '../html/newDictionary'
import { send_email } from '../send-email'
import { notifyAdminsOnNewDictionary } from './composeMessages'
-export async function send_dictionary_emails(dictionary: Tables<'dictionaries'>, user: IUser) {
+export async function send_dictionary_emails(dictionary: Tables<'dictionaries'>, email: string) {
try {
await send_email({
- to: [{ email: user.email }],
+ to: [{ email }],
subject: 'New Living Dictionary Created',
type: 'text/html',
body: newDictionary(dictionary.name, dictionary.id),
})
await send_email({
- to: getAdminRecipients(user.email),
+ to: getAdminRecipients(email),
subject: `Living Dictionary created: ${dictionary.name}`,
type: 'text/plain',
- body: notifyAdminsOnNewDictionary(dictionary, user),
+ body: notifyAdminsOnNewDictionary(dictionary, email),
})
} catch (err) {
console.error(`Error with email send request: ${err.message}`)
diff --git a/packages/site/src/routes/api/email/new_user/+server.ts b/packages/site/src/routes/api/email/new_user/+server.ts
index 62f40c317..035bdea81 100644
--- a/packages/site/src/routes/api/email/new_user/+server.ts
+++ b/packages/site/src/routes/api/email/new_user/+server.ts
@@ -1,55 +1,47 @@
import { error, json } from '@sveltejs/kit'
-import type { IUser } from '@living-dictionaries/types'
import { getAdminRecipients } from '../addresses'
import newUserWelcome from '../html/newUserWelcome'
import { send_email } from '../send-email'
-import { save_user_to_supabase } from './save-user-to-supabase'
import type { RequestHandler } from './$types'
import { ResponseCodes } from '$lib/constants'
-import { decodeToken } from '$lib/server/firebase-admin'
-export interface NewUserRequestBody {
- auth_token: string
- user: IUser
+export interface NewUserEmailRequestBody {
+ // language: LanguageCode
}
-export const POST: RequestHandler = async ({ request }) => {
- const { auth_token, user } = await request.json() as NewUserRequestBody
-
- const decodedToken = await decodeToken(auth_token)
- if (!decodedToken?.uid)
- error(ResponseCodes.UNAUTHORIZED, { message: 'Unauthorized' })
- if (user.email !== decodedToken.email)
- error(ResponseCodes.BAD_REQUEST, { message: 'token email does not match user email' })
+export const POST: RequestHandler = async ({ locals: { getSession } }) => {
+ const { data: session_data, error: _error, supabase } = await getSession()
+ if (_error || !session_data?.user)
+ error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
try {
await send_email({
- to: [{ email: user.email }],
+ to: [{ email: session_data.user.email }],
subject: 'Thank you for creating a Living Dictionaries account!',
type: 'text/html',
body: newUserWelcome,
})
- const supabase_user_id = await save_user_to_supabase(user)
- console.info({ supabase_user_id })
-
await send_email({
- to: getAdminRecipients(decodedToken.email),
- subject: `New Living Dictionaries user: ${user.displayName}`,
+ to: getAdminRecipients(session_data.user.email),
+ subject: `New Living Dictionaries user: ${session_data.user.email}`,
type: 'text/plain',
body: `Hey Admins,
-${user.displayName} has just created a Living Dictionaries account, and we sent an automatic welcome email to ${user.email}
+${session_data.user.email} has just created a Living Dictionaries account, and we sent them an automatic welcome email.
-Thanks,
-Our automatic Vercel Function
+~ Our automatic Vercel Function
https://livingdictionaries.app`,
})
- return json('success')
+ const { error: updating_welcome_email_error } = await supabase.from('user_data').update({ welcome_email_sent: new Date().toISOString() }).eq('id', session_data.user.id)
+ if (updating_welcome_email_error)
+ console.error({ updating_welcome_email_error })
+
+ return json({ result: 'success' })
} catch (err) {
- console.error(`Error with email send request: ${err.message}`)
- error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error with email send request: ${err.message}`)
+ console.error(`Error with welcome email send request: ${err.message}`)
+ error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error with welcome email send request: ${err.message}`)
}
}
diff --git a/packages/site/src/routes/api/email/new_user/_call.ts b/packages/site/src/routes/api/email/new_user/_call.ts
new file mode 100644
index 000000000..cc18aff88
--- /dev/null
+++ b/packages/site/src/routes/api/email/new_user/_call.ts
@@ -0,0 +1,6 @@
+import type { NewUserEmailRequestBody } from './+server'
+import { post_request } from '$lib/helpers/get-post-requests'
+
+export async function api_email_new_user(body: NewUserEmailRequestBody) {
+ return await post_request(`/api/email/new_user`, body)
+}
diff --git a/packages/site/src/routes/api/email/new_user/save-user-to-supabase.ts b/packages/site/src/routes/api/email/new_user/save-user-to-supabase.ts
deleted file mode 100644
index c0bd4bc0a..000000000
--- a/packages/site/src/routes/api/email/new_user/save-user-to-supabase.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import type { GoogleAuthUserMetaData, IUser } from '@living-dictionaries/types'
-import { getAdminSupabaseClient } from '$lib/supabase/admin'
-
-export async function save_user_to_supabase(user: IUser): Promise {
- console.info({ user })
- const adminSupabase = getAdminSupabaseClient()
- const { data, error } = await adminSupabase.auth.admin.createUser({
- email: user.email,
- email_confirm: true,
- app_metadata: { fb_uid: user.uid },
- user_metadata: get_firebase_user_meta_data(user),
- })
- console.info({ data, error })
- return data?.user?.id
-}
-
-function get_firebase_user_meta_data({ displayName, photoURL }: IUser) {
- const metadata: GoogleAuthUserMetaData = {}
- if (displayName)
- metadata.full_name = displayName
- if (photoURL)
- metadata.avatar_url = photoURL
- return metadata
-}
diff --git a/packages/site/src/routes/api/email/otp/+server.ts b/packages/site/src/routes/api/email/otp/+server.ts
new file mode 100644
index 000000000..2136a1420
--- /dev/null
+++ b/packages/site/src/routes/api/email/otp/+server.ts
@@ -0,0 +1,43 @@
+import { error, json } from '@sveltejs/kit'
+import { send_email } from '../send-email'
+import type { RequestHandler } from './$types'
+import { dev } from '$app/environment'
+import { ResponseCodes } from '$lib/constants'
+import { getAdminSupabaseClient } from '$lib/supabase/admin'
+
+export interface OTPEmailRequestBody {
+ email: string
+}
+
+export interface OTPEmailResponseBody {
+ result: 'success'
+ otp?: string
+}
+
+export const POST: RequestHandler = async ({ request }) => {
+ const { email } = await request.json() as OTPEmailRequestBody
+ if (!email)
+ error(ResponseCodes.BAD_REQUEST, 'No email provided')
+
+ const admin_supabase = getAdminSupabaseClient()
+ const { data, error: get_link_error } = await admin_supabase.auth.admin.generateLink({ email, type: 'magiclink' })
+ if (get_link_error)
+ error(500, get_link_error)
+
+ if (dev)
+ return json({ result: 'success', otp: data.properties.email_otp } satisfies OTPEmailResponseBody)
+
+ try {
+ await send_email({
+ to: [{ email }],
+ subject: 'Your One-Time Passcode for Living Dictionaries',
+ body: `${data.properties.email_otp} is your one-time passcode for Living Dictionaries.`,
+ type: 'text/plain',
+ })
+
+ return json({ result: 'success' } satisfies OTPEmailResponseBody)
+ } catch (err) {
+ console.error(`Error with email send request: ${err.message}`)
+ error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error with email send request: ${err.message}`)
+ }
+}
diff --git a/packages/site/src/routes/api/email/otp/_call.ts b/packages/site/src/routes/api/email/otp/_call.ts
new file mode 100644
index 000000000..b5647e5e8
--- /dev/null
+++ b/packages/site/src/routes/api/email/otp/_call.ts
@@ -0,0 +1,6 @@
+import type { OTPEmailRequestBody, OTPEmailResponseBody } from './+server'
+import { post_request } from '$lib/helpers/get-post-requests'
+
+export async function api_email_otp(body: OTPEmailRequestBody) {
+ return await post_request(`/api/email/otp`, body)
+}
diff --git a/packages/site/src/routes/api/email/request_access/+server.ts b/packages/site/src/routes/api/email/request_access/+server.ts
index 5b0139126..13746bf69 100644
--- a/packages/site/src/routes/api/email/request_access/+server.ts
+++ b/packages/site/src/routes/api/email/request_access/+server.ts
@@ -1,11 +1,10 @@
-import type { IHelper, IUser } from '@living-dictionaries/types'
import { error, json } from '@sveltejs/kit'
import { getSupportMessageRecipients } from '../addresses'
import { type Address, send_email } from '../send-email'
import type { RequestHandler } from './$types'
import { dev } from '$app/environment'
-import { getDb } from '$lib/server/firebase-admin'
import { ResponseCodes } from '$lib/constants'
+import { getAdminSupabaseClient } from '$lib/supabase/admin'
export interface RequestAccessBody {
email: string
@@ -16,17 +15,27 @@ export interface RequestAccessBody {
dictionaryName: string
}
-async function getManagerAddresses(dictionaryId: string): Promise {
- const db = getDb()
- const managers = (await db.collection(`dictionaries/${dictionaryId}/managers`).get()).docs.map(doc => doc.data() as IHelper)
- const userPromises = managers.map((manager) => {
- return db.doc(`users/${manager.id}`).get()
- })
- const users = (await Promise.all(userPromises)).map(doc => doc.data() as IUser)
- return users.map((user) => {
+async function get_manager_addresses(dictionary_id: string): Promise {
+ const admin_supabase = getAdminSupabaseClient()
+
+ const { data: managers, error: manager_error } = await admin_supabase.from('dictionary_roles')
+ .select(`
+ dictionary_id,
+ user_id,
+ role,
+ profile:profiles_view (
+ full_name,
+ email
+ )
+ `)
+ .eq('role', 'manager')
+ .eq('dictionary_id', dictionary_id)
+ if (manager_error) throw new Error(manager_error.message)
+
+ return managers.map((manager) => {
return {
- name: user.displayName,
- email: user.email,
+ name: manager.profile.full_name,
+ email: manager.profile.email,
}
})
}
@@ -35,7 +44,7 @@ export const POST: RequestHandler = async ({ request }) => {
try {
const { email, message, name, url, dictionaryId, dictionaryName } = await request.json() as RequestAccessBody
- const managerAddresses = await getManagerAddresses(dictionaryId)
+ const managerAddresses = await get_manager_addresses(dictionaryId)
await send_email({
to: [...getSupportMessageRecipients({ dev }), ...managerAddresses],
reply_to: { email },
diff --git a/packages/site/src/routes/api/email/request_access/_call.ts b/packages/site/src/routes/api/email/request_access/_call.ts
new file mode 100644
index 000000000..4e5a35e02
--- /dev/null
+++ b/packages/site/src/routes/api/email/request_access/_call.ts
@@ -0,0 +1,6 @@
+import type { RequestAccessBody } from './+server'
+import { post_request } from '$lib/helpers/get-post-requests'
+
+export async function api_request_access(body: RequestAccessBody) {
+ return await post_request(`/api/email/request_access`, body)
+}
diff --git a/packages/site/src/routes/api/email/send-email.ts b/packages/site/src/routes/api/email/send-email.ts
index 787df5f9e..de9398739 100644
--- a/packages/site/src/routes/api/email/send-email.ts
+++ b/packages/site/src/routes/api/email/send-email.ts
@@ -1,69 +1,18 @@
+// import { DKIM_PRIVATE_KEY, MAILCHANNELS_API_KEY } from '$env/static/private'
+import { SESClient, SendEmailCommand, type SendEmailCommandOutput } from '@aws-sdk/client-ses'
import { dictionary_address, no_reply_address } from './addresses'
-import { DKIM_PRIVATE_KEY, MAILCHANNELS_API_KEY } from '$env/static/private'
+import { AWS_SES_ACCESS_KEY_ID, AWS_SES_REGION, AWS_SES_SECRET_ACCESS_KEY } from '$env/static/private'
-const MAILCHANNELS_API_URL = 'https://api.mailchannels.net/tx/v1/send'
-
-export async function send_email({ from, to, cc, bcc, reply_to, subject, body, type, dry_run }: EmailParts) {
- if (!MAILCHANNELS_API_KEY)
- throw new Error('MAILCHANNELS_API_KEY env variable not configured')
-
- if (!DKIM_PRIVATE_KEY)
- throw new Error('DKIM_PRIVATE_KEY env variable not configured')
-
- if (to.length + (cc?.length || 0) + (bcc?.length || 0) > 1000)
- throw new Error('Maximum of 1000 recipients allowed')
-
- const mail_channels_send_body: MailChannelsSendBody = {
- personalizations: [{
- to,
- cc: cc || [],
- bcc: bcc || [],
- dkim_domain: 'livingdictionaries.app',
- dkim_selector: 'notification',
- dkim_private_key: DKIM_PRIVATE_KEY,
- }],
- from: from || no_reply_address,
- reply_to: reply_to || dictionary_address,
- subject,
- content: [{
- type,
- value: body,
- }],
- }
-
- const url = dry_run ? `${MAILCHANNELS_API_URL}?dry-run=true` : MAILCHANNELS_API_URL
-
- const response = await fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-API-Key': MAILCHANNELS_API_KEY,
- },
- body: JSON.stringify(mail_channels_send_body),
- })
-
- // receives status 202 from MailChannels to indicate send pending
- if (!response.status.toString().startsWith('2')) {
- const body = await response.json()
- throw new Error(`MailChannels error: ${response.status} ${body.errors?.[0]}`)
- }
- return response
-}
-
-/** See https://api.mailchannels.net/tx/v1/documentation */
export interface EmailParts {
from?: Address
- /** 1-1000 */
to: Address[]
- /** 0-1000 */
cc?: Address[]
- /** 0-1000 */
bcc?: Address[]
reply_to?: Address
subject: string
body: string
type: 'text/plain' | 'text/html'
- /** Defaults to false, when setting true the message will not be sent. Instead, the fully rendered message is returned from MailChannels. */
+ /** Defaults to false, when setting true the message will not be sent. */
dry_run?: boolean
}
@@ -72,34 +21,89 @@ export interface Address {
name?: string
}
-/** Source https://api.mailchannels.net/tx/v1/documentation */
-interface MailChannelsSendBody {
- subject: string
- content: {
- /** The mime type of the content you are including in your email */
- type: 'text/plain' | 'text/html'
- /** The actual content of the specified mime type that you are including in the message */
- value: string
- }[]
- from: Address
- personalizations: {
- /** 1-1000 */
- to: Address[]
- from?: Address
- reply_to?: Address
- /** 0-1000 */
- cc?: Address[]
- /** 0-1000 */
- bcc?: Address[]
- subject?: string
- /* see https://mailchannels.zendesk.com/hc/en-us/articles/7122849237389-Adding-a-DKIM-Signature */
- dkim_domain: string
- /* Encoded in Base64 */
- dkim_private_key: string
- dkim_selector: string
- headers?: Record // same as other headers
- }[] // (0...1000)
- reply_to?: Address
- /** A JSON object containing key/value pairs of header names and the value to substitute for them. The Key/value pairs must be strings. You must ensure these are properly encoded if they contain unicode characters. Must not be one of the reserved headers (received, dkim-signature, Content-Type, Content-Transfer-Encoding, To, From, Subject, Reply-To, CC, BCC). */
- headers?: Record
+function format_address({ name, email }: Address): string {
+ return name ? `${name} <${email}>` : email
+}
+
+// Rate-limiting variables
+let sent_timestamps: number[] = [] // Tracks timestamps of sent emails
+let sending_promise = Promise.resolve() as unknown as Promise // Ensures sequential sending
+
+export function send_email({ from, to, cc, bcc, reply_to, subject, body, type, dry_run }: EmailParts) {
+ const send_single_email = async (recipient: Address) => {
+ if (dry_run) {
+ console.info('Dry run: email not sent')
+ console.info('From:', format_address(from || no_reply_address))
+ console.info('To:', format_address(recipient))
+ if (cc)
+ console.info('CC:', cc.map(format_address).join(', '))
+ if (bcc)
+ console.info('BCC:', bcc.map(format_address).join(', '))
+ console.info('Reply-To:', format_address(reply_to || dictionary_address))
+ console.info('Subject:', subject)
+ console.info('Body:', body)
+ return { message: 'Dry run completed, email not sent' }
+ }
+
+ // Remove timestamps older than 1 second
+ const now = Date.now()
+ sent_timestamps = sent_timestamps.filter(ts => now - ts < 1000)
+ // If 7 or more emails were sent in the last second, wait
+ if (sent_timestamps.length >= 7) {
+ const [oldest] = sent_timestamps
+ const waitTime = oldest + 1000 - now
+ if (waitTime > 0) {
+ await new Promise(resolve => setTimeout(resolve, waitTime))
+ }
+ }
+
+ if (!AWS_SES_ACCESS_KEY_ID || !AWS_SES_REGION || !AWS_SES_SECRET_ACCESS_KEY) {
+ throw new Error('AWS_SES credentials not configured')
+ }
+
+ const sesClient = new SESClient({
+ region: AWS_SES_REGION,
+ credentials: {
+ accessKeyId: AWS_SES_ACCESS_KEY_ID,
+ secretAccessKey: AWS_SES_SECRET_ACCESS_KEY,
+ },
+ })
+
+ try {
+ const command = new SendEmailCommand({
+ Source: format_address(from || no_reply_address),
+ Destination: {
+ ToAddresses: [format_address(recipient)], // Single recipient per email
+ CcAddresses: cc?.map(format_address) || [],
+ BccAddresses: bcc?.map(format_address) || [],
+ },
+ Message: {
+ Subject: { Data: subject },
+ Body: {
+ [type === 'text/plain' ? 'Text' : 'Html']: { Data: body },
+ },
+ },
+ ReplyToAddresses: [format_address(reply_to || dictionary_address)],
+ })
+
+ const response = await sesClient.send(command)
+ console.info('Email sent successfully to', recipient.email, 'Message ID:', response.MessageId)
+ // Record timestamp after successful send
+ sent_timestamps.push(Date.now())
+ return response
+ } catch (error) {
+ console.error('Error sending email to', recipient.email, ':', error)
+ throw new Error(`Failed to send email to ${recipient.email}: ${error.message}`)
+ }
+ }
+
+ // Chain email sends for all recipients sequentially
+ for (const recipient of to) {
+ sending_promise = sending_promise.then(() => send_single_email(recipient))
+ }
+
+ // Return the promise chain so the caller can await completion
+ return sending_promise
}
diff --git a/packages/site/src/routes/api/gcs_serving_url/+server.ts b/packages/site/src/routes/api/gcs_serving_url/+server.ts
index 1e0f3d855..4de897cbb 100644
--- a/packages/site/src/routes/api/gcs_serving_url/+server.ts
+++ b/packages/site/src/routes/api/gcs_serving_url/+server.ts
@@ -1,11 +1,9 @@
import { error, json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'
import { PROCESS_IMAGE_URL } from '$env/static/private'
-import { decodeToken } from '$lib/server/firebase-admin'
import { ResponseCodes } from '$lib/constants'
export interface GCSServingUrlRequestBody {
- auth_token: string
storage_path: string
}
@@ -13,20 +11,20 @@ export interface GCSServingUrlResponseBody {
serving_url: string
}
-export const POST: RequestHandler = async ({ request, fetch }) => {
+export const POST: RequestHandler = async ({ request, fetch, locals: { getSession } }) => {
if (!PROCESS_IMAGE_URL)
error(ResponseCodes.INTERNAL_SERVER_ERROR, 'Missing PROCESS_IMAGE_URL')
- const { auth_token, storage_path } = await request.json() as GCSServingUrlRequestBody
+ const { data: session_data, error: _error } = await getSession()
+ if (_error || !session_data?.user)
+ error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
+
+ const { storage_path } = await request.json() as GCSServingUrlRequestBody
if (!storage_path)
error(ResponseCodes.BAD_REQUEST, 'Missing storage_location')
try {
- const decodedToken = await decodeToken(auth_token)
- if (!decodedToken?.uid)
- throw new Error('No user id found in token')
-
const processAndLocationUrl = `${PROCESS_IMAGE_URL}/${storage_path}`
const result = await fetch(processAndLocationUrl)
diff --git a/packages/site/src/routes/api/gcs_serving_url/_call.ts b/packages/site/src/routes/api/gcs_serving_url/_call.ts
index 6db1a0ee2..0825f6dea 100644
--- a/packages/site/src/routes/api/gcs_serving_url/_call.ts
+++ b/packages/site/src/routes/api/gcs_serving_url/_call.ts
@@ -1,11 +1,6 @@
-import { get } from 'svelte/store'
-import { authState } from 'sveltefirets'
import type { GCSServingUrlRequestBody, GCSServingUrlResponseBody } from './+server'
import { post_request } from '$lib/helpers/get-post-requests'
-export async function api_gcs_serving_url(body: Omit) {
- const auth_state_user = get(authState)
- const auth_token = await auth_state_user.getIdToken()
-
- return await post_request(`/api/gcs_serving_url`, { ...body, auth_token })
+export async function api_gcs_serving_url(body: GCSServingUrlRequestBody) {
+ return await post_request(`/api/gcs_serving_url`, body)
}
diff --git a/packages/site/src/routes/api/upload/+server.ts b/packages/site/src/routes/api/upload/+server.ts
index 67a70168c..a573e1727 100644
--- a/packages/site/src/routes/api/upload/+server.ts
+++ b/packages/site/src/routes/api/upload/+server.ts
@@ -1,14 +1,12 @@
import { PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
-import { error, json } from '@sveltejs/kit'
-import { firebaseConfig } from 'sveltefirets'
+import { type RequestHandler, error, json } from '@sveltejs/kit'
import { check_can_edit } from '$api/db/check-permission'
import { ResponseCodes } from '$lib/constants'
import { GCLOUD_MEDIA_BUCKET_S3 } from '$lib/server/gcloud'
-import { decodeToken } from '$lib/server/firebase-admin'
+import { mode } from '$lib/supabase'
export interface UploadRequestBody {
- auth_token: string
folder: string
dictionary_id: string
file_name: string
@@ -22,18 +20,17 @@ export interface UploadResponseBody {
item_id: string
}
-export async function POST({ request }) {
- try {
- const { auth_token, folder, dictionary_id, file_name, file_type } = await request.json() as UploadRequestBody
-
- if (!auth_token)
- throw new Error('missing auth_token')
+export const POST: RequestHandler = async ({ request, locals: { getSession } }) => {
+ const { data: session_data, error: _error, supabase } = await getSession()
+ if (_error || !session_data?.user)
+ error(ResponseCodes.UNAUTHORIZED, { message: _error.message || 'Unauthorized' })
- const decoded_token = await decodeToken(auth_token)
- if (!decoded_token?.uid)
- throw new Error('No user id found in token')
+ try {
+ const { folder, dictionary_id, file_name, file_type } = await request.json() as UploadRequestBody
- await check_can_edit(decoded_token.uid, dictionary_id)
+ if (!session_data.user.app_metadata.admin) {
+ await check_can_edit(supabase, dictionary_id)
+ }
if (!folder)
throw new Error('Missing folder')
@@ -42,7 +39,7 @@ export async function POST({ request }) {
if (!file_type?.trim())
throw new Error('Missing file_type')
- const bucket = firebaseConfig.storageBucket
+ const bucket = mode === 'development' ? 'talking-dictionaries-dev.appspot.com' : 'talking-dictionaries-alpha.appspot.com'
const extension = file_name.split('.').pop()
const item_id = Date.now().toString()
const object_key = `${folder}/${item_id}.${extension}`
@@ -58,7 +55,7 @@ export async function POST({ request }) {
return json({ presigned_upload_url, bucket, object_key, item_id } satisfies UploadResponseBody)
} catch (err) {
- console.error(`Error creating dictionary: ${err.message}`)
- error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error creating dictionary: ${err.message}`)
+ console.error(`Error uploading: ${err.message}`)
+ error(ResponseCodes.INTERNAL_SERVER_ERROR, `Error uploading: ${err.message}`)
}
}
diff --git a/packages/site/src/routes/api/upload/_call.ts b/packages/site/src/routes/api/upload/_call.ts
index 2eca254c0..d9522aed5 100644
--- a/packages/site/src/routes/api/upload/_call.ts
+++ b/packages/site/src/routes/api/upload/_call.ts
@@ -1,10 +1,6 @@
-import { get } from 'svelte/store'
-import { authState } from 'sveltefirets'
import type { UploadRequestBody, UploadResponseBody } from './+server'
import { post_request } from '$lib/helpers/get-post-requests'
-export async function api_upload(body: Omit) {
- const auth_state_user = get(authState)
- const auth_token = await auth_state_user.getIdToken()
- return await post_request(`/api/upload`, { ...body, auth_token })
+export async function api_upload(body: UploadRequestBody) {
+ return await post_request(`/api/upload`, body)
}
diff --git a/packages/site/src/routes/create-dictionary/+page.svelte b/packages/site/src/routes/create-dictionary/+page.svelte
index 41c9f1add..ce75e45bf 100644
--- a/packages/site/src/routes/create-dictionary/+page.svelte
+++ b/packages/site/src/routes/create-dictionary/+page.svelte
@@ -1,9 +1,17 @@
-
-
{$page.data.t('create.create_new_dictionary')}
-
-
- {$page.data.t('misc.out_of_service')}
-
- {$page.data.t('misc.transmitter')}
-
-
-
-
-
-
-
-
-
+
+ {$page.data.t('terms.agree_by_submit')}
+ {$page.data.t('dictionary.terms_of_use')}.
+
+
+ {/if}
+
+
+{/if}
{#if modal === 'auth'}
{#await import('$lib/components/shell/AuthModal.svelte') then { default: AuthModal }}
{
+ on_close={() => {
modal = null
}} />
{/await}
diff --git a/packages/site/src/routes/create-dictionary/+page.ts b/packages/site/src/routes/create-dictionary/+page.ts
index b37e79ab2..4e5b00489 100644
--- a/packages/site/src/routes/create-dictionary/+page.ts
+++ b/packages/site/src/routes/create-dictionary/+page.ts
@@ -1,20 +1,21 @@
-import { docExists, firebaseConfig, setOnline, updateOnline } from 'sveltefirets'
-import { arrayUnion, serverTimestamp } from 'firebase/firestore/lite'
-import type { IHelper, IUser, TablesInsert } from '@living-dictionaries/types'
+import type { TablesInsert } from '@living-dictionaries/types'
import { get } from 'svelte/store'
import type { PageLoad } from './$types'
import { pruneObject } from '$lib/helpers/prune'
import { api_create_dictionary } from '$api/db/create-dictionary/_call'
+import { mode } from '$lib/supabase'
export const load = (({ parent }) => {
const MIN_URL_LENGTH = 3
- async function dictionary_with_url_exists(url: string): Promise {
- return await docExists(`dictionaries/${url}`)
+ async function dictionary_id_exists(url: string): Promise {
+ const { supabase } = await parent()
+ const { data: exists } = await supabase.from('dictionaries').select('id').eq('id', url).single()
+ return !!exists
}
- async function create_dictionary(dictionary: Omit, 'created_by' | 'updated_by'>) {
- const { t, user } = await parent()
+ async function create_dictionary(dictionary: TablesInsert<'dictionaries'>) {
+ const { t, user, supabase } = await parent()
const $user = get(user)
if (!$user) return alert('Please login first') // this should never fire as should be caught in page
@@ -24,25 +25,23 @@ export const load = (({ parent }) => {
try {
const pruned_dictionary = pruneObject(dictionary)
- if (firebaseConfig.projectId === 'talking-dictionaries-dev') {
+ if (mode === 'development') {
console.info(pruned_dictionary)
if (!confirm('Dictionary value logged to console because in dev mode. Do you still want to create this dictionary?')) {
return
}
}
- const { error } = await api_create_dictionary({ dictionary: pruned_dictionary, fb_user: $user })
+ const { error } = await api_create_dictionary({ dictionary: pruned_dictionary })
if (error)
throw new Error(error.message)
- await setOnline(`dictionaries/${dictionary.id}/managers/${$user.uid}`, {
- id: $user.uid,
- name: $user.displayName,
- })
- await updateOnline(`users/${$user.uid}`, {
- managing: arrayUnion(dictionary.id),
- termsAgreement: serverTimestamp(),
- })
+ const { error: terms_agreement_error } = await supabase.from('user_data').update({
+ terms_agreement: new Date().toISOString(),
+ }).eq('id', $user.id)
+ if (terms_agreement_error) {
+ console.error(terms_agreement_error)
+ }
window.location.replace(`/${dictionary.id}/entries`)
} catch (err) {
@@ -51,7 +50,7 @@ export const load = (({ parent }) => {
}
return {
MIN_URL_LENGTH,
- dictionary_with_url_exists,
+ dictionary_id_exists,
create_dictionary,
}
}) satisfies PageLoad
diff --git a/packages/site/src/routes/dictionaries/+page.ts b/packages/site/src/routes/dictionaries/+page.ts
index 59d1d0ade..627d2f1a9 100644
--- a/packages/site/src/routes/dictionaries/+page.ts
+++ b/packages/site/src/routes/dictionaries/+page.ts
@@ -3,8 +3,7 @@ import type { PageLoad } from './$types'
import { ResponseCodes } from '$lib/constants'
export const load: PageLoad = async ({ parent }) => {
- const { user_from_cookies, supabase } = await parent()
- const admin = !!user_from_cookies?.roles?.admin
+ const { admin, supabase } = await parent()
const query = supabase.from('materialized_dictionaries_view')
.select()
diff --git a/packages/site/src/service-worker.ts b/packages/site/src/service-worker.ts
index 5dc4de41a..75095bcfe 100644
--- a/packages/site/src/service-worker.ts
+++ b/packages/site/src/service-worker.ts
@@ -1,68 +1,67 @@
-/* eslint-disable no-undef */
-import { version, files, build } from '$service-worker';
-
-const ASSETS = `cache${version}`;
-
-// `build` is an array of all the files generated by the bundler,
-// `files` is an array of everything in the `static` directory
-const to_cache = (build as string[]).concat(files as string[]);
-const cached = new Set(to_cache);
-
-self.addEventListener('install', (event: EventType) => {
- event.waitUntil(
- caches
- .open(ASSETS)
- .then((cache) => cache.addAll(to_cache))
- .then(() => {
- ((self as any) as ServiceWorkerGlobalScope).skipWaiting();
- })
- );
-});
-
-self.addEventListener('activate', (event: EventType) => {
- event.waitUntil(
- caches.keys().then(async (keys) => {
- // delete old caches
- for (const key of keys)
- if (key !== ASSETS) await caches.delete(key);
-
-
- ((self as any) as ServiceWorkerGlobalScope).clients.claim();
- })
- );
-});
-
-self.addEventListener('fetch', (event: EventType) => {
- // If Needed: add for compatibility of image upload to Firestorage on Safari
- // if (event.request.url.indexOf('firebasestorage.googleapis.com') !== -1) return;
-
- if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
-
- const url = new URL(event.request.url);
+///
+///
+///
+///
+import { build, files, prerendered, version } from '$service-worker'
+
+// eslint-disable-next-line no-restricted-globals
+const _self = self as unknown as ServiceWorkerGlobalScope
+const current_cache = `cache-${version}`
+
+const assets_for_cache = [
+ ...build,
+ ...prerendered,
+ ...files,
+]
+const cached = new Set(assets_for_cache)
+
+_self.addEventListener('install', (event) => {
+ event.waitUntil(addFilesToCache())
+})
+
+async function addFilesToCache() {
+ const cache = await caches.open(current_cache)
+ await cache.addAll(assets_for_cache)
+ _self.skipWaiting()
+}
+
+_self.addEventListener('activate', (event) => {
+ event.waitUntil(deleteOldCaches())
+})
+
+async function deleteOldCaches() {
+ const cacheNames = await caches.keys()
+ const deletionPromises = cacheNames.map((name) => {
+ if (name !== current_cache)
+ return caches.delete(name)
+ return null
+ })
+ await Promise.all(deletionPromises)
+ _self.clients.claim()
+}
+
+_self.addEventListener('fetch', (event) => {
+ const isNotGetRequest = event.request.method !== 'GET'
+ const isPartialRequest = event.request.headers.has('range') // as in videos
+ if (isNotGetRequest || isPartialRequest)
+ return
+
+ const url = new URL(event.request.url)
// don't try to handle e.g. data: URIs
- if (!url.protocol.startsWith('http')) return;
+ if (!url.protocol.startsWith('http')) return
// ignore dev server requests
- if (url.hostname === self.location.hostname && url.port !== self.location.port) return;
+ if (url.hostname === _self.location.hostname && url.port !== _self.location.port) return
// always serve static files and bundler-generated assets from cache
- if (url.host === self.location.host && cached.has(url.pathname)) {
- event.respondWith(caches.match(event.request));
- return;
+ if (url.host === _self.location.host && cached.has(url.pathname)) {
+ // @ts-expect-error
+ event.respondWith(caches.match(event.request))
+ return
}
- // for pages, you might want to serve a build `service-worker-index.html` file,
- // which Sapper has generated for you. It's not right for every
- // app, but if it's right for yours then uncomment this section
- /*
- if (url.origin === self.origin && routes.find(route => route.pattern.test(url.pathname))) {
- event.respondWith(caches.match('/service-worker-index.html'));
- return;
- }
- */
-
- if (event.request.cache === 'only-if-cached') return;
+ if (event.request.cache === 'only-if-cached') return
// for everything else, try the network first, falling back to
// cache if the user is offline. (If the pages never change, you
@@ -70,15 +69,15 @@ self.addEventListener('fetch', (event: EventType)
event.respondWith(
caches.open(`offline${version}`).then(async (cache) => {
try {
- const response = await fetch(event.request);
- cache.put(event.request, response.clone());
- return response;
+ const response = await fetch(event.request)
+ cache.put(event.request, response.clone())
+ return response
} catch (err) {
- const response = await cache.match(event.request);
- if (response) return response;
+ const response = await cache.match(event.request)
+ if (response) return response
- throw err;
+ throw err
}
- })
- );
-});
+ }),
+ )
+})
diff --git a/packages/site/tsconfig.json b/packages/site/tsconfig.json
index 6f2f661ff..9f36503cf 100644
--- a/packages/site/tsconfig.json
+++ b/packages/site/tsconfig.json
@@ -28,7 +28,8 @@
"resolveJsonModule": true,
"types": [
"vitest/globals",
- "vitest/importMeta"
+ "vitest/importMeta",
+ "google-one-tap"
],
"allowJs": true,
"checkJs": true,
diff --git a/packages/types/dictionary-settings.interface.ts b/packages/types/dictionary-settings.interface.ts
deleted file mode 100644
index 4d8efcf2a..000000000
--- a/packages/types/dictionary-settings.interface.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface IDictionarySettings {
- definition: boolean;
- localOrthography: boolean;
- dialect: boolean;
-}
diff --git a/packages/types/dictionary.interface.ts b/packages/types/dictionary.interface.ts
index ae656e199..307800e7b 100644
--- a/packages/types/dictionary.interface.ts
+++ b/packages/types/dictionary.interface.ts
@@ -1,5 +1,5 @@
import type { IFirestoreMetaData } from 'sveltefirets'
-import type { PartnerPhoto } from '.'
+import type { PartnerPhoto } from './photo.interface'
export interface IAbout extends IFirestoreMetaData {
about: string
diff --git a/packages/types/helper.interface.ts b/packages/types/helper.interface.ts
deleted file mode 100644
index f84dba218..000000000
--- a/packages/types/helper.interface.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { IFirestoreMetaData } from 'sveltefirets';
-
-export interface IHelper extends IFirestoreMetaData {
- name: string;
-}
-
-export type HelperRoles = 'manager' | 'contributor' | 'writeInCollaborator';
diff --git a/packages/types/index.ts b/packages/types/index.ts
index 3e68fd3ec..b006f1d1b 100644
--- a/packages/types/index.ts
+++ b/packages/types/index.ts
@@ -2,19 +2,15 @@ import type { Tables } from './supabase/combined.types'
export type { EntryView, SenseWithSentences, AudioWithSpeakerIds } from './supabase/entry.interface'
export type { VideoCustomMetadata, HostedVideo } from './video.interface'
-export type { IAbout, IGrammar, Citation, Partner } from './dictionary.interface'
export type { Coordinates, IPoint, IRegion } from './coordinates.interface'
export type { IGlossLanguages, IGlossLanguage } from './gloss-language.interface'
export type { MultiString } from './gloss.interface'
export type { IExampleSentence } from './example-sentence.interface'
-export type { DictionaryPhoto, PartnerPhoto } from './photo.interface'
+export type { DictionaryPhoto } from './photo.interface'
export type { SemanticDomain } from './semantic-domain.interface'
-export type { IUser, GoogleAuthUserMetaData } from './user.interface'
-export type { IInvite } from './invite.interface'
-export type { IDictionarySettings } from './dictionary-settings.interface'
+export type { GoogleAuthUserMetaData } from './user.interface'
export type { PartOfSpeech } from './part-of-speech.interface'
export type { IColumn } from './column.interface'
-export type { HelperRoles, IHelper } from './helper.interface'
export { type IPrintFields, StandardPrintFields } from './print-entry.interface'
export { type EntryFieldValue, type i18nEntryFieldKey } from './entry-fields.enum'
export type { ContentUpdateRequestBody } from './supabase/content-update.interface'
@@ -24,3 +20,12 @@ export type { Orthography } from './supabase/orthography.interface'
export type { IFirestoreMetaData } from 'sveltefirets'
export type DictionaryView = Tables<'dictionaries_view'>
+export interface PartnerWithPhoto {
+ id: string
+ name: string
+ photo: {
+ id: string
+ storage_path: string
+ serving_url: string
+ }
+}
diff --git a/packages/types/invite.interface.ts b/packages/types/invite.interface.ts
index b07bc2f06..caf612bdf 100644
--- a/packages/types/invite.interface.ts
+++ b/packages/types/invite.interface.ts
@@ -1,10 +1,14 @@
-import type { IFirestoreMetaData } from 'sveltefirets';
+import type { IFirestoreMetaData } from 'sveltefirets'
export interface IInvite extends IFirestoreMetaData {
- inviterEmail: string;
- inviterName: string;
- dictionaryName: string;
- targetEmail: string;
- role: 'manager' | 'contributor';
- status: 'queued' | 'sent' | 'claimed' | 'cancelled';
+ inviterEmail: string
+ inviterName: string
+ dictionaryName: string
+ targetEmail: string
+ role: 'manager' | 'contributor'
+ status: 'queued' | 'sent' | 'claimed' | 'cancelled'
+}
+
+export interface IHelper extends IFirestoreMetaData {
+ name: string
}
diff --git a/packages/types/supabase/augments.types.ts b/packages/types/supabase/augments.types.ts
index aee0b3734..52b20b847 100644
--- a/packages/types/supabase/augments.types.ts
+++ b/packages/types/supabase/augments.types.ts
@@ -7,6 +7,7 @@ import type { AudioWithSpeakerIds, EntryMainFields, SenseWithSentences } from '.
import type { ImportContentUpdate } from './content-import.interface'
import type { Orthography } from './orthography.interface'
import type { DictionaryMetadata } from './dictionary.types'
+import type { DictionaryRolesWithoutUser } from './users.types'
export interface DatabaseAugments {
public: {
@@ -183,6 +184,15 @@ export interface DatabaseAugments {
}
}
Functions: {
+ // dictionaries_with_editors: {
+ // Returns: {
+ // coordinates: Coordinates
+ // featured_image: DictionaryPhoto
+ // metadata: DictionaryMetadata
+ // orthographies: Orthography[]
+ // editors: any[]
+ // }[]
+ // }
entries_from_timestamp: {
Returns: {
main: EntryMainFields
@@ -201,6 +211,14 @@ export interface DatabaseAugments {
tag_ids: string[] | null
}[]
}
+ get_my_claim: {
+ Returns: any
+ }
+ users_with_dictionary_roles: {
+ Returns: {
+ dictionary_roles: DictionaryRolesWithoutUser[]
+ }[]
+ }
}
}
}
diff --git a/packages/types/supabase/combined.types.ts b/packages/types/supabase/combined.types.ts
index ac1e01927..513456352 100644
--- a/packages/types/supabase/combined.types.ts
+++ b/packages/types/supabase/combined.types.ts
@@ -7,6 +7,7 @@ import type { AudioWithSpeakerIds, EntryMainFields, SenseWithSentences } from '.
import type { ImportContentUpdate } from './content-import.interface'
import type { Orthography } from './orthography.interface'
import type { DictionaryMetadata } from './dictionary.types'
+import type { DictionaryRolesWithoutUser } from './users.types'
export interface Database {
public: {
@@ -55,6 +56,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'audio_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'audio_created_by_fkey'
columns: [
@@ -143,6 +155,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'audio_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'audio_updated_by_fkey'
columns: [
@@ -201,6 +224,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'audio_speakers_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'audio_speakers_created_by_fkey'
columns: [
@@ -455,6 +489,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'content_updates_user_id_fkey'
+ columns: [
+ 'user_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'content_updates_user_id_fkey'
columns: [
@@ -533,6 +578,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'dialects_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'dialects_created_by_fkey'
columns: [
@@ -588,6 +644,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'dialects_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'dialects_updated_by_fkey'
columns: [
@@ -647,7 +714,7 @@ export interface Database {
coordinates?: Coordinates | null
copyright?: string | null
created_at?: string
- created_by: string
+ created_by?: string
deleted?: string | null
featured_image?: DictionaryPhoto | null
gloss_languages?: string[] | null
@@ -663,7 +730,7 @@ export interface Database {
print_access?: boolean | null
public?: boolean
updated_at?: string
- updated_by: string
+ updated_by?: string
}
Update: {
alternate_names?: string[] | null
@@ -693,9 +760,424 @@ export interface Database {
}
Relationships: [
{
- foreignKeyName: 'dictionaries_created_by_fkey'
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
+ dictionary_info: {
+ Row: {
+ about: string | null
+ citation: string | null
+ created_at: string
+ created_by: string
+ grammar: string | null
+ id: string
+ updated_at: string
+ updated_by: string
+ write_in_collaborators: string[] | null
+ }
+ Insert: {
+ about?: string | null
+ citation?: string | null
+ created_at?: string
+ created_by?: string
+ grammar?: string | null
+ id: string
+ updated_at?: string
+ updated_by?: string
+ write_in_collaborators?: string[] | null
+ }
+ Update: {
+ about?: string | null
+ citation?: string | null
+ created_at?: string
+ created_by?: string
+ grammar?: string | null
+ id?: string
+ updated_at?: string
+ updated_by?: string
+ write_in_collaborators?: string[] | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'dictionaries'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
+ dictionary_partners: {
+ Row: {
+ created_at: string
+ created_by: string
+ dictionary_id: string
+ id: string
+ name: string
+ photo_id: string | null
+ updated_at: string
+ updated_by: string
+ }
+ Insert: {
+ created_at?: string
+ created_by?: string
+ dictionary_id: string
+ id?: string
+ name: string
+ photo_id?: string | null
+ updated_at?: string
+ updated_by?: string
+ }
+ Update: {
+ created_at?: string
+ created_by?: string
+ dictionary_id?: string
+ id?: string
+ name?: string
+ photo_id?: string | null
+ updated_at?: string
+ updated_by?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_photo_id_fkey'
+ columns: [
+ 'photo_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'photos'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
+ dictionary_roles: {
+ Row: {
+ created_at: string
+ dictionary_id: string
+ invited_by: string | null
+ role: Database['public']['Enums']['role_enum']
+ user_id: string
+ }
+ Insert: {
+ created_at?: string
+ dictionary_id: string
+ invited_by?: string | null
+ role: Database['public']['Enums']['role_enum']
+ user_id?: string
+ }
+ Update: {
+ created_at?: string
+ dictionary_id?: string
+ invited_by?: string | null
+ role?: Database['public']['Enums']['role_enum']
+ user_id?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
columns: [
- 'created_by',
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
+ columns: [
+ 'invited_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
+ columns: [
+ 'invited_by',
]
isOneToOne: false
referencedRelation: 'user_emails'
@@ -704,9 +1186,9 @@ export interface Database {
]
},
{
- foreignKeyName: 'dictionaries_created_by_fkey'
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
columns: [
- 'created_by',
+ 'invited_by',
]
isOneToOne: false
referencedRelation: 'users'
@@ -715,9 +1197,20 @@ export interface Database {
]
},
{
- foreignKeyName: 'dictionaries_updated_by_fkey'
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
columns: [
- 'updated_by',
+ 'user_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: [
+ 'user_id',
]
isOneToOne: false
referencedRelation: 'user_emails'
@@ -726,9 +1219,9 @@ export interface Database {
]
},
{
- foreignKeyName: 'dictionaries_updated_by_fkey'
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
columns: [
- 'updated_by',
+ 'user_id',
]
isOneToOne: false
referencedRelation: 'users'
@@ -797,6 +1290,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entries_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'entries_created_by_fkey'
columns: [
@@ -852,6 +1356,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'entries_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'entries_updated_by_fkey'
columns: [
@@ -899,6 +1414,17 @@ export interface Database {
entry_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entry_dialects_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'entry_dialects_created_by_fkey'
columns: [
@@ -968,6 +1494,17 @@ export interface Database {
tag_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entry_tags_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'entry_tags_created_by_fkey'
columns: [
@@ -1054,6 +1591,106 @@ export interface Database {
Relationships: [
]
}
+ invites: {
+ Row: {
+ created_at: string
+ created_by: string
+ dictionary_id: string
+ id: string
+ inviter_email: string
+ role: Database['public']['Enums']['role_enum']
+ status: Database['public']['Enums']['status_enum']
+ target_email: string
+ }
+ Insert: {
+ created_at?: string
+ created_by?: string
+ dictionary_id: string
+ id?: string
+ inviter_email: string
+ role: Database['public']['Enums']['role_enum']
+ status: Database['public']['Enums']['status_enum']
+ target_email: string
+ }
+ Update: {
+ created_at?: string
+ created_by?: string
+ dictionary_id?: string
+ id?: string
+ inviter_email?: string
+ role?: Database['public']['Enums']['role_enum']
+ status?: Database['public']['Enums']['status_enum']
+ target_email?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
photos: {
Row: {
created_at: string
@@ -1070,7 +1707,7 @@ export interface Database {
}
Insert: {
created_at?: string
- created_by: string
+ created_by?: string
deleted?: string | null
dictionary_id: string
id?: string
@@ -1079,7 +1716,7 @@ export interface Database {
source?: string | null
storage_path: string
updated_at?: string
- updated_by: string
+ updated_by?: string
}
Update: {
created_at?: string
@@ -1095,6 +1732,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'photos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'photos_created_by_fkey'
columns: [
@@ -1150,6 +1798,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'photos_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'photos_updated_by_fkey'
columns: [
@@ -1197,6 +1856,17 @@ export interface Database {
sense_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sense_photos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'sense_photos_created_by_fkey'
columns: [
@@ -1266,6 +1936,17 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sense_videos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'sense_videos_created_by_fkey'
columns: [
@@ -1412,6 +2093,17 @@ export interface Database {
sentence_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'senses_in_sentences_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'senses_in_sentences_created_by_fkey'
columns: [
@@ -1481,6 +2173,17 @@ export interface Database {
sentence_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentence_photos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'sentence_photos_created_by_fkey'
columns: [
@@ -1550,6 +2253,17 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentence_videos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'sentence_videos_created_by_fkey'
columns: [
@@ -1645,6 +2359,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentences_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'sentences_created_by_fkey'
columns: [
@@ -1701,12 +2426,23 @@ export interface Database {
]
},
{
- foreignKeyName: 'sentences_text_id_fkey'
+ foreignKeyName: 'sentences_text_id_fkey'
+ columns: [
+ 'text_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'texts'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'sentences_updated_by_fkey'
columns: [
- 'text_id',
+ 'updated_by',
]
isOneToOne: false
- referencedRelation: 'texts'
+ referencedRelation: 'profiles_view'
referencedColumns: [
'id',
]
@@ -1779,6 +2515,17 @@ export interface Database {
user_id?: string | null
}
Relationships: [
+ {
+ foreignKeyName: 'speakers_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'speakers_created_by_fkey'
columns: [
@@ -1834,6 +2581,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'speakers_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'speakers_updated_by_fkey'
columns: [
@@ -1856,6 +2614,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'speakers_user_id_fkey'
+ columns: [
+ 'user_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'speakers_user_id_fkey'
columns: [
@@ -1915,6 +2684,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'tags_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'tags_created_by_fkey'
columns: [
@@ -1970,6 +2750,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'tags_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'tags_updated_by_fkey'
columns: [
@@ -2029,6 +2820,17 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'texts_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'texts_created_by_fkey'
columns: [
@@ -2084,6 +2886,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'texts_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'texts_updated_by_fkey'
columns: [
@@ -2108,6 +2921,64 @@ export interface Database {
},
]
}
+ user_data: {
+ Row: {
+ id: string
+ terms_agreement: string | null
+ unsubscribed_from_emails: string | null
+ updated_at: string
+ welcome_email_sent: string | null
+ }
+ Insert: {
+ id: string
+ terms_agreement?: string | null
+ unsubscribed_from_emails?: string | null
+ updated_at?: string
+ welcome_email_sent?: string | null
+ }
+ Update: {
+ id?: string
+ terms_agreement?: string | null
+ unsubscribed_from_emails?: string | null
+ updated_at?: string
+ welcome_email_sent?: string | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: [
+ 'id',
+ ]
+ isOneToOne: true
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
video_speakers: {
Row: {
created_at: string
@@ -2131,6 +3002,17 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'video_speakers_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'video_speakers_created_by_fkey'
columns: [
@@ -2243,6 +3125,17 @@ export interface Database {
videographer?: string | null
}
Relationships: [
+ {
+ foreignKeyName: 'videos_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'videos_created_by_fkey'
columns: [
@@ -2309,6 +3202,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'videos_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'videos_updated_by_fkey'
columns: [
@@ -2373,6 +3277,17 @@ export interface Database {
'id',
]
},
+ {
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: [
+ 'created_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
{
foreignKeyName: 'dictionaries_created_by_fkey'
columns: [
@@ -2401,6 +3316,94 @@ export interface Database {
'updated_by',
]
isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: [
+ 'updated_by',
+ ]
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ ]
+ }
+ dictionary_roles_with_profiles: {
+ Row: {
+ avatar_url: string | null
+ dictionary_id: string | null
+ full_name: string | null
+ role: Database['public']['Enums']['role_enum'] | null
+ user_id: string | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: [
+ 'dictionary_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: [
+ 'user_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: [
+ 'user_id',
+ ]
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: [
+ 'id',
+ ]
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: [
+ 'user_id',
+ ]
+ isOneToOne: false
referencedRelation: 'user_emails'
referencedColumns: [
'id',
@@ -2458,6 +3461,28 @@ export interface Database {
Relationships: [
]
}
+ profiles_view: {
+ Row: {
+ avatar_url: string | null
+ email: string | null
+ full_name: string | null
+ id: string | null
+ }
+ Insert: {
+ avatar_url?: never
+ email?: string | null
+ full_name?: never
+ id?: string | null
+ }
+ Update: {
+ avatar_url?: never
+ email?: string | null
+ full_name?: never
+ id?: string | null
+ }
+ Relationships: [
+ ]
+ }
speakers_view: {
Row: {
birthplace: string | null
@@ -2648,6 +3673,30 @@ export interface Database {
tag_ids: string[] | null
}[]
}
+ get_my_claim: {
+ Args: {
+ claim: string
+ }
+ Returns: any
+ }
+ is_admin: {
+ Args: Record
+ Returns: boolean
+ }
+ users_with_dictionary_roles: {
+ Args: Record
+ Returns: {
+ id: string
+ email: string
+ full_name: string
+ avatar_url: string
+ last_sign_in_at: string
+ created_at: string
+ unsubscribed_from_emails: string
+ terms_agreement: string
+ dictionary_roles: DictionaryRolesWithoutUser[]
+ }[]
+ }
}
Enums: {
certainty: 'yes' | 'no' | 'unknown'
@@ -2655,6 +3704,8 @@ export interface Database {
entry_columns: 'deleted' | 'glosses' | 'parts_of_speech' | 'semantic_domains' | 'write_in_semantic_domains' | 'noun_class' | 'definition'
entry_tables: 'senses'
gender: 'm' | 'f' | 'o'
+ role_enum: 'manager' | 'contributor'
+ status_enum: 'queued' | 'sent' | 'claimed' | 'cancelled'
}
CompositeTypes: {
[_ in never]: never;
diff --git a/packages/types/supabase/content-update.interface.ts b/packages/types/supabase/content-update.interface.ts
index 07e43e22e..021633302 100644
--- a/packages/types/supabase/content-update.interface.ts
+++ b/packages/types/supabase/content-update.interface.ts
@@ -34,14 +34,8 @@ export type ContentUpdateRequestBody =
interface ContentUpdateBase {
update_id: string // id of the change, a uuidv4 created on client to make things idempotent
- auth_token: string
dictionary_id: string
import_id?: string
-
- import_meta?: {
- user_id?: string
- timestamp?: string
- }
}
interface Insert_Entry extends ContentUpdateBase {
diff --git a/packages/types/supabase/generated.types.ts b/packages/types/supabase/generated.types.ts
index 78e4dcc96..0c0ee3dd3 100644
--- a/packages/types/supabase/generated.types.ts
+++ b/packages/types/supabase/generated.types.ts
@@ -53,6 +53,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'audio_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'audio_created_by_fkey'
columns: ['created_by']
@@ -109,6 +116,13 @@ export interface Database {
referencedRelation: 'texts'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'audio_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'audio_updated_by_fkey'
columns: ['updated_by']
@@ -155,6 +169,13 @@ export interface Database {
referencedRelation: 'audio'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'audio_speakers_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'audio_speakers_created_by_fkey'
columns: ['created_by']
@@ -341,6 +362,13 @@ export interface Database {
referencedRelation: 'texts'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'content_updates_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'content_updates_user_id_fkey'
columns: ['user_id']
@@ -403,6 +431,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'dialects_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'dialects_created_by_fkey'
columns: ['created_by']
@@ -438,6 +473,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'dialects_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'dialects_updated_by_fkey'
columns: ['updated_by']
@@ -489,7 +531,7 @@ export interface Database {
coordinates?: Json | null
copyright?: string | null
created_at?: string
- created_by: string
+ created_by?: string
deleted?: string | null
featured_image?: Json | null
gloss_languages?: string[] | null
@@ -505,7 +547,7 @@ export interface Database {
print_access?: boolean | null
public?: boolean
updated_at?: string
- updated_by: string
+ updated_by?: string
}
Update: {
alternate_names?: string[] | null
@@ -534,6 +576,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'dictionaries_created_by_fkey'
columns: ['created_by']
@@ -548,6 +597,13 @@ export interface Database {
referencedRelation: 'users'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'dictionaries_updated_by_fkey'
columns: ['updated_by']
@@ -564,6 +620,298 @@ export interface Database {
},
]
}
+ dictionary_info: {
+ Row: {
+ about: string | null
+ citation: string | null
+ created_at: string
+ created_by: string
+ grammar: string | null
+ id: string
+ updated_at: string
+ updated_by: string
+ write_in_collaborators: string[] | null
+ }
+ Insert: {
+ about?: string | null
+ citation?: string | null
+ created_at?: string
+ created_by?: string
+ grammar?: string | null
+ id: string
+ updated_at?: string
+ updated_by?: string
+ write_in_collaborators?: string[] | null
+ }
+ Update: {
+ about?: string | null
+ citation?: string | null
+ created_at?: string
+ created_by?: string
+ grammar?: string | null
+ id?: string
+ updated_at?: string
+ updated_by?: string
+ write_in_collaborators?: string[] | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'dictionaries'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_info_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ ]
+ }
+ dictionary_partners: {
+ Row: {
+ created_at: string
+ created_by: string
+ dictionary_id: string
+ id: string
+ name: string
+ photo_id: string | null
+ updated_at: string
+ updated_by: string
+ }
+ Insert: {
+ created_at?: string
+ created_by?: string
+ dictionary_id: string
+ id?: string
+ name: string
+ photo_id?: string | null
+ updated_at?: string
+ updated_by?: string
+ }
+ Update: {
+ created_at?: string
+ created_by?: string
+ dictionary_id?: string
+ id?: string
+ name?: string
+ photo_id?: string | null
+ updated_at?: string
+ updated_by?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_photo_id_fkey'
+ columns: ['photo_id']
+ isOneToOne: false
+ referencedRelation: 'photos'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_partners_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ ]
+ }
+ dictionary_roles: {
+ Row: {
+ created_at: string
+ dictionary_id: string
+ invited_by: string | null
+ role: Database['public']['Enums']['role_enum']
+ user_id: string
+ }
+ Insert: {
+ created_at?: string
+ dictionary_id: string
+ invited_by?: string | null
+ role: Database['public']['Enums']['role_enum']
+ user_id?: string
+ }
+ Update: {
+ created_at?: string
+ dictionary_id?: string
+ invited_by?: string | null
+ role?: Database['public']['Enums']['role_enum']
+ user_id?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
+ columns: ['invited_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
+ columns: ['invited_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_invited_by_fkey'
+ columns: ['invited_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ ]
+ }
entries: {
Row: {
coordinates: Json | null
@@ -623,6 +971,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entries_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'entries_created_by_fkey'
columns: ['created_by']
@@ -658,6 +1013,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'entries_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'entries_updated_by_fkey'
columns: ['updated_by']
@@ -697,6 +1059,13 @@ export interface Database {
entry_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entry_dialects_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'entry_dialects_created_by_fkey'
columns: ['created_by']
@@ -750,6 +1119,13 @@ export interface Database {
tag_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'entry_tags_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'entry_tags_created_by_fkey'
columns: ['created_by']
@@ -819,6 +1195,82 @@ export interface Database {
}
Relationships: []
}
+ invites: {
+ Row: {
+ created_at: string
+ created_by: string
+ dictionary_id: string
+ id: string
+ inviter_email: string
+ role: Database['public']['Enums']['role_enum']
+ status: Database['public']['Enums']['status_enum']
+ target_email: string
+ }
+ Insert: {
+ created_at?: string
+ created_by?: string
+ dictionary_id: string
+ id?: string
+ inviter_email: string
+ role: Database['public']['Enums']['role_enum']
+ status: Database['public']['Enums']['status_enum']
+ target_email: string
+ }
+ Update: {
+ created_at?: string
+ created_by?: string
+ dictionary_id?: string
+ id?: string
+ inviter_email?: string
+ role?: Database['public']['Enums']['role_enum']
+ status?: Database['public']['Enums']['status_enum']
+ target_email?: string
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'invites_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'invites_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: ['id']
+ },
+ ]
+ }
photos: {
Row: {
created_at: string
@@ -835,7 +1287,7 @@ export interface Database {
}
Insert: {
created_at?: string
- created_by: string
+ created_by?: string
deleted?: string | null
dictionary_id: string
id?: string
@@ -844,7 +1296,7 @@ export interface Database {
source?: string | null
storage_path: string
updated_at?: string
- updated_by: string
+ updated_by?: string
}
Update: {
created_at?: string
@@ -860,6 +1312,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'photos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'photos_created_by_fkey'
columns: ['created_by']
@@ -895,6 +1354,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'photos_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'photos_updated_by_fkey'
columns: ['updated_by']
@@ -934,6 +1400,13 @@ export interface Database {
sense_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sense_photos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sense_photos_created_by_fkey'
columns: ['created_by']
@@ -987,6 +1460,13 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sense_videos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sense_videos_created_by_fkey'
columns: ['created_by']
@@ -1109,6 +1589,13 @@ export interface Database {
sentence_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'senses_in_sentences_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'senses_in_sentences_created_by_fkey'
columns: ['created_by']
@@ -1162,6 +1649,13 @@ export interface Database {
sentence_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentence_photos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sentence_photos_created_by_fkey'
columns: ['created_by']
@@ -1215,6 +1709,13 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentence_videos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sentence_videos_created_by_fkey'
columns: ['created_by']
@@ -1290,6 +1791,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'sentences_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sentences_created_by_fkey'
columns: ['created_by']
@@ -1332,6 +1840,13 @@ export interface Database {
referencedRelation: 'texts'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'sentences_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'sentences_updated_by_fkey'
columns: ['updated_by']
@@ -1392,6 +1907,13 @@ export interface Database {
user_id?: string | null
}
Relationships: [
+ {
+ foreignKeyName: 'speakers_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'speakers_created_by_fkey'
columns: ['created_by']
@@ -1427,6 +1949,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'speakers_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'speakers_updated_by_fkey'
columns: ['updated_by']
@@ -1441,6 +1970,13 @@ export interface Database {
referencedRelation: 'users'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'speakers_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'speakers_user_id_fkey'
columns: ['user_id']
@@ -1492,6 +2028,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'tags_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'tags_created_by_fkey'
columns: ['created_by']
@@ -1527,6 +2070,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'tags_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'tags_updated_by_fkey'
columns: ['updated_by']
@@ -1578,6 +2128,13 @@ export interface Database {
updated_by?: string
}
Relationships: [
+ {
+ foreignKeyName: 'texts_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'texts_created_by_fkey'
columns: ['created_by']
@@ -1613,6 +2170,13 @@ export interface Database {
referencedRelation: 'materialized_dictionaries_view'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'texts_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'texts_updated_by_fkey'
columns: ['updated_by']
@@ -1629,6 +2193,52 @@ export interface Database {
},
]
}
+ user_data: {
+ Row: {
+ id: string
+ terms_agreement: string | null
+ unsubscribed_from_emails: string | null
+ updated_at: string
+ welcome_email_sent: string | null
+ }
+ Insert: {
+ id: string
+ terms_agreement?: string | null
+ unsubscribed_from_emails?: string | null
+ updated_at?: string
+ welcome_email_sent?: string | null
+ }
+ Update: {
+ id?: string
+ terms_agreement?: string | null
+ unsubscribed_from_emails?: string | null
+ updated_at?: string
+ welcome_email_sent?: string | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'user_data_id_fkey'
+ columns: ['id']
+ isOneToOne: true
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ ]
+ }
video_speakers: {
Row: {
created_at: string
@@ -1652,6 +2262,13 @@ export interface Database {
video_id?: string
}
Relationships: [
+ {
+ foreignKeyName: 'video_speakers_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'video_speakers_created_by_fkey'
columns: ['created_by']
@@ -1740,6 +2357,13 @@ export interface Database {
videographer?: string | null
}
Relationships: [
+ {
+ foreignKeyName: 'videos_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'videos_created_by_fkey'
columns: ['created_by']
@@ -1782,6 +2406,13 @@ export interface Database {
referencedRelation: 'texts'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'videos_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'videos_updated_by_fkey'
columns: ['updated_by']
@@ -1834,6 +2465,13 @@ export interface Database {
referencedRelation: 'users'
referencedColumns: ['id']
},
+ {
+ foreignKeyName: 'dictionaries_created_by_fkey'
+ columns: ['created_by']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
{
foreignKeyName: 'dictionaries_created_by_fkey'
columns: ['created_by']
@@ -1852,6 +2490,66 @@ export interface Database {
foreignKeyName: 'dictionaries_updated_by_fkey'
columns: ['updated_by']
isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionaries_updated_by_fkey'
+ columns: ['updated_by']
+ isOneToOne: false
+ referencedRelation: 'user_emails'
+ referencedColumns: ['id']
+ },
+ ]
+ }
+ dictionary_roles_with_profiles: {
+ Row: {
+ avatar_url: string | null
+ dictionary_id: string | null
+ full_name: string | null
+ role: Database['public']['Enums']['role_enum'] | null
+ user_id: string | null
+ }
+ Relationships: [
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_dictionary_id_fkey'
+ columns: ['dictionary_id']
+ isOneToOne: false
+ referencedRelation: 'materialized_dictionaries_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'users'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
+ referencedRelation: 'profiles_view'
+ referencedColumns: ['id']
+ },
+ {
+ foreignKeyName: 'dictionary_roles_user_id_fkey'
+ columns: ['user_id']
+ isOneToOne: false
referencedRelation: 'user_emails'
referencedColumns: ['id']
},
@@ -1904,6 +2602,27 @@ export interface Database {
}
Relationships: []
}
+ profiles_view: {
+ Row: {
+ avatar_url: string | null
+ email: string | null
+ full_name: string | null
+ id: string | null
+ }
+ Insert: {
+ avatar_url?: never
+ email?: string | null
+ full_name?: never
+ id?: string | null
+ }
+ Update: {
+ avatar_url?: never
+ email?: string | null
+ full_name?: never
+ id?: string | null
+ }
+ Relationships: []
+ }
speakers_view: {
Row: {
birthplace: string | null
@@ -2065,6 +2784,30 @@ export interface Database {
tag_ids: Json
}[]
}
+ get_my_claim: {
+ Args: {
+ claim: string
+ }
+ Returns: Json
+ }
+ is_admin: {
+ Args: Record
+ Returns: boolean
+ }
+ users_with_dictionary_roles: {
+ Args: Record
+ Returns: {
+ id: string
+ email: string
+ full_name: string
+ avatar_url: string
+ last_sign_in_at: string
+ created_at: string
+ unsubscribed_from_emails: string
+ terms_agreement: string
+ dictionary_roles: Json
+ }[]
+ }
}
Enums: {
certainty: 'yes' | 'no' | 'unknown'
@@ -2094,6 +2837,8 @@ export interface Database {
| 'definition'
entry_tables: 'senses'
gender: 'm' | 'f' | 'o'
+ role_enum: 'manager' | 'contributor'
+ status_enum: 'queued' | 'sent' | 'claimed' | 'cancelled'
}
CompositeTypes: {
[_ in never]: never
diff --git a/packages/types/supabase/users.types.ts b/packages/types/supabase/users.types.ts
new file mode 100644
index 000000000..18a306104
--- /dev/null
+++ b/packages/types/supabase/users.types.ts
@@ -0,0 +1,4 @@
+import type { Database, Tables } from './combined.types'
+
+export type DictionaryRolesWithoutUser = Omit, 'user_id'>
+export type UserWithDictionaryRoles = Database['public']['Functions']['users_with_dictionary_roles']['Returns'][0]
diff --git a/packages/types/user.interface.ts b/packages/types/user.interface.ts
index e6af4ad96..858aabea5 100644
--- a/packages/types/user.interface.ts
+++ b/packages/types/user.interface.ts
@@ -12,7 +12,7 @@ export interface IUser extends IBaseUser {
export interface IRoles {
// editor?: boolean; // can edit and delete any content
- admin?: number // 1 controls content; 2 controls user roles also (both can turn off admin role to view as normal user until page refresh)
+ admin?: number // 1 controls content; 2 controls user roles also
}
export interface GoogleAuthUserMetaData {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 90d8ae8e4..f622fecfe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -119,6 +119,9 @@ importers:
'@aws-sdk/client-s3':
specifier: ^3.693.0
version: 3.731.1
+ '@aws-sdk/client-ses':
+ specifier: ^3.750.0
+ version: 3.750.0
'@aws-sdk/s3-request-presigner':
specifier: ^3.693.0
version: 3.731.1
@@ -179,6 +182,9 @@ importers:
'@types/geojson':
specifier: ^7946.0.10
version: 7946.0.10
+ '@types/google-one-tap':
+ specifier: ^1.2.6
+ version: 1.2.6
'@types/mapbox-gl':
specifier: ^2.7.6
version: 2.7.6
@@ -218,12 +224,6 @@ importers:
file-saver:
specifier: ^2.0.5
version: 2.0.5
- firebase:
- specifier: ^10.9.0
- version: 10.9.0
- firebase-admin:
- specifier: ^12.7.0
- version: 12.7.0(encoding@0.1.13)
idb-keyval:
specifier: ^6.2.1
version: 6.2.1
@@ -257,9 +257,6 @@ importers:
svelte-pieces:
specifier: 2.0.0-next.16
version: 2.0.0-next.16(svelte@4.2.12)
- sveltefirets:
- specifier: 0.0.42
- version: 0.0.42(firebase@10.9.0)(svelte@4.2.12)
topojson-client:
specifier: ^3.1.0
version: 3.1.0
@@ -407,6 +404,10 @@ packages:
engines: {node: '>=18.0.0'}
deprecated: Please upgrade to >=3.735.0, which contains fixes for checksum validation in S3 getObject calls. Details https://github.com/aws/aws-sdk-js-v3/issues/6827
+ '@aws-sdk/client-ses@3.750.0':
+ resolution: {integrity: sha512-0apX2PEzT/09XiO42jNHjkszz/k2RLcIiaLbl1ngcKY1lWzMzIiGIqXw7Emei8iye2o6EsWuBG1p3k30iSyjhg==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/client-sso-oidc@3.679.0':
resolution: {integrity: sha512-/dBYWcCwbA/id4sFCIVZvf0UsvzHCC68SryxeNQk/PDkY9N4n5yRcMUkZDaEyQCjowc3kY4JOXp2AdUP037nhA==}
engines: {node: '>=16.0.0'}
@@ -421,6 +422,10 @@ packages:
resolution: {integrity: sha512-O4C/UYGgqMsBg21MMApFdgyh8BX568hQhbdoNFmRVTBoSnCZ3w+H4a1wBPX4Gyl0NX+ab6Xxo9rId8HiyPXJ0A==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/client-sso@3.750.0':
+ resolution: {integrity: sha512-y0Rx6pTQXw0E61CaptpZF65qNggjqOgymq/RYZU5vWba5DGQ+iqGt8Yq8s+jfBoBBNXshxq8l8Dl5Uq/JTY1wg==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/client-sts@3.679.0':
resolution: {integrity: sha512-3CvrT8w1RjFu1g8vKA5Azfr5V83r2/b68Ock43WE003Bq/5Y38mwmYX7vk0fPHzC3qejt4YMAWk/C3fSKOy25g==}
engines: {node: '>=16.0.0'}
@@ -433,6 +438,10 @@ packages:
resolution: {integrity: sha512-ithBN1VWASkvAIlozJmenqDvNnFddr/SZXAs58+jCnBHgy3tXLHABZGVNCjetZkHRqNdXEO1kirnoxaFeXMeDA==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/core@3.750.0':
+ resolution: {integrity: sha512-bZ5K7N5L4+Pa2epbVpUQqd1XLG2uU8BGs/Sd+2nbgTf+lNQJyIxAg/Qsrjz9MzmY8zzQIeRQEkNmR6yVAfCmmQ==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-env@3.679.0':
resolution: {integrity: sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA==}
engines: {node: '>=16.0.0'}
@@ -441,6 +450,10 @@ packages:
resolution: {integrity: sha512-h0WWZg4QMLgFVyIvQrC43zpVqsUWg1mPM1clpogP43B8+wEhDEQ4qWRzvFs3dQ4cqx/FLyDUZZF4cqgd94z7kw==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-env@3.750.0':
+ resolution: {integrity: sha512-In6bsG0p/P31HcH4DBRKBbcDS/3SHvEPjfXV8ODPWZO/l3/p7IRoYBdQ07C9R+VMZU2D0+/Sc/DWK/TUNDk1+Q==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-http@3.679.0':
resolution: {integrity: sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw==}
engines: {node: '>=16.0.0'}
@@ -449,6 +462,10 @@ packages:
resolution: {integrity: sha512-iRtrjtcYaWgbvtu2cvDhIsPWXZGvhy1Hgks4682MEBNTc9AUwlfvDrYz2EEnTtJJyrbOdEHVrYrzqD8qPyVLCg==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-http@3.750.0':
+ resolution: {integrity: sha512-wFB9qqfa20AB0dElsQz5ZlZT5o+a+XzpEpmg0erylmGYqEOvh8NQWfDUVpRmQuGq9VbvW/8cIbxPoNqEbPtuWQ==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-ini@3.679.0':
resolution: {integrity: sha512-Rg7t8RwUzKcumpipG4neZqaeJ6DF+Bco1+FHn5BZB68jpvwvjBjcQUuWkxj18B6ctYHr1fkunnzeKEn/+vy7+w==}
engines: {node: '>=16.0.0'}
@@ -459,6 +476,10 @@ packages:
resolution: {integrity: sha512-0M0ejuqW8iHNcTH2ZXSY9m+I7Y06qVkj6k3vfQU9XaB//mTUCxxfGfqWAtgfr7Yi73egABTcPc0jyPdcvSW4Kw==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-ini@3.750.0':
+ resolution: {integrity: sha512-2YIZmyEr5RUd3uxXpxOLD9G67Bibm4I/65M6vKFP17jVMUT+R1nL7mKqmhEVO2p+BoeV+bwMyJ/jpTYG368PCg==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-node@3.679.0':
resolution: {integrity: sha512-E3lBtaqCte8tWs6Rkssc8sLzvGoJ10TLGvpkijOlz43wPd6xCRh1YLwg6zolf9fVFtEyUs/GsgymiASOyxhFtw==}
engines: {node: '>=16.0.0'}
@@ -467,6 +488,10 @@ packages:
resolution: {integrity: sha512-5c0ZiagMTPmWilXNffeXJCLoCEz97jilHr3QJWwf2GaTay4tzN+Ld71rpdfEenzUR7fuxEWFfVlwQbFOzFNYHg==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-node@3.750.0':
+ resolution: {integrity: sha512-THWHHAceLwsOiowPEmKyhWVDlEUxH07GHSw5AQFDvNQtGKOQl0HSIFO1mKObT2Q2Vqzji9Bq8H58SO5BFtNPRw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-process@3.679.0':
resolution: {integrity: sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ==}
engines: {node: '>=16.0.0'}
@@ -475,6 +500,10 @@ packages:
resolution: {integrity: sha512-6yNMY6q3xHLbs2f2+C6GhvMrjTgtFBiPJJqKaPLsTIhlTRvh4sK8pGm3ITcma0jOxtPDIuoPfBAV8N8XVMBlZg==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-process@3.750.0':
+ resolution: {integrity: sha512-Q78SCH1n0m7tpu36sJwfrUSxI8l611OyysjQeMiIOliVfZICEoHcLHLcLkiR+tnIpZ3rk7d2EQ6R1jwlXnalMQ==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-sso@3.679.0':
resolution: {integrity: sha512-SAtWonhi9asxn0ukEbcE81jkyanKgqpsrtskvYPpO9Z9KOednM4Cqt6h1bfcS9zaHjN2zu815Gv8O7WiV+F/DQ==}
engines: {node: '>=16.0.0'}
@@ -483,6 +512,10 @@ packages:
resolution: {integrity: sha512-p1tp+rMUf5YNQLr8rVRmDgNtKGYLL0KCdq3K2hwwvFnx9MjReF1sA4lfm3xWsxBQM+j3QN9AvMQqBzDJ+NOSdw==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-sso@3.750.0':
+ resolution: {integrity: sha512-FGYrDjXN/FOQVi/t8fHSv8zCk+NEvtFnuc4cZUj5OIbM4vrfFc5VaPyn41Uza3iv6Qq9rZg0QOwWnqK8lNrqUw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/credential-provider-web-identity@3.679.0':
resolution: {integrity: sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung==}
engines: {node: '>=16.0.0'}
@@ -493,6 +526,10 @@ packages:
resolution: {integrity: sha512-+ynAvEGWDR5ZJFxgpwwzhvlQ3WQ7BleWXU6JwpIw3yFrD4eZEn85b8DZC1aEz7C9kb1HSV6B3gpqHqlyS6wj8g==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/credential-provider-web-identity@3.750.0':
+ resolution: {integrity: sha512-Nz8zs3YJ+GOTSrq+LyzbbC1Ffpt7pK38gcOyNZv76pP5MswKTUKNYBJehqwa+i7FcFQHsCk3TdhR8MT1ZR23uA==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/middleware-bucket-endpoint@3.679.0':
resolution: {integrity: sha512-5EpiPhhGgnF+uJR4DzWUk6Lx3pOn9oM6JGXxeHsiynfoBfq7vHMleq+uABHHSQS+y7XzbyZ7x8tXNQlliMwOsg==}
engines: {node: '>=16.0.0'}
@@ -526,6 +563,10 @@ packages:
resolution: {integrity: sha512-ndAJsm5uWPPJRZowLKpB1zuL17qWlWVtCJP4I/ynBkq1PU1DijDXBul2UZaG6Mpvsgms1NXo/h9noHuK7T3v8w==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-host-header@3.734.0':
+ resolution: {integrity: sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/middleware-location-constraint@3.679.0':
resolution: {integrity: sha512-SA1C1D3XgoKTGxyNsOqd016ONpk46xJLWDgJUd00Zb21Ox5wYCoY6aDRKiaMRW+1VfCJdezs1Do3XLyIU9KxyA==}
engines: {node: '>=16.0.0'}
@@ -542,6 +583,10 @@ packages:
resolution: {integrity: sha512-IIZrOdjbY2vKzPJPrwE7FoFQCIPEL6UqURi8LEaiVyCag4p2fvaTN5pgKuQtGC2+iYd/HHcGT4qn2bAqF5Jmmw==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-logger@3.734.0':
+ resolution: {integrity: sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/middleware-recursion-detection@3.679.0':
resolution: {integrity: sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ==}
engines: {node: '>=16.0.0'}
@@ -550,6 +595,10 @@ packages:
resolution: {integrity: sha512-y6FLASB1iKWuR5tUipMyo77bt0lEl3OnCrrd2xw/H24avq1HhJjjPR0HHhJE6QKJzF/FYXeV88tcyPSMe32VDw==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-recursion-detection@3.734.0':
+ resolution: {integrity: sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/middleware-sdk-s3@3.679.0':
resolution: {integrity: sha512-4zcT193F7RkEfqlS6ZdwyNQ0UUp9s66msNXgItugasTbjf7oqfWDas7N+BG8ADB/Ql3wvRUh9I+zdrVkxxw3BQ==}
engines: {node: '>=16.0.0'}
@@ -574,10 +623,18 @@ packages:
resolution: {integrity: sha512-Ngr2Gz0aec/uduoKaO3srN52SYkEHndYtFzkK/gDUyQwQzi4ha2eIisxPiuHEX6RvXT31V9ouqn/YtVkt0R76A==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/middleware-user-agent@3.750.0':
+ resolution: {integrity: sha512-YYcslDsP5+2NZoN3UwuhZGkhAHPSli7HlJHBafBrvjGV/I9f8FuOO1d1ebxGdEP4HyRXUGyh+7Ur4q+Psk0ryw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/nested-clients@3.731.1':
resolution: {integrity: sha512-/L8iVrulnXZl+kgmTn+oxRxNnhcSIbf+r12C06vGUq60w0YMidLvxJZN7vt8H9SnCAGCHqud2MS7ExCEvhc0gA==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/nested-clients@3.750.0':
+ resolution: {integrity: sha512-OH68BRF0rt9nDloq4zsfeHI0G21lj11a66qosaljtEP66PWm7tQ06feKbFkXHT5E1K3QhJW3nVyK8v2fEBY5fg==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/region-config-resolver@3.679.0':
resolution: {integrity: sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw==}
engines: {node: '>=16.0.0'}
@@ -586,6 +643,10 @@ packages:
resolution: {integrity: sha512-XlDpRNkDVHF59f07JmkuAidEv//m3hT6/JL85h0l3+zrpaRWhf8n8lVUyAPNq35ZujK8AcorYM+93u7hdWsliQ==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/region-config-resolver@3.734.0':
+ resolution: {integrity: sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/s3-request-presigner@3.731.1':
resolution: {integrity: sha512-GdG0pXkcTgBpenouB834FoCHyLaivV2rGQn7OEQBiT8SBaTxSackZ6tGlJQAlzZQkiQfE/NePUJU7DczJZZvrg==}
engines: {node: '>=18.0.0'}
@@ -608,6 +669,10 @@ packages:
resolution: {integrity: sha512-t34GOPwBZsX7zGHjiTXmMHGY3kHM7fLiQ60Jqk0On9P0ASHTDE5U75RgCXboE3u+qEv9wyKyaqMNyMWj9qQlFg==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/token-providers@3.750.0':
+ resolution: {integrity: sha512-X/KzqZw41iWolwNdc8e3RMcNSMR364viHv78u6AefXOO5eRM40c4/LuST1jDzq35/LpnqRhL7/MuixOetw+sFw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/types@3.679.0':
resolution: {integrity: sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==}
engines: {node: '>=16.0.0'}
@@ -616,6 +681,10 @@ packages:
resolution: {integrity: sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/types@3.734.0':
+ resolution: {integrity: sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/util-arn-parser@3.679.0':
resolution: {integrity: sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg==}
engines: {node: '>=16.0.0'}
@@ -632,6 +701,10 @@ packages:
resolution: {integrity: sha512-riztxTAfncFS9yQWcBJffGgOgLoKSa63ph+rxWJxKl6BHAmWEvHICj1qDcVmnWfIcvJ5cClclY75l9qKaUH7rQ==}
engines: {node: '>=18.0.0'}
+ '@aws-sdk/util-endpoints@3.743.0':
+ resolution: {integrity: sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==}
+ engines: {node: '>=18.0.0'}
+
'@aws-sdk/util-format-url@3.731.0':
resolution: {integrity: sha512-wZHObjnYmiz8wFlUQ4/5dHsT7k0at+GvZM02LgvshcRJLnFjYdrzjelMKuNynd/NNK3gLgTsFTGuIgPpz9r4rA==}
engines: {node: '>=18.0.0'}
@@ -646,6 +719,9 @@ packages:
'@aws-sdk/util-user-agent-browser@3.731.0':
resolution: {integrity: sha512-EnYXxTkCNCjTTBjW/pelRPv4Thsi9jepoB6qQjPMA9/ixrZ71BhhQecz9kgqzZLR9BPCwb6hgJ/Yd702jqJ4aQ==}
+ '@aws-sdk/util-user-agent-browser@3.734.0':
+ resolution: {integrity: sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==}
+
'@aws-sdk/util-user-agent-node@3.679.0':
resolution: {integrity: sha512-Bw4uXZ+NU5ed6TNfo4tBbhBSW+2eQxXYjYBGl5gLUNUpg2pDFToQAP6rXBFiwcG52V2ny5oLGiD82SoYuYkAVg==}
engines: {node: '>=16.0.0'}
@@ -664,6 +740,15 @@ packages:
aws-crt:
optional: true
+ '@aws-sdk/util-user-agent-node@3.750.0':
+ resolution: {integrity: sha512-84HJj9G9zbrHX2opLk9eHfDceB+UIHVrmflMzWHpsmo9fDuro/flIBqaVDlE021Osj6qIM0SJJcnL6s23j7JEw==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ aws-crt: '>=1.0.0'
+ peerDependenciesMeta:
+ aws-crt:
+ optional: true
+
'@aws-sdk/xml-builder@3.679.0':
resolution: {integrity: sha512-nPmhVZb39ty5bcQ7mAwtjezBcsBqTYZ9A2D9v/lE92KCLdu5RhSkPH7O71ZqbZx1mUSg9fAOxHPiG79U5VlpLQ==}
engines: {node: '>=16.0.0'}
@@ -2026,6 +2111,10 @@ packages:
resolution: {integrity: sha512-hhUZlBWYuh9t6ycAcN90XOyG76C1AzwxZZgaCVPMYpWqqk9uMFo7HGG5Zu2cEhCJn7DdOi5krBmlibWWWPgdsw==}
engines: {node: '>=18.0.0'}
+ '@smithy/core@3.1.4':
+ resolution: {integrity: sha512-wFExFGK+7r2wYriOqe7RRIBNpvxwiS95ih09+GSLRBdoyK/O1uZA7K7pKesj5CBvwJuSBeXwLyR88WwIAY+DGA==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/credential-provider-imds@3.2.5':
resolution: {integrity: sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==}
engines: {node: '>=16.0.0'}
@@ -2148,6 +2237,10 @@ packages:
resolution: {integrity: sha512-Z9m67CXizGpj8CF/AW/7uHqYNh1VXXOn9Ap54fenWsCa0HnT4cJuE61zqG3cBkTZJDCy0wHJphilI41co/PE5g==}
engines: {node: '>=18.0.0'}
+ '@smithy/middleware-endpoint@4.0.5':
+ resolution: {integrity: sha512-cPzGZV7qStHwboFrm6GfrzQE+YDiCzWcTh4+7wKrP/ZQ4gkw+r7qDjV8GjM4N0UYsuUyLfpzLGg5hxsYTU11WA==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/middleware-retry@3.0.25':
resolution: {integrity: sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==}
engines: {node: '>=16.0.0'}
@@ -2156,6 +2249,10 @@ packages:
resolution: {integrity: sha512-TiKwwQTwUDeDtwWW8UWURTqu7s6F3wN2pmziLU215u7bqpVT9Mk2oEvURjpRLA+5XeQhM68R5BpAGzVtomsqgA==}
engines: {node: '>=18.0.0'}
+ '@smithy/middleware-retry@4.0.6':
+ resolution: {integrity: sha512-s8QzuOQnbdvRymD9Gt9c9zMq10wUQAHQ3z72uirrBHCwZcLTrL5iCOuVTMdka2IXOYhQE890WD5t6G24+F+Qcg==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/middleware-serde@3.0.8':
resolution: {integrity: sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==}
engines: {node: '>=16.0.0'}
@@ -2164,6 +2261,10 @@ packages:
resolution: {integrity: sha512-Fh0E2SOF+S+P1+CsgKyiBInAt3o2b6Qk7YOp2W0Qx2XnfTdfMuSDKUEcnrtpxCzgKJnqXeLUZYqtThaP0VGqtA==}
engines: {node: '>=18.0.0'}
+ '@smithy/middleware-serde@4.0.2':
+ resolution: {integrity: sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/middleware-stack@3.0.8':
resolution: {integrity: sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==}
engines: {node: '>=16.0.0'}
@@ -2252,6 +2353,10 @@ packages:
resolution: {integrity: sha512-0yApeHWBqocelHGK22UivZyShNxFbDNrgREBllGh5Ws0D0rg/yId/CJfeoKKpjbfY2ju8j6WgDUGZHYQmINZ5w==}
engines: {node: '>=18.0.0'}
+ '@smithy/smithy-client@4.1.5':
+ resolution: {integrity: sha512-DMXYoYeL4QkElr216n1yodTFeATbfb4jwYM9gKn71Rw/FNA1/Sm36tkTSCsZEs7mgpG3OINmkxL9vgVFzyGPaw==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/types@3.6.0':
resolution: {integrity: sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==}
engines: {node: '>=16.0.0'}
@@ -2318,6 +2423,10 @@ packages:
resolution: {integrity: sha512-7c5SF1fVK0EOs+2EOf72/qF199zwJflU1d02AevwKbAUPUZyE9RUZiyJxeUmhVxfKDWdUKaaVojNiaDQgnHL9g==}
engines: {node: '>=18.0.0'}
+ '@smithy/util-defaults-mode-browser@4.0.6':
+ resolution: {integrity: sha512-N8+VCt+piupH1A7DgSVDNrVHqRLz8r6DvBkpS7EWHiIxsUk4jqGuQLjqC/gnCzmwGkVBdNruHoYAzzaSQ8e80w==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/util-defaults-mode-node@3.0.25':
resolution: {integrity: sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==}
engines: {node: '>= 10.0.0'}
@@ -2326,6 +2435,10 @@ packages:
resolution: {integrity: sha512-CVnD42qYD3JKgDlImZ9+On+MqJHzq9uJgPbMdeBE8c2x8VJ2kf2R3XO/yVFx+30ts5lD/GlL0eFIShY3x9ROgQ==}
engines: {node: '>=18.0.0'}
+ '@smithy/util-defaults-mode-node@4.0.6':
+ resolution: {integrity: sha512-9zhx1shd1VwSSVvLZB8CM3qQ3RPD3le7A3h/UPuyh/PC7g4OaWDi2xUNzamsVoSmCGtmUBONl56lM2EU6LcH7A==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/util-endpoints@2.1.4':
resolution: {integrity: sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==}
engines: {node: '>=16.0.0'}
@@ -2366,6 +2479,10 @@ packages:
resolution: {integrity: sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==}
engines: {node: '>=18.0.0'}
+ '@smithy/util-stream@4.1.1':
+ resolution: {integrity: sha512-+Xvh8nhy0Wjv1y71rBVyV3eJU3356XsFQNI8dEZVNrQju7Eib8G31GWtO+zMa9kTCGd41Mflu+ZKfmQL/o2XzQ==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/util-uri-escape@3.0.0':
resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==}
engines: {node: '>=16.0.0'}
@@ -2989,6 +3106,9 @@ packages:
'@types/geojson@7946.0.8':
resolution: {integrity: sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==}
+ '@types/google-one-tap@1.2.6':
+ resolution: {integrity: sha512-REmJsXVHvKb/sgI8DF+7IesMbDbcsEokHBqxU01ENZ8d98UPWdRLhUCtxEm9bhNFFg6PJGy7PNFdvovp0hK3jA==}
+
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
@@ -6345,9 +6465,6 @@ packages:
tslib@2.6.1:
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
- tslib@2.6.2:
- resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
-
tslib@2.7.0:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
@@ -6818,14 +6935,14 @@ snapshots:
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.679.0
- tslib: 2.7.0
+ '@aws-sdk/types': 3.731.0
+ tslib: 2.8.1
'@aws-crypto/crc32c@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.679.0
- tslib: 2.7.0
+ '@aws-sdk/types': 3.731.0
+ tslib: 2.8.1
'@aws-crypto/sha1-browser@5.2.0':
dependencies:
@@ -6841,7 +6958,7 @@ snapshots:
'@aws-crypto/sha256-js': 5.2.0
'@aws-crypto/supports-web-crypto': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.731.0
+ '@aws-sdk/types': 3.734.0
'@aws-sdk/util-locate-window': 3.679.0
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
@@ -6849,7 +6966,7 @@ snapshots:
'@aws-crypto/sha256-js@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
- '@aws-sdk/types': 3.731.0
+ '@aws-sdk/types': 3.734.0
tslib: 2.8.1
'@aws-crypto/supports-web-crypto@5.2.0':
@@ -6986,6 +7103,51 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/client-ses@3.750.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/credential-provider-node': 3.750.0
+ '@aws-sdk/middleware-host-header': 3.734.0
+ '@aws-sdk/middleware-logger': 3.734.0
+ '@aws-sdk/middleware-recursion-detection': 3.734.0
+ '@aws-sdk/middleware-user-agent': 3.750.0
+ '@aws-sdk/region-config-resolver': 3.734.0
+ '@aws-sdk/types': 3.734.0
+ '@aws-sdk/util-endpoints': 3.743.0
+ '@aws-sdk/util-user-agent-browser': 3.734.0
+ '@aws-sdk/util-user-agent-node': 3.750.0
+ '@smithy/config-resolver': 4.0.1
+ '@smithy/core': 3.1.4
+ '@smithy/fetch-http-handler': 5.0.1
+ '@smithy/hash-node': 4.0.1
+ '@smithy/invalid-dependency': 4.0.1
+ '@smithy/middleware-content-length': 4.0.1
+ '@smithy/middleware-endpoint': 4.0.5
+ '@smithy/middleware-retry': 4.0.6
+ '@smithy/middleware-serde': 4.0.2
+ '@smithy/middleware-stack': 4.0.1
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/node-http-handler': 4.0.2
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ '@smithy/url-parser': 4.0.1
+ '@smithy/util-base64': 4.0.0
+ '@smithy/util-body-length-browser': 4.0.0
+ '@smithy/util-body-length-node': 4.0.0
+ '@smithy/util-defaults-mode-browser': 4.0.6
+ '@smithy/util-defaults-mode-node': 4.0.6
+ '@smithy/util-endpoints': 3.0.1
+ '@smithy/util-middleware': 4.0.1
+ '@smithy/util-retry': 4.0.1
+ '@smithy/util-utf8': 4.0.0
+ '@smithy/util-waiter': 4.0.2
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/client-sso-oidc@3.679.0(@aws-sdk/client-sts@3.679.0)':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
@@ -7027,7 +7189,7 @@ snapshots:
'@smithy/util-middleware': 3.0.8
'@smithy/util-retry': 3.0.8
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -7117,6 +7279,49 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/client-sso@3.750.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/middleware-host-header': 3.734.0
+ '@aws-sdk/middleware-logger': 3.734.0
+ '@aws-sdk/middleware-recursion-detection': 3.734.0
+ '@aws-sdk/middleware-user-agent': 3.750.0
+ '@aws-sdk/region-config-resolver': 3.734.0
+ '@aws-sdk/types': 3.734.0
+ '@aws-sdk/util-endpoints': 3.743.0
+ '@aws-sdk/util-user-agent-browser': 3.734.0
+ '@aws-sdk/util-user-agent-node': 3.750.0
+ '@smithy/config-resolver': 4.0.1
+ '@smithy/core': 3.1.4
+ '@smithy/fetch-http-handler': 5.0.1
+ '@smithy/hash-node': 4.0.1
+ '@smithy/invalid-dependency': 4.0.1
+ '@smithy/middleware-content-length': 4.0.1
+ '@smithy/middleware-endpoint': 4.0.5
+ '@smithy/middleware-retry': 4.0.6
+ '@smithy/middleware-serde': 4.0.2
+ '@smithy/middleware-stack': 4.0.1
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/node-http-handler': 4.0.2
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ '@smithy/url-parser': 4.0.1
+ '@smithy/util-base64': 4.0.0
+ '@smithy/util-body-length-browser': 4.0.0
+ '@smithy/util-body-length-node': 4.0.0
+ '@smithy/util-defaults-mode-browser': 4.0.6
+ '@smithy/util-defaults-mode-node': 4.0.6
+ '@smithy/util-endpoints': 3.0.1
+ '@smithy/util-middleware': 4.0.1
+ '@smithy/util-retry': 4.0.1
+ '@smithy/util-utf8': 4.0.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/client-sts@3.679.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
@@ -7158,7 +7363,7 @@ snapshots:
'@smithy/util-middleware': 3.0.8
'@smithy/util-retry': 3.0.8
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -7174,7 +7379,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-middleware': 3.0.8
fast-xml-parser: 4.4.1
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/core@3.731.0':
dependencies:
@@ -7190,13 +7395,27 @@ snapshots:
fast-xml-parser: 4.4.1
tslib: 2.8.1
+ '@aws-sdk/core@3.750.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/core': 3.1.4
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/property-provider': 4.0.1
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/signature-v4': 5.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ '@smithy/util-middleware': 4.0.1
+ fast-xml-parser: 4.4.1
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-env@3.679.0':
dependencies:
'@aws-sdk/core': 3.679.0
'@aws-sdk/types': 3.679.0
'@smithy/property-provider': 3.1.8
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/credential-provider-env@3.731.0':
dependencies:
@@ -7206,6 +7425,14 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/credential-provider-env@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/property-provider': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-http@3.679.0':
dependencies:
'@aws-sdk/core': 3.679.0
@@ -7217,7 +7444,7 @@ snapshots:
'@smithy/smithy-client': 3.4.2
'@smithy/types': 3.6.0
'@smithy/util-stream': 3.2.1
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/credential-provider-http@3.731.0':
dependencies:
@@ -7232,6 +7459,19 @@ snapshots:
'@smithy/util-stream': 4.0.2
tslib: 2.8.1
+ '@aws-sdk/credential-provider-http@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/fetch-http-handler': 5.0.1
+ '@smithy/node-http-handler': 4.0.2
+ '@smithy/property-provider': 4.0.1
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ '@smithy/util-stream': 4.1.1
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-ini@3.679.0(@aws-sdk/client-sso-oidc@3.679.0(@aws-sdk/client-sts@3.679.0))(@aws-sdk/client-sts@3.679.0)':
dependencies:
'@aws-sdk/client-sts': 3.679.0
@@ -7246,7 +7486,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/shared-ini-file-loader': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/client-sso-oidc'
- aws-crt
@@ -7269,6 +7509,24 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/credential-provider-ini@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/credential-provider-env': 3.750.0
+ '@aws-sdk/credential-provider-http': 3.750.0
+ '@aws-sdk/credential-provider-process': 3.750.0
+ '@aws-sdk/credential-provider-sso': 3.750.0
+ '@aws-sdk/credential-provider-web-identity': 3.750.0
+ '@aws-sdk/nested-clients': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/credential-provider-imds': 4.0.1
+ '@smithy/property-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/credential-provider-node@3.679.0(@aws-sdk/client-sso-oidc@3.679.0(@aws-sdk/client-sts@3.679.0))(@aws-sdk/client-sts@3.679.0)':
dependencies:
'@aws-sdk/credential-provider-env': 3.679.0
@@ -7282,7 +7540,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/shared-ini-file-loader': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/client-sso-oidc'
- '@aws-sdk/client-sts'
@@ -7305,6 +7563,23 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/credential-provider-node@3.750.0':
+ dependencies:
+ '@aws-sdk/credential-provider-env': 3.750.0
+ '@aws-sdk/credential-provider-http': 3.750.0
+ '@aws-sdk/credential-provider-ini': 3.750.0
+ '@aws-sdk/credential-provider-process': 3.750.0
+ '@aws-sdk/credential-provider-sso': 3.750.0
+ '@aws-sdk/credential-provider-web-identity': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/credential-provider-imds': 4.0.1
+ '@smithy/property-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/credential-provider-process@3.679.0':
dependencies:
'@aws-sdk/core': 3.679.0
@@ -7312,7 +7587,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/shared-ini-file-loader': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/credential-provider-process@3.731.0':
dependencies:
@@ -7323,6 +7598,15 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/credential-provider-process@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/property-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-sso@3.679.0(@aws-sdk/client-sso-oidc@3.679.0(@aws-sdk/client-sts@3.679.0))':
dependencies:
'@aws-sdk/client-sso': 3.679.0
@@ -7332,7 +7616,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/shared-ini-file-loader': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
transitivePeerDependencies:
- '@aws-sdk/client-sso-oidc'
- aws-crt
@@ -7350,6 +7634,19 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/credential-provider-sso@3.750.0':
+ dependencies:
+ '@aws-sdk/client-sso': 3.750.0
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/token-providers': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/property-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/credential-provider-web-identity@3.679.0(@aws-sdk/client-sts@3.679.0)':
dependencies:
'@aws-sdk/client-sts': 3.679.0
@@ -7357,7 +7654,7 @@ snapshots:
'@aws-sdk/types': 3.679.0
'@smithy/property-provider': 3.1.8
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/credential-provider-web-identity@3.731.1':
dependencies:
@@ -7370,6 +7667,17 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/credential-provider-web-identity@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/nested-clients': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/property-provider': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/middleware-bucket-endpoint@3.679.0':
dependencies:
'@aws-sdk/types': 3.679.0
@@ -7378,7 +7686,7 @@ snapshots:
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
'@smithy/util-config-provider': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-bucket-endpoint@3.731.0':
dependencies:
@@ -7395,7 +7703,7 @@ snapshots:
'@aws-sdk/types': 3.679.0
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-expect-continue@3.731.0':
dependencies:
@@ -7416,7 +7724,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-middleware': 3.0.8
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-flexible-checksums@3.731.0':
dependencies:
@@ -7439,7 +7747,7 @@ snapshots:
'@aws-sdk/types': 3.679.0
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-host-header@3.731.0':
dependencies:
@@ -7448,11 +7756,18 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/middleware-host-header@3.734.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/middleware-location-constraint@3.679.0':
dependencies:
'@aws-sdk/types': 3.679.0
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-location-constraint@3.731.0':
dependencies:
@@ -7464,7 +7779,7 @@ snapshots:
dependencies:
'@aws-sdk/types': 3.679.0
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-logger@3.731.0':
dependencies:
@@ -7472,12 +7787,18 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/middleware-logger@3.734.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/middleware-recursion-detection@3.679.0':
dependencies:
'@aws-sdk/types': 3.679.0
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-recursion-detection@3.731.0':
dependencies:
@@ -7486,6 +7807,13 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/middleware-recursion-detection@3.734.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/middleware-sdk-s3@3.679.0':
dependencies:
'@aws-sdk/core': 3.679.0
@@ -7501,7 +7829,7 @@ snapshots:
'@smithy/util-middleware': 3.0.8
'@smithy/util-stream': 3.2.1
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-sdk-s3@3.731.0':
dependencies:
@@ -7524,7 +7852,7 @@ snapshots:
dependencies:
'@aws-sdk/types': 3.679.0
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-ssec@3.731.0':
dependencies:
@@ -7540,7 +7868,7 @@ snapshots:
'@smithy/core': 2.5.1
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/middleware-user-agent@3.731.0':
dependencies:
@@ -7552,41 +7880,94 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
- '@aws-sdk/nested-clients@3.731.1':
+ '@aws-sdk/middleware-user-agent@3.750.0':
+ dependencies:
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@aws-sdk/util-endpoints': 3.743.0
+ '@smithy/core': 3.1.4
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
+ '@aws-sdk/nested-clients@3.731.1':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.731.0
+ '@aws-sdk/middleware-host-header': 3.731.0
+ '@aws-sdk/middleware-logger': 3.731.0
+ '@aws-sdk/middleware-recursion-detection': 3.731.0
+ '@aws-sdk/middleware-user-agent': 3.731.0
+ '@aws-sdk/region-config-resolver': 3.731.0
+ '@aws-sdk/types': 3.731.0
+ '@aws-sdk/util-endpoints': 3.731.0
+ '@aws-sdk/util-user-agent-browser': 3.731.0
+ '@aws-sdk/util-user-agent-node': 3.731.0
+ '@smithy/config-resolver': 4.0.1
+ '@smithy/core': 3.1.1
+ '@smithy/fetch-http-handler': 5.0.1
+ '@smithy/hash-node': 4.0.1
+ '@smithy/invalid-dependency': 4.0.1
+ '@smithy/middleware-content-length': 4.0.1
+ '@smithy/middleware-endpoint': 4.0.2
+ '@smithy/middleware-retry': 4.0.3
+ '@smithy/middleware-serde': 4.0.1
+ '@smithy/middleware-stack': 4.0.1
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/node-http-handler': 4.0.2
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/smithy-client': 4.1.2
+ '@smithy/types': 4.1.0
+ '@smithy/url-parser': 4.0.1
+ '@smithy/util-base64': 4.0.0
+ '@smithy/util-body-length-browser': 4.0.0
+ '@smithy/util-body-length-node': 4.0.0
+ '@smithy/util-defaults-mode-browser': 4.0.3
+ '@smithy/util-defaults-mode-node': 4.0.3
+ '@smithy/util-endpoints': 3.0.1
+ '@smithy/util-middleware': 4.0.1
+ '@smithy/util-retry': 4.0.1
+ '@smithy/util-utf8': 4.0.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
+ '@aws-sdk/nested-clients@3.750.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.731.0
- '@aws-sdk/middleware-host-header': 3.731.0
- '@aws-sdk/middleware-logger': 3.731.0
- '@aws-sdk/middleware-recursion-detection': 3.731.0
- '@aws-sdk/middleware-user-agent': 3.731.0
- '@aws-sdk/region-config-resolver': 3.731.0
- '@aws-sdk/types': 3.731.0
- '@aws-sdk/util-endpoints': 3.731.0
- '@aws-sdk/util-user-agent-browser': 3.731.0
- '@aws-sdk/util-user-agent-node': 3.731.0
+ '@aws-sdk/core': 3.750.0
+ '@aws-sdk/middleware-host-header': 3.734.0
+ '@aws-sdk/middleware-logger': 3.734.0
+ '@aws-sdk/middleware-recursion-detection': 3.734.0
+ '@aws-sdk/middleware-user-agent': 3.750.0
+ '@aws-sdk/region-config-resolver': 3.734.0
+ '@aws-sdk/types': 3.734.0
+ '@aws-sdk/util-endpoints': 3.743.0
+ '@aws-sdk/util-user-agent-browser': 3.734.0
+ '@aws-sdk/util-user-agent-node': 3.750.0
'@smithy/config-resolver': 4.0.1
- '@smithy/core': 3.1.1
+ '@smithy/core': 3.1.4
'@smithy/fetch-http-handler': 5.0.1
'@smithy/hash-node': 4.0.1
'@smithy/invalid-dependency': 4.0.1
'@smithy/middleware-content-length': 4.0.1
- '@smithy/middleware-endpoint': 4.0.2
- '@smithy/middleware-retry': 4.0.3
- '@smithy/middleware-serde': 4.0.1
+ '@smithy/middleware-endpoint': 4.0.5
+ '@smithy/middleware-retry': 4.0.6
+ '@smithy/middleware-serde': 4.0.2
'@smithy/middleware-stack': 4.0.1
'@smithy/node-config-provider': 4.0.1
'@smithy/node-http-handler': 4.0.2
'@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.2
+ '@smithy/smithy-client': 4.1.5
'@smithy/types': 4.1.0
'@smithy/url-parser': 4.0.1
'@smithy/util-base64': 4.0.0
'@smithy/util-body-length-browser': 4.0.0
'@smithy/util-body-length-node': 4.0.0
- '@smithy/util-defaults-mode-browser': 4.0.3
- '@smithy/util-defaults-mode-node': 4.0.3
+ '@smithy/util-defaults-mode-browser': 4.0.6
+ '@smithy/util-defaults-mode-node': 4.0.6
'@smithy/util-endpoints': 3.0.1
'@smithy/util-middleware': 4.0.1
'@smithy/util-retry': 4.0.1
@@ -7602,7 +7983,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-config-provider': 3.0.0
'@smithy/util-middleware': 3.0.8
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/region-config-resolver@3.731.0':
dependencies:
@@ -7613,6 +7994,15 @@ snapshots:
'@smithy/util-middleware': 4.0.1
tslib: 2.8.1
+ '@aws-sdk/region-config-resolver@3.734.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/types': 4.1.0
+ '@smithy/util-config-provider': 4.0.0
+ '@smithy/util-middleware': 4.0.1
+ tslib: 2.8.1
+
'@aws-sdk/s3-request-presigner@3.731.1':
dependencies:
'@aws-sdk/signature-v4-multi-region': 3.731.0
@@ -7631,7 +8021,7 @@ snapshots:
'@smithy/protocol-http': 4.1.5
'@smithy/signature-v4': 4.2.1
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/signature-v4-multi-region@3.731.0':
dependencies:
@@ -7662,19 +8052,35 @@ snapshots:
transitivePeerDependencies:
- aws-crt
+ '@aws-sdk/token-providers@3.750.0':
+ dependencies:
+ '@aws-sdk/nested-clients': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/property-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - aws-crt
+
'@aws-sdk/types@3.679.0':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/types@3.731.0':
dependencies:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/types@3.734.0':
+ dependencies:
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/util-arn-parser@3.679.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/util-arn-parser@3.723.0':
dependencies:
@@ -7685,7 +8091,7 @@ snapshots:
'@aws-sdk/types': 3.679.0
'@smithy/types': 3.6.0
'@smithy/util-endpoints': 2.1.4
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/util-endpoints@3.731.0':
dependencies:
@@ -7694,6 +8100,13 @@ snapshots:
'@smithy/util-endpoints': 3.0.1
tslib: 2.8.1
+ '@aws-sdk/util-endpoints@3.743.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/types': 4.1.0
+ '@smithy/util-endpoints': 3.0.1
+ tslib: 2.8.1
+
'@aws-sdk/util-format-url@3.731.0':
dependencies:
'@aws-sdk/types': 3.731.0
@@ -7710,7 +8123,7 @@ snapshots:
'@aws-sdk/types': 3.679.0
'@smithy/types': 3.6.0
bowser: 2.11.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/util-user-agent-browser@3.731.0':
dependencies:
@@ -7719,13 +8132,20 @@ snapshots:
bowser: 2.11.0
tslib: 2.8.1
+ '@aws-sdk/util-user-agent-browser@3.734.0':
+ dependencies:
+ '@aws-sdk/types': 3.734.0
+ '@smithy/types': 4.1.0
+ bowser: 2.11.0
+ tslib: 2.8.1
+
'@aws-sdk/util-user-agent-node@3.679.0':
dependencies:
'@aws-sdk/middleware-user-agent': 3.679.0
'@aws-sdk/types': 3.679.0
'@smithy/node-config-provider': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/util-user-agent-node@3.731.0':
dependencies:
@@ -7735,10 +8155,18 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@aws-sdk/util-user-agent-node@3.750.0':
+ dependencies:
+ '@aws-sdk/middleware-user-agent': 3.750.0
+ '@aws-sdk/types': 3.734.0
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@aws-sdk/xml-builder@3.679.0':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@aws-sdk/xml-builder@3.723.0':
dependencies:
@@ -8460,7 +8888,7 @@ snapshots:
'@firebase/app-compat': 0.2.29
'@firebase/component': 0.6.5
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8473,7 +8901,7 @@ snapshots:
'@firebase/installations': 0.6.5(@firebase/app@0.9.29)
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/app-check-compat@0.3.9(@firebase/app-compat@0.2.29)(@firebase/app@0.9.29)':
dependencies:
@@ -8483,7 +8911,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8499,7 +8927,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/app-compat@0.2.29':
dependencies:
@@ -8507,7 +8935,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/app-types@0.9.0': {}
@@ -8519,7 +8947,7 @@ snapshots:
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
idb: 7.1.1
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/auth-compat@0.5.4(@firebase/app-compat@0.2.29)(@firebase/app-types@0.9.0)(@firebase/app@0.9.29)':
dependencies:
@@ -8528,7 +8956,7 @@ snapshots:
'@firebase/auth-types': 0.12.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.4)
'@firebase/component': 0.6.5
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
undici: 5.28.3
transitivePeerDependencies:
- '@firebase/app'
@@ -8550,7 +8978,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
undici: 5.28.3
'@firebase/component@0.6.5':
@@ -8570,7 +8998,7 @@ snapshots:
'@firebase/database-types': 1.0.1
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/database-compat@1.0.8':
dependencies:
@@ -8599,7 +9027,7 @@ snapshots:
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
faye-websocket: 0.11.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/database@1.0.8':
dependencies:
@@ -8618,7 +9046,7 @@ snapshots:
'@firebase/firestore': 4.5.0(@firebase/app@0.9.29)
'@firebase/firestore-types': 3.0.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.4)
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
- '@firebase/app-types'
@@ -8637,7 +9065,7 @@ snapshots:
'@firebase/webchannel-wrapper': 0.10.5
'@grpc/grpc-js': 1.9.13
'@grpc/proto-loader': 0.7.10
- tslib: 2.6.2
+ tslib: 2.8.1
undici: 5.28.3
'@firebase/functions-compat@0.3.8(@firebase/app-compat@0.2.29)(@firebase/app@0.9.29)':
@@ -8647,7 +9075,7 @@ snapshots:
'@firebase/functions': 0.11.2(@firebase/app@0.9.29)
'@firebase/functions-types': 0.6.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8661,7 +9089,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/messaging-interop-types': 0.2.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
undici: 5.28.3
'@firebase/installations-compat@0.2.5(@firebase/app-compat@0.2.29)(@firebase/app-types@0.9.0)(@firebase/app@0.9.29)':
@@ -8671,7 +9099,7 @@ snapshots:
'@firebase/installations': 0.6.5(@firebase/app@0.9.29)
'@firebase/installations-types': 0.5.0(@firebase/app-types@0.9.0)
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
- '@firebase/app-types'
@@ -8686,7 +9114,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/util': 1.9.4
idb: 7.1.1
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/logger@0.4.0':
dependencies:
@@ -8702,7 +9130,7 @@ snapshots:
'@firebase/component': 0.6.5
'@firebase/messaging': 0.12.6(@firebase/app@0.9.29)
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8716,7 +9144,7 @@ snapshots:
'@firebase/messaging-interop-types': 0.2.0
'@firebase/util': 1.9.4
idb: 7.1.1
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/performance-compat@0.2.5(@firebase/app-compat@0.2.29)(@firebase/app@0.9.29)':
dependencies:
@@ -8726,7 +9154,7 @@ snapshots:
'@firebase/performance': 0.6.5(@firebase/app@0.9.29)
'@firebase/performance-types': 0.2.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8739,7 +9167,7 @@ snapshots:
'@firebase/installations': 0.6.5(@firebase/app@0.9.29)
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/remote-config-compat@0.2.5(@firebase/app-compat@0.2.29)(@firebase/app@0.9.29)':
dependencies:
@@ -8749,7 +9177,7 @@ snapshots:
'@firebase/remote-config': 0.4.5(@firebase/app@0.9.29)
'@firebase/remote-config-types': 0.3.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
@@ -8762,7 +9190,7 @@ snapshots:
'@firebase/installations': 0.6.5(@firebase/app@0.9.29)
'@firebase/logger': 0.4.0
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/storage-compat@0.3.5(@firebase/app-compat@0.2.29)(@firebase/app-types@0.9.0)(@firebase/app@0.9.29)':
dependencies:
@@ -8771,7 +9199,7 @@ snapshots:
'@firebase/storage': 0.12.2(@firebase/app@0.9.29)
'@firebase/storage-types': 0.8.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.4)
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
transitivePeerDependencies:
- '@firebase/app'
- '@firebase/app-types'
@@ -8786,7 +9214,7 @@ snapshots:
'@firebase/app': 0.9.29
'@firebase/component': 0.6.5
'@firebase/util': 1.9.4
- tslib: 2.6.2
+ tslib: 2.8.1
undici: 5.28.3
'@firebase/util@1.10.0':
@@ -8795,7 +9223,7 @@ snapshots:
'@firebase/util@1.9.4':
dependencies:
- tslib: 2.6.2
+ tslib: 2.8.1
'@firebase/webchannel-wrapper@0.10.5': {}
@@ -9203,7 +9631,7 @@ snapshots:
'@smithy/abort-controller@3.1.6':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/abort-controller@4.0.1':
dependencies:
@@ -9213,7 +9641,7 @@ snapshots:
'@smithy/chunked-blob-reader-native@3.0.1':
dependencies:
'@smithy/util-base64': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/chunked-blob-reader-native@4.0.0':
dependencies:
@@ -9222,7 +9650,7 @@ snapshots:
'@smithy/chunked-blob-reader@4.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/chunked-blob-reader@5.0.0':
dependencies:
@@ -9234,7 +9662,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-config-provider': 3.0.0
'@smithy/util-middleware': 3.0.8
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/config-resolver@4.0.1':
dependencies:
@@ -9253,7 +9681,7 @@ snapshots:
'@smithy/util-middleware': 3.0.8
'@smithy/util-stream': 3.2.1
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/core@3.1.1':
dependencies:
@@ -9266,13 +9694,24 @@ snapshots:
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
+ '@smithy/core@3.1.4':
+ dependencies:
+ '@smithy/middleware-serde': 4.0.2
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/types': 4.1.0
+ '@smithy/util-body-length-browser': 4.0.0
+ '@smithy/util-middleware': 4.0.1
+ '@smithy/util-stream': 4.1.1
+ '@smithy/util-utf8': 4.0.0
+ tslib: 2.8.1
+
'@smithy/credential-provider-imds@3.2.5':
dependencies:
'@smithy/node-config-provider': 3.1.9
'@smithy/property-provider': 3.1.8
'@smithy/types': 3.6.0
'@smithy/url-parser': 3.0.8
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/credential-provider-imds@4.0.1':
dependencies:
@@ -9300,7 +9739,7 @@ snapshots:
dependencies:
'@smithy/eventstream-serde-universal': 3.0.10
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/eventstream-serde-browser@4.0.1':
dependencies:
@@ -9311,7 +9750,7 @@ snapshots:
'@smithy/eventstream-serde-config-resolver@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/eventstream-serde-config-resolver@4.0.1':
dependencies:
@@ -9322,7 +9761,7 @@ snapshots:
dependencies:
'@smithy/eventstream-serde-universal': 3.0.10
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/eventstream-serde-node@4.0.1':
dependencies:
@@ -9334,7 +9773,7 @@ snapshots:
dependencies:
'@smithy/eventstream-codec': 3.1.7
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/eventstream-serde-universal@4.0.1':
dependencies:
@@ -9348,7 +9787,7 @@ snapshots:
'@smithy/querystring-builder': 3.0.8
'@smithy/types': 3.6.0
'@smithy/util-base64': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/fetch-http-handler@4.0.0':
dependencies:
@@ -9356,7 +9795,7 @@ snapshots:
'@smithy/querystring-builder': 3.0.8
'@smithy/types': 3.6.0
'@smithy/util-base64': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/fetch-http-handler@5.0.1':
dependencies:
@@ -9371,7 +9810,7 @@ snapshots:
'@smithy/chunked-blob-reader': 4.0.0
'@smithy/chunked-blob-reader-native': 3.0.1
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/hash-blob-browser@4.0.1':
dependencies:
@@ -9385,7 +9824,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-buffer-from': 3.0.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/hash-node@4.0.1':
dependencies:
@@ -9398,7 +9837,7 @@ snapshots:
dependencies:
'@smithy/types': 3.6.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/hash-stream-node@4.0.1':
dependencies:
@@ -9409,7 +9848,7 @@ snapshots:
'@smithy/invalid-dependency@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/invalid-dependency@4.0.1':
dependencies:
@@ -9422,7 +9861,7 @@ snapshots:
'@smithy/is-array-buffer@3.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/is-array-buffer@4.0.0':
dependencies:
@@ -9432,7 +9871,7 @@ snapshots:
dependencies:
'@smithy/types': 3.6.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/md5-js@4.0.1':
dependencies:
@@ -9444,7 +9883,7 @@ snapshots:
dependencies:
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/middleware-content-length@4.0.1':
dependencies:
@@ -9461,7 +9900,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/url-parser': 3.0.8
'@smithy/util-middleware': 3.0.8
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/middleware-endpoint@4.0.2':
dependencies:
@@ -9474,6 +9913,17 @@ snapshots:
'@smithy/util-middleware': 4.0.1
tslib: 2.8.1
+ '@smithy/middleware-endpoint@4.0.5':
+ dependencies:
+ '@smithy/core': 3.1.4
+ '@smithy/middleware-serde': 4.0.2
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/shared-ini-file-loader': 4.0.1
+ '@smithy/types': 4.1.0
+ '@smithy/url-parser': 4.0.1
+ '@smithy/util-middleware': 4.0.1
+ tslib: 2.8.1
+
'@smithy/middleware-retry@3.0.25':
dependencies:
'@smithy/node-config-provider': 3.1.9
@@ -9483,7 +9933,7 @@ snapshots:
'@smithy/types': 3.6.0
'@smithy/util-middleware': 3.0.8
'@smithy/util-retry': 3.0.8
- tslib: 2.7.0
+ tslib: 2.8.1
uuid: 9.0.1
'@smithy/middleware-retry@4.0.3':
@@ -9498,20 +9948,37 @@ snapshots:
tslib: 2.8.1
uuid: 9.0.1
+ '@smithy/middleware-retry@4.0.6':
+ dependencies:
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/service-error-classification': 4.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ '@smithy/util-middleware': 4.0.1
+ '@smithy/util-retry': 4.0.1
+ tslib: 2.8.1
+ uuid: 9.0.1
+
'@smithy/middleware-serde@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/middleware-serde@4.0.1':
dependencies:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@smithy/middleware-serde@4.0.2':
+ dependencies:
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@smithy/middleware-stack@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/middleware-stack@4.0.1':
dependencies:
@@ -9523,7 +9990,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/shared-ini-file-loader': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/node-config-provider@4.0.1':
dependencies:
@@ -9538,7 +10005,7 @@ snapshots:
'@smithy/protocol-http': 4.1.5
'@smithy/querystring-builder': 3.0.8
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/node-http-handler@4.0.2':
dependencies:
@@ -9551,7 +10018,7 @@ snapshots:
'@smithy/property-provider@3.1.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/property-provider@4.0.1':
dependencies:
@@ -9561,7 +10028,7 @@ snapshots:
'@smithy/protocol-http@4.1.5':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/protocol-http@5.0.1':
dependencies:
@@ -9572,7 +10039,7 @@ snapshots:
dependencies:
'@smithy/types': 3.6.0
'@smithy/util-uri-escape': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/querystring-builder@4.0.1':
dependencies:
@@ -9583,7 +10050,7 @@ snapshots:
'@smithy/querystring-parser@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/querystring-parser@4.0.1':
dependencies:
@@ -9601,7 +10068,7 @@ snapshots:
'@smithy/shared-ini-file-loader@3.1.9':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/shared-ini-file-loader@4.0.1':
dependencies:
@@ -9617,7 +10084,7 @@ snapshots:
'@smithy/util-middleware': 3.0.8
'@smithy/util-uri-escape': 3.0.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/signature-v4@5.0.1':
dependencies:
@@ -9638,7 +10105,7 @@ snapshots:
'@smithy/protocol-http': 4.1.5
'@smithy/types': 3.6.0
'@smithy/util-stream': 3.2.1
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/smithy-client@4.1.2':
dependencies:
@@ -9650,9 +10117,19 @@ snapshots:
'@smithy/util-stream': 4.0.2
tslib: 2.8.1
+ '@smithy/smithy-client@4.1.5':
+ dependencies:
+ '@smithy/core': 3.1.4
+ '@smithy/middleware-endpoint': 4.0.5
+ '@smithy/middleware-stack': 4.0.1
+ '@smithy/protocol-http': 5.0.1
+ '@smithy/types': 4.1.0
+ '@smithy/util-stream': 4.1.1
+ tslib: 2.8.1
+
'@smithy/types@3.6.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/types@4.1.0':
dependencies:
@@ -9662,7 +10139,7 @@ snapshots:
dependencies:
'@smithy/querystring-parser': 3.0.8
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/url-parser@4.0.1':
dependencies:
@@ -9674,7 +10151,7 @@ snapshots:
dependencies:
'@smithy/util-buffer-from': 3.0.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-base64@4.0.0':
dependencies:
@@ -9684,7 +10161,7 @@ snapshots:
'@smithy/util-body-length-browser@3.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-body-length-browser@4.0.0':
dependencies:
@@ -9692,7 +10169,7 @@ snapshots:
'@smithy/util-body-length-node@3.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-body-length-node@4.0.0':
dependencies:
@@ -9706,7 +10183,7 @@ snapshots:
'@smithy/util-buffer-from@3.0.0':
dependencies:
'@smithy/is-array-buffer': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-buffer-from@4.0.0':
dependencies:
@@ -9715,7 +10192,7 @@ snapshots:
'@smithy/util-config-provider@3.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-config-provider@4.0.0':
dependencies:
@@ -9727,7 +10204,7 @@ snapshots:
'@smithy/smithy-client': 3.4.2
'@smithy/types': 3.6.0
bowser: 2.11.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-defaults-mode-browser@4.0.3':
dependencies:
@@ -9737,6 +10214,14 @@ snapshots:
bowser: 2.11.0
tslib: 2.8.1
+ '@smithy/util-defaults-mode-browser@4.0.6':
+ dependencies:
+ '@smithy/property-provider': 4.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ bowser: 2.11.0
+ tslib: 2.8.1
+
'@smithy/util-defaults-mode-node@3.0.25':
dependencies:
'@smithy/config-resolver': 3.0.10
@@ -9745,7 +10230,7 @@ snapshots:
'@smithy/property-provider': 3.1.8
'@smithy/smithy-client': 3.4.2
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-defaults-mode-node@4.0.3':
dependencies:
@@ -9757,11 +10242,21 @@ snapshots:
'@smithy/types': 4.1.0
tslib: 2.8.1
+ '@smithy/util-defaults-mode-node@4.0.6':
+ dependencies:
+ '@smithy/config-resolver': 4.0.1
+ '@smithy/credential-provider-imds': 4.0.1
+ '@smithy/node-config-provider': 4.0.1
+ '@smithy/property-provider': 4.0.1
+ '@smithy/smithy-client': 4.1.5
+ '@smithy/types': 4.1.0
+ tslib: 2.8.1
+
'@smithy/util-endpoints@2.1.4':
dependencies:
'@smithy/node-config-provider': 3.1.9
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-endpoints@3.0.1':
dependencies:
@@ -9771,7 +10266,7 @@ snapshots:
'@smithy/util-hex-encoding@3.0.0':
dependencies:
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-hex-encoding@4.0.0':
dependencies:
@@ -9780,7 +10275,7 @@ snapshots:
'@smithy/util-middleware@3.0.8':
dependencies:
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-middleware@4.0.1':
dependencies:
@@ -9791,7 +10286,7 @@ snapshots:
dependencies:
'@smithy/service-error-classification': 3.0.8
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-retry@4.0.1':
dependencies:
@@ -9808,7 +10303,7 @@ snapshots:
'@smithy/util-buffer-from': 3.0.0
'@smithy/util-hex-encoding': 3.0.0
'@smithy/util-utf8': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-stream@4.0.2':
dependencies:
@@ -9821,6 +10316,17 @@ snapshots:
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
+ '@smithy/util-stream@4.1.1':
+ dependencies:
+ '@smithy/fetch-http-handler': 5.0.1
+ '@smithy/node-http-handler': 4.0.2
+ '@smithy/types': 4.1.0
+ '@smithy/util-base64': 4.0.0
+ '@smithy/util-buffer-from': 4.0.0
+ '@smithy/util-hex-encoding': 4.0.0
+ '@smithy/util-utf8': 4.0.0
+ tslib: 2.8.1
+
'@smithy/util-uri-escape@3.0.0':
dependencies:
tslib: 2.8.1
@@ -9837,7 +10343,7 @@ snapshots:
'@smithy/util-utf8@3.0.0':
dependencies:
'@smithy/util-buffer-from': 3.0.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-utf8@4.0.0':
dependencies:
@@ -9848,7 +10354,7 @@ snapshots:
dependencies:
'@smithy/abort-controller': 3.1.6
'@smithy/types': 3.6.0
- tslib: 2.7.0
+ tslib: 2.8.1
'@smithy/util-waiter@4.0.2':
dependencies:
@@ -11129,6 +11635,8 @@ snapshots:
'@types/geojson@7946.0.8': {}
+ '@types/google-one-tap@1.2.6': {}
+
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.10
@@ -11618,7 +12126,7 @@ snapshots:
sirv: 3.0.0
tinyglobby: 0.2.10
tinyrainbow: 1.2.0
- vitest: 2.1.4(@types/node@18.11.18)(@vitest/ui@2.1.4)
+ vitest: 2.1.4(@types/node@22.8.6)(@vitest/ui@2.1.4)
'@vitest/utils@2.1.4':
dependencies:
@@ -15074,11 +15582,6 @@ snapshots:
magic-string: 0.30.12
periscopic: 3.1.0
- sveltefirets@0.0.42(firebase@10.9.0)(svelte@4.2.12):
- dependencies:
- firebase: 10.9.0
- svelte: 4.2.12
-
sveltefirets@0.0.42(firebase@10.9.0)(svelte@4.2.19):
dependencies:
firebase: 10.9.0
@@ -15186,8 +15689,6 @@ snapshots:
tslib@2.6.1: {}
- tslib@2.6.2: {}
-
tslib@2.7.0: {}
tslib@2.8.1: {}
diff --git a/supabase/config.toml b/supabase/config.toml
index 7dc6d17bb..78c7506ab 100644
--- a/supabase/config.toml
+++ b/supabase/config.toml
@@ -91,9 +91,9 @@ enabled = false
[auth]
enabled = true
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used in emails.
-site_url = "http://127.0.0.1:3000"
+site_url = "http://127.0.0.1:3041"
# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
-additional_redirect_urls = [ "https://127.0.0.1:3000" ]
+additional_redirect_urls = [ "https://127.0.0.1:3041" ]
# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week).
jwt_expiry = 3600
# If disabled, the refresh token will never expire.
@@ -188,11 +188,12 @@ verify_enabled = false
# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`,
# `twitter`, `slack`, `spotify`, `workos`, `zoom`.
-[auth.external.apple]
-enabled = false
-client_id = ""
+[auth.external.google]
+enabled = true
+client_id = "215143435444-fugm4gpav71r3l89n6i0iath4m436qnv.apps.googleusercontent.com"
# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead:
-secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)"
+secret = "not-needed"
+# secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)"
# Overrides the default auth redirectUrl.
redirect_uri = ""
# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
diff --git a/supabase/ideas/202502_dialects_combined_with_tags__add_linguistic_history.sql b/supabase/ideas/202502_dialects_combined_with_tags__add_linguistic_history.sql
new file mode 100644
index 000000000..96bbde030
--- /dev/null
+++ b/supabase/ideas/202502_dialects_combined_with_tags__add_linguistic_history.sql
@@ -0,0 +1,153 @@
+CREATE TYPE tag_type_enum AS ENUM ('dialect', 'tag', 'foo');
+
+ALTER TABLE tags
+ALTER COLUMN name TYPE jsonb USING name::jsonb, -- MultiString (was text previously), TODO: migrate existing data
+ADD COLUMN type tag_type_enum NOT NULL DEFAULT 'tag';
+
+DROP TABLE dialects; -- TODO: requires migrating them to tags first
+DROP TABLE entry_dialects;
+
+ALTER TABLE entries
+ADD COLUMN linguistic_history jsonb -- MultiString;
+
+DROP FUNCTION entries_from_timestamp(timestamp with time zone, text) CASCADE; -- must drop and recreate if changing the shape of the function
+CREATE FUNCTION entries_from_timestamp(
+ get_newer_than timestamp with time zone,
+ dict_id text
+) RETURNS TABLE(
+ id text,
+ dictionary_id text,
+ created_at timestamp with time zone,
+ updated_at timestamp with time zone,
+ deleted timestamp with time zone,
+ main jsonb,
+ senses jsonb,
+ audios jsonb,
+ tag_ids jsonb
+) AS $$
+ WITH aggregated_audio AS (
+ SELECT
+ audio.entry_id,
+ jsonb_agg(
+ jsonb_strip_nulls(
+ jsonb_build_object(
+ 'id', audio.id,
+ 'storage_path', audio.storage_path,
+ 'source', audio.source,
+ 'speaker_ids', audio_speakers.speaker_ids
+ )
+ )
+ ORDER BY audio.created_at) AS audios
+ FROM audio
+ LEFT JOIN (
+ SELECT
+ audio_id,
+ jsonb_agg(speaker_id) AS speaker_ids
+ FROM audio_speakers
+ WHERE deleted IS NULL
+ GROUP BY audio_id
+ ) AS audio_speakers ON audio_speakers.audio_id = audio.id
+ WHERE audio.deleted IS NULL
+ GROUP BY audio.entry_id
+ )
+ SELECT
+ entries.id AS id,
+ entries.dictionary_id AS dictionary_id,
+ entries.created_at,
+ entries.updated_at,
+ entries.deleted,
+ jsonb_strip_nulls(
+ jsonb_build_object(
+ 'lexeme', entries.lexeme,
+ 'phonetic', entries.phonetic,
+ 'interlinearization', entries.interlinearization,
+ 'morphology', entries.morphology,
+ 'notes', entries.notes,
+ 'sources', entries.sources,
+ 'scientific_names', entries.scientific_names,
+ 'coordinates', entries.coordinates,
+ 'linguistic_history', entries.linguistic_history, -- TODO: `pnpm generate-types`, then update augments.types.ts to handle new Jsonb fields in generated.types.ts, then run `pnpm generate-types` again
+ 'unsupported_fields', entries.unsupported_fields,
+ 'elicitation_id', entries.elicitation_id
+ )
+ ) AS main,
+ CASE
+ WHEN COUNT(senses.id) > 0 THEN jsonb_agg(
+ jsonb_strip_nulls(
+ jsonb_build_object(
+ 'id', senses.id,
+ 'glosses', senses.glosses,
+ 'parts_of_speech', senses.parts_of_speech,
+ 'semantic_domains', senses.semantic_domains,
+ 'write_in_semantic_domains', senses.write_in_semantic_domains,
+ 'noun_class', senses.noun_class,
+ 'definition', senses.definition,
+ 'plural_form', senses.plural_form,
+ 'variant', senses.variant,
+ 'sentence_ids', sentence_ids,
+ 'photo_ids', photo_ids,
+ 'video_ids', video_ids
+ )
+ )
+ ORDER BY senses.created_at
+ )
+ ELSE NULL
+ END AS senses,
+ aggregated_audio.audios,
+ tag_ids.tag_ids
+ FROM entries
+ LEFT JOIN senses ON senses.entry_id = entries.id AND senses.deleted IS NULL
+ LEFT JOIN aggregated_audio ON aggregated_audio.entry_id = entries.id
+ LEFT JOIN (
+ SELECT
+ entry_id,
+ jsonb_agg(tag_id) AS tag_ids
+ FROM entry_tags
+ WHERE deleted IS NULL
+ GROUP BY entry_id
+ ) AS tag_ids ON tag_ids.entry_id = entries.id
+ LEFT JOIN (
+ SELECT
+ senses_in_sentences.sense_id,
+ jsonb_agg(senses_in_sentences.sentence_id) AS sentence_ids
+ FROM senses_in_sentences
+ JOIN sentences ON sentences.id = senses_in_sentences.sentence_id
+ WHERE sentences.deleted IS NULL AND senses_in_sentences.deleted IS NULL
+ GROUP BY senses_in_sentences.sense_id
+ ) AS sense_sentences ON sense_sentences.sense_id = senses.id
+ LEFT JOIN (
+ SELECT
+ sense_photos.sense_id,
+ jsonb_agg(sense_photos.photo_id) AS photo_ids
+ FROM sense_photos
+ JOIN photos ON photos.id = sense_photos.photo_id
+ WHERE photos.deleted IS NULL AND sense_photos.deleted IS NULL
+ GROUP BY sense_photos.sense_id
+ ) AS aggregated_photo_ids ON aggregated_photo_ids.sense_id = senses.id
+ LEFT JOIN (
+ SELECT
+ sense_videos.sense_id,
+ jsonb_agg(sense_videos.video_id) AS video_ids
+ FROM sense_videos
+ JOIN videos ON videos.id = sense_videos.video_id
+ WHERE videos.deleted IS NULL AND sense_videos.deleted IS NULL
+ GROUP BY sense_videos.sense_id
+ ) AS aggregated_video_ids ON aggregated_video_ids.sense_id = senses.id
+ WHERE entries.updated_at > get_newer_than AND (dict_id = '' OR entries.dictionary_id = dict_id)
+ GROUP BY entries.id, aggregated_audio.audios, tag_ids.tag_ids
+ ORDER BY entries.updated_at ASC;
+$$ LANGUAGE SQL SECURITY DEFINER;
+
+-- TODO: see if we need to renew the views or if they're good to go
+-- CREATE MATERIALIZED VIEW materialized_entries_view AS
+-- SELECT * FROM entries_from_timestamp('1970-01-01 01:00:00+00', '');
+
+-- CREATE UNIQUE INDEX idx_materialized_entries_view_id ON materialized_entries_view (id);
+-- REFRESH MATERIALIZED VIEW CONCURRENTLY materialized_entries_view;
+
+-- CREATE INDEX idx_materialized_entries_view_updated_at_dictionary_id
+-- ON materialized_entries_view (updated_at, dictionary_id);
+
+-- DROP VIEW IF EXISTS entries_view;
+-- CREATE VIEW entries_view AS
+-- SELECT * FROM entries_from_timestamp('1970-01-01 01:00:00+00', '');
\ No newline at end of file
diff --git a/supabase/ideas/202503_move-content-update-client-side.sql b/supabase/ideas/202503_move-content-update-client-side.sql
new file mode 100644
index 000000000..3426a6f9b
--- /dev/null
+++ b/supabase/ideas/202503_move-content-update-client-side.sql
@@ -0,0 +1,29 @@
+ALTER TABLE photos
+ALTER COLUMN created_by SET DEFAULT auth.uid(),
+ALTER COLUMN updated_by SET DEFAULT auth.uid();
+
+CREATE POLICY "Users can insert photos for dictionaries they manage or contribute to"
+ON photos FOR INSERT
+TO authenticated
+WITH CHECK (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = photos.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ )
+);
+
+CREATE POLICY "Users can update photos for dictionaries they manage or contribute to"
+ON photos FOR UPDATE
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = photos.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ )
+);
+
+-- continue through rest of tables
\ No newline at end of file
diff --git a/supabase/migrations/20240221012557_dictionaries-entries-sentences-texts-tables.sql b/supabase/migrations/20240221012557_dictionaries-entries-sentences-texts-tables.sql
index ef710747e..adf8eb7ad 100644
--- a/supabase/migrations/20240221012557_dictionaries-entries-sentences-texts-tables.sql
+++ b/supabase/migrations/20240221012557_dictionaries-entries-sentences-texts-tables.sql
@@ -1,6 +1,6 @@
CREATE TYPE certainty AS ENUM ('yes', 'no', 'unknown');
-CREATE TABLE dictionaries ( -- TODO: migrate from Firestore
+CREATE TABLE dictionaries (
id text unique primary key NOT NULL,
name text NOT NULL,
alternate_names text[],
diff --git a/supabase/migrations/20240824035215_fb-sb-migration-needs-connect-audio-video--pl-variant-dialects-ei-field-plus-meta.sql b/supabase/migrations/20240824035215_fb-sb-migration-needs-connect-audio-video--pl-variant-dialects-ei-field-plus-meta.sql
index e293e08e5..12da12593 100644
--- a/supabase/migrations/20240824035215_fb-sb-migration-needs-connect-audio-video--pl-variant-dialects-ei-field-plus-meta.sql
+++ b/supabase/migrations/20240824035215_fb-sb-migration-needs-connect-audio-video--pl-variant-dialects-ei-field-plus-meta.sql
@@ -233,7 +233,7 @@ SELECT * FROM entries_view; -- DROP MATERIALIZED VIEW materialized_entries_view;
CREATE UNIQUE INDEX idx_materialized_entries_view_id ON materialized_entries_view (id); -- When you refresh data for a materialized view, PostgreSQL locks the underlying tables. To avoid this, use the CONCURRENTLY option so that PostgreSQL creates a temporary updated version of the materialized view, compares two versions, and performs INSERT and UPDATE on only the differences. To use CONCURRENTLY the materialized view must have a UNIQUE index:
-CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA extensions;
+CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog;
GRANT USAGE ON SCHEMA cron TO postgres;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA cron TO postgres;
diff --git a/supabase/migrations/20250205145245_user-profiles.sql b/supabase/migrations/20250205145245_user-profiles.sql
new file mode 100644
index 000000000..607420ea4
--- /dev/null
+++ b/supabase/migrations/20250205145245_user-profiles.sql
@@ -0,0 +1,44 @@
+CREATE VIEW profiles_view AS
+SELECT
+ id,
+ email,
+ raw_user_meta_data->>'full_name' AS full_name,
+ raw_user_meta_data->>'avatar_url' AS avatar_url
+FROM auth.users;
+REVOKE ALL ON public.profiles_view FROM anon, authenticated, public;
+
+CREATE TABLE user_data (
+ id uuid references auth.users not null primary key,
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
+ welcome_email_sent timestamp with time zone,
+ unsubscribed_from_emails timestamp with time zone,
+ terms_agreement timestamp with time zone
+);
+
+ALTER TABLE user_data ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY "Users can view their user_data."
+ON user_data FOR SELECT
+TO authenticated
+USING (auth.uid() = id);
+
+CREATE POLICY "Users can update their user_data."
+ON user_data FOR UPDATE
+TO authenticated
+USING (auth.uid() = id);
+
+CREATE FUNCTION handle_new_user()
+RETURNS TRIGGER AS $$
+DECLARE
+ result INT;
+BEGIN
+ INSERT INTO public.user_data (id)
+ VALUES (NEW.id);
+
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+CREATE TRIGGER on_auth_user_created
+AFTER INSERT ON auth.users
+FOR EACH ROW EXECUTE PROCEDURE handle_new_user();
\ No newline at end of file
diff --git a/supabase/migrations/20250205145246_dictionary-roles-invites.sql b/supabase/migrations/20250205145246_dictionary-roles-invites.sql
new file mode 100644
index 000000000..40a6f8bb8
--- /dev/null
+++ b/supabase/migrations/20250205145246_dictionary-roles-invites.sql
@@ -0,0 +1,149 @@
+ALTER TABLE photos
+ALTER COLUMN created_by SET DEFAULT auth.uid(),
+ALTER COLUMN updated_by SET DEFAULT auth.uid();
+
+ALTER TABLE dictionaries
+ALTER COLUMN created_by SET DEFAULT auth.uid(),
+ALTER COLUMN updated_by SET DEFAULT auth.uid();
+
+CREATE POLICY "Users can create dictionaries."
+ON dictionaries FOR INSERT
+TO authenticated
+WITH CHECK (
+ auth.uid() = created_by
+ AND auth.uid() = updated_by
+ AND length(id) >= 3
+);
+
+CREATE TYPE role_enum AS ENUM ('manager', 'contributor');
+
+CREATE TABLE dictionary_roles (
+ dictionary_id text NOT NULL REFERENCES dictionaries ON DELETE CASCADE,
+ user_id uuid NOT NULL REFERENCES auth.users ON DELETE CASCADE DEFAULT auth.uid(),
+ role role_enum NOT NULL,
+ created_at timestamp with time zone NOT NULL DEFAULT now(),
+ invited_by uuid REFERENCES auth.users,
+ PRIMARY KEY (dictionary_id, user_id, role)
+);
+
+ALTER TABLE dictionary_roles ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY "Managers and contributors can view dictionary roles."
+ON dictionary_roles FOR SELECT
+TO authenticated
+USING (auth.uid() = user_id);
+
+CREATE FUNCTION add_manager_on_new_dictionary()
+RETURNS TRIGGER AS $$
+BEGIN
+ INSERT INTO public.dictionary_roles (dictionary_id, user_id, role)
+ VALUES (NEW.id, NEW.created_by, 'manager');
+
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+CREATE TRIGGER on_dictionary_created
+AFTER INSERT ON public.dictionaries
+FOR EACH ROW EXECUTE PROCEDURE add_manager_on_new_dictionary();
+
+CREATE POLICY "Managers can remove contributors."
+ON dictionary_roles FOR DELETE
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionary_roles.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE POLICY "Managers can update dictionaries."
+ON dictionaries FOR UPDATE
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionaries.id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE VIEW dictionary_roles_with_profiles AS
+SELECT
+ dictionary_roles.dictionary_id,
+ dictionary_roles.user_id,
+ dictionary_roles.role,
+ profiles_view.full_name,
+ profiles_view.avatar_url
+FROM
+ dictionary_roles
+JOIN
+ profiles_view ON dictionary_roles.user_id = profiles_view.id;
+
+CREATE TYPE status_enum AS ENUM ('queued', 'sent', 'claimed', 'cancelled');
+
+CREATE TABLE invites (
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
+ dictionary_id text NOT NULL REFERENCES dictionaries ON DELETE CASCADE,
+ inviter_email text NOT NULL,
+ target_email text NOT NULL,
+ role role_enum NOT NULL,
+ status status_enum NOT NULL,
+ created_at timestamp with time zone NOT NULL DEFAULT now(),
+ created_by uuid NOT NULL REFERENCES auth.users default auth.uid()
+);
+
+ALTER TABLE invites ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY "Managers can do everything on invites."
+ON invites FOR ALL
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = invites.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+)
+WITH CHECK (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = invites.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE POLICY "Invited users can view their invites."
+ON invites FOR SELECT
+TO authenticated
+USING (
+ auth.jwt() ->> 'email' = invites.target_email
+);
+
+CREATE POLICY "Invited users can mark their invites claimed."
+ON invites FOR UPDATE
+TO authenticated
+USING (auth.jwt() ->> 'email' = invites.target_email);
+
+CREATE POLICY "Invited users can add themselves to the dictionary roles."
+ON dictionary_roles FOR INSERT
+TO authenticated
+WITH CHECK (
+ EXISTS (
+ SELECT 1
+ FROM invites
+ WHERE invites.dictionary_id = dictionary_roles.dictionary_id
+ AND invites.role = dictionary_roles.role
+ AND invites.target_email = auth.jwt() ->> 'email'
+ AND invites.status = 'sent'
+ )
+);
diff --git a/supabase/migrations/20250205145249_dictionary-info-partners.sql b/supabase/migrations/20250205145249_dictionary-info-partners.sql
new file mode 100644
index 000000000..ffabf5c26
--- /dev/null
+++ b/supabase/migrations/20250205145249_dictionary-info-partners.sql
@@ -0,0 +1,84 @@
+CREATE TABLE dictionary_info (
+ id text NOT NULL REFERENCES dictionaries ON DELETE CASCADE,
+ about text,
+ grammar text,
+ citation text,
+ write_in_collaborators text[],
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
+ created_by uuid NOT NULL REFERENCES auth.users default auth.uid(),
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
+ updated_by uuid NOT NULL REFERENCES auth.users default auth.uid(),
+ PRIMARY KEY (id)
+);
+
+ALTER TABLE dictionary_info ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY "Managers can insert dictionary info."
+ON dictionary_info FOR INSERT
+TO authenticated
+WITH CHECK (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionary_info.id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE POLICY "Managers can update dictionary info."
+ON dictionary_info FOR UPDATE
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionary_info.id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE POLICY "Anyone can view dictionary info."
+ON dictionary_info FOR SELECT
+USING (true);
+
+
+CREATE TABLE dictionary_partners (
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
+ dictionary_id text NOT NULL REFERENCES dictionaries ON DELETE CASCADE,
+ name text NOT NULL,
+ photo_id uuid REFERENCES photos,
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
+ created_by uuid NOT NULL REFERENCES auth.users default auth.uid(),
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
+ updated_by uuid NOT NULL REFERENCES auth.users default auth.uid()
+);
+
+ALTER TABLE dictionary_partners ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY "Managers can add, edit, remove dictionary partners."
+ON dictionary_partners FOR ALL
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionary_partners.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+)
+WITH CHECK (
+ EXISTS (
+ SELECT 1
+ FROM dictionary_roles
+ WHERE dictionary_roles.dictionary_id = dictionary_partners.dictionary_id
+ AND dictionary_roles.user_id = auth.uid()
+ AND dictionary_roles.role = 'manager'
+ )
+);
+
+CREATE POLICY "Anyone can view dictionary partners."
+ON dictionary_partners FOR SELECT
+USING (true);
\ No newline at end of file
diff --git a/supabase/migrations/20250205145250_admin-rls.sql b/supabase/migrations/20250205145250_admin-rls.sql
new file mode 100644
index 000000000..eb7647648
--- /dev/null
+++ b/supabase/migrations/20250205145250_admin-rls.sql
@@ -0,0 +1,27 @@
+CREATE OR REPLACE FUNCTION get_my_claim(claim TEXT) RETURNS "jsonb"
+ LANGUAGE "sql" STABLE
+ AS $$
+ select
+ coalesce(nullif(current_setting('request.jwt.claims', true), '')::jsonb -> 'app_metadata' -> claim, null)
+$$;
+
+CREATE OR REPLACE FUNCTION is_admin() RETURNS BOOLEAN
+ LANGUAGE sql STABLE
+ AS $$
+ SELECT coalesce(get_my_claim('admin')::numeric, 0) > 0
+$$;
+
+DO $$
+DECLARE
+ r RECORD;
+BEGIN
+ FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
+ EXECUTE format('
+ CREATE POLICY "Admin level 1 can perform any action on %I"
+ ON %I FOR ALL
+ TO authenticated
+ USING (is_admin())
+ WITH CHECK (is_admin());
+ ', r.tablename, r.tablename);
+ END LOOP;
+END $$;
diff --git a/supabase/migrations/20250205145300_users_with_dictionary_roles.sql b/supabase/migrations/20250205145300_users_with_dictionary_roles.sql
new file mode 100644
index 000000000..b3f6d5ee3
--- /dev/null
+++ b/supabase/migrations/20250205145300_users_with_dictionary_roles.sql
@@ -0,0 +1,125 @@
+CREATE OR REPLACE FUNCTION users_with_dictionary_roles()
+RETURNS TABLE (
+ id uuid,
+ email text,
+ full_name text,
+ avatar_url text,
+ last_sign_in_at timestamp with time zone,
+ created_at timestamp with time zone,
+ unsubscribed_from_emails timestamp with time zone,
+ terms_agreement timestamp with time zone,
+ dictionary_roles jsonb
+) AS $$
+BEGIN
+ IF NOT is_admin() THEN
+ RETURN;
+ END IF;
+
+ RETURN QUERY
+ SELECT
+ auth.users.id,
+ auth.users.email::text,
+ auth.users.raw_user_meta_data->>'full_name' AS full_name,
+ auth.users.raw_user_meta_data->>'avatar_url' AS avatar_url,
+ auth.users.last_sign_in_at,
+ auth.users.created_at,
+ user_data.unsubscribed_from_emails,
+ user_data.terms_agreement,
+ COALESCE(
+ jsonb_agg(
+ jsonb_build_object(
+ 'dictionary_id', dictionary_roles.dictionary_id,
+ 'role', dictionary_roles.role,
+ 'created_at', dictionary_roles.created_at,
+ 'invited_by', dictionary_roles.invited_by
+ )
+ ) FILTER (WHERE dictionary_roles.dictionary_id IS NOT NULL),
+ '[]'::jsonb
+ ) AS dictionary_roles
+ FROM auth.users
+ LEFT JOIN user_data ON auth.users.id = user_data.id
+ LEFT JOIN dictionary_roles ON auth.users.id = dictionary_roles.user_id
+ GROUP BY auth.users.id, user_data.unsubscribed_from_emails, user_data.terms_agreement;
+END;
+$$ LANGUAGE plpgsql SECURITY DEFINER;
+
+-- CREATE OR REPLACE FUNCTION dictionaries_with_editors()
+-- RETURNS TABLE (
+-- id text,
+-- name text,
+-- alternate_names text[],
+-- gloss_languages text[],
+-- location text,
+-- coordinates jsonb,
+-- iso_639_3 text,
+-- glottocode text,
+-- public boolean,
+-- print_access boolean,
+-- metadata jsonb,
+-- entry_count bigint,
+-- orthographies jsonb[],
+-- featured_image jsonb,
+-- author_connection text,
+-- community_permission public.certainty,
+-- language_used_by_community boolean,
+-- con_language_description text,
+-- copyright text,
+-- created_at timestamp with time zone,
+-- created_by uuid,
+-- updated_at timestamp with time zone,
+-- updated_by uuid,
+-- editors jsonb
+-- ) AS $$
+-- BEGIN
+-- IF NOT is_admin() THEN
+-- RETURN;
+-- END IF;
+
+-- RETURN QUERY
+-- WITH editors_cte AS (
+-- SELECT
+-- dictionary_roles.dictionary_id,
+-- jsonb_agg(
+-- jsonb_build_object(
+-- 'user_id', auth.users.id,
+-- 'email', auth.users.email::text,
+-- 'full_name', auth.users.raw_user_meta_data->>'full_name',
+-- 'avatar_url', auth.users.raw_user_meta_data->>'avatar_url',
+-- 'role', dictionary_roles.role,
+-- 'created_at', dictionary_roles.created_at,
+-- 'invited_by', dictionary_roles.invited_by
+-- )
+-- ) FILTER (WHERE dictionary_roles.dictionary_id IS NOT NULL) AS editors
+-- FROM dictionary_roles
+-- LEFT JOIN auth.users ON dictionary_roles.user_id = auth.users.id
+-- GROUP BY dictionary_roles.dictionary_id
+-- )
+-- SELECT
+-- dictionaries_view.id,
+-- dictionaries_view.name,
+-- dictionaries_view.alternate_names,
+-- dictionaries_view.gloss_languages,
+-- dictionaries_view.location,
+-- dictionaries_view.coordinates,
+-- dictionaries_view.iso_639_3,
+-- dictionaries_view.glottocode,
+-- dictionaries_view.public,
+-- dictionaries_view.print_access,
+-- dictionaries_view.metadata,
+-- dictionaries_view.entry_count,
+-- dictionaries_view.orthographies,
+-- dictionaries_view.featured_image,
+-- dictionaries_view.author_connection,
+-- dictionaries_view.community_permission,
+-- dictionaries_view.language_used_by_community,
+-- dictionaries_view.con_language_description,
+-- dictionaries_view.copyright,
+-- dictionaries_view.created_at,
+-- dictionaries_view.created_by,
+-- dictionaries_view.updated_at,
+-- dictionaries_view.updated_by,
+-- COALESCE(editors_cte.editors, '[]'::jsonb) AS editors
+-- FROM dictionaries_view
+-- LEFT JOIN editors_cte ON dictionaries_view.id = editors_cte.dictionary_id;
+-- END;
+-- $$ LANGUAGE plpgsql SECURITY DEFINER;
\ No newline at end of file
| |