@plutocms/supabase 0.0.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/nuxt/GENERATION.md +5 -0
- package/.agents/skills/nuxt/SKILL.md +55 -0
- package/.agents/skills/nuxt/references/advanced-hooks.md +289 -0
- package/.agents/skills/nuxt/references/advanced-layers.md +299 -0
- package/.agents/skills/nuxt/references/advanced-module-authoring.md +554 -0
- package/.agents/skills/nuxt/references/best-practices-data-fetching.md +357 -0
- package/.agents/skills/nuxt/references/best-practices-ssr.md +355 -0
- package/.agents/skills/nuxt/references/core-cli.md +263 -0
- package/.agents/skills/nuxt/references/core-config.md +162 -0
- package/.agents/skills/nuxt/references/core-data-fetching.md +236 -0
- package/.agents/skills/nuxt/references/core-deployment.md +224 -0
- package/.agents/skills/nuxt/references/core-directory-structure.md +269 -0
- package/.agents/skills/nuxt/references/core-modules.md +292 -0
- package/.agents/skills/nuxt/references/core-routing.md +226 -0
- package/.agents/skills/nuxt/references/features-components-autoimport.md +328 -0
- package/.agents/skills/nuxt/references/features-components.md +264 -0
- package/.agents/skills/nuxt/references/features-composables.md +276 -0
- package/.agents/skills/nuxt/references/features-server.md +265 -0
- package/.agents/skills/nuxt/references/features-state.md +194 -0
- package/.agents/skills/nuxt/references/rendering-modes.md +237 -0
- package/.agents/skills/nuxt-ui/SKILL.md +334 -0
- package/.agents/skills/nuxt-ui/references/components.md +377 -0
- package/.agents/skills/nuxt-ui/references/composables.md +127 -0
- package/.agents/skills/nuxt-ui/references/layouts/chat.md +195 -0
- package/.agents/skills/nuxt-ui/references/layouts/dashboard.md +220 -0
- package/.agents/skills/nuxt-ui/references/layouts/docs.md +141 -0
- package/.agents/skills/nuxt-ui/references/layouts/editor.md +167 -0
- package/.agents/skills/nuxt-ui/references/layouts/page.md +260 -0
- package/.agents/skills/nuxt-ui/references/theming.md +427 -0
- package/.agents/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/CLAUDE.md +1 -0
- package/.agents/skills/supabase-postgres-best-practices/README.md +116 -0
- package/.agents/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/.agents/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/.agents/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/.agents/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/.agents/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/.editorconfig +12 -0
- package/.env.example +2 -0
- package/.eslintignore +1 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +10 -0
- package/.vscode/settings.json +61 -0
- package/README.md +62 -0
- package/app/components/navbar/NavbarAdmin.vue +169 -0
- package/app/composables/auth.ts +81 -0
- package/app/layouts/setup.vue +13 -0
- package/app/middleware/auth.ts +27 -0
- package/app/middleware/setup-check.ts +25 -0
- package/app/pages/admin/(auth)/login.vue +84 -0
- package/app/pages/admin/(auth)/signup.vue +128 -0
- package/app/pages/admin/settings.vue +116 -0
- package/app/pages/admin/setup.vue +300 -0
- package/app/pages/admin.vue +38 -0
- package/bun.lock +2685 -0
- package/eslint.config.mjs +88 -0
- package/nuxt.config.ts +55 -0
- package/package.json +44 -0
- package/public/schema.sql +103 -0
- package/scripts/supabase-typegen.ts +36 -0
- package/server/api/category/list.get.ts +9 -0
- package/server/api/category/new.post.ts +31 -0
- package/server/api/settings/first_setup.get.ts +39 -0
- package/server/api/settings/index.get.ts +27 -0
- package/server/api/settings/update.post.ts +36 -0
- package/server/api/setup/create.post.ts +58 -0
- package/server/api/signup.post.ts +28 -0
- package/server/api/users/[id].get.ts +21 -0
- package/server/api/users/index.get.ts +16 -0
- package/shared/types/supabase.ts +406 -0
- package/skills-lock.json +20 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import antfu from '@antfu/eslint-config'
|
|
3
|
+
import withNuxt from './.nuxt/eslint.config.mjs'
|
|
4
|
+
|
|
5
|
+
// See: https://eslint.vuejs.org/rules
|
|
6
|
+
export default withNuxt(
|
|
7
|
+
// @ts-ignore - Antfu's config is ESM only, but Nuxt's ESLint config is CJS --- IGNORE ---
|
|
8
|
+
antfu({
|
|
9
|
+
rules: {
|
|
10
|
+
'node/prefer-global/process': ['off', 'never'],
|
|
11
|
+
|
|
12
|
+
'style/indent-binary-ops': 'off',
|
|
13
|
+
'style/arrow-parens': ['warn', 'always'],
|
|
14
|
+
'style/brace-style': ['warn', '1tbs'],
|
|
15
|
+
|
|
16
|
+
'vue/block-order': [
|
|
17
|
+
'warn',
|
|
18
|
+
{
|
|
19
|
+
order: ['script', 'template', 'style'],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
'vue/multiline-html-element-content-newline': 'off',
|
|
24
|
+
'vue/html-closing-bracket-newline': 'off',
|
|
25
|
+
|
|
26
|
+
'vue/html-self-closing': [
|
|
27
|
+
'warn',
|
|
28
|
+
{
|
|
29
|
+
html: {
|
|
30
|
+
void: 'always',
|
|
31
|
+
normal: 'always',
|
|
32
|
+
component: 'always',
|
|
33
|
+
},
|
|
34
|
+
svg: 'always',
|
|
35
|
+
math: 'always',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
'vue/attributes-order': [
|
|
40
|
+
'warn',
|
|
41
|
+
{
|
|
42
|
+
order: [
|
|
43
|
+
'DEFINITION',
|
|
44
|
+
'LIST_RENDERING',
|
|
45
|
+
'CONDITIONALS',
|
|
46
|
+
'RENDER_MODIFIERS',
|
|
47
|
+
'GLOBAL',
|
|
48
|
+
'UNIQUE',
|
|
49
|
+
'TWO_WAY_BINDING',
|
|
50
|
+
'SLOT',
|
|
51
|
+
'OTHER_DIRECTIVES',
|
|
52
|
+
'ATTR_DYNAMIC',
|
|
53
|
+
'ATTR_STATIC',
|
|
54
|
+
'ATTR_SHORTHAND_BOOL',
|
|
55
|
+
'EVENTS',
|
|
56
|
+
'CONTENT',
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
|
|
61
|
+
'vue/multi-word-component-names': 'off',
|
|
62
|
+
|
|
63
|
+
'vue/eqeqeq': ['error', 'always'],
|
|
64
|
+
|
|
65
|
+
'style/comma-dangle': [
|
|
66
|
+
'warn',
|
|
67
|
+
{
|
|
68
|
+
arrays: 'always-multiline',
|
|
69
|
+
objects: 'always-multiline',
|
|
70
|
+
functions: 'never',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
'vue/comma-dangle': [
|
|
75
|
+
'warn',
|
|
76
|
+
{
|
|
77
|
+
arrays: 'always-multiline',
|
|
78
|
+
objects: 'always-multiline',
|
|
79
|
+
functions: 'never',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
|
|
83
|
+
'style/operator-linebreak': 'off',
|
|
84
|
+
|
|
85
|
+
'vue/singleline-html-element-content-newline': 'off',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
)
|
package/nuxt.config.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const cwd = process.cwd()
|
|
2
|
+
|
|
3
|
+
const isDev = cwd.includes('supabase')
|
|
4
|
+
|
|
5
|
+
export default defineNuxtConfig({
|
|
6
|
+
extends: [['@plutocms/pluto', { install: true }], '@plutocms/utils'],
|
|
7
|
+
|
|
8
|
+
modules: ['@nuxt/eslint', '@nuxtjs/supabase'],
|
|
9
|
+
|
|
10
|
+
$meta: {
|
|
11
|
+
name: 'supabase',
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
runtimeConfig: {
|
|
15
|
+
supabaseUrl: process.env.SUPABASE_URL,
|
|
16
|
+
supabaseKey: process.env.SUPABASE_KEY,
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
alias: {
|
|
20
|
+
cookie: 'cookie-es',
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
ignore: ['./scripts/**', './supabase/**'],
|
|
24
|
+
|
|
25
|
+
nitro: {
|
|
26
|
+
alias: {
|
|
27
|
+
cookie: 'cookie-es',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
eslint: {
|
|
32
|
+
config: {
|
|
33
|
+
nuxt: {
|
|
34
|
+
sortConfigKeys: true,
|
|
35
|
+
},
|
|
36
|
+
standalone: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
supabase: !isDev
|
|
41
|
+
? {
|
|
42
|
+
types: '#shared',
|
|
43
|
+
cookiePrefix: 'access_token',
|
|
44
|
+
|
|
45
|
+
redirectOptions: {
|
|
46
|
+
login: '/admin/login',
|
|
47
|
+
callback: '/auth/confirm',
|
|
48
|
+
include: ['/admin/signup'],
|
|
49
|
+
saveRedirectToCookie: false,
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
redirect: false,
|
|
53
|
+
}
|
|
54
|
+
: undefined,
|
|
55
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@plutocms/supabase",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1-alpha.1",
|
|
5
|
+
"main": "./nuxt.config.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "nuxt build",
|
|
8
|
+
"dev": "nuxt dev",
|
|
9
|
+
"generate": "nuxt generate",
|
|
10
|
+
"preview": "nuxt preview",
|
|
11
|
+
"postinstall": "nuxt prepare",
|
|
12
|
+
"prettier": "prettier --write .",
|
|
13
|
+
"lint": "eslint .",
|
|
14
|
+
"lint:fix": "eslint . --fix && prettier --write .",
|
|
15
|
+
"supabase-types": "bun ./scripts/supabase-typegen.ts"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@nuxtjs/supabase": "2.0.3",
|
|
19
|
+
"postgres": "^3.4.8",
|
|
20
|
+
"supabase": "^2.76.15",
|
|
21
|
+
"yup": "1.7.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@antfu/eslint-config": "^6.2.0",
|
|
25
|
+
"@nuxt/eslint": "^1.15.1",
|
|
26
|
+
"@nuxt/ui": "^4.4.0",
|
|
27
|
+
"@plutocms/pluto": "^0.0.1-alpha.3",
|
|
28
|
+
"@plutocms/utils": "^0.0.1-alpha.4",
|
|
29
|
+
"@types/bun": "^1.3.9",
|
|
30
|
+
"@vueuse/nuxt": "^14.2.1",
|
|
31
|
+
"eslint": "^9.39.1",
|
|
32
|
+
"nuxt": "^4.3.1",
|
|
33
|
+
"prettier": "^3.8.1",
|
|
34
|
+
"vue": "^3.5.28",
|
|
35
|
+
"vue-router": "^5.0.2"
|
|
36
|
+
},
|
|
37
|
+
"trustedDependencies": [
|
|
38
|
+
"@parcel/watcher",
|
|
39
|
+
"esbuild",
|
|
40
|
+
"supabase",
|
|
41
|
+
"unrs-resolver",
|
|
42
|
+
"vue-demi"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
-- Supabase Schema SQL
|
|
2
|
+
-- Settings
|
|
3
|
+
--- Create enum type for settings
|
|
4
|
+
do $$
|
|
5
|
+
begin
|
|
6
|
+
if not exists (select 1 from pg_type where typname = 'tsettings') then
|
|
7
|
+
create type TSettings as enum (
|
|
8
|
+
'website_title',
|
|
9
|
+
'website_url',
|
|
10
|
+
'website_description'
|
|
11
|
+
);
|
|
12
|
+
end if;
|
|
13
|
+
end
|
|
14
|
+
$$;
|
|
15
|
+
|
|
16
|
+
--- Create settings table
|
|
17
|
+
create table public.settings (
|
|
18
|
+
id bigint generated by default as identity not null,
|
|
19
|
+
setting_name public.TSettings not null,
|
|
20
|
+
setting_value text not null,
|
|
21
|
+
constraint settings_pkey primary key (id),
|
|
22
|
+
constraint settings_setting_name_key unique (setting_name)
|
|
23
|
+
) TABLESPACE pg_default;
|
|
24
|
+
|
|
25
|
+
--- Insert default settings (website_title, website_url, website_description)
|
|
26
|
+
insert into public.settings (setting_name, setting_value) values
|
|
27
|
+
('website_title', 'My Website'),
|
|
28
|
+
('website_url', 'https://mywebsite.test'),
|
|
29
|
+
('website_description', 'This is my website.');
|
|
30
|
+
|
|
31
|
+
alter table public.settings enable row level security;
|
|
32
|
+
|
|
33
|
+
-- Policies for settings table
|
|
34
|
+
create policy "Enable insert for authenticated users only"
|
|
35
|
+
on public.settings
|
|
36
|
+
to authenticated, dashboard_user
|
|
37
|
+
with check (true);
|
|
38
|
+
|
|
39
|
+
--- Create enum type for healthcheck names
|
|
40
|
+
do $$
|
|
41
|
+
begin
|
|
42
|
+
if not exists (select 1 from pg_type where typname = 'thealthcheck') then
|
|
43
|
+
create type Healthcheck as enum (
|
|
44
|
+
'first_setup'
|
|
45
|
+
);
|
|
46
|
+
end if;
|
|
47
|
+
end
|
|
48
|
+
$$;
|
|
49
|
+
|
|
50
|
+
--- Create healthcheck table (name/value format like settings)
|
|
51
|
+
create table public.healthcheck (
|
|
52
|
+
id bigint generated by default as identity not null,
|
|
53
|
+
check_name public.Healthcheck not null,
|
|
54
|
+
check_value text not null,
|
|
55
|
+
constraint healthcheck_pkey primary key (id),
|
|
56
|
+
constraint healthcheck_check_name_key unique (check_name)
|
|
57
|
+
) TABLESPACE pg_default;
|
|
58
|
+
|
|
59
|
+
--- Insert default healthcheck values
|
|
60
|
+
insert into public.healthcheck (check_name, check_value) values
|
|
61
|
+
('first_setup', 'true');
|
|
62
|
+
|
|
63
|
+
alter table public.healthcheck enable row level security;
|
|
64
|
+
|
|
65
|
+
-- Allow read access to healthcheck for public and dashboard_user (no login required)
|
|
66
|
+
create policy "Enable read access for healthcheck to public and dashboard_user"
|
|
67
|
+
on public.healthcheck
|
|
68
|
+
for select
|
|
69
|
+
to public, dashboard_user
|
|
70
|
+
using (true);
|
|
71
|
+
|
|
72
|
+
-- Profiles
|
|
73
|
+
create table profiles (
|
|
74
|
+
id uuid references auth.users on delete cascade not null primary key,
|
|
75
|
+
updated_at timestamp with time zone,
|
|
76
|
+
first_name text,
|
|
77
|
+
last_name text,
|
|
78
|
+
constraint first_name_length check (char_length(first_name) >= 3)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
alter table public.profiles enable row level security;
|
|
82
|
+
|
|
83
|
+
create policy "Enable read access for authenticated users"
|
|
84
|
+
on public.profiles
|
|
85
|
+
to authenticated, dashboard_user
|
|
86
|
+
for select using (true);
|
|
87
|
+
|
|
88
|
+
--- inserts a row into public.profiles
|
|
89
|
+
create function public.handle_new_user()
|
|
90
|
+
returns trigger
|
|
91
|
+
language plpgsql
|
|
92
|
+
security definer set search_path = ''
|
|
93
|
+
as $$
|
|
94
|
+
begin
|
|
95
|
+
insert into public.profiles (id, first_name, last_name)
|
|
96
|
+
values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');
|
|
97
|
+
return new;
|
|
98
|
+
end;
|
|
99
|
+
$$;
|
|
100
|
+
--- trigger the function every time a user is created
|
|
101
|
+
create trigger on_auth_user_created
|
|
102
|
+
after insert on auth.users
|
|
103
|
+
for each row execute procedure public.handle_new_user();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { cwd } from 'node:process'
|
|
4
|
+
import { $ } from 'bun'
|
|
5
|
+
import { consola } from 'consola'
|
|
6
|
+
|
|
7
|
+
const projectUrl = process.env.SUPABASE_URL
|
|
8
|
+
const projectId = projectUrl?.split('.supabase.co')[0]?.replace('https://', '')
|
|
9
|
+
const _path = path
|
|
10
|
+
.resolve(cwd(), './shared/types/supabase.ts')
|
|
11
|
+
.replace(/\\/g, '/')
|
|
12
|
+
|
|
13
|
+
;(async () => {
|
|
14
|
+
consola.info(`Generating Supabase types for project: ${projectId}`)
|
|
15
|
+
|
|
16
|
+
// Create file if it doesn't exist
|
|
17
|
+
if (!fs.existsSync(_path)) {
|
|
18
|
+
consola.info('Types file does not exist. Creating a new one.')
|
|
19
|
+
|
|
20
|
+
fs.mkdirSync(path.dirname(_path), { recursive: true })
|
|
21
|
+
fs.writeFileSync(_path, '', 'utf-8')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const args = [
|
|
25
|
+
`--project-id=${projectId || ''}`,
|
|
26
|
+
'--schema=public',
|
|
27
|
+
'>',
|
|
28
|
+
`"${_path}"`,
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await $`bunx supabase gen types typescript ${{ raw: args.join(' ') }}`
|
|
33
|
+
} catch (error) {
|
|
34
|
+
consola.error('Unhandled error:', error)
|
|
35
|
+
}
|
|
36
|
+
})()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
5
|
+
|
|
6
|
+
const body = await readBody(event)
|
|
7
|
+
|
|
8
|
+
if (!body) {
|
|
9
|
+
throw createError({
|
|
10
|
+
message: 'No file sent.',
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { data, error } = await client
|
|
15
|
+
.from('categories')
|
|
16
|
+
.insert(body)
|
|
17
|
+
.select()
|
|
18
|
+
.single()
|
|
19
|
+
|
|
20
|
+
if (error) {
|
|
21
|
+
throw createError({
|
|
22
|
+
message: error.message,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (error) {
|
|
27
|
+
throw createError({ message: 'error' })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { data }
|
|
31
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
type HealthcheckSettingKey =
|
|
4
|
+
Database['public']['Tables']['healthcheck']['Row']['config_name']
|
|
5
|
+
|
|
6
|
+
export default defineEventHandler(async (event) => {
|
|
7
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
8
|
+
|
|
9
|
+
const { data, error } = await client.from('healthcheck').select('*')
|
|
10
|
+
|
|
11
|
+
if (error) {
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
message: 'Error retrieving healthcheck settings',
|
|
15
|
+
error: error.message,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// @ts-expect-error: Object.fromEntries may not infer the correct type
|
|
20
|
+
const isFirstSetup: Record<HealthcheckSettingKey, string> = {
|
|
21
|
+
...Object.fromEntries(
|
|
22
|
+
data?.map((item) => [item.config_name, item.config_value]) || []
|
|
23
|
+
),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
data?.forEach((item) => {
|
|
27
|
+
if (!item.config_name || !item.config_value) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
isFirstSetup[item.config_name] = item.config_value
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
message: 'Healthcheck settings retrieved successfully',
|
|
37
|
+
is_first_setup: isFirstSetup.first_setup === 'true',
|
|
38
|
+
}
|
|
39
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
5
|
+
|
|
6
|
+
const { data } = await client.from('settings').select('*')
|
|
7
|
+
|
|
8
|
+
type SettingsKey =
|
|
9
|
+
Database['public']['Tables']['settings']['Row']['setting_name']
|
|
10
|
+
|
|
11
|
+
// @ts-expect-error: Object.fromEntries may not infer the correct type for settings
|
|
12
|
+
const settings: Record<SettingsKey, string> = {
|
|
13
|
+
...Object.fromEntries(
|
|
14
|
+
data?.map((item) => [item.setting_name, item.setting_value]) || []
|
|
15
|
+
),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
data?.forEach((item) => {
|
|
19
|
+
if (!item.setting_name || !item.setting_value) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
settings[item.setting_name] = item.setting_value
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return { settings }
|
|
27
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
type SettingName =
|
|
5
|
+
Database['public']['Tables']['settings']['Insert']['setting_name']
|
|
6
|
+
type FormBody = Record<SettingName, string>
|
|
7
|
+
|
|
8
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
9
|
+
const body = await readBody<FormBody>(event)
|
|
10
|
+
|
|
11
|
+
if (!body) {
|
|
12
|
+
throw createError({ statusMessage: 'No payload sent.' })
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const transformed = [
|
|
16
|
+
...Object.entries(body).map(([key, value]) => ({
|
|
17
|
+
setting_name: key as SettingName,
|
|
18
|
+
setting_value: value,
|
|
19
|
+
})),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const { data, error } = await client
|
|
23
|
+
.from('settings')
|
|
24
|
+
.upsert(transformed, { onConflict: 'setting_name' })
|
|
25
|
+
.select()
|
|
26
|
+
|
|
27
|
+
if (error) {
|
|
28
|
+
throw createError({ statusMessage: error.message })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
message: 'Settings updated successfully.',
|
|
33
|
+
statusCode: 200,
|
|
34
|
+
data,
|
|
35
|
+
}
|
|
36
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import postgres from 'postgres'
|
|
2
|
+
|
|
3
|
+
interface Payload {
|
|
4
|
+
baseUrl: string
|
|
5
|
+
connectionString: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const body = await readBody<Payload>(event)
|
|
10
|
+
|
|
11
|
+
let schema = await $fetch<string | Blob>('/schema.sql', {
|
|
12
|
+
baseURL: body.baseUrl,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
if (typeof schema !== 'string') {
|
|
16
|
+
schema = await schema.text()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const supabaseUrl = process.env.SUPABASE_URL
|
|
20
|
+
const supabaseKey = process.env.SUPABASE_KEY
|
|
21
|
+
|
|
22
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Supabase URL or Key is not defined in environment variables.'
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const connectionString = body.connectionString
|
|
29
|
+
|
|
30
|
+
const sql = postgres(connectionString)
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
/* await sql.begin(async (sql) => {
|
|
34
|
+
await sql.unsafe(schema)
|
|
35
|
+
}) */
|
|
36
|
+
|
|
37
|
+
// Set the first_setup flag in the healthcheck table to false
|
|
38
|
+
await sql`
|
|
39
|
+
UPDATE healthcheck
|
|
40
|
+
SET config_value = 'false'
|
|
41
|
+
WHERE config_name = 'first_setup';
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
message: 'Database setup completed successfully.',
|
|
47
|
+
}
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
console.error('Error setting up database:', error)
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: error.message,
|
|
54
|
+
}
|
|
55
|
+
} finally {
|
|
56
|
+
sql.end()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
5
|
+
|
|
6
|
+
const body = await readBody(event)
|
|
7
|
+
|
|
8
|
+
const { data, error } = await client.auth.signUp({
|
|
9
|
+
email: body.email,
|
|
10
|
+
password: body.password,
|
|
11
|
+
options: {
|
|
12
|
+
data: {
|
|
13
|
+
first_name: body.first_name,
|
|
14
|
+
last_name: body.last_name,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (error) {
|
|
20
|
+
throw createError({
|
|
21
|
+
statusCode: Number.parseInt(error.code ?? '500'),
|
|
22
|
+
cause: error.cause,
|
|
23
|
+
message: error.message,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { message: 'User registered successfully', data }
|
|
28
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
5
|
+
|
|
6
|
+
const id = event.context.params?.id as string
|
|
7
|
+
|
|
8
|
+
const { data: user, error } = await client
|
|
9
|
+
.from('profiles')
|
|
10
|
+
.select('*')
|
|
11
|
+
.eq('id', id)
|
|
12
|
+
|
|
13
|
+
if (error) {
|
|
14
|
+
throw createError({
|
|
15
|
+
statusCode: 500,
|
|
16
|
+
statusMessage: `Error fetching user: ${error.message}`,
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return user
|
|
21
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { serverSupabaseClient } from '#supabase/server'
|
|
2
|
+
|
|
3
|
+
export default defineEventHandler(async (event) => {
|
|
4
|
+
const client = await serverSupabaseClient<Database>(event)
|
|
5
|
+
|
|
6
|
+
const { data: users, error } = await client.from('profiles').select('*')
|
|
7
|
+
|
|
8
|
+
if (error) {
|
|
9
|
+
throw createError({
|
|
10
|
+
statusCode: 500,
|
|
11
|
+
statusMessage: `Error fetching users: ${error.message}`,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return users
|
|
16
|
+
})
|