@edgedev/create-edge-app 1.1.29 → 1.2.30
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.md +2 -0
- package/bin/cli.js +13 -5
- package/deploy-services.sh +237 -0
- package/deploy.sh +88 -1
- package/edge/components/cms/blockEditor.vue +748 -7
- package/{pages/app/dashboard/blocks/index.vue → edge/components/cms/blocksManager.vue} +9 -24
- package/edge/components/cms/menu.vue +100 -15
- package/edge/components/cms/site.vue +83 -3
- package/edge/components/cms/sitesManager.vue +110 -0
- package/edge/components/cms/themeEditor.vue +9 -3
- package/edge/components/dashboard.vue +22 -3
- package/edge/components/editor.vue +100 -35
- package/edge/components/organizationMembers.vue +294 -221
- package/edge/components/shad/combobox.vue +2 -2
- package/edge/composables/global.ts +1 -1
- package/edge/routes/cms/dashboard/blocks/index.vue +21 -0
- package/edge/routes/cms/dashboard/sites/index.vue +13 -0
- package/edge/routes/cms/nuxtHooks.js +52 -0
- package/edge/routes/cms/routes.js +56 -0
- package/firebase_init.sh +63 -2
- package/nuxt.config.ts +19 -2
- package/package.json +1 -1
- package/services/.deploy.shared.env.example +12 -0
- package/pages/app/dashboard/sites/index.vue +0 -114
- /package/{pages/app → edge/routes/cms}/dashboard/blocks/[block].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/media/index.vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/sites/[site]/[[page]].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/sites/[site].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/templates/[page].vue +0 -0
- /package/{pages/app/dashboard/templates.vue → edge/routes/cms/dashboard/templates/index.vue} +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/themes/[theme].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/themes/index.vue +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const state = reactive({
|
|
3
|
+
mounted: false,
|
|
4
|
+
})
|
|
5
|
+
|
|
6
|
+
definePageMeta({
|
|
7
|
+
middleware: 'auth',
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
onMounted(() => {
|
|
11
|
+
state.mounted = true
|
|
12
|
+
})
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div
|
|
17
|
+
v-if="edgeGlobal.edgeState.organizationDocPath && state.mounted"
|
|
18
|
+
>
|
|
19
|
+
<edge-cms-blocks-manager />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolve as resolvePath } from 'node:path'
|
|
2
|
+
import { cmsRoutes } from './routes'
|
|
3
|
+
|
|
4
|
+
const resolveRouteFileFromCwd = (file) => {
|
|
5
|
+
const normalizedPath = String(file || '').replace(/^\.\//, '')
|
|
6
|
+
return resolvePath(process.cwd(), normalizedPath)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const createCmsNuxtHooks = (resolveFile) => {
|
|
10
|
+
const resolveRouteFile = typeof resolveFile === 'function'
|
|
11
|
+
? resolveFile
|
|
12
|
+
: resolveRouteFileFromCwd
|
|
13
|
+
|
|
14
|
+
const routeExists = (routeList, name) => {
|
|
15
|
+
for (const route of routeList) {
|
|
16
|
+
if (route.name === name)
|
|
17
|
+
return true
|
|
18
|
+
if (route.children?.length && routeExists(route.children, name))
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const resolveCmsRoute = (route, appRootRoute, isChild = false) => {
|
|
25
|
+
const shouldStripAppPrefix = Boolean(appRootRoute) && !isChild
|
|
26
|
+
const nextRoute = {
|
|
27
|
+
...route,
|
|
28
|
+
path: shouldStripAppPrefix
|
|
29
|
+
? route.path.replace(/^\/app\/?/, '')
|
|
30
|
+
: route.path,
|
|
31
|
+
file: resolveRouteFile(route.file),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (route.children?.length)
|
|
35
|
+
nextRoute.children = route.children.map(child => resolveCmsRoute(child, appRootRoute, true))
|
|
36
|
+
|
|
37
|
+
return nextRoute
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
'pages:extend': (pages) => {
|
|
42
|
+
const appRootRoute = pages.find(page => page.path === '/app')
|
|
43
|
+
const targetRoutes = appRootRoute?.children || pages
|
|
44
|
+
|
|
45
|
+
cmsRoutes.forEach((route) => {
|
|
46
|
+
const routeToAdd = resolveCmsRoute(route, appRootRoute)
|
|
47
|
+
if (!routeExists(targetRoutes, routeToAdd.name))
|
|
48
|
+
targetRoutes.push(routeToAdd)
|
|
49
|
+
})
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const cmsRoutes = [
|
|
2
|
+
{
|
|
3
|
+
name: 'cms-dashboard-sites-index',
|
|
4
|
+
path: '/app/dashboard/sites',
|
|
5
|
+
file: './edge/routes/cms/dashboard/sites/index.vue',
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
name: 'cms-dashboard-blocks-index',
|
|
9
|
+
path: '/app/dashboard/blocks',
|
|
10
|
+
file: './edge/routes/cms/dashboard/blocks/index.vue',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'cms-dashboard-blocks-block',
|
|
14
|
+
path: '/app/dashboard/blocks/:block',
|
|
15
|
+
file: './edge/routes/cms/dashboard/blocks/[block].vue',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'cms-dashboard-media-index',
|
|
19
|
+
path: '/app/dashboard/media',
|
|
20
|
+
file: './edge/routes/cms/dashboard/media/index.vue',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'cms-dashboard-sites-site',
|
|
24
|
+
path: '/app/dashboard/sites/:site',
|
|
25
|
+
file: './edge/routes/cms/dashboard/sites/[site].vue',
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
name: 'cms-dashboard-sites-site-page',
|
|
29
|
+
path: ':page',
|
|
30
|
+
file: './edge/routes/cms/dashboard/sites/[site]/[[page]].vue',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'cms-dashboard-templates-index',
|
|
36
|
+
path: '/app/dashboard/templates',
|
|
37
|
+
file: './edge/routes/cms/dashboard/templates/index.vue',
|
|
38
|
+
children: [
|
|
39
|
+
{
|
|
40
|
+
name: 'cms-dashboard-templates-page',
|
|
41
|
+
path: ':page',
|
|
42
|
+
file: './edge/routes/cms/dashboard/templates/[page].vue',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'cms-dashboard-themes-index',
|
|
48
|
+
path: '/app/dashboard/themes',
|
|
49
|
+
file: './edge/routes/cms/dashboard/themes/index.vue',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'cms-dashboard-themes-theme',
|
|
53
|
+
path: '/app/dashboard/themes/:theme',
|
|
54
|
+
file: './edge/routes/cms/dashboard/themes/[theme].vue',
|
|
55
|
+
},
|
|
56
|
+
]
|
package/firebase_init.sh
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
# Install gcloud if missing (best-effort by platform)
|
|
2
|
+
ensure_gcloud() {
|
|
3
|
+
if command -v gcloud >/dev/null 2>&1; then
|
|
4
|
+
return 0
|
|
5
|
+
fi
|
|
6
|
+
|
|
7
|
+
echo "gcloud CLI not found. Attempting to install Google Cloud SDK..."
|
|
8
|
+
|
|
9
|
+
if command -v brew >/dev/null 2>&1; then
|
|
10
|
+
if ! brew list --cask google-cloud-sdk >/dev/null 2>&1; then
|
|
11
|
+
brew install --cask google-cloud-sdk || {
|
|
12
|
+
echo "Failed to install Google Cloud SDK with Homebrew."
|
|
13
|
+
exit 1
|
|
14
|
+
}
|
|
15
|
+
fi
|
|
16
|
+
elif command -v snap >/dev/null 2>&1; then
|
|
17
|
+
if [ "$(id -u)" -eq 0 ]; then
|
|
18
|
+
snap install google-cloud-cli --classic || {
|
|
19
|
+
echo "Failed to install Google Cloud SDK with Snap."
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
elif command -v sudo >/dev/null 2>&1; then
|
|
23
|
+
sudo snap install google-cloud-cli --classic || {
|
|
24
|
+
echo "Failed to install Google Cloud SDK with Snap."
|
|
25
|
+
exit 1
|
|
26
|
+
}
|
|
27
|
+
else
|
|
28
|
+
echo "Snap install requires elevated privileges, but 'sudo' is unavailable."
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
else
|
|
32
|
+
echo "Unable to auto-install Google Cloud SDK on this system."
|
|
33
|
+
echo "Please install it manually: https://cloud.google.com/sdk/docs/install"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if ! command -v gcloud >/dev/null 2>&1; then
|
|
38
|
+
echo "gcloud is still not available in PATH after install."
|
|
39
|
+
echo "Open a new terminal and run this script again."
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
1
44
|
# Prompt for the Firebase configuration values
|
|
2
45
|
echo "Please enter your Firebase project ID:"
|
|
3
46
|
read project_id
|
|
@@ -34,11 +77,29 @@ if [ -z "$project_id" ]; then
|
|
|
34
77
|
fi
|
|
35
78
|
|
|
36
79
|
# Check if firebase is installed
|
|
37
|
-
if ! command -v firebase
|
|
80
|
+
if ! command -v firebase >/dev/null 2>&1; then
|
|
38
81
|
echo "Firebase CLI could not be found. Please install it and try again."
|
|
39
82
|
exit 1
|
|
40
83
|
fi
|
|
41
84
|
|
|
85
|
+
ensure_gcloud
|
|
86
|
+
|
|
87
|
+
# Initialize gcloud account/config if needed, then set active project
|
|
88
|
+
active_account="$(gcloud auth list --filter=status:ACTIVE --format='value(account)' 2>/dev/null)"
|
|
89
|
+
if [ -z "$active_account" ]; then
|
|
90
|
+
echo "No active gcloud account found. Running gcloud init..."
|
|
91
|
+
if ! gcloud init; then
|
|
92
|
+
echo "gcloud init failed."
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
echo "Setting gcloud project to '$project_id'..."
|
|
98
|
+
if ! gcloud config set project "$project_id"; then
|
|
99
|
+
echo "Failed to set gcloud project to '$project_id'."
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
42
103
|
# Backup firebase.json if it exists
|
|
43
104
|
if [ -f ./firebase.json ]; then
|
|
44
105
|
cp ./firebase.json ./firebase.json.temp
|
|
@@ -93,4 +154,4 @@ echo "VITE_FIREBASE_EMULATOR_FIRESTORE=8080" >> .env.dev
|
|
|
93
154
|
echo "VITE_FIREBASE_EMULATOR_FUNCTIONS=5001" >> .env.dev
|
|
94
155
|
echo "VITE_FIREBASE_EMULATOR_STORAGE=9199" >> .env.dev
|
|
95
156
|
echo "REGISTRATION_CODE=organization-registration-template" >> .env.dev
|
|
96
|
-
echo "DEVELOPMENT_MODE=true" >> .env.dev
|
|
157
|
+
echo "DEVELOPMENT_MODE=true" >> .env.dev
|
package/nuxt.config.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { createCmsNuxtHooks } from './edge/routes/cms/nuxtHooks'
|
|
2
|
+
|
|
3
|
+
const cmsRouteHooks = {}
|
|
4
|
+
Object.assign(cmsRouteHooks, createCmsNuxtHooks()) // Comment out this line to disable CMS routes.
|
|
5
|
+
|
|
1
6
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
2
7
|
export default defineNuxtConfig({
|
|
3
8
|
ssr: false,
|
|
@@ -16,6 +21,12 @@ export default defineNuxtConfig({
|
|
|
16
21
|
{ rel: 'apple-touch-icon', sizes: '180x180', href: '/favicon/apple-touch-icon.png' },
|
|
17
22
|
{ rel: 'manifest', href: '/favicon/site.webmanifest' },
|
|
18
23
|
],
|
|
24
|
+
meta: [
|
|
25
|
+
{
|
|
26
|
+
'http-equiv': 'Content-Security-Policy',
|
|
27
|
+
'content': 'font-src \'self\' https://files.edgemarketingdesign.com https://use.typekit.net https://p.typekit.net data:;',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
19
30
|
},
|
|
20
31
|
},
|
|
21
32
|
modules: ['@vant/nuxt', '@nuxtjs/tailwindcss', '@nuxtjs/color-mode', 'shadcn-nuxt'],
|
|
@@ -40,9 +51,11 @@ export default defineNuxtConfig({
|
|
|
40
51
|
},
|
|
41
52
|
components: {
|
|
42
53
|
dirs: [
|
|
43
|
-
{ path: '~/components
|
|
54
|
+
{ path: '~/components' },
|
|
55
|
+
|
|
56
|
+
// Namespaced wrappers — keep them, but lower priority is fine
|
|
44
57
|
{ path: '~/edge/components', global: true, prefix: 'edge' },
|
|
45
|
-
'~/components',
|
|
58
|
+
{ path: '~/components/formSubtypes', global: true, prefix: 'edge-form-subtypes' },
|
|
46
59
|
],
|
|
47
60
|
},
|
|
48
61
|
vite: {
|
|
@@ -50,11 +63,15 @@ export default defineNuxtConfig({
|
|
|
50
63
|
'process.env.DEBUG': false,
|
|
51
64
|
},
|
|
52
65
|
server: {
|
|
66
|
+
watch: {
|
|
67
|
+
ignored: ['**/.nuxt/**', '**/.output/**', '**/dist/**', '**/node_modules/**'],
|
|
68
|
+
},
|
|
53
69
|
hmr: {
|
|
54
70
|
port: 3000, // Make sure this port matches your Nuxt server port
|
|
55
71
|
clientPort: 3000, // Ensure this matches your Nuxt server port as well
|
|
56
72
|
},
|
|
57
73
|
},
|
|
58
74
|
},
|
|
75
|
+
hooks: cmsRouteHooks,
|
|
59
76
|
devtools: { enabled: false },
|
|
60
77
|
})
|
package/package.json
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Optional shared deploy settings for all services.
|
|
2
|
+
# Copy to services/.deploy.shared.env and customize as needed.
|
|
3
|
+
|
|
4
|
+
# Defaults to .firebaserc project if omitted.
|
|
5
|
+
# PROJECT_ID=clearwater-hub
|
|
6
|
+
|
|
7
|
+
# Falls back to VITE_FIREBASE_FUNCTIONS_REGION from root .env if omitted.
|
|
8
|
+
# REGION=us-central1
|
|
9
|
+
|
|
10
|
+
# Comma-separated IAM members to grant Cloud Run Invoker when ALLOW_UNAUTHENTICATED=false.
|
|
11
|
+
# If member type is omitted, serviceAccount: is assumed by deploy-services.sh.
|
|
12
|
+
# INVOKER_MEMBERS=serviceAccount:201505998870-compute@developer.gserviceaccount.com
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { toTypedSchema } from '@vee-validate/zod'
|
|
3
|
-
import * as z from 'zod'
|
|
4
|
-
import { Loader2 } from 'lucide-vue-next'
|
|
5
|
-
const route = useRoute()
|
|
6
|
-
// const edgeGlobal = inject('edgeGlobal')
|
|
7
|
-
|
|
8
|
-
const edgeFirebase = inject('edgeFirebase')
|
|
9
|
-
const isAiBusy = status => status === 'queued' || status === 'running'
|
|
10
|
-
|
|
11
|
-
const state = reactive({
|
|
12
|
-
filter: '',
|
|
13
|
-
newDocs: {
|
|
14
|
-
sites: {
|
|
15
|
-
name: { bindings: { 'field-type': 'text', 'label': 'Name', 'helper': 'Name' }, cols: '12', value: '' },
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const schemas = {
|
|
21
|
-
sites: toTypedSchema(z.object({
|
|
22
|
-
name: z.string({
|
|
23
|
-
required_error: 'Name is required',
|
|
24
|
-
}).min(1, { message: 'Name is required' }),
|
|
25
|
-
})),
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const collection = computed(() => {
|
|
29
|
-
if (route.params.collection) {
|
|
30
|
-
return route.params.collection
|
|
31
|
-
}
|
|
32
|
-
return ''
|
|
33
|
-
})
|
|
34
|
-
const docId = computed(() => {
|
|
35
|
-
if (route.params.docId) {
|
|
36
|
-
return route.params.docId
|
|
37
|
-
}
|
|
38
|
-
return ''
|
|
39
|
-
})
|
|
40
|
-
definePageMeta({
|
|
41
|
-
middleware: 'auth',
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
onBeforeMount(() => {
|
|
45
|
-
// edgeGlobal.showLeftPanel(true)
|
|
46
|
-
})
|
|
47
|
-
const isAdmin = computed(() => {
|
|
48
|
-
return edgeGlobal.isAdminGlobal(edgeFirebase).value
|
|
49
|
-
})
|
|
50
|
-
const queryField = computed(() => {
|
|
51
|
-
if (!isAdmin.value) {
|
|
52
|
-
return 'users'
|
|
53
|
-
}
|
|
54
|
-
return ''
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const queryValue = computed(() => {
|
|
58
|
-
if (!isAdmin.value) {
|
|
59
|
-
return [edgeFirebase?.user?.uid]
|
|
60
|
-
}
|
|
61
|
-
return ''
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const queryOperator = computed(() => {
|
|
65
|
-
if (!isAdmin.value) {
|
|
66
|
-
return 'array-contains-any'
|
|
67
|
-
}
|
|
68
|
-
return ''
|
|
69
|
-
})
|
|
70
|
-
</script>
|
|
71
|
-
|
|
72
|
-
<template>
|
|
73
|
-
<div
|
|
74
|
-
v-if="edgeGlobal.edgeState.organizationDocPath"
|
|
75
|
-
>
|
|
76
|
-
<edge-dashboard :load-first-if-one="!isAdmin" :filter="state.filter" :query-field="queryField" :query-value="queryValue" :query-operator="queryOperator" collection="sites" class="flex-1 pt-0">
|
|
77
|
-
<template #list="slotProps">
|
|
78
|
-
<template v-for="item in slotProps.filtered" :key="item.docId">
|
|
79
|
-
<edge-shad-button
|
|
80
|
-
variant="text"
|
|
81
|
-
class="cursor-pointer w-full flex justify-between items-center py-2 gap-3"
|
|
82
|
-
:to="isAiBusy(item.aiBootstrapStatus) ? undefined : `/app/dashboard/sites/${item.docId}`"
|
|
83
|
-
:disabled="isAiBusy(item.aiBootstrapStatus)"
|
|
84
|
-
>
|
|
85
|
-
<div>
|
|
86
|
-
<Avatar class="cursor-pointer p-0 h-8 w-8 mr-2">
|
|
87
|
-
<FilePenLine class="h-5 w-5" />
|
|
88
|
-
</Avatar>
|
|
89
|
-
</div>
|
|
90
|
-
<div class="grow text-left">
|
|
91
|
-
<div class="text-lg">
|
|
92
|
-
{{ item.name }}
|
|
93
|
-
</div>
|
|
94
|
-
<div v-if="isAiBusy(item.aiBootstrapStatus)" class="flex items-center gap-2 text-xs text-muted-foreground">
|
|
95
|
-
<Loader2 class="h-3 w-3 animate-spin" />
|
|
96
|
-
<span>AI is preparing this site</span>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
<div>
|
|
100
|
-
<edge-shad-button
|
|
101
|
-
size="icon"
|
|
102
|
-
class="bg-slate-600 h-7 w-7"
|
|
103
|
-
@click.stop="slotProps.deleteItem(item.docId)"
|
|
104
|
-
>
|
|
105
|
-
<Trash class="h-5 w-5" />
|
|
106
|
-
</edge-shad-button>
|
|
107
|
-
</div>
|
|
108
|
-
</edge-shad-button>
|
|
109
|
-
<Separator class="dark:bg-slate-600" />
|
|
110
|
-
</template>
|
|
111
|
-
</template>
|
|
112
|
-
</edge-dashboard>
|
|
113
|
-
</div>
|
|
114
|
-
</template>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{pages/app/dashboard/templates.vue → edge/routes/cms/dashboard/templates/index.vue}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|