Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6a524f5f5 | |||
| e9b764b72f | |||
| 260eadd837 | |||
| ad0c5b2d40 | |||
| 3ac731c5ee | |||
| f40c48370c |
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,6 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## v0.0.3
|
||||
|
||||
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v0.0.2...v0.0.3)
|
||||
|
||||
|
||||
### 🚀 Enhancements
|
||||
|
||||
- Duplicate document ([f40c483](https://github.com/arashsheyda/nuxt-mongoose/commit/f40c483))
|
||||
- Initial pagination ([3ac731c](https://github.com/arashsheyda/nuxt-mongoose/commit/3ac731c))
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
- Initial pagination ([ad0c5b2](https://github.com/arashsheyda/nuxt-mongoose/commit/ad0c5b2))
|
||||
- Handle errors ([e9b764b](https://github.com/arashsheyda/nuxt-mongoose/commit/e9b764b))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- Move readyState ([260eadd](https://github.com/arashsheyda/nuxt-mongoose/commit/260eadd))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Arashsheyda <sheidaeearash1999@gmail.com>
|
||||
|
||||
## v0.0.2
|
||||
|
||||
[compare changes](https://github.com/arashsheyda/nuxt-mongoose/compare/v0.0.1...v0.0.2)
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import './styles/global.css'
|
||||
|
||||
const readyState = computedAsync(async () => await rpc.readyState())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Html>
|
||||
<Body h-screen>
|
||||
<NuxtLayout v-if="readyState === 1">
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
<Connection v-else :connection="readyState" />
|
||||
</Body>
|
||||
</Html>
|
||||
</template>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
<!-- eslint-disable no-console -->
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
collection: {
|
||||
@ -7,8 +6,19 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
// TODO: save in local storage
|
||||
const pagination = reactive({ limit: 20, page: 1 })
|
||||
|
||||
const countDocuments = computedAsync(async () => {
|
||||
return await rpc.countDocuments(props.collection)
|
||||
})
|
||||
|
||||
const documents = computedAsync(async () => {
|
||||
return await rpc.listDocuments(props.collection)
|
||||
return await rpc.listDocuments(props.collection, pagination)
|
||||
})
|
||||
|
||||
watch(pagination, async () => {
|
||||
documents.value = await rpc.listDocuments(props.collection, pagination)
|
||||
})
|
||||
|
||||
const schema = computedAsync<any>(async () => {
|
||||
@ -68,19 +78,24 @@ function editDocument(document: any) {
|
||||
selectedDocument.value = { ...document }
|
||||
}
|
||||
|
||||
async function saveDocument() {
|
||||
await rpc.createDocument(props.collection, selectedDocument.value)
|
||||
editing.value = false
|
||||
selectedDocument.value = undefined
|
||||
documents.value = await rpc.listDocuments(props.collection)
|
||||
}
|
||||
async function saveDocument(document: any, create = true) {
|
||||
const method = create ? rpc.createDocument : rpc.updateDocument
|
||||
const newDocument = await method(props.collection, document)
|
||||
if (newDocument?.error)
|
||||
return alert(newDocument.error.message)
|
||||
|
||||
async function updateDocument() {
|
||||
// TODO: validate & show errors
|
||||
await rpc.updateDocument(props.collection, selectedDocument.value)
|
||||
editing.value = false
|
||||
selectedDocument.value = undefined
|
||||
documents.value = await rpc.listDocuments(props.collection)
|
||||
if (create) {
|
||||
if (!documents.value.length) {
|
||||
documents.value = await rpc.listDocuments(props.collection, pagination)
|
||||
return discardEditing()
|
||||
}
|
||||
documents.value.push({ _id: newDocument.insertedId, ...document })
|
||||
}
|
||||
else {
|
||||
const index = documents.value.findIndex((doc: any) => doc._id === newDocument.value._id)
|
||||
documents.value[index] = document
|
||||
}
|
||||
discardEditing()
|
||||
}
|
||||
|
||||
function discardEditing() {
|
||||
@ -89,9 +104,14 @@ function discardEditing() {
|
||||
}
|
||||
|
||||
async function deleteDocument(document: any) {
|
||||
rpc.deleteDocument(props.collection, document._id)
|
||||
documents.value = await rpc.listDocuments(props.collection)
|
||||
const newDocument = await rpc.deleteDocument(props.collection, document._id)
|
||||
if (newDocument.deletedCount === 0)
|
||||
return alert('Failed to delete document')
|
||||
|
||||
documents.value = documents.value.filter((doc: any) => doc._id !== document._id)
|
||||
}
|
||||
|
||||
const copy = useCopy()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -102,9 +122,29 @@ async function deleteDocument(document: any) {
|
||||
Add Document
|
||||
</NButton>
|
||||
</template>
|
||||
<div op50>
|
||||
<span v-if="search">{{ filtered.length }} matched · </span>
|
||||
<span>{{ documents?.length }} documents in total</span>
|
||||
<div v-if="countDocuments" flex items-center>
|
||||
<div op50>
|
||||
<span v-if="search">{{ filtered.length }} matched · </span>
|
||||
<span>{{ documents?.length }} of {{ countDocuments }} documents in total</span>
|
||||
</div>
|
||||
<div flex-auto />
|
||||
<div flex gap-2>
|
||||
<NSelect v-if="pagination.limit !== 0" v-model="pagination.page">
|
||||
<option v-for="i in Math.ceil(countDocuments / pagination.limit)" :key="i" :value="i">
|
||||
page:
|
||||
{{ i }}
|
||||
</option>
|
||||
</NSelect>
|
||||
<NSelect v-model="pagination.limit">
|
||||
<option v-for="i in [1, 2, 3, 4, 5]" :key="i" :value="i * 10">
|
||||
show:
|
||||
{{ i * 10 }}
|
||||
</option>
|
||||
<option :value="0">
|
||||
show all
|
||||
</option>
|
||||
</NSelect>
|
||||
</div>
|
||||
</div>
|
||||
</Navbar>
|
||||
<table v-if="documents?.length || selectedDocument" w-full mb10 :class="{ 'editing-mode': editing }">
|
||||
@ -119,7 +159,6 @@ async function deleteDocument(document: any) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- hover-bg-green hover-bg-opacity-5 hover-text-green cursor-pointer -->
|
||||
<tr v-for="document in filtered" :key="document._id" :class="{ isEditing: editing && selectedDocument._id === document._id }">
|
||||
<td v-for="field of fields" :key="field" @dblclick="editDocument(document)">
|
||||
<template v-if="editing && selectedDocument._id === document._id">
|
||||
@ -130,14 +169,16 @@ async function deleteDocument(document: any) {
|
||||
</span>
|
||||
</td>
|
||||
<td class="actions">
|
||||
<div flex justify-center gap2>
|
||||
<div flex justify-center gap2 class="group">
|
||||
<template v-if="editing && selectedDocument._id === document._id">
|
||||
<NIconButton icon="carbon-save" @click="updateDocument" />
|
||||
<NIconButton icon="carbon-save" @click="saveDocument(selectedDocument, false)" />
|
||||
<NIconButton icon="carbon-close" @click="discardEditing" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<NIconButton icon="carbon-edit" @click="editDocument(document)" />
|
||||
<NIconButton icon="carbon-delete" @click="deleteDocument(document)" />
|
||||
<NIconButton icon="carbon-document-multiple-02" @click="saveDocument(document)" />
|
||||
<NIconButton absolute right-4 opacity-0 group-hover="opacity-100" transition-all icon="carbon-copy" @click="copy(JSON.stringify(document))" />
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
@ -148,7 +189,7 @@ async function deleteDocument(document: any) {
|
||||
<input v-else placeholder="ObjectId(_id)" disabled>
|
||||
</td>
|
||||
<td flex justify-center gap2 class="actions">
|
||||
<NIconButton icon="carbon-save" @click="saveDocument" />
|
||||
<NIconButton icon="carbon-save" @click="saveDocument(selectedDocument)" />
|
||||
<NIconButton icon="carbon-close" @click="discardEditing" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
10
client/composables/editor.ts
Normal file
10
client/composables/editor.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
|
||||
export function useCopy() {
|
||||
const clipboard = useClipboard()
|
||||
|
||||
return (text: string) => {
|
||||
clipboard.copy(text)
|
||||
// TODO: show toast
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
const readyState = computedAsync(async () => await rpc.readyState())
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div h-full of-auto>
|
||||
<slot />
|
||||
<slot v-if="readyState === 1" />
|
||||
<Connection v-else :connection="readyState" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "nuxt-mongoose",
|
||||
"type": "module",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"description": "Nuxt 3 module for MongoDB with Mongoose",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { logger } from '@nuxt/kit'
|
||||
import mongoose from 'mongoose'
|
||||
import type { NuxtDevtoolsServerContext, ServerFunctions } from '../types'
|
||||
|
||||
@ -10,26 +9,69 @@ export function setupDatabaseRPC({ options }: NuxtDevtoolsServerContext): any {
|
||||
return mongoose.connection.readyState
|
||||
},
|
||||
async createCollection(name: string) {
|
||||
return await mongoose.connection.db.createCollection(name)
|
||||
try {
|
||||
return await mongoose.connection.db.createCollection(name)
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async listCollections() {
|
||||
return await mongoose.connection.db.listCollections().toArray()
|
||||
try {
|
||||
return await mongoose.connection.db.listCollections().toArray()
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async getCollection(name: string) {
|
||||
return mongoose.connection.db.collection(name)
|
||||
try {
|
||||
return await mongoose.connection.db.collection(name).findOne()
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async dropCollection(name: string) {
|
||||
return await mongoose.connection.db.collection(name).drop()
|
||||
try {
|
||||
return await mongoose.connection.db.dropCollection(name)
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
|
||||
async createDocument(collection: string, data: any) {
|
||||
return await mongoose.connection.db.collection(collection).insertOne(data)
|
||||
const { _id, ...rest } = data
|
||||
try {
|
||||
return await mongoose.connection.db.collection(collection).insertOne(rest)
|
||||
}
|
||||
catch (error: any) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async listDocuments(collection: string) {
|
||||
return await mongoose.connection.db.collection(collection).find().toArray()
|
||||
async countDocuments(collection: string) {
|
||||
try {
|
||||
return await mongoose.connection.db.collection(collection).countDocuments()
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async getDocument(collection: string, document: {}) {
|
||||
return await mongoose.connection.db.collection(collection).findOne({ document })
|
||||
async listDocuments(collection: string, options: { page: number; limit: number } = { page: 1, limit: 10 }) {
|
||||
const skip = (options.page - 1) * options.limit
|
||||
const cursor = mongoose.connection.db.collection(collection).find().skip(skip)
|
||||
if (options.limit !== 0)
|
||||
cursor.limit(options.limit)
|
||||
return await cursor.toArray()
|
||||
},
|
||||
async getDocument(collection: string, document: any) {
|
||||
try {
|
||||
return await mongoose.connection.db.collection(collection).findOne({ document })
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async updateDocument(collection: string, data: any) {
|
||||
const { _id, ...rest } = data
|
||||
@ -37,12 +79,25 @@ export function setupDatabaseRPC({ options }: NuxtDevtoolsServerContext): any {
|
||||
return await mongoose.connection.db.collection(collection).findOneAndUpdate({ _id: new mongoose.Types.ObjectId(_id) }, { $set: rest })
|
||||
}
|
||||
catch (error) {
|
||||
logger.log(error)
|
||||
return error
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
async deleteDocument(collection: string, id: string) {
|
||||
return await mongoose.connection.db.collection(collection).deleteOne({ _id: new mongoose.Types.ObjectId(id) })
|
||||
try {
|
||||
return await mongoose.connection.db.collection(collection).deleteOne({ _id: new mongoose.Types.ObjectId(id) })
|
||||
}
|
||||
catch (error) {
|
||||
return ErrorIT(error)
|
||||
}
|
||||
},
|
||||
} satisfies Partial<ServerFunctions>
|
||||
}
|
||||
|
||||
function ErrorIT(error: any) {
|
||||
return {
|
||||
error: {
|
||||
message: error?.message,
|
||||
code: error?.code,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import fs from 'fs-extra'
|
||||
import { resolve } from 'pathe'
|
||||
import mongoose from 'mongoose'
|
||||
import type { Collection, NuxtDevtoolsServerContext, Resource, ServerFunctions } from '../types'
|
||||
import { generateApiRoute, generateSchemaFile } from '../utils/schematics'
|
||||
import { capitalize, pluralize, singularize } from '../utils/formatting'
|
||||
|
||||
export function setupResourceRPC({ nuxt }: NuxtDevtoolsServerContext): any {
|
||||
export function setupResourceRPC({ nuxt, rpc }: NuxtDevtoolsServerContext): any {
|
||||
return {
|
||||
// TODO: maybe separate functions
|
||||
async generateResource(collection: Collection, resources: Resource[]) {
|
||||
@ -45,8 +44,9 @@ export function setupResourceRPC({ nuxt }: NuxtDevtoolsServerContext): any {
|
||||
}
|
||||
|
||||
// create collection if not exists
|
||||
if (!mongoose.connection.modelNames().includes(dbName))
|
||||
await mongoose.connection.db.createCollection(plural)
|
||||
const collections = await rpc.functions.listCollections()
|
||||
if (!collections.find((c: any) => c.name === plural))
|
||||
await rpc.functions.createCollection(plural)
|
||||
},
|
||||
async resourceSchema(collection: string) {
|
||||
// TODO: use magicast
|
||||
|
||||
@ -8,7 +8,8 @@ export interface ServerFunctions {
|
||||
|
||||
// Database - documents
|
||||
createDocument(collection: string, data: any): Promise<any>
|
||||
listDocuments(collection: string): 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>
|
||||
|
||||
Reference in New Issue
Block a user