mirror of
https://github.com/ArthurDanjou/arthome.git
synced 2026-01-14 12:14:33 +01:00
Working
This commit is contained in:
@@ -30,7 +30,7 @@ const { canCreateTabInCategory } = await useUserLimits()
|
|||||||
<UDropdown
|
<UDropdown
|
||||||
:items="dropdownItems"
|
:items="dropdownItems"
|
||||||
:popper="{ placement: 'bottom-end', arrow: true }"
|
:popper="{ placement: 'bottom-end', arrow: true }"
|
||||||
:ui="{ width: 'w-40', shadow: 'shadow-xl' }"
|
:ui="{ container: 'group z-50', width: 'w-40', shadow: 'shadow-xl' }"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
color="white"
|
color="white"
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ function visitLink() {
|
|||||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full opacity-75" :class="`bg-${tab.color}-400`" />
|
<span class="animate-ping absolute inline-flex h-full w-full rounded-full opacity-75" :class="`bg-${tab.color}-400`" />
|
||||||
<span class="relative inline-flex rounded-full h-3 w-3" :class="`bg-${tab.color}-400`" />
|
<span class="relative inline-flex rounded-full h-3 w-3" :class="`bg-${tab.color}-400`" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between h-full">
|
<div class="flex items-center justify-between h-full z-20">
|
||||||
<div class="flex gap-4 items-center h-full">
|
<div class="flex gap-4 items-center h-full">
|
||||||
<UBadge :color="tab.color" class="p-2" variant="soft">
|
<UBadge :color="tab.color" class="p-2" variant="soft">
|
||||||
<UIcon :name="tab.icon" size="32" />
|
<UIcon :name="tab.icon" size="32" />
|
||||||
@@ -83,7 +83,7 @@ function visitLink() {
|
|||||||
<UDropdown
|
<UDropdown
|
||||||
:items="items"
|
:items="items"
|
||||||
:popper="{ placement: 'bottom-end', arrow: true }"
|
:popper="{ placement: 'bottom-end', arrow: true }"
|
||||||
:ui="{ container: 'z-50 group', width: 'w-40', shadow: 'shadow-2xl', wrapper: 'absolute inline-flex -top-3 -right-3' }"
|
:ui="{ container: 'z-40 group', width: 'w-40', shadow: 'shadow-2xl', wrapper: 'absolute inline-flex -top-3 -right-3' }"
|
||||||
>
|
>
|
||||||
<UButton
|
<UButton
|
||||||
v-show="editMode"
|
v-show="editMode"
|
||||||
|
|||||||
@@ -28,27 +28,18 @@ watchEffect(() => {
|
|||||||
state.email = props.user.email
|
state.email = props.user.email
|
||||||
})
|
})
|
||||||
|
|
||||||
async function handleUpdate(event: FormSubmitEvent<UpdateUserSchemaType>) {
|
const { deleteAvatar, uploadAvatar, updateUser } = await useUser()
|
||||||
try {
|
|
||||||
await useRequestFetch()(`/api/users/me`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify({
|
|
||||||
username: event.data.username,
|
|
||||||
name: event.data.name,
|
|
||||||
description: event.data.description,
|
|
||||||
location: event.data.location,
|
|
||||||
language: event.data.language,
|
|
||||||
private: event.data.private,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
useSuccessToast('Profile successfully updated!')
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
useErrorToast('Profile update failed!', error as string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { deleteAvatar, uploadAvatar } = await useUser()
|
async function handleUpdate(event: FormSubmitEvent<UpdateUserSchemaType>) {
|
||||||
|
await updateUser({
|
||||||
|
username: event.data.username,
|
||||||
|
name: event.data.name,
|
||||||
|
description: event.data.description,
|
||||||
|
location: event.data.location,
|
||||||
|
language: event.data.language,
|
||||||
|
private: event.data.private,
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
const config = useRuntimeConfig()
|
|
||||||
|
|
||||||
const coordinates = ref<[number, number]>([2.179040, 48.877419])
|
|
||||||
const zoom = ref(11)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ClientOnly>
|
|
||||||
<UCard :ui="{ base: 'h-72 md:col-span-2', body: { padding: '' } }">
|
|
||||||
<div class="relative">
|
|
||||||
<MapboxMap
|
|
||||||
:options="{
|
|
||||||
accessToken: config.public.mapbox.accessToken,
|
|
||||||
style: config.public.mapbox.style,
|
|
||||||
center: coordinates,
|
|
||||||
zoom,
|
|
||||||
projection: 'globe',
|
|
||||||
}"
|
|
||||||
class="absolute h-72"
|
|
||||||
map-id="map"
|
|
||||||
>
|
|
||||||
<MapboxDefaultMarker
|
|
||||||
:lnglat="coordinates"
|
|
||||||
:options="{
|
|
||||||
color: '#808080',
|
|
||||||
size: 1.5,
|
|
||||||
}"
|
|
||||||
marker-id="marker"
|
|
||||||
/>
|
|
||||||
</MapboxMap>
|
|
||||||
</div>
|
|
||||||
</UCard>
|
|
||||||
</ClientOnly>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.mapboxgl-control-container {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mapboxgl-canvas {
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import type { WeatherType } from '~~/types/types'
|
|
||||||
|
|
||||||
const { data: weather } = await useFetch<WeatherType>('/api/weather')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<section>
|
|
||||||
<UCard v-if="weather" :ui="{ base: 'h-full' }">
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center gap-2 text-blue-600 dark:text-blue-300">
|
|
||||||
<UIcon name="i-ph:cloud-duotone" size="24" />
|
|
||||||
<h3 class="font-bold text-lg">
|
|
||||||
Météo
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #default>
|
|
||||||
<div class="flex items-center h-full">
|
|
||||||
<p class="text-lg h-full">
|
|
||||||
Il fait actuellement
|
|
||||||
<span class="text-blue-600 dark:text-blue-300">{{ weather.weather }}</span>
|
|
||||||
à
|
|
||||||
<span>{{ weather.city }}</span>,
|
|
||||||
avec une température de
|
|
||||||
<span class="text-blue-600 dark:text-blue-300">{{ weather.temp }}
|
|
||||||
</span>
|
|
||||||
°C
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UCard>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
@@ -14,7 +14,7 @@ export async function useCategories() {
|
|||||||
await useSuccessToast('Category successfully created!', category.color)
|
await useSuccessToast('Category successfully created!', category.color)
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Category creation failed!', error as string)
|
useErrorToast('Category creation failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export async function useCategories() {
|
|||||||
await useSuccessToast('Category successfully updated!')
|
await useSuccessToast('Category successfully updated!')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Category update failed!', error as string)
|
useErrorToast('Category update failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export async function useCategories() {
|
|||||||
await useSuccessToast('Category successfully deleted!')
|
await useSuccessToast('Category successfully deleted!')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Category deletion failed!', error as string)
|
useErrorToast('Category deletion failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export async function useTabs() {
|
|||||||
useSuccessToast('Tab successfully created!', tab.color)
|
useSuccessToast('Tab successfully created!', tab.color)
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Tab creation failed!', error as string)
|
useErrorToast('Tab creation failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export async function useTabs() {
|
|||||||
useSuccessToast('Tab successfully updated!')
|
useSuccessToast('Tab successfully updated!')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Tab update failed!', error as string)
|
useErrorToast('Tab update failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export async function useTabs() {
|
|||||||
useSuccessToast(`Tab ${tab.name} ${primary ? 'set as favorite' : 'unset as favorite'}!`, 'yellow')
|
useSuccessToast(`Tab ${tab.name} ${primary ? 'set as favorite' : 'unset as favorite'}!`, 'yellow')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Cannot toggle favorite state for tab!', error as string)
|
useErrorToast('Cannot toggle favorite state for tab!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ export async function useTabs() {
|
|||||||
useSuccessToast('Tab successfully deleted!')
|
useSuccessToast('Tab successfully deleted!')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('Tab deletion failed!', error as string)
|
useErrorToast('Tab deletion failed!', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ export async function useUserLimits() {
|
|||||||
function canCreateCategory() {
|
function canCreateCategory() {
|
||||||
if (hasPaidPlan.value)
|
if (hasPaidPlan.value)
|
||||||
return true
|
return true
|
||||||
|
if (!userLimits.value.categories)
|
||||||
|
return false
|
||||||
return userLimits.value.categories.length < MAX_CATEGORIES
|
return userLimits.value.categories.length < MAX_CATEGORIES
|
||||||
}
|
}
|
||||||
|
|
||||||
function canCreateTabInCategory(categoryId: number): boolean {
|
function canCreateTabInCategory(categoryId: number): boolean {
|
||||||
if (hasPaidPlan.value)
|
if (hasPaidPlan.value)
|
||||||
return true
|
return true
|
||||||
|
if (!userLimits.value.categories || !userLimits.value.categories.find(category => category.id === categoryId))
|
||||||
|
return false
|
||||||
return userLimits.value.categories.find(category => category.id === categoryId).tabs.length < MAX_TABS_PER_CATEGORY
|
return userLimits.value.categories.find(category => category.id === categoryId).tabs.length < MAX_TABS_PER_CATEGORY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
export async function useUser() {
|
export async function useUser() {
|
||||||
const { fetch } = useUserSession()
|
const { fetch, session } = useUserSession()
|
||||||
|
|
||||||
async function deleteAvatar() {
|
async function deleteAvatar() {
|
||||||
try {
|
try {
|
||||||
await useRequestFetch()('/api/users/avatars', {
|
await $fetch('/api/users/avatars', {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
})
|
})
|
||||||
useSuccessToast('Avatar successfully deleted!')
|
useSuccessToast('Avatar successfully deleted!')
|
||||||
await fetch()
|
await fetch()
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('An error occurred while deleting your avatar', error as string)
|
useErrorToast('An error occurred while deleting your avatar', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export async function useUser() {
|
|||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await useRequestFetch()('/api/users/avatars', {
|
await $fetch('/api/users/avatars', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
})
|
})
|
||||||
@@ -32,12 +32,29 @@ export async function useUser() {
|
|||||||
useSuccessToast('Avatar successfully uploaded!')
|
useSuccessToast('Avatar successfully uploaded!')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
useErrorToast('An error occurred while uploading your avatar', error as string)
|
useErrorToast('An error occurred while uploading your avatar', String(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateUser(user: Partial<UserInsert>) {
|
||||||
|
try {
|
||||||
|
await $fetch('/api/users/me', {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify(user),
|
||||||
|
})
|
||||||
|
console.log(session.value)
|
||||||
|
await fetch()
|
||||||
|
console.log(session.value)
|
||||||
|
useSuccessToast('User successfully updated!')
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
useErrorToast('An error occurred while updating your user', String(error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deleteAvatar,
|
deleteAvatar,
|
||||||
uploadAvatar,
|
uploadAvatar,
|
||||||
|
updateUser,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ onMounted(() => {
|
|||||||
setInterval(() => date.value = new Date(), 1000)
|
setInterval(() => date.value = new Date(), 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
const { user } = await useUserSession()
|
const { user, loggedIn } = await useUserSession()
|
||||||
const { categories } = await useCategories()
|
const { categories } = await useCategories()
|
||||||
const { getTabsForCategory } = await useTabs()
|
const { getTabsForCategory } = await useTabs()
|
||||||
const { canCreateCategory } = await useUserLimits()
|
const { canCreateCategory } = await useUserLimits()
|
||||||
@@ -94,7 +94,7 @@ defineShortcuts({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main v-if="user" class="my-12">
|
<main v-if="user && loggedIn" class="my-12">
|
||||||
<div v-if="date" class="flex flex-col items-center mb-12">
|
<div v-if="date" class="flex flex-col items-center mb-12">
|
||||||
<h1 class="text-6xl md:text-9xl font-bold">
|
<h1 class="text-6xl md:text-9xl font-bold">
|
||||||
{{ useDateFormat(date, 'HH') }}
|
{{ useDateFormat(date, 'HH') }}
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ export default defineNuxtConfig({
|
|||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
'@nuxtjs/google-fonts',
|
'@nuxtjs/google-fonts',
|
||||||
'nuxt-auth-utils',
|
'nuxt-auth-utils',
|
||||||
'@nuxt/content',
|
|
||||||
'@nuxthq/studio',
|
|
||||||
'@nuxt/image',
|
'@nuxt/image',
|
||||||
'nuxt-mapbox',
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// Nuxt UI
|
// Nuxt UI
|
||||||
@@ -77,22 +74,9 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
// Nuxt Env
|
// Nuxt Env
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
openWeather: {
|
|
||||||
apiKey: '',
|
|
||||||
lat: '',
|
|
||||||
lon: '',
|
|
||||||
lang: '',
|
|
||||||
units: '',
|
|
||||||
},
|
|
||||||
postgres: {
|
postgres: {
|
||||||
url: '',
|
url: '',
|
||||||
dir: './server/db',
|
dir: './server/db',
|
||||||
},
|
},
|
||||||
public: {
|
|
||||||
mapbox: {
|
|
||||||
style: '',
|
|
||||||
accessToken: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt dev --host",
|
"dev": "nuxt dev --host",
|
||||||
"remote": "nuxt dev --remote --host",
|
"remote": "nuxt dev --remote --host",
|
||||||
"postinstall": "nuxt prepare",
|
"postinstall": "nuxt prepare && node script.cjs",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"db:generate": "drizzle-kit generate",
|
"db:generate": "drizzle-kit generate",
|
||||||
"db:migrate": "drizzle-kit migrate",
|
"db:migrate": "drizzle-kit migrate",
|
||||||
@@ -14,9 +14,7 @@
|
|||||||
"db:pull": "drizzle-kit pull"
|
"db:pull": "drizzle-kit pull"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/content": "^2.13.2",
|
|
||||||
"@nuxt/image": "^1.7.1",
|
"@nuxt/image": "^1.7.1",
|
||||||
"@nuxthq/studio": "^2.0.3",
|
|
||||||
"@nuxthub/core": "^0.7.7",
|
"@nuxthub/core": "^0.7.7",
|
||||||
"@nuxtjs/google-fonts": "^3.2.0",
|
"@nuxtjs/google-fonts": "^3.2.0",
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
@@ -37,9 +35,7 @@
|
|||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"drizzle-kit": "^0.24.2",
|
"drizzle-kit": "^0.24.2",
|
||||||
"eslint": "^9.9.1",
|
"eslint": "^9.9.1",
|
||||||
"mapbox-gl": "^3.6.0",
|
|
||||||
"nuxt": "^3.13.0",
|
"nuxt": "^3.13.0",
|
||||||
"nuxt-mapbox": "^1.6.0",
|
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"vue-tsc": "^2.0.29",
|
"vue-tsc": "^2.0.29",
|
||||||
"wrangler": "^3.72.3"
|
"wrangler": "^3.72.3"
|
||||||
|
|||||||
1938
pnpm-lock.yaml
generated
1938
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
25
script.cjs
Normal file
25
script.cjs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const fs = require('node:fs')
|
||||||
|
const path = require('node:path')
|
||||||
|
|
||||||
|
const filesToModify = [
|
||||||
|
'node_modules/drizzle-orm/pg-core/columns/timestamp.js',
|
||||||
|
'node_modules/drizzle-orm/pg-core/columns/timestamp.cjs',
|
||||||
|
]
|
||||||
|
|
||||||
|
filesToModify.forEach((file) => {
|
||||||
|
const filePath = path.join(__dirname, file)
|
||||||
|
console.log(`Checking path: ${filePath}`)
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
let fileContent = fs.readFileSync(filePath, 'utf8')
|
||||||
|
fileContent = fileContent.replace(
|
||||||
|
'return value.toISOString()',
|
||||||
|
'return value',
|
||||||
|
)
|
||||||
|
fs.writeFileSync(filePath, fileContent, 'utf8')
|
||||||
|
console.log(`Modified: ${file}`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(`File not found: ${filePath}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -7,7 +7,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(tables.categories.id, id),
|
eq(tables.categories.id, id),
|
||||||
eq(tables.categories.userId, user.id),
|
eq(tables.categories.userId, user.user.id),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return { statusCode: 200 }
|
return { statusCode: 200 }
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(tables.categories.id, id),
|
eq(tables.categories.id, id),
|
||||||
eq(tables.categories.userId, user.id),
|
eq(tables.categories.userId, user.user.id),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return { statusCode: 200 }
|
return { statusCode: 200 }
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
const user = await getUserSession(event)
|
const user = await getUserSession(event)
|
||||||
const body = await useValidatedBody(event, CreateCategorySchema)
|
const body = await useValidatedBody(event, CreateCategorySchema)
|
||||||
await useDrizzle().insert(tables.categories).values({
|
await useDrizzle().insert(tables.categories).values({
|
||||||
userId: user.id,
|
userId: user.user.id,
|
||||||
...body,
|
...body,
|
||||||
})
|
})
|
||||||
return { statusCode: 200 }
|
return { statusCode: 200 }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const { user, session } = await requireUserSession(event)
|
const { user } = await requireUserSession(event)
|
||||||
|
|
||||||
if (!user.avatar) {
|
if (!user.avatar) {
|
||||||
return sendNoContent(event, 204)
|
return sendNoContent(event, 204)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
const avatar = await hubBlob().put(filename, file, {
|
const avatar = await hubBlob().put(filename, file, {
|
||||||
addRandomSuffix: false,
|
addRandomSuffix: false,
|
||||||
prefix: 'avatars/',
|
prefix: 'avatars',
|
||||||
})
|
})
|
||||||
|
|
||||||
const updatedUser = {
|
const updatedUser = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const user = await requireUserSession(event)
|
const user = await requireUserSession(event)
|
||||||
return useDrizzle().query.users.findFirst({
|
return useDrizzle().query.users.findFirst({
|
||||||
where: eq(tables.users.id, user.id),
|
where: eq(tables.users.id, user.user.id),
|
||||||
with: {
|
with: {
|
||||||
categories: {
|
categories: {
|
||||||
with: {
|
with: {
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await updateUser(user.id, updatedUser)
|
await updateUser(user.id, updatedUser)
|
||||||
await replaceUserSession(event, updatedUser)
|
await setUserSession(event, {
|
||||||
|
id: user.id,
|
||||||
|
user: updatedUser,
|
||||||
|
})
|
||||||
|
|
||||||
return sendNoContent(event, 204)
|
return sendNoContent(event, 204)
|
||||||
})
|
})
|
||||||
@@ -11,8 +11,8 @@ CREATE TABLE IF NOT EXISTS "categories" (
|
|||||||
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone',
|
"icon" text DEFAULT 'i-ph:circle-wavy-question-duotone',
|
||||||
"color" text DEFAULT 'gray',
|
"color" text DEFAULT 'gray',
|
||||||
"user_id" integer NOT NULL,
|
"user_id" integer NOT NULL,
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp(0) with time zone DEFAULT now(),
|
||||||
"updated_at" timestamp (3)
|
"updated_at" timestamp(0) with time zone DEFAULT now()
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE IF NOT EXISTS "tabs" (
|
CREATE TABLE IF NOT EXISTS "tabs" (
|
||||||
@@ -23,8 +23,8 @@ CREATE TABLE IF NOT EXISTS "tabs" (
|
|||||||
"color" text DEFAULT 'gray',
|
"color" text DEFAULT 'gray',
|
||||||
"link" text DEFAULT '',
|
"link" text DEFAULT '',
|
||||||
"category_id" integer NOT NULL,
|
"category_id" integer NOT NULL,
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp(0) with time zone DEFAULT now(),
|
||||||
"updated_at" timestamp (3)
|
"updated_at" timestamp(0) with time zone DEFAULT now()
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
CREATE TABLE IF NOT EXISTS "users" (
|
CREATE TABLE IF NOT EXISTS "users" (
|
||||||
@@ -42,8 +42,8 @@ CREATE TABLE IF NOT EXISTS "users" (
|
|||||||
"language" text DEFAULT 'en-EN',
|
"language" text DEFAULT 'en-EN',
|
||||||
"location" text DEFAULT 'unknown',
|
"location" text DEFAULT 'unknown',
|
||||||
"subscription" "subscription" DEFAULT 'free',
|
"subscription" "subscription" DEFAULT 'free',
|
||||||
"created_at" timestamp (3) DEFAULT now(),
|
"created_at" timestamp(0) with time zone DEFAULT now(),
|
||||||
"updated_at" timestamp (3),
|
"updated_at" timestamp(0) with time zone DEFAULT now(),
|
||||||
CONSTRAINT "users_username_unique" UNIQUE("username"),
|
CONSTRAINT "users_username_unique" UNIQUE("username"),
|
||||||
CONSTRAINT "users_email_unique" UNIQUE("email"),
|
CONSTRAINT "users_email_unique" UNIQUE("email"),
|
||||||
CONSTRAINT "users_github_id_unique" UNIQUE("github_id"),
|
CONSTRAINT "users_github_id_unique" UNIQUE("github_id"),
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"id": "37cfa6b0-d0e9-4999-9ead-06b419388528",
|
"id": "21470761-4c33-4588-be9b-4927bbcbfe2c",
|
||||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"dialect": "postgresql",
|
"dialect": "postgresql",
|
||||||
@@ -50,16 +50,17 @@
|
|||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "now()"
|
"default": "now()"
|
||||||
},
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"name": "updated_at",
|
"name": "updated_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
@@ -134,16 +135,17 @@
|
|||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "now()"
|
"default": "now()"
|
||||||
},
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"name": "updated_at",
|
"name": "updated_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
@@ -262,16 +264,17 @@
|
|||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false,
|
"notNull": false,
|
||||||
"default": "now()"
|
"default": "now()"
|
||||||
},
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"name": "updated_at",
|
"name": "updated_at",
|
||||||
"type": "timestamp (3)",
|
"type": "timestamp(0) with time zone",
|
||||||
"primaryKey": false,
|
"primaryKey": false,
|
||||||
"notNull": false
|
"notNull": false,
|
||||||
|
"default": "now()"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes": {},
|
"indexes": {},
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
{
|
{
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"version": "7",
|
"version": "7",
|
||||||
"when": 1725282814515,
|
"when": 1725302227098,
|
||||||
"tag": "0000_cloudy_lifeguard",
|
"tag": "0000_noisy_randall_flagg",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,28 +3,6 @@ export default oauthGitHubEventHandler({
|
|||||||
emailRequired: true,
|
emailRequired: true,
|
||||||
},
|
},
|
||||||
async onSuccess(event, { user: oauthUser, tokens }) {
|
async onSuccess(event, { user: oauthUser, tokens }) {
|
||||||
const userSession = await getUserSession(event)
|
|
||||||
|
|
||||||
// If the user is already signed in, link the account
|
|
||||||
if (userSession?.id) {
|
|
||||||
const user = await findUserById(userSession.id)
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
await updateUser(userSession.id, {
|
|
||||||
githubId: oauthUser.id,
|
|
||||||
githubToken: tokens.access_token,
|
|
||||||
})
|
|
||||||
|
|
||||||
await setUserSession(event, {
|
|
||||||
id: userSession.id,
|
|
||||||
user: userSession,
|
|
||||||
githubId: oauthUser.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return sendRedirect(event, '/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user is not signed in, search for an existing user with that GitHub ID
|
// If the user is not signed in, search for an existing user with that GitHub ID
|
||||||
// If it exists, sign in as that user and refresh the token
|
// If it exists, sign in as that user and refresh the token
|
||||||
let user = await findUserByGitHubId(oauthUser.id)
|
let user = await findUserByGitHubId(oauthUser.id)
|
||||||
@@ -35,7 +13,7 @@ export default oauthGitHubEventHandler({
|
|||||||
githubToken: tokens.access_token,
|
githubToken: tokens.access_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
await setUserSession(event, {
|
await replaceUserSession(event, {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
@@ -76,9 +54,9 @@ export default oauthGitHubEventHandler({
|
|||||||
subscription: 'free',
|
subscription: 'free',
|
||||||
})
|
})
|
||||||
|
|
||||||
await setUserSession(event, {
|
await replaceUserSession(event, {
|
||||||
id: createdUser.id,
|
id: createdUser.id,
|
||||||
user: createdUser,
|
user: createdUser[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
return sendRedirect(event, '/')
|
return sendRedirect(event, '/')
|
||||||
|
|||||||
@@ -4,28 +4,6 @@ export default oauthGoogleEventHandler({
|
|||||||
scope: ['email', 'profile'],
|
scope: ['email', 'profile'],
|
||||||
},
|
},
|
||||||
async onSuccess(event, { user: oauthUser, tokens }) {
|
async onSuccess(event, { user: oauthUser, tokens }) {
|
||||||
const userSession = await getUserSession(event)
|
|
||||||
|
|
||||||
// If the user is already signed in, link the account
|
|
||||||
if (userSession?.id) {
|
|
||||||
const user = await findUserById(userSession.id)
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
await updateUser(userSession.id, {
|
|
||||||
googleId: oauthUser.sub,
|
|
||||||
googleToken: tokens.access_token,
|
|
||||||
})
|
|
||||||
|
|
||||||
await setUserSession(event, {
|
|
||||||
id: userSession.id,
|
|
||||||
user: userSession,
|
|
||||||
googleId: oauthUser.sub,
|
|
||||||
})
|
|
||||||
|
|
||||||
return sendRedirect(event, '/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user is not signed in, search for an existing user with that Google ID
|
// If the user is not signed in, search for an existing user with that Google ID
|
||||||
// If it exists, sign in as that user and refresh the token
|
// If it exists, sign in as that user and refresh the token
|
||||||
let user = await findUserByGoogleId(oauthUser.sub)
|
let user = await findUserByGoogleId(oauthUser.sub)
|
||||||
@@ -36,7 +14,7 @@ export default oauthGoogleEventHandler({
|
|||||||
googleToken: tokens.access_token,
|
googleToken: tokens.access_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
await setUserSession(event, {
|
await replaceUserSession(event, {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
@@ -77,9 +55,9 @@ export default oauthGoogleEventHandler({
|
|||||||
subscription: 'free',
|
subscription: 'free',
|
||||||
})
|
})
|
||||||
|
|
||||||
await setUserSession(event, {
|
await replaceUserSession(event, {
|
||||||
id: createdUser.id,
|
id: createdUser.id,
|
||||||
user: createdUser,
|
user: createdUser[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
return sendRedirect(event, '/')
|
return sendRedirect(event, '/')
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { serial, timestamp } from 'drizzle-orm/pg-core'
|
|||||||
* A centralized list of standardized Drizzle ORM schema field definitions to prevent duplication errors
|
* A centralized list of standardized Drizzle ORM schema field definitions to prevent duplication errors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const createdAt = timestamp('created_at', { mode: 'date', precision: 3 }).defaultNow()
|
export const createdAt = timestamp('created_at', { mode: 'string', withTimezone: true, precision: 0 }).defaultNow()
|
||||||
export const updatedAt = timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date())
|
export const updatedAt = timestamp('updated_at', { mode: 'string', withTimezone: true, precision: 0 }).defaultNow().$onUpdateFn(() => sql`(current_timestamp)`)
|
||||||
export const id = serial('id').primaryKey()
|
export const id = serial('id').primaryKey()
|
||||||
|
|
||||||
export const timestamps = {
|
export const timestamps = {
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
import type { SQL } from 'drizzle-orm'
|
import type { SQL } from 'drizzle-orm'
|
||||||
import type { UserInsert } from '~~/server/utils/db'
|
import type { UserInsert } from '~~/server/utils/db'
|
||||||
|
|
||||||
export async function findUserById(userId: number) {
|
|
||||||
return useDrizzle()
|
|
||||||
.query
|
|
||||||
.users
|
|
||||||
.findFirst({
|
|
||||||
where: eq(tables.users.id, userId),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function findUserByGitHubId(githubId: number) {
|
export async function findUserByGitHubId(githubId: number) {
|
||||||
return useDrizzle()
|
return useDrizzle()
|
||||||
.query
|
.query
|
||||||
@@ -41,18 +32,21 @@ export async function createUser(user: UserInsert) {
|
|||||||
return useDrizzle()
|
return useDrizzle()
|
||||||
.insert(tables.users)
|
.insert(tables.users)
|
||||||
.values(user)
|
.values(user)
|
||||||
|
.onConflictDoNothing()
|
||||||
.returning()
|
.returning()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateUser(userId: number, user: Partial<UserInsert>) {
|
export async function updateUser(userId: number, user: Partial<UserInsert>) {
|
||||||
await useDrizzle()
|
await useDrizzle()
|
||||||
.update(tables.users)
|
.update(tables.users)
|
||||||
.set(user)
|
.set({
|
||||||
|
...user,
|
||||||
|
})
|
||||||
.where(eq(tables.users.id, userId))
|
.where(eq(tables.users.id, userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteProfilePicture(avatar: string) {
|
export async function deleteProfilePicture(avatar: string) {
|
||||||
if (avatar.startsWith('avatars/')) {
|
if (avatar.startsWith('avatars')) {
|
||||||
await hubBlob().del(avatar)
|
await hubBlob().del(avatar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user