Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d746f451dd | |||
| 5a7936aac4 | |||
| 64a4fa5383 | |||
| bf7f9db63d | |||
| 71837428dd | |||
| 12d62d873b | |||
| 61cc32381b | |||
| 42b3b8375a | |||
| bdab2c5395 | |||
| d6b8e91f06 | |||
| 8bb7665af5 | |||
| 644ccc237d | |||
| 84a2c821e9 | |||
| 386c7dac05 | |||
| 1bec7a703d | |||
| 0b575fa322 | |||
| 4c5a0345a8 | |||
| ea7c7b02d6 | |||
| 9296ae7d78 | |||
| 549b2f30be | |||
| 4bc07425b4 | |||
| b0fb2edc24 | |||
| 9b63877eaa | |||
| ba845931da | |||
| 3d7503e227 | |||
| c570597d06 | |||
| e7c7beb8fd | |||
| 037920620e | |||
| 0f73464afe | |||
| 5bf554fbdf | |||
| b566196c96 | |||
| 4da24986ee | |||
| ced2c77427 | |||
| 431d3784fe | |||
| 033380e051 | |||
| 0db6bddbc4 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
github: [arashsheyda]
|
||||||
2
.github/workflows/studio.yml
vendored
2
.github/workflows/studio.yml
vendored
@ -58,7 +58,7 @@ jobs:
|
|||||||
cache: ${{ steps.pkgman.outputs.cache }}
|
cache: ${{ steps.pkgman.outputs.cache }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: ${{ steps.pkgman.outputs.package_manager }} install
|
run: ${{ steps.pkgman.outputs.package_manager }} install --no-frozen-lockfile
|
||||||
|
|
||||||
- name: Install @nuxthq/studio
|
- name: Install @nuxthq/studio
|
||||||
run: ${{ steps.pkgman.outputs.package_manager }} add -D @nuxthq/studio
|
run: ${{ steps.pkgman.outputs.package_manager }} add -D @nuxthq/studio
|
||||||
|
|||||||
3
.nuxtrc
3
.nuxtrc
@ -1 +1,2 @@
|
|||||||
typescript.includeWorkspace=true
|
# enable TypeScript bundler module resolution - https://www.typescriptlang.org/docs/handbook/modules/reference.html#bundler
|
||||||
|
experimental.typescriptBundlerResolution=true
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
}
|
|
||||||
122
CHANGELOG.md
122
CHANGELOG.md
@ -1,6 +1,127 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
## v1.0.5
|
||||||
|
|
||||||
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v1.0.4...v1.0.5)
|
||||||
|
|
||||||
|
### 🩹 Fixes
|
||||||
|
|
||||||
|
- Replace consola with logger ([5a7936a](https://github.com/arashsheyda/nuxt-mongoose/commit/5a7936a))
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- **release:** V1.0.4 ([64a4fa5](https://github.com/arashsheyda/nuxt-mongoose/commit/64a4fa5))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arash <arashi.sheyda@gmail.com>
|
||||||
|
|
||||||
|
## v1.0.4
|
||||||
|
|
||||||
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v1.0.3...v1.0.5)
|
||||||
|
|
||||||
|
### 💅 Refactors
|
||||||
|
|
||||||
|
- Split services into individual files ([d6b8e91](https://github.com/arashsheyda/nuxt-mongoose/commit/d6b8e91))
|
||||||
|
|
||||||
|
### 📖 Documentation
|
||||||
|
|
||||||
|
- Update dependencies ([12d62d8](https://github.com/arashsheyda/nuxt-mongoose/commit/12d62d8))
|
||||||
|
- Update dependencies ([7183742](https://github.com/arashsheyda/nuxt-mongoose/commit/7183742))
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- Test bundler module resolution ([#36](https://github.com/arashsheyda/nuxt-mongoose/pull/36))
|
||||||
|
- Update dependencies ([#38](https://github.com/arashsheyda/nuxt-mongoose/pull/38))
|
||||||
|
- Update dependencies ([bdab2c5](https://github.com/arashsheyda/nuxt-mongoose/commit/bdab2c5))
|
||||||
|
- Update playground ([42b3b83](https://github.com/arashsheyda/nuxt-mongoose/commit/42b3b83))
|
||||||
|
- Update cover ([61cc323](https://github.com/arashsheyda/nuxt-mongoose/commit/61cc323))
|
||||||
|
- Update cover ([bf7f9db](https://github.com/arashsheyda/nuxt-mongoose/commit/bf7f9db))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arash
|
||||||
|
- Amir H. Moayeri
|
||||||
|
- Daniel Roe <daniel@roe.dev>
|
||||||
|
|
||||||
|
## v1.0.3
|
||||||
|
|
||||||
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v1.0.2...v1.0.3)
|
||||||
|
|
||||||
|
### 🩹 Fixes
|
||||||
|
|
||||||
|
- Add schema type ([4bc0742](https://github.com/arashsheyda/nuxt-mongoose/commit/4bc0742))
|
||||||
|
|
||||||
|
### 💅 Refactors
|
||||||
|
|
||||||
|
- Update to DevTools v1 ([1bec7a7](https://github.com/arashsheyda/nuxt-mongoose/commit/1bec7a7))
|
||||||
|
|
||||||
|
### 📖 Documentation
|
||||||
|
|
||||||
|
- Update ui ([9296ae7](https://github.com/arashsheyda/nuxt-mongoose/commit/9296ae7))
|
||||||
|
- Fix playground path ([ea7c7b0](https://github.com/arashsheyda/nuxt-mongoose/commit/ea7c7b0))
|
||||||
|
- Fix install workflow ([4c5a034](https://github.com/arashsheyda/nuxt-mongoose/commit/4c5a034))
|
||||||
|
- Update link ([0b575fa](https://github.com/arashsheyda/nuxt-mongoose/commit/0b575fa))
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- Remove comment ([549b2f3](https://github.com/arashsheyda/nuxt-mongoose/commit/549b2f3))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arash
|
||||||
|
|
||||||
|
## v1.0.2
|
||||||
|
|
||||||
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v1.0.1...v1.0.2)
|
||||||
|
|
||||||
|
### 🩹 Fixes
|
||||||
|
|
||||||
|
- Resolve build stuck issue with nitro pre-render enabled ([#26](https://github.com/arashsheyda/nuxt-mongoose/pull/26))
|
||||||
|
|
||||||
|
### 📖 Documentation
|
||||||
|
|
||||||
|
- Default connection ([e7c7beb](https://github.com/arashsheyda/nuxt-mongoose/commit/e7c7beb))
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- **release:** V1.0.2 ([ba8459](https://github.com/arashsheyda/nuxt-mongoose/commit/ba8459))
|
||||||
|
- Lint ([3d7503e](https://github.com/arashsheyda/nuxt-mongoose/commit/3d7503e))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arash Sheyda <sheidaeearash1999@gmail.com>
|
||||||
|
- Arash
|
||||||
|
|
||||||
|
## v1.0.1
|
||||||
|
|
||||||
|
### 🏡 Chore
|
||||||
|
|
||||||
|
- **release:** V1.0.0 ([b566196](https://github.com/arashsheyda/nuxt-mongoose/commit/b566196))
|
||||||
|
- **release:** V1.0.1 ([5bf554f](https://github.com/arashsheyda/nuxt-mongoose/commit/5bf554f))
|
||||||
|
- Update dependencies ([0f73464](https://github.com/arashsheyda/nuxt-mongoose/commit/0f73464))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arash Sheyda <sheidaeearash1999@gmail.com>
|
||||||
|
|
||||||
|
## v1.0.0
|
||||||
|
|
||||||
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v0.0.9...v1.0.0)
|
||||||
|
|
||||||
|
### 🚀 Enhancements
|
||||||
|
|
||||||
|
- Version 1.0.0 ([#21](https://github.com/arashsheyda/nuxt-mongoose/pull/21))
|
||||||
|
|
||||||
|
### 📖 Documentation
|
||||||
|
|
||||||
|
- Update docus ([0db6bdd](https://github.com/arashsheyda/nuxt-mongoose/commit/0db6bdd))
|
||||||
|
|
||||||
|
### ❤️ Contributors
|
||||||
|
|
||||||
|
- Arashsheyda <sheidaeearash1999@gmail.com>
|
||||||
|
|
||||||
## v0.0.9
|
## v0.0.9
|
||||||
|
|
||||||
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v0.0.8...v0.0.9)
|
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v0.0.8...v0.0.9)
|
||||||
@ -32,7 +153,6 @@
|
|||||||
### ❤️ Contributors
|
### ❤️ Contributors
|
||||||
|
|
||||||
- Arash Sheyda <sheidaeearash1999@gmail.com>
|
- Arash Sheyda <sheidaeearash1999@gmail.com>
|
||||||
- Arash
|
|
||||||
- Amir-al-mohamad111
|
- Amir-al-mohamad111
|
||||||
- Oumar Barry ([@oumarbarry](http://github.com/oumarbarry))
|
- Oumar Barry ([@oumarbarry](http://github.com/oumarbarry))
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>Nuxt Mongoose</h1>
|
<h1>Nuxt Mongoose</h1>
|
||||||
|
|||||||
@ -1,7 +1,3 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import './styles/global.css'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Html>
|
<Html>
|
||||||
<Body h-screen>
|
<Body h-screen>
|
||||||
|
|||||||
@ -1,33 +1,53 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineProps({
|
import { computed } from 'vue'
|
||||||
connection: {
|
|
||||||
|
const props = defineProps({
|
||||||
|
code: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const connections = [
|
||||||
|
{
|
||||||
|
color: 'text-red-5',
|
||||||
|
border: 'border-red-5',
|
||||||
|
status: 'Not Connected',
|
||||||
|
description: 'Please Check Your Connection!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: 'text-green-5',
|
||||||
|
border: 'border-green-5',
|
||||||
|
status: 'Connected',
|
||||||
|
description: 'Everything is Working Perfectly!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: 'text-yellow-5',
|
||||||
|
border: 'border-yellow-5',
|
||||||
|
status: 'Connecting',
|
||||||
|
description: 'Just a Moment, We"re Getting There!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
color: 'text-orange-5',
|
||||||
|
border: 'border-orange-5',
|
||||||
|
status: 'Disconnecting',
|
||||||
|
description: 'Preparing to Safely Disconnect!',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const connection = computed(() => connections[props.code])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NPanelGrids>
|
<NPanelGrids>
|
||||||
<div flex="~ gap-2" animate-pulse items-center text-yellow>
|
<div flex="~ gap-2" animate-pulse items-center text-lg font-bold :class="connection.color">
|
||||||
<NIcon icon="carbon-flow-connection" />
|
({{ code }}):
|
||||||
Please check your mongodb connection
|
{{ connection.status }},
|
||||||
</div>
|
{{ connection.description }}
|
||||||
<div flex="~ gap-2" items-center text-light>
|
|
||||||
Your current connection is: {{ connection }}
|
|
||||||
</div>
|
</div>
|
||||||
<div absolute bottom-10 left-10 right-10 flex justify-around>
|
<div absolute bottom-10 left-10 right-10 flex justify-around>
|
||||||
<NCard p2 text-red-5>
|
<NCard v-for="item, index of connections" :key="index" p2 :class="[item.color, item.status === connection.status ? item.border : '']">
|
||||||
0: Not connected
|
({{ index }}): {{ item.status }}
|
||||||
</NCard>
|
|
||||||
<NCard p2 text-green-5>
|
|
||||||
1: Connected
|
|
||||||
</NCard>
|
|
||||||
<NCard p2 text-yellow-5>
|
|
||||||
2: Connecting
|
|
||||||
</NCard>
|
|
||||||
<NCard p2 text-orange-5>
|
|
||||||
3: Disconnecting
|
|
||||||
</NCard>
|
</NCard>
|
||||||
</div>
|
</div>
|
||||||
</NPanelGrids>
|
</NPanelGrids>
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useRouter } from 'nuxt/app'
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
import { rpc } from '../composables/rpc'
|
||||||
|
|
||||||
interface ColumnInterface {
|
interface ColumnInterface {
|
||||||
name: string
|
name: string
|
||||||
type: string
|
type: string
|
||||||
@ -115,18 +119,16 @@ const convertedBread = computed(() => {
|
|||||||
const formattedFields = computed(() => {
|
const formattedFields = computed(() => {
|
||||||
return fields.value.map((field) => {
|
return fields.value.map((field) => {
|
||||||
for (const [key, value] of Object.entries(field)) {
|
for (const [key, value] of Object.entries(field)) {
|
||||||
if (!value) {
|
if (!value)
|
||||||
// @ts-expect-error - no need for type checking
|
|
||||||
delete field[key]
|
delete field[key]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return field
|
return field
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function generate() {
|
async function generate() {
|
||||||
await rpc.generateResource(
|
await rpc.value?.generateResource(
|
||||||
{
|
{
|
||||||
name: collection.value,
|
name: collection.value,
|
||||||
fields: schema.value ? formattedFields.value : undefined,
|
fields: schema.value ? formattedFields.value : undefined,
|
||||||
@ -142,6 +144,7 @@ async function generate() {
|
|||||||
const toggleSchema = computed({
|
const toggleSchema = computed({
|
||||||
get() {
|
get() {
|
||||||
if (hasBread.value)
|
if (hasBread.value)
|
||||||
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
return schema.value = true
|
return schema.value = true
|
||||||
return schema.value
|
return schema.value
|
||||||
},
|
},
|
||||||
@ -246,8 +249,8 @@ const toggleSchema = computed({
|
|||||||
<NTextInput v-else v-model="column.default" n="orange" />
|
<NTextInput v-else v-model="column.default" n="orange" />
|
||||||
</div>
|
</div>
|
||||||
<div flex justify-center gap2>
|
<div flex justify-center gap2>
|
||||||
<NIconButton icon="carbon-add" n="cyan" @click="addField(index)" />
|
<NButton icon="carbon-add" n="cyan" @click="addField(index)" />
|
||||||
<NIconButton icon="carbon-delete" n="red" @click="removeField(index)" />
|
<NButton icon="carbon-trash-can" n="red" @click="removeField(index)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -257,5 +260,3 @@ const toggleSchema = computed({
|
|||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed, reactive, ref, watch } from 'vue'
|
||||||
|
import { computedAsync } from '@vueuse/core'
|
||||||
|
import { rpc } from '../composables/rpc'
|
||||||
|
import { useCopy } from '../composables/editor'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
collection: {
|
collection: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -10,19 +15,19 @@ const props = defineProps({
|
|||||||
const pagination = reactive({ limit: 20, page: 1 })
|
const pagination = reactive({ limit: 20, page: 1 })
|
||||||
|
|
||||||
const countDocuments = computedAsync(async () => {
|
const countDocuments = computedAsync(async () => {
|
||||||
return await rpc.countDocuments(props.collection)
|
return await rpc.value?.countDocuments(props.collection)
|
||||||
})
|
})
|
||||||
|
|
||||||
const documents = computedAsync(async () => {
|
const documents = computedAsync(async () => {
|
||||||
return await rpc.listDocuments(props.collection, pagination)
|
return await rpc.value?.listDocuments(props.collection, pagination)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(pagination, async () => {
|
watch(pagination, async () => {
|
||||||
documents.value = await rpc.listDocuments(props.collection, pagination)
|
documents.value = await rpc.value?.listDocuments(props.collection, pagination)
|
||||||
})
|
})
|
||||||
|
|
||||||
const schema = computedAsync<any>(async () => {
|
const schema = computedAsync<any>(async () => {
|
||||||
return await rpc.resourceSchema(props.collection)
|
return await rpc.value?.resourceSchema(props.collection)
|
||||||
})
|
})
|
||||||
|
|
||||||
const fields = computed(() => {
|
const fields = computed(() => {
|
||||||
@ -79,7 +84,9 @@ function editDocument(document: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveDocument(document: any, create = true) {
|
async function saveDocument(document: any, create = true) {
|
||||||
const method = create ? rpc.createDocument : rpc.updateDocument
|
const method = create ? rpc.value?.createDocument : rpc.value?.updateDocument
|
||||||
|
if (!method)
|
||||||
|
return
|
||||||
const newDocument = await method(props.collection, document)
|
const newDocument = await method(props.collection, document)
|
||||||
// TODO: show toast
|
// TODO: show toast
|
||||||
if (newDocument?.error)
|
if (newDocument?.error)
|
||||||
@ -87,7 +94,7 @@ async function saveDocument(document: any, create = true) {
|
|||||||
|
|
||||||
if (create) {
|
if (create) {
|
||||||
if (!documents.value.length) {
|
if (!documents.value.length) {
|
||||||
documents.value = await rpc.listDocuments(props.collection, pagination)
|
documents.value = await rpc.value?.listDocuments(props.collection, pagination)
|
||||||
return discardEditing()
|
return discardEditing()
|
||||||
}
|
}
|
||||||
documents.value.push({ _id: newDocument.insertedId, ...document })
|
documents.value.push({ _id: newDocument.insertedId, ...document })
|
||||||
@ -105,7 +112,7 @@ function discardEditing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function deleteDocument(document: any) {
|
async function deleteDocument(document: any) {
|
||||||
const newDocument = await rpc.deleteDocument(props.collection, document._id)
|
const newDocument = await rpc.value?.deleteDocument(props.collection, document._id)
|
||||||
// TODO: show toast
|
// TODO: show toast
|
||||||
if (newDocument.deletedCount === 0)
|
if (newDocument.deletedCount === 0)
|
||||||
return
|
return
|
||||||
@ -118,7 +125,7 @@ const copy = useCopy()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="dbContainer" :class="{ 'h-full': !documents?.length }">
|
<div ref="dbContainer" :class="{ 'h-full': !documents?.length }">
|
||||||
<Navbar v-model:search="search" sticky top-0 px4 py2 backdrop-blur z-10>
|
<NNavbar v-model:search="search" sticky top-0 px4 py2 backdrop-blur z-10>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<NButton icon="carbon:add" n="green" @click="addDocument">
|
<NButton icon="carbon:add" n="green" @click="addDocument">
|
||||||
Add Document
|
Add Document
|
||||||
@ -148,7 +155,7 @@ const copy = useCopy()
|
|||||||
</NSelect>
|
</NSelect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</NNavbar>
|
||||||
<table v-if="documents?.length || selectedDocument" w-full mb10 :class="{ 'editing-mode': editing }">
|
<table v-if="documents?.length || selectedDocument" w-full mb10 :class="{ 'editing-mode': editing }">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -170,17 +177,17 @@ const copy = useCopy()
|
|||||||
{{ document[field] }}
|
{{ document[field] }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="actions">
|
<td>
|
||||||
<div flex justify-center gap2 class="group">
|
<div flex justify-center gap2 class="group">
|
||||||
<template v-if="editing && selectedDocument._id === document._id">
|
<template v-if="editing && selectedDocument._id === document._id">
|
||||||
<NIconButton icon="carbon-save" @click="saveDocument(selectedDocument, false)" />
|
<NButton title="Save" icon="carbon-save" n="blue" @click="saveDocument(selectedDocument, false)" />
|
||||||
<NIconButton icon="carbon-close" @click="discardEditing" />
|
<NButton title="Cancel" icon="carbon-close" n="red" @click="discardEditing" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NIconButton icon="carbon-edit" @click="editDocument(document)" />
|
<NButton title="Edit" icon="carbon-edit" n="blue" @click="editDocument(document)" />
|
||||||
<NIconButton icon="carbon-delete" @click="deleteDocument(document)" />
|
<NButton title="Delete" icon="carbon-trash-can" n="red" @click="deleteDocument(document)" />
|
||||||
<NIconButton icon="carbon-document-multiple-02" @click="saveDocument(document)" />
|
<NButton title="Duplicate" icon="carbon-document-multiple-02" n="cyan" @click="saveDocument(document)" />
|
||||||
<NIconButton absolute right-4 opacity-0 group-hover="opacity-100" transition-all icon="carbon-copy" @click="copy(JSON.stringify(document))" />
|
<NButton title="Copy" n="xs purple" absolute right-4 opacity-0 group-hover="opacity-100" transition-all icon="carbon-copy" @click="copy(JSON.stringify(document))" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -190,14 +197,14 @@ const copy = useCopy()
|
|||||||
<input v-if="field !== '_id'" v-model="selectedDocument[field]" :placeholder="field">
|
<input v-if="field !== '_id'" v-model="selectedDocument[field]" :placeholder="field">
|
||||||
<input v-else placeholder="ObjectId(_id)" disabled>
|
<input v-else placeholder="ObjectId(_id)" disabled>
|
||||||
</td>
|
</td>
|
||||||
<td flex justify-center gap2 class="actions">
|
<td flex="~ justify-center gap2">
|
||||||
<NIconButton icon="carbon-save" @click="saveDocument(selectedDocument)" />
|
<NButton title="Save" icon="carbon-save" n="green" @click="saveDocument(selectedDocument)" />
|
||||||
<NIconButton icon="carbon-close" @click="discardEditing" />
|
<NButton title="Cancel" icon="carbon-close" n="red" @click="discardEditing" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div v-else flex justify-center items-center h-full text-2xl>
|
<div v-else flex="~ justify-center items-center" h-full text-2xl>
|
||||||
<NIcon icon="carbon-document" mr1 />
|
<NIcon icon="carbon-document" mr1 />
|
||||||
No documents found
|
No documents found
|
||||||
</div>
|
</div>
|
||||||
@ -205,11 +212,6 @@ const copy = useCopy()
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
// TODO:
|
|
||||||
.actions .n-icon {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
table {
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
tr {
|
tr {
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps<{
|
|
||||||
modelValue?: boolean
|
|
||||||
navbar?: HTMLElement
|
|
||||||
autoClose?: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'close'): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const el = ref<HTMLElement>()
|
|
||||||
|
|
||||||
const { height: top } = useElementSize(() => props.navbar, undefined, { box: 'border-box' })
|
|
||||||
|
|
||||||
onClickOutside(el, () => {
|
|
||||||
if (props.modelValue && props.autoClose)
|
|
||||||
emit('close')
|
|
||||||
}, {
|
|
||||||
ignore: ['#open-drawer-right'],
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
inheritAttrs: false,
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Transition
|
|
||||||
enter-active-class="duration-200 ease-in"
|
|
||||||
enter-from-class="transform translate-x-1/1"
|
|
||||||
enter-to-class="opacity-100"
|
|
||||||
leave-active-class="duration-200 ease-out"
|
|
||||||
leave-from-class="opacity-100"
|
|
||||||
leave-to-class="transform translate-x-1/1"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="modelValue"
|
|
||||||
ref="el"
|
|
||||||
border="l base"
|
|
||||||
flex="~ col gap-1"
|
|
||||||
fixed bottom-0 right-0 z-10 z-20 of-auto text-sm backdrop-blur-lg
|
|
||||||
:style="{ top: `${top}px` }"
|
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
|
||||||
<NIconButton absolute right-2 top-2 z-20 text-xl icon="carbon-close" @click="$emit('close')" />
|
|
||||||
<div relative h-full w-full of-auto>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</template>
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
defineProps({
|
|
||||||
search: {
|
|
||||||
type: String,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
noPadding: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: 'Search...',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: 'update:search', value: string): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function update(event: any) {
|
|
||||||
emit('update:search', event.target.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div flex="~ col gap2" border="b base" flex-1 navbar-glass :class="[{ p4: !noPadding }]">
|
|
||||||
<div flex="~ gap4">
|
|
||||||
<slot name="search">
|
|
||||||
<NTextInput
|
|
||||||
:placeholder="placeholder"
|
|
||||||
icon="carbon-search"
|
|
||||||
n="primary" flex-auto
|
|
||||||
:class="{ 'px-5 py-2': !noPadding }"
|
|
||||||
:value="search"
|
|
||||||
@input="update"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
<slot name="actions" />
|
|
||||||
</div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { Pane, Splitpanes } from 'splitpanes'
|
|
||||||
import 'splitpanes/dist/splitpanes.css'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
minLeft: {
|
|
||||||
type: Number,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
minRight: {
|
|
||||||
type: Number,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
maxLeft: {
|
|
||||||
type: Number,
|
|
||||||
default: 90,
|
|
||||||
},
|
|
||||||
maxRight: {
|
|
||||||
type: Number,
|
|
||||||
default: 90,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const size = ref(props.maxLeft)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Splitpanes h-full of-hidden @resize="size = $event[0].size">
|
|
||||||
<Pane border="r base" h-full class="of-auto!" :size="size" :min-size="minLeft" :max-size="maxLeft">
|
|
||||||
<slot name="left" />
|
|
||||||
</Pane>
|
|
||||||
<Pane relative h-full class="of-auto!" :min-size="minRight">
|
|
||||||
<slot name="right" />
|
|
||||||
</Pane>
|
|
||||||
</Splitpanes>
|
|
||||||
</template>
|
|
||||||
@ -1,10 +1,14 @@
|
|||||||
import { useClipboard } from '@vueuse/core'
|
import { useClipboard } from '@vueuse/core'
|
||||||
|
// import { } from '@nuxt/devtools-ui-kit'
|
||||||
|
|
||||||
export function useCopy() {
|
export function useCopy() {
|
||||||
const clipboard = useClipboard()
|
const clipboard = useClipboard()
|
||||||
|
|
||||||
return (text: string) => {
|
return (text: string) => {
|
||||||
clipboard.copy(text)
|
clipboard.copy(text)
|
||||||
// TODO: show toast
|
// devtoolsUiShowNotification({
|
||||||
|
// message: 'Copied to clipboard',
|
||||||
|
// icon: 'carbon-copy',
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,59 +1,18 @@
|
|||||||
import { createBirpc } from 'birpc'
|
import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client'
|
||||||
import { parse, stringify } from 'flatted'
|
import type { BirpcReturn } from 'birpc'
|
||||||
import { createHotContext } from 'vite-hot-client'
|
import { ref } from 'vue'
|
||||||
|
import type { NuxtDevtoolsClient } from '@nuxt/devtools-kit/dist/types'
|
||||||
import type { ClientFunctions, ServerFunctions } from '../../src/types'
|
import type { ClientFunctions, ServerFunctions } from '../../src/types'
|
||||||
import { WS_EVENT_NAME } from '../../src/constants'
|
import { RPC_NAMESPACE } from '../../src/constants'
|
||||||
|
|
||||||
export const wsConnecting = ref(true)
|
export const devtools = ref<NuxtDevtoolsClient>()
|
||||||
export const wsError = ref<any>()
|
export const devtoolsRpc = ref<NuxtDevtoolsClient['rpc']>()
|
||||||
export const wsConnectingDebounced = useDebounce(wsConnecting, 2000)
|
export const rpc = ref<BirpcReturn<ServerFunctions, ClientFunctions>>()
|
||||||
|
|
||||||
const connectPromise = connectVite()
|
onDevtoolsClientConnected(async (client) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
devtoolsRpc.value = client.devtools.rpc
|
||||||
let onMessage: Function = () => {}
|
devtools.value = client.devtools
|
||||||
|
|
||||||
export const clientFunctions = {
|
rpc.value = client.devtools.extendClientRpc<ServerFunctions, ClientFunctions>(RPC_NAMESPACE, {
|
||||||
// will be added in app.vue
|
|
||||||
} as ClientFunctions
|
|
||||||
|
|
||||||
export const extendedRpcMap = new Map<string, any>()
|
|
||||||
|
|
||||||
export const rpc = createBirpc<ServerFunctions>(clientFunctions, {
|
|
||||||
post: async (d) => {
|
|
||||||
(await connectPromise).send(WS_EVENT_NAME, d)
|
|
||||||
},
|
|
||||||
on: (fn) => {
|
|
||||||
onMessage = fn
|
|
||||||
},
|
|
||||||
serialize: stringify,
|
|
||||||
deserialize: parse,
|
|
||||||
resolver(name, fn) {
|
|
||||||
if (fn)
|
|
||||||
return fn
|
|
||||||
if (!name.includes(':'))
|
|
||||||
return
|
|
||||||
const [namespace, fnName] = name.split(':')
|
|
||||||
return extendedRpcMap.get(namespace)?.[fnName]
|
|
||||||
},
|
|
||||||
onError(error, name) {
|
|
||||||
console.error(`[nuxt-devtools] RPC error on executing "${name}":`, error)
|
|
||||||
},
|
|
||||||
timeout: 120_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
async function connectVite() {
|
|
||||||
const hot = await createHotContext()
|
|
||||||
|
|
||||||
if (!hot)
|
|
||||||
throw new Error('Unable to connect to devtools')
|
|
||||||
|
|
||||||
hot.on(WS_EVENT_NAME, (data) => {
|
|
||||||
onMessage(data)
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
// TODO:
|
|
||||||
// hot.on('vite:connect', (data) => {})
|
|
||||||
// hot.on('vite:disconnect', (data) => {})
|
|
||||||
|
|
||||||
return hot
|
|
||||||
}
|
|
||||||
|
|||||||
1
client/composables/state.ts
Normal file
1
client/composables/state.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const selectedCollection = useState('mongo:collection', () => '')
|
||||||
@ -1,10 +1,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const readyState = computedAsync(async () => await rpc.readyState())
|
import { computedAsync } from '@vueuse/core'
|
||||||
|
import { rpc } from '../composables/rpc'
|
||||||
|
|
||||||
|
const readyState = computedAsync(async () => await rpc.value?.readyState())
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div h-full of-auto>
|
<div h-full of-auto>
|
||||||
<slot v-if="readyState === 1" />
|
<slot v-if="readyState === 1" />
|
||||||
<Connection v-else :connection="readyState" />
|
<Connection v-else :code="readyState" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { PATH_CLIENT } from '../src/constants'
|
import { CLIENT_PATH } from '../src/constants'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -22,6 +22,6 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
baseURL: PATH_CLIENT,
|
baseURL: CLIENT_PATH,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const route = useRoute()
|
import { computed, ref } from 'vue'
|
||||||
|
import { computedAsync } from '@vueuse/core'
|
||||||
|
import { useRouter } from 'nuxt/app'
|
||||||
|
import { rpc } from '../composables/rpc'
|
||||||
|
import { selectedCollection } from '../composables/state'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const selectedCollection = ref()
|
|
||||||
const drawer = ref(false)
|
const drawer = ref(false)
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
|
|
||||||
const collections = computedAsync(async () => {
|
const collections = computedAsync(async () => {
|
||||||
return await rpc.listCollections()
|
return await rpc.value?.listCollections()
|
||||||
})
|
})
|
||||||
|
|
||||||
const filtered = computed(() => {
|
const filtered = computed(() => {
|
||||||
@ -16,46 +20,48 @@ const filtered = computed(() => {
|
|||||||
return collections.value.filter((c: any) => c.name.toLowerCase().includes(search.value.toLowerCase()))
|
return collections.value.filter((c: any) => c.name.toLowerCase().includes(search.value.toLowerCase()))
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (route.query.table)
|
|
||||||
selectedCollection.value = route.query.table
|
|
||||||
})
|
|
||||||
|
|
||||||
async function dropCollection(table: any) {
|
async function dropCollection(table: any) {
|
||||||
await rpc.dropCollection(table.name)
|
await rpc.value?.dropCollection(table.name)
|
||||||
collections.value = await rpc.listCollections()
|
collections.value = await rpc.value?.listCollections()
|
||||||
if (selectedCollection.value === table.name) {
|
if (selectedCollection.value === table.name) {
|
||||||
selectedCollection.value = undefined
|
selectedCollection.value = ''
|
||||||
router.push({ name: 'index' })
|
router.push({ name: 'index' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
collections.value = await rpc.listCollections()
|
collections.value = await rpc.value?.listCollections()
|
||||||
drawer.value = false
|
drawer.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PanelLeftRight :min-left="13" :max-left="20">
|
<NSplitPane :min-left="13" :max-left="20">
|
||||||
<template #left>
|
<template #left>
|
||||||
<div px4>
|
<div px4>
|
||||||
<Navbar v-model:search="search" :placeholder="`${collections?.length ?? '-'} collection in total`" mt2>
|
<NNavbar v-model:search="search" :placeholder="`${collections?.length ?? '-'} collection in total`" mt2 no-padding>
|
||||||
<div flex items-center gap2>
|
<div flex items-center gap2>
|
||||||
<NIconButton w-full mb1.5 icon="carbon-reset" title="Refresh" @click="refresh" />
|
<NButton n="blue" w-full mb1.5 icon="carbon-reset" title="Refresh" @click="refresh" />
|
||||||
<NIconButton w-full mb1.5 icon="carbon-data-base" title="Default" text-green-5 />
|
<!-- <NButton w-full mb1.5 icon="carbon-data-base" title="Default" n="green" /> -->
|
||||||
<NIconButton id="open-drawer-right" w-full mb1.5 icon="carbon-add" title="Create Collection" @click="drawer = true" />
|
<NButton n="green" w-full mb1.5 icon="carbon-add" title="Create Collection" @click="drawer = true" />
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</NNavbar>
|
||||||
<div grid gird-cols-1 my2 mx1>
|
<div grid gird-cols-1 my2 mx1>
|
||||||
<NuxtLink v-for="table in filtered" :key="table.name" :to="{ name: 'index', query: { table: table.name } }" flex justify-between p2 my1 hover-bg-green hover-bg-opacity-5 hover-text-green rounded-lg :class="{ 'bg-green bg-opacity-5 text-green': selectedCollection === table.name }" @click="selectedCollection = table.name">
|
<NuxtLink
|
||||||
|
v-for="table in filtered"
|
||||||
|
:key="table.name"
|
||||||
|
:to="{ name: 'index', query: { table: table.name } }"
|
||||||
|
flex items-center justify-between p2 my1 hover-bg-green hover-bg-opacity-5 hover-text-green rounded-lg
|
||||||
|
:class="{ 'bg-green bg-opacity-5 text-green': selectedCollection === table.name }"
|
||||||
|
@click="selectedCollection = table.name"
|
||||||
|
>
|
||||||
<span>
|
<span>
|
||||||
<NIcon icon="carbon-db2-database" />
|
<NIcon icon="carbon-db2-database" />
|
||||||
{{ table.name }}
|
{{ table.name }}
|
||||||
</span>
|
</span>
|
||||||
<div flex gap2>
|
<div flex="~ items-center gap-2">
|
||||||
<NIconButton block n="red" icon="carbon-delete" @click="dropCollection(table)" />
|
<NButton :border="false" n="red" icon="carbon-trash-can" @click="dropCollection(table)" />
|
||||||
<!-- <NIconButton icon="carbon-overflow-menu-horizontal" /> -->
|
<!-- <NButton :border="false" icon="carbon-overflow-menu-horizontal" /> -->
|
||||||
</div>
|
</div>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
@ -72,8 +78,8 @@ async function refresh() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</PanelLeftRight>
|
</NSplitPane>
|
||||||
<DrawerRight v-model="drawer" style="width: calc(80.5%);" auto-close @close="drawer = false">
|
<NDrawer v-model="drawer" style="width: calc(80.5%);" auto-close @close="drawer = false" z-20>
|
||||||
<CreateResource @refresh="refresh" />
|
<CreateResource @refresh="refresh" />
|
||||||
</DrawerRight>
|
</NDrawer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
21
client/splitpanes.d.ts
vendored
21
client/splitpanes.d.ts
vendored
@ -1,21 +0,0 @@
|
|||||||
// TODO install @types/splitpanes once updated
|
|
||||||
declare module 'splitpanes' {
|
|
||||||
import { Component } from 'vue'
|
|
||||||
|
|
||||||
export interface SplitpaneProps {
|
|
||||||
horizontal: boolean
|
|
||||||
pushOtherPanes: boolean
|
|
||||||
dblClickSplitter: boolean
|
|
||||||
firstSplitter: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PaneProps {
|
|
||||||
size: number | string
|
|
||||||
minSize: number | string
|
|
||||||
maxSize: number | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Pane = Component<PaneProps>
|
|
||||||
export const Pane: Pane
|
|
||||||
export const Splitpanes: Component<SplitpaneProps>
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
/* Splitpanes */
|
|
||||||
.splitpanes__splitter {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.splitpanes__splitter:before {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
transition: .2s ease;
|
|
||||||
content: '';
|
|
||||||
transition: opacity 0.4s;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
.splitpanes__splitter:hover:before {
|
|
||||||
background: #8881;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.splitpanes--vertical>.splitpanes__splitter {
|
|
||||||
min-width: 0 !important;
|
|
||||||
width: 0 !important;
|
|
||||||
}
|
|
||||||
.splitpanes--horizontal>.splitpanes__splitter {
|
|
||||||
min-height: 0 !important;
|
|
||||||
height: 0 !important;
|
|
||||||
}
|
|
||||||
.splitpanes--vertical>.splitpanes__splitter:before {
|
|
||||||
left: -5px;
|
|
||||||
right: -4px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.splitpanes--horizontal>.splitpanes__splitter:before {
|
|
||||||
top: -5px;
|
|
||||||
bottom: -4px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
@ -15,22 +15,16 @@ export default defineAppConfig({
|
|||||||
edit: true,
|
edit: true,
|
||||||
},
|
},
|
||||||
aside: {
|
aside: {
|
||||||
level: 0,
|
level: 1,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
exclude: [],
|
exclude: [],
|
||||||
},
|
},
|
||||||
main: {
|
|
||||||
padded: true,
|
|
||||||
fluid: true,
|
|
||||||
},
|
|
||||||
header: {
|
header: {
|
||||||
logo: true,
|
logo: true,
|
||||||
showLinkIcon: true,
|
showLinkIcon: true,
|
||||||
exclude: [],
|
exclude: [],
|
||||||
fluid: true,
|
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
fluid: true,
|
|
||||||
iconLinks: [
|
iconLinks: [
|
||||||
{
|
{
|
||||||
href: 'https://nuxt.com',
|
href: 'https://nuxt.com',
|
||||||
|
|||||||
295
docs/components/content/Illustration.vue
Normal file
295
docs/components/content/Illustration.vue
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="403"
|
||||||
|
height="226"
|
||||||
|
viewBox="0 0 403 226"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#clip0_1180_138)">
|
||||||
|
<g opacity="0.54">
|
||||||
|
<path
|
||||||
|
d="M0 12C0 5.37259 5.37258 0 12 0H391C397.627 0 403 5.37258 403 12V214C403 220.627 397.627 226 391 226H12C5.37259 226 0 220.627 0 214V12Z"
|
||||||
|
fill="url(#paint0_linear_1180_138)"
|
||||||
|
fill-opacity="0.3"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M12 0.5H391C397.351 0.5 402.5 5.64873 402.5 12V214C402.5 220.351 397.351 225.5 391 225.5H12C5.64873 225.5 0.5 220.351 0.5 214V12C0.5 5.64873 5.64873 0.5 12 0.5Z"
|
||||||
|
stroke="url(#paint1_linear_1180_138)"
|
||||||
|
stroke-opacity="0.4"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
opacity="0.54"
|
||||||
|
d="M49.7811 28H57.5876C57.8356 28 58.0792 27.9354 58.2939 27.8125C58.5086 27.6896 58.6869 27.5128 58.8108 27.3C58.9347 27.0871 58.9999 26.8457 58.9998 26.5999C58.9997 26.3542 58.9343 26.1128 58.8102 25.9001L53.5676 16.9001C53.4437 16.6873 53.2654 16.5105 53.0507 16.3877C52.836 16.2648 52.5925 16.2001 52.3446 16.2001C52.0967 16.2001 51.8532 16.2648 51.6385 16.3877C51.4238 16.5105 51.2455 16.6873 51.1216 16.9001L49.7811 19.2028L47.1602 14.6998C47.0361 14.487 46.8578 14.3104 46.6431 14.1875C46.4283 14.0647 46.1847 14 45.9368 14C45.6889 14 45.4453 14.0647 45.2305 14.1875C45.0158 14.3104 44.8375 14.487 44.7134 14.6998L38.1896 25.9001C38.0655 26.1128 38.0001 26.3542 38 26.5999C37.9999 26.8457 38.0651 27.0871 38.189 27.3C38.3129 27.5128 38.4912 27.6896 38.7059 27.8125C38.9207 27.9354 39.1643 28 39.4122 28H44.3125C46.254 28 47.6859 27.1547 48.6711 25.5057L51.063 21.4001L52.3442 19.2028L56.1893 25.8028H51.063L49.7811 28ZM44.2326 25.8005L40.8128 25.7998L45.9391 17.0004L48.4969 21.4001L46.7843 24.3407C46.13 25.4107 45.3867 25.8005 44.2326 25.8005Z"
|
||||||
|
fill="white"
|
||||||
|
fill-opacity="0.1"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="75.5"
|
||||||
|
x2="84.5"
|
||||||
|
y2="75.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="147.5"
|
||||||
|
x2="63.5"
|
||||||
|
y2="147.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="172.5"
|
||||||
|
y1="147.5"
|
||||||
|
x2="192.899"
|
||||||
|
y2="147.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="301.5"
|
||||||
|
y1="147.5"
|
||||||
|
x2="321.5"
|
||||||
|
y2="147.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="143.5"
|
||||||
|
y1="20.5"
|
||||||
|
x2="167.5"
|
||||||
|
y2="20.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="184.5"
|
||||||
|
y1="20.5"
|
||||||
|
x2="208.5"
|
||||||
|
y2="20.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="225.5"
|
||||||
|
y1="20.5"
|
||||||
|
x2="249.5"
|
||||||
|
y2="20.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="93.5"
|
||||||
|
x2="178.5"
|
||||||
|
y2="93.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="162.5"
|
||||||
|
x2="115.544"
|
||||||
|
y2="162.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="172.5"
|
||||||
|
y1="162.5"
|
||||||
|
x2="244.5"
|
||||||
|
y2="162.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="301.5"
|
||||||
|
y1="162.5"
|
||||||
|
x2="373.5"
|
||||||
|
y2="162.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="111.5"
|
||||||
|
x2="178.5"
|
||||||
|
y2="111.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="43.5"
|
||||||
|
y1="177.5"
|
||||||
|
x2="115.5"
|
||||||
|
y2="177.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="172.5"
|
||||||
|
y1="177.5"
|
||||||
|
x2="244.5"
|
||||||
|
y2="177.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<line
|
||||||
|
opacity="0.54"
|
||||||
|
x1="301.5"
|
||||||
|
y1="177.5"
|
||||||
|
x2="373.5"
|
||||||
|
y2="177.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-opacity="0.1"
|
||||||
|
stroke-width="5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
opacity="0.54"
|
||||||
|
x="335.465"
|
||||||
|
y="17.619"
|
||||||
|
width="41.1089"
|
||||||
|
height="11.7454"
|
||||||
|
rx="2.93635"
|
||||||
|
fill="white"
|
||||||
|
fill-opacity="0.1"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
opacity="0.54"
|
||||||
|
x="212"
|
||||||
|
y="70"
|
||||||
|
width="164"
|
||||||
|
height="44"
|
||||||
|
rx="5"
|
||||||
|
fill="url(#paint2_linear_1180_138)"
|
||||||
|
fill-opacity="0.1"
|
||||||
|
/>
|
||||||
|
<!-- Mongo Logo -->
|
||||||
|
<g transform="translate(170, 80) scale(2)">
|
||||||
|
<rect width="32" height="32" rx="7.5" style="fill:#023430" />
|
||||||
|
<path d="M21.4,13.4C20.1,7.6,17.3,6.1,16.6,5a10.1,10.1,0,0,1-.7-1.5,1.6,1.6,0,0,1-.6,1.2,14.1,14.1,0,0,0-4.9,10.5c-.3,6.1,4.5,9.9,5.1,10.3a1.4,1.4,0,0,0,1.4-.2,12.1,12.1,0,0,0,4.5-11.9" style="fill:#10aa50" />
|
||||||
|
<path d="M16.1,22.2a17.8,17.8,0,0,1-.5,3.3s.2,1.5.3,3h.5a30.6,30.6,0,0,1,.5-3.2C16.3,25,16.1,23.6,16.1,22.2Z" style="fill:#b8c4c2" />
|
||||||
|
<path d="M16.9,25.3c-.6-.3-.8-1.7-.8-3.1s.2-4.3.1-6.5,0-10.7-.3-12.1L16.6,5c.7,1.1,3.5,2.6,4.8,8.4A12,12,0,0,1,16.9,25.3Z" style="fill:#12924f" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter
|
||||||
|
id="filter0_d_1180_138"
|
||||||
|
x="162.895"
|
||||||
|
y="74.8947"
|
||||||
|
width="76.2105"
|
||||||
|
height="76.2105"
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
>
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||||
|
<feColorMatrix
|
||||||
|
in="SourceAlpha"
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||||
|
result="hardAlpha"
|
||||||
|
/>
|
||||||
|
<feOffset dy="4" />
|
||||||
|
<feGaussianBlur stdDeviation="2" />
|
||||||
|
<feComposite in2="hardAlpha" operator="out" />
|
||||||
|
<feColorMatrix
|
||||||
|
type="matrix"
|
||||||
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.44 0"
|
||||||
|
/>
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in2="BackgroundImageFix"
|
||||||
|
result="effect1_dropShadow_1180_138"
|
||||||
|
/>
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in="SourceGraphic"
|
||||||
|
in2="effect1_dropShadow_1180_138"
|
||||||
|
result="shape"
|
||||||
|
/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1180_138"
|
||||||
|
x1="201.5"
|
||||||
|
y1="0"
|
||||||
|
x2="201.5"
|
||||||
|
y2="102.822"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint1_linear_1180_138"
|
||||||
|
x1="201.5"
|
||||||
|
y1="-1.34109e-07"
|
||||||
|
x2="201.527"
|
||||||
|
y2="153.598"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="paint2_linear_1180_138"
|
||||||
|
x1="294"
|
||||||
|
y1="70"
|
||||||
|
x2="294"
|
||||||
|
y2="114"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="white" />
|
||||||
|
<stop offset="1" stop-color="white" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
<clipPath id="clip0_1180_138">
|
||||||
|
<rect width="403" height="226" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
@ -12,7 +12,7 @@ main:
|
|||||||
---
|
---
|
||||||
cta:
|
cta:
|
||||||
- Get started
|
- Get started
|
||||||
- /getting-started/setup
|
- /docs/getting-started/setup
|
||||||
secondary:
|
secondary:
|
||||||
- Open on GitHub →
|
- Open on GitHub →
|
||||||
- https://github.com/arashsheyda/nuxt-mongoose
|
- https://github.com/arashsheyda/nuxt-mongoose
|
||||||
@ -25,12 +25,54 @@ Nuxt [Mongoose]{style="color: var(--color-primary-500)"}
|
|||||||
A Nuxt module for simplifying the use of [Mongoose](https://mongoosejs.com/) in your project.
|
A Nuxt module for simplifying the use of [Mongoose](https://mongoosejs.com/) in your project.
|
||||||
|
|
||||||
#support
|
#support
|
||||||
::terminal
|
:illustration
|
||||||
---
|
|
||||||
content:
|
|
||||||
- npm i nuxt-mongoose -D
|
|
||||||
---
|
|
||||||
::
|
|
||||||
::
|
::
|
||||||
|
|
||||||
<!-- TODO: features -->
|
::card-grid
|
||||||
|
#title
|
||||||
|
<h1 class="center">Elevate Your <br/> <span class=highlight> Developer Experience</span></h1>
|
||||||
|
|
||||||
|
#root
|
||||||
|
:ellipsis{left=15rem width=40rem top=10rem blur=140px}
|
||||||
|
|
||||||
|
#default
|
||||||
|
::card{icon=tabler:code}
|
||||||
|
#title
|
||||||
|
Nuxt DevTools Support
|
||||||
|
#description
|
||||||
|
Dive into your database with confidence. Benefit from Nuxt Devtools support, allowing you to inspect and debug your DB operations seamlessly.
|
||||||
|
::
|
||||||
|
|
||||||
|
::card{icon=tabler:cloud-bolt}
|
||||||
|
#title
|
||||||
|
Auto Connection
|
||||||
|
#description
|
||||||
|
Forget about manual connection hassles. This module seamlessly handles the connection to your Mongoose database, making setup a breeze.
|
||||||
|
::
|
||||||
|
|
||||||
|
::card{icon=tabler:plug}
|
||||||
|
#title
|
||||||
|
Extendable & hackable
|
||||||
|
#description
|
||||||
|
This module grants you access to the full spectrum of Mongoose functionalities. Leverage the power of Mongoose in your project.
|
||||||
|
::
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.highlight {
|
||||||
|
color: var(--color-primary-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.card svg {
|
||||||
|
height: 3rem!important;
|
||||||
|
width: 3rem!important;
|
||||||
|
}
|
||||||
|
.support {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export default defineNuxtConfig({
|
|||||||
## Options
|
## Options
|
||||||
|
|
||||||
You can configure the module by adding a `mongoose` section to your `nuxt.config` file.
|
You can configure the module by adding a `mongoose` section to your `nuxt.config` file.
|
||||||
read more about [Mongoose options](/getting-started/configuration).
|
read more about [Mongoose options](/docs/getting-started/configuration).
|
||||||
|
|
||||||
```ts [nuxt.config]
|
```ts [nuxt.config]
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
@ -54,7 +54,12 @@ This function creates a new Mongoose model with schema. Example usage:
|
|||||||
| `hooks` | [`(schema: Schema<T>) => void`](https://mongoosejs.com/docs/middleware.html) | false | Schema Hooks Function to customize Model |
|
| `hooks` | [`(schema: Schema<T>) => void`](https://mongoosejs.com/docs/middleware.html) | false | Schema Hooks Function to customize Model |
|
||||||
|
|
||||||
|
|
||||||
|
::alert
|
||||||
|
you can access the default connection with importing it from mongoose:
|
||||||
|
::
|
||||||
|
```
|
||||||
|
import { connection } from 'mongoose'
|
||||||
|
```
|
||||||
|
|
||||||
## `defineMongooseConnection`
|
## `defineMongooseConnection`
|
||||||
This function creates a new Mongoose connection.
|
This function creates a new Mongoose connection.
|
||||||
5
docs/content/1.docs/2.api/3.examples.md
Normal file
5
docs/content/1.docs/2.api/3.examples.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
Here are a few examples:
|
||||||
|
|
||||||
|
- Nuxt Mongoose Minimal: [:icon{name="tabler:brand-github"} Github](https://github.com/arashsheyda/nuxt-mongoose-minimal) [:icon{name="tabler:world"} Website](https://nuxt-mongoose-minimal.vercel.app/)
|
||||||
1
docs/content/1.docs/_dir.yml
Normal file
1
docs/content/1.docs/_dir.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
icon: tabler:file-description
|
||||||
5
docs/content/2.playground/1.index.md
Normal file
5
docs/content/2.playground/1.index.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Playground
|
||||||
|
|
||||||
|
:ellipsis{right=0px width=75% blur=150px}
|
||||||
|
|
||||||
|
:sandbox{src="https://stackblitz.com/github/arashsheyda/nuxt-mongoose-minimal"}
|
||||||
1
docs/content/2.playground/_dir.yml
Normal file
1
docs/content/2.playground/_dir.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
icon: tabler:play-volleyball
|
||||||
3
docs/content/3.releases/_dir.yml
Normal file
3
docs/content/3.releases/_dir.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
title: Releases
|
||||||
|
icon: tabler:clipboard-text
|
||||||
|
navigation.redirect: https://github.com/arashsheyda/nuxt-mongoose/releases
|
||||||
0
docs/content/3.releases/index.md
Normal file
0
docs/content/3.releases/index.md
Normal file
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "docus-starter",
|
"name": "nuxt-mongoose-docs",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -10,8 +10,8 @@
|
|||||||
"lint": "eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt-themes/docus": "^1.12.0",
|
"@nuxt-themes/docus": "^1.15.0",
|
||||||
"@nuxthq/studio": "^0.13.2",
|
"@nuxthq/studio": "^1.0.8",
|
||||||
"nuxt": "^3.5.0"
|
"nuxt": "^3.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6606
docs/pnpm-lock.yaml
generated
6606
docs/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 237 KiB |
15
module.cjs
15
module.cjs
@ -1,15 +0,0 @@
|
|||||||
/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
module.exports = function (...args) {
|
|
||||||
const nuxt = this.nuxt || args[1]
|
|
||||||
let _a
|
|
||||||
let version = (nuxt == null ? void 0 : nuxt._version) || (nuxt == null ? void 0 : nuxt.version) || ((_a = nuxt == null ? void 0 : nuxt.constructor) == null ? void 0 : _a.version) || ''
|
|
||||||
version = version.replace(/^v/g, '')
|
|
||||||
// Nuxt DevTools is not compatible with Nuxt 2, disabled
|
|
||||||
if (version.startsWith('2.')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return import('./dist/module.mjs').then(m => m.default.call(this, ...args))
|
|
||||||
}
|
|
||||||
const _meta = module.exports.meta = require('./dist/module.json')
|
|
||||||
module.exports.getMeta = () => Promise.resolve(_meta)
|
|
||||||
89
package.json
89
package.json
@ -1,69 +1,74 @@
|
|||||||
{
|
{
|
||||||
"name": "nuxt-mongoose",
|
"name": "nuxt-mongoose",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.9",
|
"version": "1.0.5",
|
||||||
|
"private": false,
|
||||||
|
"packageManager": "pnpm@8.7.4",
|
||||||
"description": "Nuxt 3 module for MongoDB with Mongoose",
|
"description": "Nuxt 3 module for MongoDB with Mongoose",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"funding": "https://github.com/sponsors/arashsheyda",
|
||||||
|
"homepage": "https://nuxt-mongoose.nuxt.space",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/arashsheyda/nuxt-mongoose"
|
"url": "git+https://github.com/arashsheyda/nuxt-mongoose"
|
||||||
},
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/arashsheyda/nuxt-mongoose/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"nuxt",
|
||||||
|
"mongoose",
|
||||||
|
"mongodb",
|
||||||
|
"devtools"
|
||||||
|
],
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/types.d.ts",
|
"types": "./dist/types.d.ts",
|
||||||
"require": "./module.cjs",
|
"import": "./dist/module.mjs",
|
||||||
"import": "./dist/module.mjs"
|
"require": "./dist/module.cjs"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"./*": "./*"
|
"build": {
|
||||||
|
"externals": [
|
||||||
|
"ofetch"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"main": "./module.cjs",
|
"main": "./dist/module.cjs",
|
||||||
"types": "./dist/types.d.ts",
|
"types": "./dist/types.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm dev:prepare && pnpm build:module && pnpm build:client",
|
"build": "nuxt-module-build && npm run build:client",
|
||||||
"build:client": "nuxi generate client",
|
"build:client": "nuxi generate client",
|
||||||
"build:module": "nuxt-build-module",
|
|
||||||
"dev": "nuxi dev playground",
|
"dev": "nuxi dev playground",
|
||||||
"dev:prepare": "nuxi prepare client",
|
"dev:prepare": "nuxt-module-build && nuxi prepare client",
|
||||||
"dev:prod": "npm run build && pnpm dev",
|
"dev:client": "nuxi dev client --port 3300",
|
||||||
"release": "npm run lint && npm run build && changelogen --release && npm publish && git push --follow-tags",
|
"dev:prod": "npm run build && npm run dev",
|
||||||
"lint": "eslint .",
|
"release": "npm run lint && npm run build && changelogen --release && npm publish",
|
||||||
"test": "vitest run",
|
"lint": "eslint . --fix"
|
||||||
"test:watch": "vitest watch"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/devtools-kit": "^0.6.7",
|
"@nuxt/devtools-kit": "1.0.8",
|
||||||
"@nuxt/kit": "^3.6.2",
|
"@nuxt/devtools-ui-kit": "1.0.8",
|
||||||
"@types/fs-extra": "^11.0.1",
|
"@nuxt/kit": "^3.9.3",
|
||||||
"birpc": "^0.2.12",
|
"@vueuse/core": "^10.7.2",
|
||||||
"defu": "^6.1.2",
|
"defu": "^6.1.4",
|
||||||
"flatted": "^3.2.7",
|
"fs-extra": "^11.2.0",
|
||||||
"fs-extra": "^11.1.1",
|
"mongoose": "^8.1.0",
|
||||||
"mongoose": "^7.3.2",
|
"ofetch": "^1.3.3",
|
||||||
"ofetch": "^1.1.1",
|
"pathe": "^1.1.2",
|
||||||
"pathe": "^1.1.1",
|
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"sirv": "^2.0.3",
|
"sirv": "^2.0.4"
|
||||||
"vite-hot-client": "^0.2.1",
|
|
||||||
"ws": "^8.13.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.39.7",
|
"@antfu/eslint-config": "2.6.3",
|
||||||
"@nuxt/devtools": "^0.6.7",
|
"@nuxt/module-builder": "^0.5.5",
|
||||||
"@nuxt/devtools-ui-kit": "^0.6.7",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@nuxt/module-builder": "^0.4.0",
|
"@types/pluralize": "^0.0.33",
|
||||||
"@nuxt/schema": "^3.6.2",
|
"changelogen": "^0.5.5",
|
||||||
"@nuxt/test-utils": "^3.6.2",
|
"eslint": "8.56.0",
|
||||||
"@types/pluralize": "^0.0.30",
|
"nuxt": "^3.9.3",
|
||||||
"@types/ws": "^8.5.5",
|
"sass": "^1.69.7"
|
||||||
"changelogen": "^0.5.4",
|
|
||||||
"eslint": "^8.44.0",
|
|
||||||
"nuxt": "^3.6.2",
|
|
||||||
"sass": "^1.63.6",
|
|
||||||
"sass-loader": "^13.3.2",
|
|
||||||
"splitpanes": "^3.1.5",
|
|
||||||
"vitest": "^0.33.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1 +1 @@
|
|||||||
MONGODB_URI="mongodb://127.0.0.1:27017/nuxt-mongoose"
|
MONGODB_URI="mongodb://localhost:27017/nuxt-mongoose"
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
<script setup>
|
<script setup lang="ts">
|
||||||
|
const { data } = await useFetch('/api/users')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
Nuxt module playground!
|
Nuxt module playground!
|
||||||
|
<br>
|
||||||
|
<pre
|
||||||
|
v-html="JSON.stringify(data, null, 2)"
|
||||||
|
style="background: #131313;color: #4EA65A;padding: 20px;border-radius: 7px;text-shadow: 0px 0px 10px #00ff22;"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,6 +1,33 @@
|
|||||||
|
import { resolve } from 'node:path'
|
||||||
|
import { defineNuxtConfig } from 'nuxt/config'
|
||||||
|
import { defineNuxtModule } from '@nuxt/kit'
|
||||||
|
import { startSubprocess } from '@nuxt/devtools-kit'
|
||||||
|
import { CLIENT_PORT } from '../src/constants'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
devtools: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
|
||||||
modules: [
|
modules: [
|
||||||
'@nuxt/devtools',
|
|
||||||
'../src/module',
|
'../src/module',
|
||||||
|
defineNuxtModule({
|
||||||
|
setup(_, nuxt) {
|
||||||
|
if (!nuxt.options.dev)
|
||||||
|
return
|
||||||
|
|
||||||
|
startSubprocess(
|
||||||
|
{
|
||||||
|
command: 'npx',
|
||||||
|
args: ['nuxi', 'dev', '--port', CLIENT_PORT.toString()],
|
||||||
|
cwd: resolve(__dirname, '../client'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nuxt-mongoose:client',
|
||||||
|
name: 'Nuxt Mongoose Client Dev',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
5
playground/server/api/hi.get.ts
Normal file
5
playground/server/api/hi.get.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default defineEventHandler(async () => {
|
||||||
|
return {
|
||||||
|
message: 'hi',
|
||||||
|
}
|
||||||
|
})
|
||||||
3
playground/server/tsconfig.json
Normal file
3
playground/server/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "../.nuxt/tsconfig.server.json"
|
||||||
|
}
|
||||||
10249
pnpm-lock.yaml
generated
10249
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@
|
|||||||
export const PATH = '/__nuxt_mongoose__'
|
export const CLIENT_PATH = '/__nuxt-mongoose'
|
||||||
export const PATH_CLIENT = `${PATH}/client`
|
export const CLIENT_PORT = 3300
|
||||||
export const WS_EVENT_NAME = 'nuxt:devtools:mongoose:rpc'
|
export const RPC_NAMESPACE = 'nuxt-mongoose-rpc'
|
||||||
|
|||||||
56
src/devtools.ts
Normal file
56
src/devtools.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { existsSync } from 'node:fs'
|
||||||
|
import type { Nuxt } from 'nuxt/schema'
|
||||||
|
import type { Resolver } from '@nuxt/kit'
|
||||||
|
import { extendServerRpc, onDevToolsInitialized } from '@nuxt/devtools-kit'
|
||||||
|
import type { ClientFunctions, ServerFunctions } from './types'
|
||||||
|
import type { ModuleOptions } from './module'
|
||||||
|
import { useViteWebSocket } from './utils'
|
||||||
|
|
||||||
|
import { setupRPC } from './rpc'
|
||||||
|
import { CLIENT_PATH, CLIENT_PORT, RPC_NAMESPACE } from './constants'
|
||||||
|
|
||||||
|
export function setupDevToolsUI(options: ModuleOptions, resolve: Resolver['resolve'], nuxt: Nuxt) {
|
||||||
|
const clientPath = resolve('./client')
|
||||||
|
const isProductionBuild = existsSync(clientPath)
|
||||||
|
|
||||||
|
if (isProductionBuild) {
|
||||||
|
nuxt.hook('vite:serverCreated', async (server) => {
|
||||||
|
const sirv = await import('sirv').then(r => r.default || r)
|
||||||
|
server.middlewares.use(
|
||||||
|
CLIENT_PATH,
|
||||||
|
sirv(clientPath, { dev: true, single: true }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nuxt.hook('vite:extendConfig', (config) => {
|
||||||
|
config.server = config.server || {}
|
||||||
|
config.server.proxy = config.server.proxy || {}
|
||||||
|
config.server.proxy[CLIENT_PATH] = {
|
||||||
|
target: `http://localhost:${CLIENT_PORT}${CLIENT_PATH}`,
|
||||||
|
changeOrigin: true,
|
||||||
|
followRedirects: true,
|
||||||
|
rewrite: path => path.replace(CLIENT_PATH, ''),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxt.hook('devtools:customTabs', (tabs) => {
|
||||||
|
tabs.push({
|
||||||
|
name: 'nuxt-mongoose',
|
||||||
|
title: 'Mongoose',
|
||||||
|
icon: 'skill-icons:mongodb',
|
||||||
|
view: {
|
||||||
|
type: 'iframe',
|
||||||
|
src: CLIENT_PATH,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const wsServer = useViteWebSocket(nuxt)
|
||||||
|
onDevToolsInitialized(async () => {
|
||||||
|
const rpcFunctions = setupRPC({ options, wsServer, nuxt })
|
||||||
|
|
||||||
|
extendServerRpc<ClientFunctions, ServerFunctions>(RPC_NAMESPACE, rpcFunctions)
|
||||||
|
})
|
||||||
|
}
|
||||||
124
src/module.ts
124
src/module.ts
@ -1,25 +1,47 @@
|
|||||||
import {
|
import {
|
||||||
addImportsDir,
|
|
||||||
addServerPlugin,
|
addServerPlugin,
|
||||||
addTemplate,
|
addTemplate,
|
||||||
addVitePlugin,
|
|
||||||
createResolver,
|
createResolver,
|
||||||
defineNuxtModule,
|
defineNuxtModule,
|
||||||
logger,
|
logger,
|
||||||
} from '@nuxt/kit'
|
} from '@nuxt/kit'
|
||||||
import { pathExists } from 'fs-extra'
|
import type { ConnectOptions } from 'mongoose'
|
||||||
|
import defu from 'defu'
|
||||||
import { join } from 'pathe'
|
import { join } from 'pathe'
|
||||||
import { defu } from 'defu'
|
import mongoose from 'mongoose'
|
||||||
import sirv from 'sirv'
|
|
||||||
import { $fetch } from 'ofetch'
|
import { $fetch } from 'ofetch'
|
||||||
import { version } from '../package.json'
|
import { version } from '../package.json'
|
||||||
|
import { setupDevToolsUI } from './devtools'
|
||||||
|
|
||||||
import { PATH_CLIENT } from './constants'
|
export interface ModuleOptions {
|
||||||
import type { ModuleOptions } from './types'
|
/**
|
||||||
|
* The MongoDB URI connection
|
||||||
import { setupRPC } from './server-rpc'
|
*
|
||||||
|
* @default process.env.MONGODB_URI
|
||||||
export type { ModuleOptions }
|
*
|
||||||
|
*/
|
||||||
|
uri: string | undefined
|
||||||
|
/**
|
||||||
|
* Nuxt DevTools
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
devtools: boolean
|
||||||
|
/**
|
||||||
|
* Mongoose Connections
|
||||||
|
*
|
||||||
|
* @default {}
|
||||||
|
*/
|
||||||
|
options?: ConnectOptions
|
||||||
|
/**
|
||||||
|
* Models Directory for auto-import
|
||||||
|
*
|
||||||
|
* @default 'models'
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
modelsDir?: string
|
||||||
|
}
|
||||||
|
|
||||||
export default defineNuxtModule<ModuleOptions>({
|
export default defineNuxtModule<ModuleOptions>({
|
||||||
meta: {
|
meta: {
|
||||||
@ -27,15 +49,18 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
configKey: 'mongoose',
|
configKey: 'mongoose',
|
||||||
},
|
},
|
||||||
defaults: {
|
defaults: {
|
||||||
|
// eslint-disable-next-line n/prefer-global/process
|
||||||
uri: process.env.MONGODB_URI as string,
|
uri: process.env.MONGODB_URI as string,
|
||||||
devtools: true,
|
devtools: true,
|
||||||
options: {},
|
options: {},
|
||||||
modelsDir: 'models',
|
modelsDir: 'models',
|
||||||
},
|
},
|
||||||
setup(options, nuxt) {
|
hooks: {
|
||||||
const { resolve } = createResolver(import.meta.url)
|
close: () => {
|
||||||
const runtimeConfig = nuxt.options.runtimeConfig as any
|
mongoose.disconnect()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async setup(options, nuxt) {
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
$fetch('https://registry.npmjs.org/nuxt-mongoose/latest').then((release) => {
|
$fetch('https://registry.npmjs.org/nuxt-mongoose/latest').then((release) => {
|
||||||
if (release.version > version)
|
if (release.version > version)
|
||||||
@ -43,62 +68,35 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
addImportsDir(resolve('./runtime/composables'))
|
|
||||||
|
|
||||||
if (!options.uri) {
|
if (!options.uri) {
|
||||||
logger.warn('Missing `MONGODB_URI` in `.env`')
|
logger.warn('Missing MongoDB URI. You can set it in your `nuxt.config` or in your `.env` as `MONGODB_URI`')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runtime Config
|
const { resolve } = createResolver(import.meta.url)
|
||||||
runtimeConfig.mongoose = defu(runtimeConfig.mongoose || {}, {
|
const config = nuxt.options.runtimeConfig as any
|
||||||
|
|
||||||
|
config.mongoose = defu(config.mongoose || {}, {
|
||||||
uri: options.uri,
|
uri: options.uri,
|
||||||
options: options.options,
|
options: options.options,
|
||||||
devtools: options.devtools,
|
devtools: options.devtools,
|
||||||
modelsDir: options.modelsDir,
|
modelsDir: join(nuxt.options.serverDir, options.modelsDir!),
|
||||||
})
|
|
||||||
|
|
||||||
// Setup devtools UI
|
|
||||||
const distResolve = (p: string) => {
|
|
||||||
const cwd = resolve('.')
|
|
||||||
if (cwd.endsWith('/dist'))
|
|
||||||
return resolve(p)
|
|
||||||
return resolve(`../dist/${p}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientPath = distResolve('./client')
|
|
||||||
const { vitePlugin } = setupRPC(nuxt, options)
|
|
||||||
|
|
||||||
addVitePlugin(vitePlugin)
|
|
||||||
|
|
||||||
nuxt.hook('vite:serverCreated', async (server) => {
|
|
||||||
if (await pathExists(clientPath))
|
|
||||||
server.middlewares.use(PATH_CLIENT, sirv(clientPath, { dev: true, single: true }))
|
|
||||||
})
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
||||||
// @ts-ignore runtime type
|
|
||||||
nuxt.hook('devtools:customTabs', (iframeTabs) => {
|
|
||||||
iframeTabs.push({
|
|
||||||
name: 'mongoose',
|
|
||||||
title: 'Mongoose',
|
|
||||||
icon: 'skill-icons:mongodb',
|
|
||||||
view: {
|
|
||||||
type: 'iframe',
|
|
||||||
src: PATH_CLIENT,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// virtual imports
|
// virtual imports
|
||||||
nuxt.hook('nitro:config', (nitroConfig) => {
|
nuxt.hook('nitro:config', (_config) => {
|
||||||
nitroConfig.alias = nitroConfig.alias || {}
|
_config.alias = _config.alias || {}
|
||||||
|
|
||||||
// Inline module runtime in Nitro bundle
|
// Inline module runtime in Nitro bundle
|
||||||
nitroConfig.externals = defu(typeof nitroConfig.externals === 'object' ? nitroConfig.externals : {}, {
|
_config.externals = defu(typeof _config.externals === 'object' ? _config.externals : {}, {
|
||||||
inline: [resolve('./runtime')],
|
inline: [resolve('./runtime')],
|
||||||
})
|
})
|
||||||
nitroConfig.alias['#nuxt/mongoose'] = resolve('./runtime/server/services')
|
_config.alias['#nuxt/mongoose'] = resolve('./runtime/server/services')
|
||||||
|
|
||||||
|
if (_config.imports) {
|
||||||
|
_config.imports.dirs = _config.imports.dirs || []
|
||||||
|
_config.imports.dirs?.push(config.mongoose.modelsDir)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
addTemplate({
|
addTemplate({
|
||||||
@ -115,15 +113,9 @@ export default defineNuxtModule<ModuleOptions>({
|
|||||||
options.references.push({ path: resolve(nuxt.options.buildDir, 'types/nuxt-mongoose.d.ts') })
|
options.references.push({ path: resolve(nuxt.options.buildDir, 'types/nuxt-mongoose.d.ts') })
|
||||||
})
|
})
|
||||||
|
|
||||||
// Nitro auto imports
|
const isDevToolsEnabled = typeof nuxt.options.devtools === 'boolean' ? nuxt.options.devtools : nuxt.options.devtools.enabled
|
||||||
nuxt.hook('nitro:config', (_nitroConfig) => {
|
if (nuxt.options.dev && isDevToolsEnabled)
|
||||||
if (_nitroConfig.imports) {
|
setupDevToolsUI(options, resolve, nuxt)
|
||||||
_nitroConfig.imports.dirs = _nitroConfig.imports.dirs || []
|
|
||||||
_nitroConfig.imports.dirs?.push(
|
|
||||||
join(nuxt.options.serverDir, runtimeConfig.mongoose.modelsDir),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add server-plugin for database connection
|
// Add server-plugin for database connection
|
||||||
addServerPlugin(resolve('./runtime/server/plugins/mongoose.db'))
|
addServerPlugin(resolve('./runtime/server/plugins/mongoose.db'))
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import type { NuxtDevtoolsServerContext, ServerFunctions } from '../types'
|
import type { DevtoolsServerContext, ServerFunctions } from '../types'
|
||||||
|
|
||||||
export function setupDatabaseRPC({ options }: NuxtDevtoolsServerContext): any {
|
|
||||||
mongoose.connect(options.uri, options.options)
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-empty-pattern
|
||||||
|
export function setupDatabaseRPC({}: DevtoolsServerContext) {
|
||||||
return {
|
return {
|
||||||
async readyState() {
|
async readyState() {
|
||||||
return mongoose.connection.readyState
|
return mongoose.connection.readyState
|
||||||
@ -62,8 +61,8 @@ export function setupDatabaseRPC({ options }: NuxtDevtoolsServerContext): any {
|
|||||||
const skip = (options.page - 1) * options.limit
|
const skip = (options.page - 1) * options.limit
|
||||||
const cursor = mongoose.connection.db.collection(collection).find().skip(skip)
|
const cursor = mongoose.connection.db.collection(collection).find().skip(skip)
|
||||||
if (options.limit !== 0)
|
if (options.limit !== 0)
|
||||||
cursor.limit(options.limit)
|
cursor?.limit(options.limit)
|
||||||
return await cursor.toArray()
|
return await cursor?.toArray()
|
||||||
},
|
},
|
||||||
async getDocument(collection: string, document: any) {
|
async getDocument(collection: string, document: any) {
|
||||||
try {
|
try {
|
||||||
23
src/rpc/index.ts
Normal file
23
src/rpc/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
import type { DevtoolsServerContext, ServerFunctions } from '../types'
|
||||||
|
|
||||||
|
import { setupDatabaseRPC } from './database'
|
||||||
|
import { setupResourceRPC } from './resource'
|
||||||
|
|
||||||
|
export function setupRPC(ctx: DevtoolsServerContext): ServerFunctions {
|
||||||
|
mongoose.connect(ctx.options.uri, ctx.options.options)
|
||||||
|
|
||||||
|
return {
|
||||||
|
getOptions() {
|
||||||
|
return ctx.options
|
||||||
|
},
|
||||||
|
|
||||||
|
...setupDatabaseRPC(ctx),
|
||||||
|
...setupResourceRPC(ctx),
|
||||||
|
|
||||||
|
async reset() {
|
||||||
|
const ws = await ctx.wsServer
|
||||||
|
ws.send('nuxt-mongoose:reset')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,23 +1,22 @@
|
|||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { resolve } from 'pathe'
|
import { join } from 'pathe'
|
||||||
import type { Collection, NuxtDevtoolsServerContext, Resource, ServerFunctions } from '../types'
|
import mongoose from 'mongoose'
|
||||||
import { generateApiRoute, generateSchemaFile } from '../utils/schematics'
|
import type { Collection, DevtoolsServerContext, Resource, ServerFunctions } from '../types'
|
||||||
import { capitalize, pluralize, singularize } from '../utils/formatting'
|
import { capitalize, generateApiRoute, generateSchemaFile, pluralize, singularize } from '../utils'
|
||||||
|
|
||||||
export function setupResourceRPC({ nuxt, rpc }: NuxtDevtoolsServerContext): any {
|
export function setupResourceRPC({ nuxt }: DevtoolsServerContext): any {
|
||||||
const runtimeConfig = nuxt.options.runtimeConfig as any
|
const config = nuxt.options.runtimeConfig.mongoose
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// TODO: maybe separate functions
|
|
||||||
async generateResource(collection: Collection, resources: Resource[]) {
|
async generateResource(collection: Collection, resources: Resource[]) {
|
||||||
const singular = singularize(collection.name).toLowerCase()
|
const singular = singularize(collection.name).toLowerCase()
|
||||||
const plural = pluralize(collection.name).toLowerCase()
|
const plural = pluralize(collection.name).toLowerCase()
|
||||||
const dbName = capitalize(singular)
|
const dbName = capitalize(singular)
|
||||||
|
|
||||||
if (collection.fields) {
|
if (collection.fields) {
|
||||||
const schemaPath = resolve(nuxt.options.serverDir, runtimeConfig.mongoose.modelsDir, `${singular}.schema.ts`)
|
const schemaPath = join(config.modelsDir, `${singular}.schema.ts`)
|
||||||
if (!fs.existsSync(schemaPath)) {
|
if (!fs.existsSync(schemaPath)) {
|
||||||
fs.ensureDirSync(resolve(nuxt.options.serverDir, runtimeConfig.mongoose.modelsDir))
|
fs.ensureDirSync(config.modelsDir)
|
||||||
fs.writeFileSync(schemaPath, generateSchemaFile(dbName, collection.fields))
|
fs.writeFileSync(schemaPath, generateSchemaFile(dbName, collection.fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +35,9 @@ export function setupResourceRPC({ nuxt, rpc }: NuxtDevtoolsServerContext): any
|
|||||||
? (routeTypes[route.type] as any)(route.by)
|
? (routeTypes[route.type] as any)(route.by)
|
||||||
: routeTypes[route.type]
|
: routeTypes[route.type]
|
||||||
|
|
||||||
const filePath = resolve(nuxt.options.serverDir, 'api', plural, fileName)
|
const filePath = join(nuxt.options.serverDir, 'api', plural, fileName)
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
fs.ensureDirSync(resolve(nuxt.options.serverDir, `api/${plural}`))
|
fs.ensureDirSync(join(nuxt.options.serverDir, `api/${plural}`))
|
||||||
const content = generateApiRoute(route.type, { model, by: route.by })
|
const content = generateApiRoute(route.type, { model, by: route.by })
|
||||||
fs.writeFileSync(filePath, content)
|
fs.writeFileSync(filePath, content)
|
||||||
}
|
}
|
||||||
@ -46,14 +45,13 @@ export function setupResourceRPC({ nuxt, rpc }: NuxtDevtoolsServerContext): any
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create collection if not exists
|
// create collection if not exists
|
||||||
const collections = await rpc.functions.listCollections()
|
const collections = await mongoose.connection.db.listCollections().toArray()
|
||||||
if (!collections.find((c: any) => c.name === plural))
|
if (!collections.find((c: any) => c.name === plural))
|
||||||
await rpc.functions.createCollection(plural)
|
return await mongoose.connection.db.createCollection(plural)
|
||||||
},
|
},
|
||||||
async resourceSchema(collection: string) {
|
async resourceSchema(collection: string) {
|
||||||
// TODO: use magicast
|
|
||||||
const singular = singularize(collection).toLowerCase()
|
const singular = singularize(collection).toLowerCase()
|
||||||
const schemaPath = resolve(nuxt.options.serverDir, runtimeConfig.mongoose.modelsDir, `${singular}.schema.ts`)
|
const schemaPath = join(config.modelsDir, `${singular}.schema.ts`)
|
||||||
if (fs.existsSync(schemaPath)) {
|
if (fs.existsSync(schemaPath)) {
|
||||||
const content = fs.readFileSync(schemaPath, 'utf-8').match(/schema: \{(.|\n)*\}/g)
|
const content = fs.readFileSync(schemaPath, 'utf-8').match(/schema: \{(.|\n)*\}/g)
|
||||||
if (content) {
|
if (content) {
|
||||||
@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Due to an upstream bug in Nuxt 3 we need to stub the plugin here, track:https://github.com/nuxt/nuxt/issues/18556
|
* Due to an upstream bug in Nuxt 3 we need to stub the plugin here, track:https://github.com/nuxt/nuxt/issues/18556
|
||||||
* */
|
*/
|
||||||
import type { NitroApp } from 'nitropack'
|
import type { NitroApp } from 'nitropack'
|
||||||
import { defineMongooseConnection } from '../services/mongoose'
|
import { defineMongooseConnection } from '../services'
|
||||||
|
|
||||||
type NitroAppPlugin = (nitro: NitroApp) => void
|
type NitroAppPlugin = (nitro: NitroApp) => void
|
||||||
|
|
||||||
|
|||||||
21
src/runtime/server/services/connection.ts
Normal file
21
src/runtime/server/services/connection.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { ConnectOptions } from 'mongoose'
|
||||||
|
import { consola } from 'consola'
|
||||||
|
import { colors } from 'consola/utils'
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import { useRuntimeConfig } from '#imports'
|
||||||
|
|
||||||
|
export async function defineMongooseConnection({ uri, options }: { uri?: string; options?: ConnectOptions } = {}): Promise<void> {
|
||||||
|
const config = useRuntimeConfig().mongoose
|
||||||
|
const mongooseUri = uri || config.uri
|
||||||
|
const mongooseOptions = options || config.options
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mongoose.connect(mongooseUri, { ...mongooseOptions })
|
||||||
|
consola.success('Connected to MongoDB')
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
consola.error(colors.red(`Error connecting to MongoDB: ${err}`))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
export { defineMongooseConnection, defineMongooseModel } from './mongoose'
|
export { defineMongooseConnection } from './connection'
|
||||||
|
export { defineMongooseModel } from './model'
|
||||||
|
|||||||
32
src/runtime/server/services/model.ts
Normal file
32
src/runtime/server/services/model.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import type { Model, SchemaDefinition, SchemaOptions } from 'mongoose'
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
export function defineMongooseModel<T>(
|
||||||
|
nameOrOptions: string | {
|
||||||
|
name: string
|
||||||
|
schema: SchemaDefinition<T>
|
||||||
|
options?: SchemaOptions
|
||||||
|
hooks?: (schema: mongoose.Schema<T>) => void
|
||||||
|
},
|
||||||
|
schema?: SchemaDefinition<T>,
|
||||||
|
options?: SchemaOptions,
|
||||||
|
hooks?: (schema: mongoose.Schema<T>) => void,
|
||||||
|
): Model<T> {
|
||||||
|
let name: string
|
||||||
|
if (typeof nameOrOptions === 'string') {
|
||||||
|
name = nameOrOptions
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
name = nameOrOptions.name
|
||||||
|
schema = nameOrOptions.schema
|
||||||
|
options = nameOrOptions.options
|
||||||
|
hooks = nameOrOptions.hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSchema = new mongoose.Schema<T>(schema, options as any)
|
||||||
|
|
||||||
|
if (hooks)
|
||||||
|
hooks(newSchema)
|
||||||
|
|
||||||
|
return mongoose.model<T>(name, newSchema)
|
||||||
|
}
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import type { ConnectOptions, Model, SchemaDefinition, SchemaOptions } from 'mongoose'
|
|
||||||
import mongoose from 'mongoose'
|
|
||||||
import { logger } from '@nuxt/kit'
|
|
||||||
|
|
||||||
import { useRuntimeConfig } from '#imports'
|
|
||||||
|
|
||||||
export async function defineMongooseConnection({ uri, options }: { uri?: string; options?: ConnectOptions } = {}): Promise<void> {
|
|
||||||
const config = useRuntimeConfig().mongoose
|
|
||||||
const mongooseUri = uri || config.uri
|
|
||||||
const mongooseOptions = options || config.options
|
|
||||||
|
|
||||||
try {
|
|
||||||
await mongoose.connect(mongooseUri, { ...mongooseOptions })
|
|
||||||
logger.success('Connected to MongoDB database')
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
logger.error('Error connecting to MongoDB database', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function defineMongooseModel<T>(
|
|
||||||
nameOrOptions: string | {
|
|
||||||
name: string
|
|
||||||
schema: SchemaDefinition
|
|
||||||
options?: SchemaOptions
|
|
||||||
hooks?: (schema: mongoose.Schema<T>) => void
|
|
||||||
},
|
|
||||||
schema?: SchemaDefinition,
|
|
||||||
options?: SchemaOptions,
|
|
||||||
hooks?: (schema: mongoose.Schema<T>) => void,
|
|
||||||
): Model<T> {
|
|
||||||
let name: string
|
|
||||||
if (typeof nameOrOptions === 'string') {
|
|
||||||
name = nameOrOptions
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
name = nameOrOptions.name
|
|
||||||
schema = nameOrOptions.schema
|
|
||||||
options = nameOrOptions.options
|
|
||||||
hooks = nameOrOptions.hooks
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSchema = new mongoose.Schema<T>(schema, options as any)
|
|
||||||
|
|
||||||
if (hooks)
|
|
||||||
hooks(newSchema)
|
|
||||||
|
|
||||||
return mongoose.model<T>(name, newSchema)
|
|
||||||
}
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
import type { WebSocket } from 'ws'
|
|
||||||
import { createBirpcGroup } from 'birpc'
|
|
||||||
import type { ChannelOptions } from 'birpc'
|
|
||||||
|
|
||||||
import { parse, stringify } from 'flatted'
|
|
||||||
import type { Plugin } from 'vite'
|
|
||||||
import type { Nuxt } from 'nuxt/schema'
|
|
||||||
import type { ClientFunctions, ModuleOptions, NuxtDevtoolsServerContext, ServerFunctions } from '../types'
|
|
||||||
import { WS_EVENT_NAME } from '../constants'
|
|
||||||
import { setupDatabaseRPC } from './database'
|
|
||||||
import { setupResourceRPC } from './resource'
|
|
||||||
|
|
||||||
export function setupRPC(nuxt: Nuxt, options: ModuleOptions): any {
|
|
||||||
const serverFunctions = {} as ServerFunctions
|
|
||||||
const extendedRpcMap = new Map<string, any>()
|
|
||||||
const rpc = createBirpcGroup<ClientFunctions, ServerFunctions>(
|
|
||||||
serverFunctions,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
resolver: (name, fn) => {
|
|
||||||
if (fn)
|
|
||||||
return fn
|
|
||||||
|
|
||||||
if (!name.includes(':'))
|
|
||||||
return
|
|
||||||
|
|
||||||
const [namespace, fnName] = name.split(':')
|
|
||||||
return extendedRpcMap.get(namespace)?.[fnName]
|
|
||||||
},
|
|
||||||
onError(error, name) {
|
|
||||||
console.error(`[nuxt-devtools] RPC error on executing "${name}":`, error)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
function refresh(event: keyof ServerFunctions) {
|
|
||||||
rpc.broadcast.refresh.asEvent(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
function extendServerRpc(namespace: string, functions: any): any {
|
|
||||||
extendedRpcMap.set(namespace, functions)
|
|
||||||
|
|
||||||
return {
|
|
||||||
broadcast: new Proxy({}, {
|
|
||||||
get: (_, key) => {
|
|
||||||
if (typeof key !== 'string')
|
|
||||||
return
|
|
||||||
return (rpc.broadcast as any)[`${namespace}:${key}`]
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ctx: NuxtDevtoolsServerContext = {
|
|
||||||
nuxt,
|
|
||||||
options,
|
|
||||||
rpc,
|
|
||||||
refresh,
|
|
||||||
extendServerRpc,
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-expect-error untyped
|
|
||||||
nuxt.devtools = ctx
|
|
||||||
|
|
||||||
Object.assign(serverFunctions, {
|
|
||||||
...setupDatabaseRPC(ctx),
|
|
||||||
...setupResourceRPC(ctx),
|
|
||||||
} satisfies Partial<ServerFunctions>)
|
|
||||||
|
|
||||||
const wsClients = new Set<WebSocket>()
|
|
||||||
|
|
||||||
const vitePlugin: Plugin = {
|
|
||||||
name: 'nuxt:devtools:rpc',
|
|
||||||
configureServer(server) {
|
|
||||||
server.ws.on('connection', (ws) => {
|
|
||||||
wsClients.add(ws)
|
|
||||||
const channel: ChannelOptions = {
|
|
||||||
post: d => ws.send(JSON.stringify({
|
|
||||||
type: 'custom',
|
|
||||||
event: WS_EVENT_NAME,
|
|
||||||
data: d,
|
|
||||||
})),
|
|
||||||
on: (fn) => {
|
|
||||||
ws.on('message', (e) => {
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(String(e)) || {}
|
|
||||||
if (data.type === 'custom' && data.event === WS_EVENT_NAME) {
|
|
||||||
// console.log(data.data)
|
|
||||||
fn(data.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
serialize: stringify,
|
|
||||||
deserialize: parse,
|
|
||||||
}
|
|
||||||
rpc.updateChannels((c) => {
|
|
||||||
c.push(channel)
|
|
||||||
})
|
|
||||||
ws.on('close', () => {
|
|
||||||
wsClients.delete(ws)
|
|
||||||
rpc.updateChannels((c) => {
|
|
||||||
const index = c.indexOf(channel)
|
|
||||||
if (index >= 0)
|
|
||||||
c.splice(index, 1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
vitePlugin,
|
|
||||||
...ctx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +1,47 @@
|
|||||||
export * from './rpc'
|
import type { Nuxt } from 'nuxt/schema'
|
||||||
export * from './server-ctx'
|
import type { WebSocketServer } from 'vite'
|
||||||
export * from './module-options'
|
import type { ModuleOptions } from '../module'
|
||||||
|
|
||||||
|
export interface ServerFunctions {
|
||||||
|
getOptions(): ModuleOptions
|
||||||
|
|
||||||
|
// Database - collections
|
||||||
|
readyState(): Promise<any>
|
||||||
|
createCollection(name: string): Promise<any>
|
||||||
|
listCollections(): Promise<any>
|
||||||
|
getCollection(name: string): Promise<any>
|
||||||
|
dropCollection(name: string): Promise<any>
|
||||||
|
|
||||||
|
// Database - documents
|
||||||
|
createDocument(collection: string, data: any): Promise<any>
|
||||||
|
countDocuments(collection: string): Promise<any>
|
||||||
|
listDocuments(collection: string, options: any): Promise<any>
|
||||||
|
getDocument(collection: string, id: string): Promise<any>
|
||||||
|
updateDocument(collection: string, data: any): Promise<any>
|
||||||
|
deleteDocument(collection: string, id: string): Promise<any>
|
||||||
|
|
||||||
|
// Resource - api-routes & models
|
||||||
|
generateResource(collection: Collection, resources: Resource[]): Promise<any>
|
||||||
|
resourceSchema(collection: string): Promise<any>
|
||||||
|
|
||||||
|
reset(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ClientFunctions {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DevtoolsServerContext {
|
||||||
|
nuxt: Nuxt
|
||||||
|
options: ModuleOptions
|
||||||
|
wsServer: Promise<WebSocketServer>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Collection {
|
||||||
|
name: string
|
||||||
|
fields?: object[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Resource {
|
||||||
|
type: 'index' | 'create' | 'show' | 'put' | 'delete'
|
||||||
|
by?: string
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import type { ConnectOptions } from 'mongoose'
|
|
||||||
|
|
||||||
export interface ModuleOptions {
|
|
||||||
uri: string
|
|
||||||
devtools: boolean
|
|
||||||
options?: ConnectOptions
|
|
||||||
modelsDir?: string
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
export interface ServerFunctions {
|
|
||||||
// Database - collections
|
|
||||||
readyState(): Promise<any>
|
|
||||||
createCollection(name: string): Promise<any>
|
|
||||||
listCollections(): Promise<any>
|
|
||||||
getCollection(name: string): Promise<any>
|
|
||||||
dropCollection(name: string): Promise<any>
|
|
||||||
|
|
||||||
// Database - documents
|
|
||||||
createDocument(collection: string, data: any): Promise<any>
|
|
||||||
countDocuments(collection: string): Promise<any>
|
|
||||||
listDocuments(collection: string, options: any): Promise<any>
|
|
||||||
getDocument(collection: string, id: string): Promise<any>
|
|
||||||
updateDocument(collection: string, data: any): Promise<any>
|
|
||||||
deleteDocument(collection: string, id: string): Promise<any>
|
|
||||||
|
|
||||||
// Resource - api-routes & models
|
|
||||||
generateResource(collection: Collection, resources: Resource[]): Promise<any>
|
|
||||||
resourceSchema(collection: string): Promise<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClientFunctions {
|
|
||||||
refresh(type: string): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Collection {
|
|
||||||
name: string
|
|
||||||
fields?: object[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Resource {
|
|
||||||
type: 'index' | 'create' | 'show' | 'put' | 'delete'
|
|
||||||
by?: string
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import type { BirpcGroup } from 'birpc'
|
|
||||||
import type { Nuxt } from 'nuxt/schema'
|
|
||||||
import type { ClientFunctions, ServerFunctions } from './rpc'
|
|
||||||
import type { ModuleOptions } from './module-options'
|
|
||||||
|
|
||||||
export interface NuxtDevtoolsServerContext {
|
|
||||||
nuxt: Nuxt
|
|
||||||
options: ModuleOptions
|
|
||||||
|
|
||||||
rpc: BirpcGroup<ClientFunctions, ServerFunctions>
|
|
||||||
|
|
||||||
refresh: (event: keyof ServerFunctions) => void
|
|
||||||
|
|
||||||
extendServerRpc: <ClientFunctions = object, ServerFunctions = object>(name: string, functions: ServerFunctions) => BirpcGroup<ClientFunctions, ServerFunctions>
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import plrz from 'pluralize'
|
|
||||||
|
|
||||||
export function normalizeToKebabOrSnakeCase(str: string) {
|
|
||||||
const STRING_DASHERIZE_REGEXP = /\s/g
|
|
||||||
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g
|
|
||||||
return str
|
|
||||||
.replace(STRING_DECAMELIZE_REGEXP, '$1-$2')
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(STRING_DASHERIZE_REGEXP, '-')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pluralize(str: string) {
|
|
||||||
return plrz.plural(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function singularize(str: string) {
|
|
||||||
return plrz.singular(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function capitalize(str: string) {
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
||||||
}
|
|
||||||
@ -1,4 +1,35 @@
|
|||||||
import { capitalize } from './formatting'
|
import type { WebSocketServer } from 'vite'
|
||||||
|
import type { Nuxt } from 'nuxt/schema'
|
||||||
|
import plrz from 'pluralize'
|
||||||
|
|
||||||
|
export function useViteWebSocket(nuxt: Nuxt) {
|
||||||
|
return new Promise<WebSocketServer>((_resolve) => {
|
||||||
|
nuxt.hooks.hook('vite:serverCreated', (viteServer) => {
|
||||||
|
_resolve(viteServer.ws)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeToKebabOrSnakeCase(str: string) {
|
||||||
|
const STRING_DASHERIZE_REGEXP = /\s/g
|
||||||
|
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g
|
||||||
|
return str
|
||||||
|
.replace(STRING_DECAMELIZE_REGEXP, '$1-$2')
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(STRING_DASHERIZE_REGEXP, '-')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pluralize(str: string) {
|
||||||
|
return plrz.plural(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function singularize(str: string) {
|
||||||
|
return plrz.singular(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalize(str: string) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
export function generateSchemaFile(name: string, fields: any) {
|
export function generateSchemaFile(name: string, fields: any) {
|
||||||
name = capitalize(name)
|
name = capitalize(name)
|
||||||
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "./client/.nuxt/tsconfig.json"
|
"extends": "./playground/.nuxt/tsconfig.json"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user