diff --git a/app/app.vue b/app/app.vue index 32cc8e6..0b922ae 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,22 +1,15 @@ + + + + diff --git a/app/components/Tab.vue b/app/components/Tab.vue new file mode 100644 index 0000000..a2b8b82 --- /dev/null +++ b/app/components/Tab.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/app/components/Application.vue b/app/components/old/Application.vue similarity index 100% rename from app/components/Application.vue rename to app/components/old/Application.vue diff --git a/app/components/Map.vue b/app/components/old/Map.vue similarity index 100% rename from app/components/Map.vue rename to app/components/old/Map.vue diff --git a/app/components/Weather.vue b/app/components/old/Weather.vue similarity index 100% rename from app/components/Weather.vue rename to app/components/old/Weather.vue diff --git a/app/composables/authorization.ts b/app/composables/authorization.ts deleted file mode 100644 index cb69077..0000000 --- a/app/composables/authorization.ts +++ /dev/null @@ -1,10 +0,0 @@ -export async function isAuthorized() { - const { user } = useUserSession() - const { data: authorized } = await useFetch('/api/authorized', { - method: 'POST', - body: { - email: user.value?.email ?? 'test@nuxt.com', - }, - }) - return authorized.value -} diff --git a/app/composables/categories.ts b/app/composables/categories.ts new file mode 100644 index 0000000..3c9fec2 --- /dev/null +++ b/app/composables/categories.ts @@ -0,0 +1,13 @@ +export function useCategories() { + async function getCategories() { + return useAsyncData(async () => { + const res = await $fetch('/api/categories') + console.log('res', res) + return res + }) + } + + return { + getCategories, + } +} diff --git a/app/composables/tabs.ts b/app/composables/tabs.ts new file mode 100644 index 0000000..f077756 --- /dev/null +++ b/app/composables/tabs.ts @@ -0,0 +1,21 @@ +export function useTabs() { + async function createTab(tab: TabType) { + console.log('createTab', tab) + return tab + } + + async function deleteTab(tab: TabType) { + console.log('deleteTab', tab) + return tab + } + + async function updateTab(tab: TabType) { + console.log('updateTab', tab) + return tab + } + + return { + createTab, + deleteTab, + } +} diff --git a/app/composables/toasts.ts b/app/composables/toasts.ts new file mode 100644 index 0000000..64a9006 --- /dev/null +++ b/app/composables/toasts.ts @@ -0,0 +1,21 @@ +export function useSuccessToast(title: string, description?: string) { + const toast = useToast() + + toast.add({ + title, + description, + color: 'green', + icon: 'i-ph:check-circle-duotone', + }) +} + +export function useErrorToast(title: string, description?: string) { + const toast = useToast() + + toast.add({ + title, + description, + color: 'red', + icon: 'i-ph:x-circle-duotone', + }) +} diff --git a/app/composables/user-limit.ts b/app/composables/user-limit.ts new file mode 100644 index 0000000..c591d8c --- /dev/null +++ b/app/composables/user-limit.ts @@ -0,0 +1,22 @@ +export function useUserLimit() { + function hasUserFreePlan() { + return true + } + + function getRemainingCategories() { + if (!hasUserFreePlan()) + return -1 + return 3 + } + + function getRemainingTabs(category_id: number) { + if (!hasUserFreePlan()) + return -1 + return category_id * 3 + } + + return { + getRemainingCategories, + getRemainingTabs, + } +} diff --git a/app/middleware/auth.ts b/app/middleware/auth.ts index 6fd88dd..6037818 100644 --- a/app/middleware/auth.ts +++ b/app/middleware/auth.ts @@ -1,8 +1,7 @@ export default defineNuxtRouteMiddleware(async () => { const { loggedIn } = useUserSession() - const authorized = await isAuthorized() - if (!loggedIn.value || !authorized) { - return navigateTo('/') + if (!loggedIn.value) { + return navigateTo('/login') } }) diff --git a/app/middleware/ghost.ts b/app/middleware/ghost.ts new file mode 100644 index 0000000..4f93b4c --- /dev/null +++ b/app/middleware/ghost.ts @@ -0,0 +1,7 @@ +export default defineNuxtRouteMiddleware(async () => { + const { loggedIn } = useUserSession() + + if (loggedIn.value) { + return navigateTo('/') + } +}) diff --git a/app/pages/[user].vue b/app/pages/[user].vue new file mode 100644 index 0000000..3e4f198 --- /dev/null +++ b/app/pages/[user].vue @@ -0,0 +1,13 @@ + + + + + diff --git a/app/pages/home.vue b/app/pages/home.vue deleted file mode 100644 index f8d8f98..0000000 --- a/app/pages/home.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/app/pages/index.vue b/app/pages/index.vue index 82d997b..bee0ebe 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,64 +1,54 @@ diff --git a/app/pages/login.vue b/app/pages/login.vue new file mode 100644 index 0000000..6217619 --- /dev/null +++ b/app/pages/login.vue @@ -0,0 +1,104 @@ + + + diff --git a/app/pages/profile.vue b/app/pages/profile.vue new file mode 100644 index 0000000..b26606c --- /dev/null +++ b/app/pages/profile.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/drizzle.config.ts b/drizzle.config.ts index 7ab5b0b..26e4610 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,7 +1,13 @@ import type { Config } from 'drizzle-kit' +import { config } from 'dotenv' + +config() export default { - dialect: 'sqlite', + dialect: 'postgresql', schema: './server/database/schema.ts', out: './server/database/migrations', + dbCredentials: { + url: process.env.NUXT_POSTGRES_URL, + }, } satisfies Config diff --git a/nuxt.config.ts b/nuxt.config.ts index 2ab326b..3f7b86b 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -35,7 +35,8 @@ export default defineNuxtConfig({ hub: { cache: true, analytics: true, - database: true, + blob: true, + kv: true, }, // Nuxt Icon @@ -79,6 +80,10 @@ export default defineNuxtConfig({ lang: '', units: '', }, + postgres: { + url: '', + dir: './server/db', + }, public: { mapbox: { style: '', diff --git a/package.json b/package.json index 9ea08bd..2d819a8 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "remote": "nuxt dev --remote --host", "postinstall": "nuxt prepare", "lint:fix": "eslint . --fix", - "db:generate": "drizzle-kit generate" + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:pull": "drizzle-kit pull" }, "dependencies": { "@nuxt/content": "^2.13.2", @@ -16,9 +19,10 @@ "@nuxthq/studio": "^2.0.3", "@nuxthub/core": "^0.7.3", "@nuxtjs/google-fonts": "^3.2.0", - "drizzle-orm": "^0.32.1", + "drizzle-orm": "^0.33.0", "h3-zod": "^0.5.3", "nuxt-auth-utils": "^0.3.4", + "postgres": "^3.4.4", "vue": "^3.4.38", "vue-router": "^4.4.3", "zod": "^3.23.8" @@ -28,9 +32,11 @@ "@nuxt/devtools": "^1.3.14", "@nuxt/ui": "^2.18.4", "@types/node": "^22.4.2", + "@types/pg": "^8.11.6", "@vueuse/core": "^11.0.1", "@vueuse/nuxt": "^11.0.1", - "drizzle-kit": "^0.23.0", + "dotenv": "^16.4.5", + "drizzle-kit": "^0.24.1", "eslint": "^9.9.0", "mapbox-gl": "^3.6.0", "nuxt": "^3.13.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29f8ba4..9b9da0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@nuxt/content': specifier: ^2.13.2 - version: 2.13.2(ioredis@5.4.1)(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) + version: 2.13.2(ioredis@5.4.1)(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) '@nuxt/image': specifier: ^1.7.0 version: 1.7.0(ioredis@5.4.1)(magicast@0.3.4)(rollup@4.21.0) @@ -24,14 +24,17 @@ importers: specifier: ^3.2.0 version: 3.2.0(magicast@0.3.4)(rollup@4.21.0) drizzle-orm: - specifier: ^0.32.1 - version: 0.32.2(@cloudflare/workers-types@4.20240821.1) + specifier: ^0.33.0 + version: 0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4) h3-zod: specifier: ^0.5.3 version: 0.5.3(h3@1.12.0)(zod@3.23.8) nuxt-auth-utils: specifier: ^0.3.4 version: 0.3.4(magicast@0.3.4)(rollup@4.21.0) + postgres: + specifier: ^3.4.4 + version: 3.4.4 vue: specifier: ^3.4.38 version: 3.4.38(typescript@5.5.4) @@ -54,15 +57,21 @@ importers: '@types/node': specifier: ^22.4.2 version: 22.5.0 + '@types/pg': + specifier: ^8.11.6 + version: 8.11.6 '@vueuse/core': specifier: ^11.0.1 version: 11.0.1(vue@3.4.38(typescript@5.5.4)) '@vueuse/nuxt': specifier: ^11.0.1 - version: 11.0.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) + version: 11.0.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 drizzle-kit: - specifier: ^0.23.0 - version: 0.23.2 + specifier: ^0.24.1 + version: 0.24.1 eslint: specifier: ^9.9.0 version: 9.9.0(jiti@1.21.6) @@ -71,7 +80,7 @@ importers: version: 3.6.0 nuxt: specifier: ^3.13.0 - version: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) + version: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) nuxt-mapbox: specifier: ^1.6.0 version: 1.6.0(magicast@0.3.4)(rollup@4.21.0)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6)) @@ -364,8 +373,8 @@ packages: peerDependencies: postcss-selector-parser: ^6.0.13 - '@drizzle-team/brocli@0.8.2': - resolution: {integrity: sha512-zTrFENsqGvOkBOuHDC1pXCkDXNd2UhP4lI3gYGhQ1R1SPeAAfqzPsV1dcpMy4uNU6kB5VpU5NGhvwxVNETR02A==} + '@drizzle-team/brocli@0.10.1': + resolution: {integrity: sha512-AHy0vjc+n/4w/8Mif+w86qpppHuF3AyXbcWW+R/W7GNA3F5/p2nuhlkCJaTXSLZheB4l1rtHzOfr9A7NwoR/Zg==} '@es-joy/jsdoccomment@0.43.1': resolution: {integrity: sha512-I238eDtOolvCuvtxrnqtlBaw0BwdQuYqK7eA6XIonicMdOOOb75mqdIzkGDUbS04+1Di007rgm9snFRNeVrOog==} @@ -1873,6 +1882,9 @@ packages: '@types/pbf@3.0.5': resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} + '@types/pg@8.11.6': + resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2965,12 +2977,12 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} - drizzle-kit@0.23.2: - resolution: {integrity: sha512-NWkQ7GD2OTbQ7HzcjsaCOf3n0tlFPSEAF38fvDpwDj8jRbGWGFtN2cD8I8wp4lU+5Os/oyP2xycTKGLHdPipUw==} + drizzle-kit@0.24.1: + resolution: {integrity: sha512-y47ZuFpy3ZEz5v2P4Q4m7CZpC9infdmFAVP8klfl92hyBBvgWlyFnriDkycK2HXqB1PrYWNTck2p5TfFY5+EWw==} hasBin: true - drizzle-orm@0.32.2: - resolution: {integrity: sha512-3fXKzPzrgZIcnWCSLiERKN5Opf9Iagrag75snfFlKeKSYB1nlgPBshzW3Zn6dQymkyiib+xc4nIz0t8U+Xdpuw==} + drizzle-orm@0.33.0: + resolution: {integrity: sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=3' @@ -4637,6 +4649,9 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + ofetch@1.3.4: resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==} @@ -4793,6 +4808,48 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.6.2: + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.12.0: + resolution: {integrity: sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -5036,6 +5093,45 @@ packages: resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + + postgres@3.4.4: + resolution: {integrity: sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==} + engines: {node: '>=12'} + potpack@2.0.0: resolution: {integrity: sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==} @@ -5471,6 +5567,10 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stable-hash@0.0.4: resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} @@ -6577,7 +6677,7 @@ snapshots: dependencies: postcss-selector-parser: 6.1.2 - '@drizzle-team/brocli@0.8.2': {} + '@drizzle-team/brocli@0.10.1': {} '@es-joy/jsdoccomment@0.43.1': dependencies: @@ -7254,13 +7354,13 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@nuxt/content@2.13.2(ioredis@5.4.1)(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': + '@nuxt/content@2.13.2(ioredis@5.4.1)(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': dependencies: '@nuxt/kit': 3.13.0(magicast@0.3.4)(rollup@4.21.0) '@nuxtjs/mdc': 0.8.3(magicast@0.3.4)(rollup@4.21.0) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) '@vueuse/head': 2.0.0(vue@3.4.38(typescript@5.5.4)) - '@vueuse/nuxt': 10.11.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) + '@vueuse/nuxt': 10.11.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4)) consola: 3.2.3 defu: 6.1.4 destr: 2.0.3 @@ -8111,6 +8211,12 @@ snapshots: '@types/pbf@3.0.5': {} + '@types/pg@8.11.6': + dependencies: + '@types/node': 22.5.0 + pg-protocol: 1.6.1 + pg-types: 4.0.2 + '@types/resolve@1.20.2': {} '@types/responselike@1.0.3': @@ -8544,13 +8650,13 @@ snapshots: '@vueuse/metadata@11.0.1': {} - '@vueuse/nuxt@10.11.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': + '@vueuse/nuxt@10.11.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': dependencies: '@nuxt/kit': 3.13.0(magicast@0.3.4)(rollup@4.21.0) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) '@vueuse/metadata': 10.11.1 local-pkg: 0.5.0 - nuxt: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) + nuxt: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' @@ -8559,13 +8665,13 @@ snapshots: - supports-color - vue - '@vueuse/nuxt@11.0.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': + '@vueuse/nuxt@11.0.1(magicast@0.3.4)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)))(rollup@4.21.0)(vue@3.4.38(typescript@5.5.4))': dependencies: '@nuxt/kit': 3.13.0(magicast@0.3.4)(rollup@4.21.0) '@vueuse/core': 11.0.1(vue@3.4.38(typescript@5.5.4)) '@vueuse/metadata': 11.0.1 local-pkg: 0.5.0 - nuxt: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) + nuxt: 3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)) vue-demi: 0.14.10(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' @@ -9168,9 +9274,9 @@ snapshots: date-fns@3.6.0: {} - db0@0.1.4(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1)): + db0@0.1.4(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4)): optionalDependencies: - drizzle-orm: 0.32.2(@cloudflare/workers-types@4.20240821.1) + drizzle-orm: 0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4) de-indent@1.0.2: {} @@ -9291,18 +9397,21 @@ snapshots: dotenv@16.4.5: {} - drizzle-kit@0.23.2: + drizzle-kit@0.24.1: dependencies: - '@drizzle-team/brocli': 0.8.2 + '@drizzle-team/brocli': 0.10.1 '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.19.12 esbuild-register: 3.6.0(esbuild@0.19.12) transitivePeerDependencies: - supports-color - drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1): + drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4): optionalDependencies: '@cloudflare/workers-types': 4.20240821.1 + '@types/pg': 8.11.6 + pg: 8.12.0 + postgres: 3.4.4 duplexer@0.1.2: {} @@ -11245,7 +11354,7 @@ snapshots: mlly: 1.7.1 pkg-types: 1.1.3 - nitropack@2.9.7(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(magicast@0.3.4): + nitropack@2.9.7(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(magicast@0.3.4): dependencies: '@cloudflare/kv-asset-handler': 0.3.4 '@netlify/functions': 2.8.1 @@ -11268,7 +11377,7 @@ snapshots: cookie-es: 1.2.2 croner: 8.1.1 crossws: 0.2.4 - db0: 0.1.4(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1)) + db0: 0.1.4(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4)) defu: 6.1.4 destr: 2.0.3 dot-prop: 8.0.2 @@ -11456,7 +11565,7 @@ snapshots: - utf-8-validate - vite - nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)): + nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@22.5.0)(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(eslint@9.9.0(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.4)(meow@9.0.0)(optionator@0.9.4)(rollup@4.21.0)(terser@5.31.6)(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6))(vue-tsc@2.0.29(typescript@5.5.4)): dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/devtools': 1.3.14(rollup@4.21.0)(vite@5.4.2(@types/node@22.5.0)(terser@5.31.6)) @@ -11490,7 +11599,7 @@ snapshots: knitwork: 1.1.0 magic-string: 0.30.11 mlly: 1.7.1 - nitropack: 2.9.7(drizzle-orm@0.32.2(@cloudflare/workers-types@4.20240821.1))(magicast@0.3.4) + nitropack: 2.9.7(drizzle-orm@0.33.0(@cloudflare/workers-types@4.20240821.1)(@types/pg@8.11.6)(pg@8.12.0)(postgres@3.4.4))(magicast@0.3.4) nuxi: 3.12.0 nypm: 0.3.9 ofetch: 1.3.4 @@ -11576,6 +11685,8 @@ snapshots: object-hash@3.0.0: {} + obuf@1.1.2: {} + ofetch@1.3.4: dependencies: destr: 2.0.3 @@ -11747,6 +11858,58 @@ snapshots: perfect-debounce@1.0.0: {} + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.6.4: + optional: true + + pg-int8@1.0.1: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.6.2(pg@8.12.0): + dependencies: + pg: 8.12.0 + optional: true + + pg-protocol@1.6.1: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + optional: true + + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.12.0: + dependencies: + pg-connection-string: 2.6.4 + pg-pool: 3.6.2(pg@8.12.0) + pg-protocol: 1.6.1 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + optional: true + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + optional: true + picocolors@1.0.1: {} picomatch@2.3.1: {} @@ -11971,6 +12134,34 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + postgres-array@2.0.0: + optional: true + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: + optional: true + + postgres-bytea@3.0.0: + dependencies: + obuf: 1.1.2 + + postgres-date@1.0.7: + optional: true + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + optional: true + + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} + + postgres@3.4.4: {} + potpack@2.0.0: {} prebuild-install@7.1.2: @@ -12516,6 +12707,9 @@ snapshots: speakingurl@14.0.1: {} + split2@4.2.0: + optional: true + stable-hash@0.0.4: {} stacktracey@2.1.8: diff --git a/server/api/authorized.post.ts b/server/api/authorized.post.ts deleted file mode 100644 index c441794..0000000 --- a/server/api/authorized.post.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useValidatedBody, z } from 'h3-zod' - -export default defineEventHandler(async (event) => { - const users = await useDB().select().from(tables.users).all() - const { email } = await useValidatedBody(event, { - email: z.string(), - }) - const user = users.find(user => user.email === email) - return !!user -}) diff --git a/server/api/categories/index.get.ts b/server/api/categories/index.get.ts new file mode 100644 index 0000000..f58db05 --- /dev/null +++ b/server/api/categories/index.get.ts @@ -0,0 +1,7 @@ +export default defineEventHandler(async (event) => { + const user = await getUserSession(event) + console.log('session', user) + return useDrizzle().query.categories.findMany({ + where: eq(tables.users.id, user.id), + }) +}) diff --git a/server/database/migrations/0000_salty_thena.sql b/server/database/migrations/0000_salty_thena.sql deleted file mode 100644 index 3c2828c..0000000 --- a/server/database/migrations/0000_salty_thena.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE `users` -( - `id` integer PRIMARY KEY NOT NULL, - `name` text DEFAULT '', - `email` text DEFAULT '', - `created_at` text DEFAULT (CURRENT_DATE) -); diff --git a/server/database/migrations/0000_wild_luke_cage.sql b/server/database/migrations/0000_wild_luke_cage.sql new file mode 100644 index 0000000..29b3d10 --- /dev/null +++ b/server/database/migrations/0000_wild_luke_cage.sql @@ -0,0 +1,73 @@ +DO $$ BEGIN + CREATE TYPE "public"."subscription" AS ENUM('free', 'paid'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "categories" ( + "id" serial PRIMARY KEY NOT NULL, + "name" text DEFAULT '' NOT NULL, + "name_visible" boolean DEFAULT true NOT NULL, + "icon" text DEFAULT 'i-ph:circle-wavy-question-duotone' NOT NULL, + "color" text DEFAULT 'gray' NOT NULL, + "page_id" integer NOT NULL, + "created_at" timestamp (3) DEFAULT now(), + "updated_at" timestamp (3) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "pages" ( + "id" serial PRIMARY KEY NOT NULL, + "user_id" integer NOT NULL, + "created_at" timestamp (3) DEFAULT now(), + "updated_at" timestamp (3) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tabs" ( + "id" serial PRIMARY KEY NOT NULL, + "name" text DEFAULT '' NOT NULL, + "name_visible" boolean DEFAULT true NOT NULL, + "icon" text DEFAULT 'i-ph:circle-wavy-question-duotone' NOT NULL, + "color" text DEFAULT 'gray' NOT NULL, + "category_id" integer NOT NULL, + "created_at" timestamp (3) DEFAULT now(), + "updated_at" timestamp (3) +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "users" ( + "id" serial PRIMARY KEY NOT NULL, + "username" text NOT NULL, + "name" text NOT NULL, + "email" text NOT NULL, + "github_id" text, + "github_token" text, + "google_id" text, + "google_token" text, + "description" text DEFAULT '', + "private" boolean DEFAULT false NOT NULL, + "timezone" text DEFAULT 'undefined' NOT NULL, + "location" text DEFAULT 'undefined' NOT NULL, + "subscription" "subscription" DEFAULT 'free' NOT NULL, + "created_at" timestamp (3) DEFAULT now(), + "updated_at" timestamp (3), + CONSTRAINT "users_email_unique" UNIQUE("email"), + CONSTRAINT "users_github_id_unique" UNIQUE("github_id"), + CONSTRAINT "users_google_id_unique" UNIQUE("google_id") +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "categories" ADD CONSTRAINT "categories_page_id_pages_id_fk" FOREIGN KEY ("page_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "pages" ADD CONSTRAINT "pages_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tabs" ADD CONSTRAINT "tabs_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/server/database/migrations/0001_goofy_dormammu.sql b/server/database/migrations/0001_goofy_dormammu.sql new file mode 100644 index 0000000..b14515a --- /dev/null +++ b/server/database/migrations/0001_goofy_dormammu.sql @@ -0,0 +1,4 @@ +ALTER TABLE "categories" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint +ALTER TABLE "pages" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint +ALTER TABLE "tabs" ALTER COLUMN "id" SET DATA TYPE integer;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "id" SET DATA TYPE integer; \ No newline at end of file diff --git a/server/database/migrations/0001_medical_joshua_kane.sql b/server/database/migrations/0001_medical_joshua_kane.sql deleted file mode 100644 index 973ad87..0000000 --- a/server/database/migrations/0001_medical_joshua_kane.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `users` - ADD `description` text DEFAULT ''; \ No newline at end of file diff --git a/server/database/migrations/0002_slim_whistler.sql b/server/database/migrations/0002_slim_whistler.sql new file mode 100644 index 0000000..a9a480a --- /dev/null +++ b/server/database/migrations/0002_slim_whistler.sql @@ -0,0 +1,13 @@ +ALTER TABLE "categories" ALTER COLUMN "name" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "categories" ALTER COLUMN "name_visible" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "categories" ALTER COLUMN "icon" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "categories" ALTER COLUMN "color" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "tabs" ALTER COLUMN "name" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "tabs" ALTER COLUMN "name_visible" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "tabs" ALTER COLUMN "icon" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "tabs" ALTER COLUMN "color" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "private" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "timezone" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "location" SET DEFAULT 'unknown';--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "location" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "subscription" DROP NOT NULL; \ No newline at end of file diff --git a/server/database/migrations/0003_curious_solo.sql b/server/database/migrations/0003_curious_solo.sql new file mode 100644 index 0000000..ea09863 --- /dev/null +++ b/server/database/migrations/0003_curious_solo.sql @@ -0,0 +1 @@ +ALTER TABLE "users" ADD COLUMN "avatar" text DEFAULT ''; \ No newline at end of file diff --git a/server/database/migrations/0004_sharp_shocker.sql b/server/database/migrations/0004_sharp_shocker.sql new file mode 100644 index 0000000..6c89e49 --- /dev/null +++ b/server/database/migrations/0004_sharp_shocker.sql @@ -0,0 +1,2 @@ +ALTER TABLE "users" RENAME COLUMN "timezone" TO "language";--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "language" SET DEFAULT 'english'; \ No newline at end of file diff --git a/server/database/migrations/0005_tense_the_order.sql b/server/database/migrations/0005_tense_the_order.sql new file mode 100644 index 0000000..8b177fb --- /dev/null +++ b/server/database/migrations/0005_tense_the_order.sql @@ -0,0 +1 @@ +ALTER TABLE "users" ALTER COLUMN "language" SET DEFAULT 'en-EN'; \ No newline at end of file diff --git a/server/database/migrations/meta/0000_snapshot.json b/server/database/migrations/meta/0000_snapshot.json index f439166..d9a1548 100644 --- a/server/database/migrations/meta/0000_snapshot.json +++ b/server/database/migrations/meta/0000_snapshot.json @@ -1,57 +1,357 @@ { - "version": "6", - "dialect": "sqlite", - "id": "a30470ea-8de9-4ff0-b0c9-d6b8a6264726", + "id": "a8ec7e1e-1087-4ab5-be19-459dc9b0a4e0", "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", "tables": { - "users": { - "name": "users", + "public.categories": { + "name": "categories", + "schema": "", "columns": { "id": { "name": "id", - "type": "integer", + "type": "serial", "primaryKey": true, - "notNull": true, - "autoincrement": false + "notNull": true }, "name": { "name": "name", "type": "text", "primaryKey": false, - "notNull": false, - "autoincrement": false, + "notNull": true, "default": "''" }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, "email": { "name": "email", "type": "text", "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "''" + "notNull": true }, - "created_at": { - "name": "created_at", + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", "type": "text", "primaryKey": false, "notNull": false, - "autoincrement": false, - "default": "(CURRENT_DATE)" + "default": "''" + }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'undefined'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'undefined'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } } }, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } }, - "internal": { - "indexes": {} + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} } -} +} \ No newline at end of file diff --git a/server/database/migrations/meta/0001_snapshot.json b/server/database/migrations/meta/0001_snapshot.json index 8b5f564..cde8d26 100644 --- a/server/database/migrations/meta/0001_snapshot.json +++ b/server/database/migrations/meta/0001_snapshot.json @@ -1,65 +1,357 @@ { - "version": "6", - "dialect": "sqlite", - "id": "77aafe70-876c-4c45-84d6-5261fa288bae", - "prevId": "a30470ea-8de9-4ff0-b0c9-d6b8a6264726", + "id": "0550ff2a-d819-4a38-a515-915d5ef620a6", + "prevId": "a8ec7e1e-1087-4ab5-be19-459dc9b0a4e0", + "version": "7", + "dialect": "postgresql", "tables": { - "users": { - "name": "users", + "public.categories": { + "name": "categories", + "schema": "", "columns": { "id": { "name": "id", "type": "integer", "primaryKey": true, - "notNull": true, - "autoincrement": false + "notNull": true }, "name": { "name": "name", "type": "text", "primaryKey": false, - "notNull": false, - "autoincrement": false, + "notNull": true, "default": "''" }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, "email": { "name": "email", "type": "text", "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": "''" + "notNull": true + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false }, "description": { "name": "description", "type": "text", "primaryKey": false, "notNull": false, - "autoincrement": false, "default": "''" }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'undefined'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'undefined'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'free'" + }, "created_at": { "name": "created_at", - "type": "text", + "type": "timestamp (3)", "primaryKey": false, "notNull": false, - "autoincrement": false, - "default": "(CURRENT_DATE)" + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false } }, "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } } }, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } }, - "internal": { - "indexes": {} + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} } -} +} \ No newline at end of file diff --git a/server/database/migrations/meta/0002_snapshot.json b/server/database/migrations/meta/0002_snapshot.json new file mode 100644 index 0000000..de068e5 --- /dev/null +++ b/server/database/migrations/meta/0002_snapshot.json @@ -0,0 +1,357 @@ +{ + "id": "7d4e591a-f6c7-48eb-b9e8-e0e200bfea26", + "prevId": "0550ff2a-d819-4a38-a515-915d5ef620a6", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'undefined'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'unknown'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } + } + }, + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/0003_snapshot.json b/server/database/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000..9ade717 --- /dev/null +++ b/server/database/migrations/meta/0003_snapshot.json @@ -0,0 +1,364 @@ +{ + "id": "d4ae60ba-5be1-4aa9-90d7-0690a599bf8e", + "prevId": "7d4e591a-f6c7-48eb-b9e8-e0e200bfea26", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'undefined'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'unknown'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } + } + }, + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/0004_snapshot.json b/server/database/migrations/meta/0004_snapshot.json new file mode 100644 index 0000000..b65e1f4 --- /dev/null +++ b/server/database/migrations/meta/0004_snapshot.json @@ -0,0 +1,364 @@ +{ + "id": "704c03b2-8d7f-47ce-a551-95289048c5f2", + "prevId": "d4ae60ba-5be1-4aa9-90d7-0690a599bf8e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'english'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'unknown'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } + } + }, + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/0005_snapshot.json b/server/database/migrations/meta/0005_snapshot.json new file mode 100644 index 0000000..5303fbc --- /dev/null +++ b/server/database/migrations/meta/0005_snapshot.json @@ -0,0 +1,364 @@ +{ + "id": "e891a8e0-61c1-4351-90fe-caace29457a8", + "prevId": "704c03b2-8d7f-47ce-a551-95289048c5f2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "categories_page_id_pages_id_fk": { + "name": "categories_page_id_pages_id_fk", + "tableFrom": "categories", + "tableTo": "pages", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "pages_user_id_users_id_fk": { + "name": "pages_user_id_users_id_fk", + "tableFrom": "pages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tabs": { + "name": "tabs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "name_visible": { + "name": "name_visible", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'i-ph:circle-wavy-question-duotone'" + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'gray'" + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tabs_category_id_categories_id_fk": { + "name": "tabs_category_id_categories_id_fk", + "tableFrom": "tabs", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "github_id": { + "name": "github_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "github_token": { + "name": "github_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "google_token": { + "name": "google_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "private": { + "name": "private", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "language": { + "name": "language", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'en-EN'" + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'unknown'" + }, + "subscription": { + "name": "subscription", + "type": "subscription", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'free'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp (3)", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "users_github_id_unique": { + "name": "users_github_id_unique", + "nullsNotDistinct": false, + "columns": [ + "github_id" + ] + }, + "users_google_id_unique": { + "name": "users_google_id_unique", + "nullsNotDistinct": false, + "columns": [ + "google_id" + ] + } + } + } + }, + "enums": { + "public.subscription": { + "name": "subscription", + "schema": "public", + "values": [ + "free", + "paid" + ] + } + }, + "schemas": {}, + "sequences": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/server/database/migrations/meta/_journal.json b/server/database/migrations/meta/_journal.json index 648b595..9d64ff3 100644 --- a/server/database/migrations/meta/_journal.json +++ b/server/database/migrations/meta/_journal.json @@ -1,20 +1,48 @@ { "version": "7", - "dialect": "sqlite", + "dialect": "postgresql", "entries": [ { "idx": 0, - "version": "6", - "when": 1724341642346, - "tag": "0000_salty_thena", + "version": "7", + "when": 1724455773734, + "tag": "0000_wild_luke_cage", "breakpoints": true }, { "idx": 1, - "version": "6", - "when": 1724343948344, - "tag": "0001_medical_joshua_kane", + "version": "7", + "when": 1724455851539, + "tag": "0001_goofy_dormammu", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1724456130150, + "tag": "0002_slim_whistler", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1724528975297, + "tag": "0003_curious_solo", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1724531645621, + "tag": "0004_sharp_shocker", + "breakpoints": true + }, + { + "idx": 5, + "version": "7", + "when": 1724532003950, + "tag": "0005_tense_the_order", "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/server/database/schema.ts b/server/database/schema.ts index af154d7..b88370e 100644 --- a/server/database/schema.ts +++ b/server/database/schema.ts @@ -1,10 +1,75 @@ -import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core' -import { sql } from 'drizzle-orm' +import { boolean, integer, pgEnum, pgTable, text } from 'drizzle-orm/pg-core' +import { relations } from 'drizzle-orm' +import { id, timestamps } from '../utils/dbFields' +import { Subscription } from '../../types/types' -export const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').default(''), - email: text('email').default(''), +export const subscriptionEnum = pgEnum('subscription', Subscription) + +export const users = pgTable('users', { + id, + username: text('username').notNull(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + githubId: text('github_id').unique(), + githubToken: text('github_token'), + googleId: text('google_id').unique(), + googleToken: text('google_token'), description: text('description').default(''), - createdAt: text('created_at').default(sql`(CURRENT_DATE)`), + avatar: text('avatar').default(''), + private: boolean('private').default(false), + language: text('language').default('en-EN'), + location: text('location').default('unknown'), + subscription: subscriptionEnum('subscription').default('free'), + ...timestamps, }) + +export const pages = pgTable('pages', { + id, + userId: integer('user_id') + .notNull() + .references(() => users.id, { onDelete: 'cascade' }), + ...timestamps, +}) + +export const categories = pgTable('categories', { + id, + name: text('name').default(''), + nameVisible: boolean('name_visible').default(true), + icon: text('icon').default('i-ph:circle-wavy-question-duotone'), + color: text('color').default('gray'), + pageId: integer('page_id') + .notNull() + .references(() => pages.id, { onDelete: 'cascade' }), + ...timestamps, +}) + +export const tabs = pgTable('tabs', { + id, + name: text('name').default(''), + nameVisible: boolean('name_visible').default(true), + icon: text('icon').default('i-ph:circle-wavy-question-duotone'), + color: text('color').default('gray'), + categoryId: integer('category_id') + .notNull() + .references(() => categories.id, { onDelete: 'cascade' }), + ...timestamps, +}) + +export const usersRelations = relations(users, ({ one }) => ({ + page: one(pages, { + fields: [users.id], + references: [pages.userId], + }), +})) + +export const pagesRelations = relations(pages, ({ many }) => ({ + categories: many(categories), +})) + +export const categoriesRelations = relations(categories, ({ one, many }) => ({ + page: one(pages, { + fields: [categories.pageId], + references: [pages.id], + }), + tabs: many(tabs), +})) diff --git a/server/plugins/migrations.ts b/server/plugins/migrations.ts deleted file mode 100644 index 2ee319f..0000000 --- a/server/plugins/migrations.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { consola } from 'consola' -import { migrate } from 'drizzle-orm/d1/migrator' - -export default defineNitroPlugin(async () => { - if (!import.meta.dev) - return - - onHubReady(async () => { - await migrate(useDB(), { migrationsFolder: 'server/database/migrations' }) - .then(() => { - consola.success('Database migrations done') - }) - .catch((err) => { - consola.error('Database migrations failed', err) - }) - }) -}) diff --git a/server/routes/auth/github.get.ts b/server/routes/auth/github.get.ts index 581984d..2481de4 100644 --- a/server/routes/auth/github.get.ts +++ b/server/routes/auth/github.get.ts @@ -2,17 +2,85 @@ export default oauthGitHubEventHandler({ config: { emailRequired: true, }, - async onSuccess(event, { user }) { - await setUserSession(event, { - user: { - email: user.email, - name: user.name, - }, + 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 it exists, sign in as that user and refresh the token + let user = await findUserByGitHubId(oauthUser.id) + + if (user) { + await updateUser(user.id, { + githubId: oauthUser.id, + githubToken: tokens.access_token, + }) + + await setUserSession(event, { + id: user.id, + user, + }) + + return sendRedirect(event, '/') + } + + // If the user is not signed in, search for an existing user with that email address without a GitHub ID + // If it exists, tells the user to sign in with that account and link the GitHub account + user = await findUserBy( + and( + eq(tables.users.email, oauthUser.email), + isNull(tables.users.githubId), + ), + ) + + if (user) { + await updateSession(event, { + password: useRuntimeConfig(event).session.password, + }, { + message: 'An existing account for this email already exists. Please login and visit your profile settings to add support for GitHub authentication.', + }) + return sendRedirect(event, '/login') + } + + // If the user is not signed in and no user exists with that GitHub ID or email address, create a new user + const createdUser = await createUser({ + username: oauthUser.login as string, + description: oauthUser.bio as string, + name: oauthUser.name as string, + email: oauthUser.email as string, + avatar: oauthUser.avatar_url as string, + githubId: oauthUser.id as number, + githubToken: tokens.access_token as string, + language: 'en-US', + location: 'unknown', + private: false, + subscription: 'free', }) - return sendRedirect(event, '/home') - }, - onError(event, error) { - console.error('GitHub OAuth error:', error) + + await setUserSession(event, { + id: createdUser.id, + user: createdUser, + }) + return sendRedirect(event, '/') }, }) diff --git a/server/routes/auth/google.get.ts b/server/routes/auth/google.get.ts index 51d0e09..a9c0dfb 100644 --- a/server/routes/auth/google.get.ts +++ b/server/routes/auth/google.get.ts @@ -2,17 +2,85 @@ export default oauthGoogleEventHandler({ config: { emailRequired: true, }, - async onSuccess(event, { user }) { - await setUserSession(event, { - user: { - email: user.email, - name: user.name, - }, + 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 replaceUserSession(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 it exists, sign in as that user and refresh the token + let user = await findUserByGoogleId(oauthUser.sub) + + if (user) { + await updateUser(user.id, { + googleId: oauthUser.sub, + googleToken: tokens.access_token, + }) + + await replaceUserSession(event, { + id: user.id, + user, + }) + + return sendRedirect(event, '/') + } + + // If the user is not signed in, search for an existing user with that email address without a Google ID + // If it exists, tells the user to sign in with that account and link the Google account + user = await findUserBy( + and( + eq(tables.users.email, oauthUser.email), + isNull(tables.users.googleId), + ), + ) + + if (user) { + await updateSession(event, { + password: useRuntimeConfig(event).session.password, + }, { + message: 'An existing account for this email already exists. Please login and visit your profile settings to add support for Google authentication.', + }) + return sendRedirect(event, '/login') + } + + // If the user is not signed in and no user exists with that Google ID or email address, create a new user + const createdUser = await createUser({ + username: oauthUser.name as string, + description: '', + name: `${oauthUser.given_name} ${oauthUser.family_name}`, + email: oauthUser.email as string, + avatar: oauthUser.picture as string, + googleId: oauthUser.sub as number, + googleToken: tokens.access_token as string, + language: 'en-US', + location: 'unknown', + private: false, + subscription: 'free', }) - return sendRedirect(event, '/home') - }, - onError(event, error) { - console.error('Google OAuth error:', error) + + await replaceUserSession(event, { + id: createdUser.id, + user: createdUser, + }) + return sendRedirect(event, '/') }, }) diff --git a/server/routes/images/[...pathname].ts b/server/routes/images/[...pathname].ts new file mode 100644 index 0000000..dfbbdc5 --- /dev/null +++ b/server/routes/images/[...pathname].ts @@ -0,0 +1,5 @@ +export default eventHandler(async (event) => { + const { pathname } = getRouterParams(event) + + return hubBlob().serve(event, pathname) +}) diff --git a/server/utils/db.ts b/server/utils/db.ts index b405639..b38de8f 100644 --- a/server/utils/db.ts +++ b/server/utils/db.ts @@ -1,10 +1,18 @@ -import { drizzle } from 'drizzle-orm/d1' +import postgres from 'postgres' +import { drizzle } from 'drizzle-orm/postgres-js' import * as schema from '../database/schema' -export { sql, eq, and, or, asc, desc, sum } from 'drizzle-orm' +export { sql, eq, and, or, asc, desc, sum, isNull } from 'drizzle-orm' export const tables = schema -export function useDB() { - return drizzle(hubDatabase(), { schema }) +export function useDrizzle() { + const config = useRuntimeConfig() + return drizzle(postgres(config.postgres.url, { prepare: false }), { schema }) } + +export type UserType = typeof schema.users.$inferSelect +export type UserInsert = typeof schema.users.$inferInsert + +export type TabType = typeof schema.tabs.$inferSelect +export type CategoryType = typeof schema.categories.$inferSelect diff --git a/server/utils/dbFields.ts b/server/utils/dbFields.ts new file mode 100644 index 0000000..569f6ce --- /dev/null +++ b/server/utils/dbFields.ts @@ -0,0 +1,20 @@ +import * as pg from 'drizzle-orm/pg-core' + +/** + * A centralized list of standardized Drizzle ORM schema field definitions to prevent duplication errors + */ + +export const createdAt = pg + .timestamp('created_at', { mode: 'date', precision: 3 }) + .defaultNow() + +export const updatedAt = pg + .timestamp('updated_at', { mode: 'date', precision: 3 }) + .$onUpdate(() => new Date()) + +export const id = pg.integer('id').primaryKey({ autoIncrement: true }) + +export const timestamps = { + createdAt, + updatedAt, +} diff --git a/server/utils/users.ts b/server/utils/users.ts new file mode 100644 index 0000000..0aad98b --- /dev/null +++ b/server/utils/users.ts @@ -0,0 +1,58 @@ +import type { SQL } from 'drizzle-orm' +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) { + return useDrizzle() + .query + .users + .findFirst({ + where: eq(tables.users.githubId, githubId), + }) +} + +export async function findUserByGoogleId(googleId: string) { + return useDrizzle() + .query + .users + .findFirst({ + where: eq(tables.users.googleId, googleId), + }) +} + +export async function findUserBy(query: SQL | undefined) { + return useDrizzle() + .query + .users + .findFirst({ + where: query, + }) +} + +export async function createUser(user: UserInsert) { + return useDrizzle() + .insert(tables.users) + .values(user) + .returning() +} + +export async function updateUser(userId: number, user: Partial) { + return useDrizzle() + .update(tables.users) + .set(user) + .where(eq(tables.users.id, userId)) +} + +export async function deleteProfilePicture(avatar: string) { + if (avatar.startsWith('profile-pictures/')) { + await hubBlob().delete(avatar) + } +} diff --git a/types/auth.d.ts b/types/auth.d.ts index d05bcd8..7ec01fa 100644 --- a/types/auth.d.ts +++ b/types/auth.d.ts @@ -1,13 +1,35 @@ // auth.d.ts +import type { Subscription } from '~~/types/types' + declare module '#auth-utils' { interface User { - email: string + id: number name: string + username: string + email: string + avatar: string | null + githubId?: number | null + googleId?: string | null + description: string + private: boolean + language: string + location: string + subscription: Subscription } interface UserSession { - email: string + id: number name: string + username: string + email: string + avatar: string | null + githubId?: number | null + googleId?: string | null + description: string + private: boolean + language: string + location: string + subscription: Subscription } } diff --git a/types/types.ts b/types/types.ts index 95fa893..2e5781d 100644 --- a/types/types.ts +++ b/types/types.ts @@ -1,5 +1,8 @@ import type { ParsedContent } from '@nuxt/content' +export const Subscription = ['free', 'paid'] as const + +// todo: delete export interface AppType extends ParsedContent { primary?: boolean name: string