Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:incubrain/astrotribe into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Drew-Macgibbon committed Oct 9, 2024
2 parents 071ed93 + a09dcc1 commit 0a46650
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 85 deletions.
16 changes: 6 additions & 10 deletions apps/main-app/components/UploadCropper.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<script setup lang="ts">
import { useFileUpload } from '#imports'
import type { CropperResult, ImageTransforms } from 'vue-advanced-cropper'
import { Cropper, Preview } from 'vue-advanced-cropper'
import 'vue-advanced-cropper/dist/style.css'
import { useNotification } from '../../../layers/crud/composables/notification'
import { useCurrentUser } from '../../../layers/crud/composables/user.current.store'
import { useFileUpload } from '#imports'
type CropperConfigTypes = 'avatar' | 'default'
const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5 MB
const emit = defineEmits(['profile-pic-update'])
const uploadInput = ref(null as HTMLInputElement | null)
const image = ref<string>('')
Expand Down Expand Up @@ -143,20 +143,16 @@ async function uploadImage(blob: Blob) {
format: 'webp',
maxFileSize: MAX_FILE_SIZE,
allowedMimeTypes: ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'],
replace: props.cropperType === 'avatar',
onProgress: (progress) => {
console.log(`Upload progress: ${progress}%`)
},
})
await supabase
.from('user_profiles')
.update({ avatar: result.publicUrl })
.eq('id', profile.value.id)
if (props.cropperType === 'avatar') {
emit('profile-pic-update', result.publicUrl)
}
toast.success({
summary: 'Image uploaded',
message: 'Your image has been successfully uploaded and processed.',
})
return result
} catch (error: any) {
setError(`Failed to upload image: ${error.message}`)
Expand Down
2 changes: 1 addition & 1 deletion apps/main-app/components/search/SearchBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const submitQuestion = async () => {
v-model="question"
auto-resize
placeholder="Ask Your Question..."
class="flex max-h-[120px] w-full items-center justify-center bg-transparent px-4 py-2 outline-none"
class="flex resize-none max-h-[120px] w-full items-center justify-center bg-transparent px-4 py-2 outline-none"
:pt="{ root: 'border-none' }"
@keyup.enter="submitQuestion"
/>
Expand Down
9 changes: 6 additions & 3 deletions apps/main-app/composables/chat.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const useChatStore = defineStore('chatStore', () => {
const logger = useLogger(domainKey)

const chat = ref({} as Chat)
const messages = ref<Array<{ role: 'user' | 'assistant' | 'system', content: string }>>([])
const messages = ref<Array<{ role: 'user' | 'assistant' | 'system'; content: string }>>([])

const question = ref('' as string)

Expand Down Expand Up @@ -91,7 +91,7 @@ export const useChatStore = defineStore('chatStore', () => {
messages.value.push({ role, content })
}

async function submitQuestion(args: { question: string, systemPrompt: string }) {
async function submitQuestion(args: { question: string; systemPrompt: string }) {
console.log('searchMessage', args)

if (loading.isLoading(domainKey)) {
Expand All @@ -105,7 +105,10 @@ export const useChatStore = defineStore('chatStore', () => {

const messageHistory = messages.value.slice(-5) // Get last 5 messages

const formattedMessages = [{ role: 'system', content: args.systemPrompt }, ...messageHistory]
const formattedMessages = [
...(args.systemPrompt ? [{ role: 'system', content: args.systemPrompt }] : []),
...messageHistory,
]

const questionResponse = await fetch('/api/ai/ask', {
method: 'POST',
Expand Down
16 changes: 8 additions & 8 deletions apps/main-app/layouts/app-settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ const settingsRoutes = computed(() => [
visible: user.profile?.providers.includes('email'),
disabled: false,
},
{
key: 'settings-payments',
label: 'Payments',
url: '/profile/settings/payments',
icon: 'mdi:credit-card',
visible: true,
disabled: false,
},
// {
// key: 'settings-payments',
// label: 'Payments',
// url: '/profile/settings/payments',
// icon: 'mdi:credit-card',
// visible: true,
// disabled: false,
// },
{
key: 'settings-notifications',
label: 'Notifications',
Expand Down
5 changes: 5 additions & 0 deletions apps/main-app/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ export default defineNuxtConfig({
'@nuxt/icon',
'@nuxt/eslint',
'@nuxtjs/tailwindcss',
'@nuxtjs/mdc',
'@primevue/nuxt-module',
],

experimental: {
asyncContext: true,
},

tailwindcss: {
configPath: `${currentDir}/tailwind.config.ts`,
cssPath: [`${currentDir}/assets/css/tailwind.css`, { injectPosition: 0 }],
Expand Down
17 changes: 16 additions & 1 deletion apps/main-app/pages/ask.vue
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ const selectAgent = (agent: ChatAgent) => {
}
}
const closeAgent = () => {
selectedAgent.value = null
selectedStarterPrompts.value = []
conversation.value = []
}
const selectPrompt = (prompt: string) => {
message.value = prompt
selectedStarterPrompts.value = []
Expand Down Expand Up @@ -380,7 +386,16 @@ const responsiveOptions = ref([
v-if="selectedStarterPrompts.length"
class="space-y-2 pb-2"
>
<p class="text-center font-semibold"> Example Questions </p>
<div class="flex justify-center items-center">
<p class="text-center flex-grow font-semibold"> Example Questions </p>
<PrimeButton
severity="secondary"
class="text-end ml-auto text-white font-semibold"
@click="closeAgent"
>
X
</PrimeButton>
</div>
<PrimeButton
v-for="prompt in selectedStarterPrompts"
:key="prompt"
Expand Down
65 changes: 17 additions & 48 deletions apps/main-app/pages/profile/settings/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,49 +46,16 @@ const SettingsAccountValidation = z.object({
const currentUser = useCurrentUser()
const {
store: userProfile,
loadMore,
refresh,
} = useSelectData<User>('user_profiles', {
columns: 'id, given_name, surname, email, avatar, dob, username',
filters: { id: { eq: currentUser.profile.id } },
initialFetch: true,
limit: 1,
})
const profileCopy = ref({})
watch(userProfile, () => {
profileCopy.value = { ...userProfile.items[0] }
onMounted(() => {
profileCopy.value = { ...currentUser.profile }
})
const { uploadFile, isUploading, uploadProgress } = useFileUpload()
const handleFileUpload = async (event: Event) => {
const file = (event.target as HTMLInputElement).files?.[0]
if (!file) return
try {
const result = await uploadFile(file, {
bucket: 'users',
path: 'profile-images',
fileType: 'profile',
serverSideOptimize: true, // Enable server-side optimization
maxWidth: 800,
maxHeight: 800,
quality: 80,
format: 'webp',
maxFileSize: 5 * 1024 * 1024, // 5MB
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
onProgress: (progress) => {
console.log(`Upload progress: ${progress}%`)
},
})
console.log('File uploaded successfully:', result)
} catch (error) {
console.error('Error uploading file:', error)
}
const updateProfileImage = (newImage: string) => {
const avatar = `${newImage}?v=${Date.now()}`
currentUser.updateProfile({ avatar })
profileCopy.value.avatar = avatar
}
definePageMeta({
Expand All @@ -101,7 +68,7 @@ definePageMeta({
<template>
<div>
<UserSettingsCard
v-if="userProfile"
v-if="currentUser"
:title="{
main: 'Account Profile',
subtitle: 'Update your account information',
Expand All @@ -111,18 +78,20 @@ definePageMeta({
<div
class="left-16 flex h-32 w-32 items-center justify-center overflow-hidden rounded-full bg-red-50"
>
<IBImage
v-if="userProfile?.avatar"
:img="{
src: userProfile.avatar,
type: 'avatar',
}"
class="h-full w-full"
<PrimeAvatar
v-if="profileCopy && profileCopy.avatar"
:image="profileCopy.avatar"
shape="circle"
class="w-full h-full cursor-pointer"
aria-haspopup="true"
aria-controls="overlay_menu"
crossorigin="anonymous"
/>
<UploadCropper
cropper-type="avatar"
class="absolute z-20"
class="absolute bottom-0 z-20"
bucket="profile-public"
@profile-pic-update="updateProfileImage"
/>
</div>
</div>
Expand Down
13 changes: 6 additions & 7 deletions apps/main-app/server/utils/rateLimiter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { serverSupabaseUser } from '#supabase/server'

type PlanKey = 'free' | 'basic' | 'intermediate' | 'premium'
type FeatureKey = 'ask'

Expand Down Expand Up @@ -50,17 +52,14 @@ const getFeatureFromPath = (path: string): FeatureKey => {
export async function rateLimiter() {
const event = useEvent()
const feature = getFeatureFromPath(event.path)
const permissions = await getUserPermissions()
const user = await serverSupabaseUser(event)

if (!permissions) {
if (!user) {
throw createError({ message: 'User not found, You must be logged in to use this endpoint' })
}

const user = permissions.user

const userPlan: PlanKey = (user.user_plan as PlanKey) ?? 'free'
const userPlan = (user?.app_metadata.plan as PlanKey) ?? 'free'
const storage = useStorage('session')
const storageKey = `rateLimit:endpoint:${userPlan}:${user.user_id}`
const storageKey = `rateLimit:endpoint:${userPlan}:${user?.id}`
const settings = rateLimitConfig[userPlan][feature]

let rateLimit = await storage.getItem<RateLimitInfo>(storageKey)
Expand Down
10 changes: 6 additions & 4 deletions layers/auth/composables/user.current.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const useCurrentUser = defineStore(DOMAIN_KEY, () => {
last_sign_in_at: user.value?.last_sign_in_at,
email: user.value?.email,
providers: user.value?.app_metadata.providers,
avatar: user.value?.user_metadata.avatar ?? user.value?.user_metadata.avatar_url,
avatar: user.value?.user_metadata.avatar || user.value?.user_metadata.avatar_url,
provider: user.value?.provider,
user_role: user.value?.app_metadata?.role,
user_plan: user.value?.app_metadata?.plan,
Expand Down Expand Up @@ -107,6 +107,7 @@ export const useCurrentUser = defineStore(DOMAIN_KEY, () => {

if (response.error) {
toast.error({ summary: 'Could not update profile', message: response.error.message })
return
} else {
toast.success({
summary: 'Profile updated successfully',
Expand All @@ -118,10 +119,11 @@ export const useCurrentUser = defineStore(DOMAIN_KEY, () => {

// update state
logger.debug('Updating user profile state')
for (const key in validData[0]) {
if (Object.hasOwnProperty.call(validData[0], key)) {
for (const key in data) {
if (Object.hasOwnProperty.call(data, key)) {
profile.value[key] = data[key]
logger.debug(`Updating profile field: ${key}`, {
newValue: validData[0][key],
newValue: data[key],
})
}
}
Expand Down
7 changes: 4 additions & 3 deletions layers/crud/composables/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface UploadOptions {
allowedMimeTypes?: string[]
serverSideOptimize?: boolean
useServerUpload?: boolean
replace?: boolean
}

interface UploadResult {
Expand All @@ -45,11 +46,11 @@ export function useFileUpload() {
const isProcessing = computed(() => uploadQueue.value.length > 0 || currentUpload.value !== null)

const getFilePath = (fileName: string, options: UploadOptions): string => {
const { bucket, path, fileType, userId } = options
const { bucket, path, fileType, userId, replace } = options
const timestamp = new Date().toISOString().replace(/[-:]/g, '').split('.')[0]
const uniqueId = uuidv4().slice(0, 8)
const userPath = userId ? `${userId}/` : ''
return `${bucket}/${fileType}/${userPath}/${timestamp}_${uniqueId}_${fileName}`
return `${bucket}/${fileType}/${userPath}${path || ''}/${replace ? '' : `${timestamp}_${uniqueId}_`}${fileName}`
}

const validateFile = (file: File, options: UploadOptions): void => {
Expand Down Expand Up @@ -141,7 +142,7 @@ export function useFileUpload() {
const filePath = getFilePath(file.name, options)
const { data, error } = await supabase.storage.from(options.bucket).upload(filePath, file, {
cacheControl: '3600',
upsert: false,
upsert: options.replace,
contentType: file.type,
})

Expand Down

0 comments on commit 0a46650

Please sign in to comment.