Skip to content

Commit

Permalink
chore: rename proxy and .hub to .data/hub
Browse files Browse the repository at this point in the history
  • Loading branch information
atinux committed Feb 26, 2024
1 parent ec7e13f commit 5d73b42
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dist
node_modules
.hub
.data
.output
.nuxt
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ dist
.build-*
.netlify

# NuxtHUb
.hub

# Env
.env

Expand Down
4 changes: 2 additions & 2 deletions docs/content/docs/1.getting-started/2.installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ bun add --dev @nuxthub/core

::

3. Add it to your `modules` section in your `nuxt.config`:
3. Add `@nuxthub/core` to your `modules` section in your `nuxt.config`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
Expand All @@ -61,7 +61,7 @@ export default defineNuxtConfig({
That's it! You can now use the NuxtHub module in your Nuxt project.

::callout
The module will create a `.hub` directory in your project root, which contains the necessary configuration files and resources for the module to work. It will also add it to the `.gitignore` file to avoid committing it to your repository.
The module will create a `.data/hub` directory in your project root, which contains the necessary configuration files and resources for the module to work. It will also add it to the `.gitignore` file to avoid committing it to your repository.
::

## Options
Expand Down
94 changes: 60 additions & 34 deletions docs/content/docs/2.storage/2.kv.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,86 @@ title: KV
description: How to use key-value data storage with NuxtHub.
---

NuxtHub KV is a layer to [Cloudflare Workers KV](https://developers.cloudflare.com/kv){target=_blank}, a global, low-latency, key-value data storage.

<!-- TODO: config, binding ? -->

Once properly configured, NuxtHub module exposes a server composable to the application.
NuxtHub KV is a layer on top of [Cloudflare Workers KV](https://developers.cloudflare.com/kv), a global, low-latency, key-value data storage.

## `hubKV()`

Server composable that returns a [Storage](https://unstorage.unjs.io/getting-started/usage#interface){target=_blank}.
Server method that returns an [unstorage instance](https://unstorage.unjs.io/guide#interface) with `keys()`, `get()`, `set()` and `del()` aliases.

### `getKeys()`
### `keys()`

Retrieves all keys from the storage.
Retrieves all keys from the KV storage (alias of `getKeys()`).

```ts[/api/kv/index.get.ts]
export default eventHandler(async () => {
return await hubKV().getKeys()
})
```ts
const keys = await hubKV().keys()
/*
[
'react',
'react:gatsby',
'react:next',
'vue',
'vue:nuxt',
'vue:quasar'
]
```
### `getItem()`
To get the keys starting with a specific prefix, you can pass the prefix as an argument.
Retrieves an item from the storage.
```ts
const vueKeys = await hubKV().keys('vue')
/*
[
'vue:nuxt',
'vue:quasar'
]
*/
```

```ts[/api/kv/[key\\].get.ts]
export default eventHandler(async () => {
const { key } = getRouterParams(event)
### `get()`

return await hubKV().getItem(key)
})
Retrieves an item from the Key-Value storage (alias of `getItem()`).

```ts
const vue = await hubKV().get('vue')
/*
{
year: 2014
}
*/
```

### `setItem()`
### `set()`

Puts an item in the storage (alias of `setItem()`)

Puts an item in the storage.
```ts
await hubKV().set('vue', { year: 2014 })
```

```ts[/api/kv/index.put.ts]
export default eventHandler(async () => {
const { key, value } = await readBody(event)
You can delimit the key with a `:` to create a namespace:

return await hubKV().setItem(key, value)
})
```ts
await hubKV().set('vue:nuxt', { year: 2016 })
```

### `deleteItem()`
### `has()`

Checks if an item exists in the storage (alias of `hasItem()`)

Deletes an item from the storage.
```ts
const hasAngular = await hubKV().has('angular')
```

```ts[/api/kv/[key\\].delete.ts]
export default eventHandler(async (event) => {
const { key } = getRouterParams(event)
### `del()`

await hubKV().removeItem(key)
Delete an item from the storage (alias of `removeItem()`)

return { key }
})
```ts
await hubKV().del('react')
```

### `...()`

::callout
You can use any other method from [unstorage](https://unstorage.unjs.io/guide#interface) as well.
::
6 changes: 3 additions & 3 deletions playground/server/api/kv/index.get.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export default eventHandler(async () => {
// List entries for the current user
const storage = hubKV()
const kv = hubKV()

const keys = await storage.getKeys()
const keys = await kv.keys()
// const items = await storage.getItems(keys)
const items = await Promise.all(keys.map(async (key) => {
const value = await storage.getItem(key)
const value = await kv.get(key)
return { key, value }
}))
return items
Expand Down
12 changes: 11 additions & 1 deletion playground/server/api/test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
export default eventHandler(async () => {
const db = hubDatabase()
const kv = hubKV()

await kv.set('vue', { year: 2014 })
await kv.set('vue:nuxt', { year: 2016 })
await kv.set('vue:quasar', { version: 2015 })
await kv.set('react', { version: 2013 })
await kv.set('react:next', { version: 2016 })
await kv.set('react:gatsby', { version: 2015 })

return kv.keys()
// const db = hubDatabase()
// return useProjectKV(projectUrl).getKeys()
// return await db.prepare('SELECT * from todos').all()
// return await db.prepare("SELECT * from todos").first()
Expand Down
12 changes: 6 additions & 6 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ export default defineNuxtModule<ModuleOptions>({
logger.info(`Remote storage available: ${Object.keys(manifest.storage).filter(k => manifest.storage[k]).map(k => `\`${k}\``).join(', ')} `)
return
} else {
log.info('Using local data from `.hub/`')
log.info('Using local data from `.data/hub`')
}

// Local development without remote connection
// Create the .hub/ directory
const hubDir = join(rootDir, './.hub')
// Create the .data/hub/ directory
const hubDir = join(rootDir, './.data/hub')
try {
await mkdir(hubDir)
await mkdir(hubDir, { recursive: true })
} catch (e: any) {
if (e.errno === -17) {
// File already exists
Expand All @@ -155,8 +155,8 @@ export default defineNuxtModule<ModuleOptions>({
// Add it to .gitignore
const gitignorePath = join(workspaceDir , '.gitignore')
const gitignore = await readFile(gitignorePath, 'utf-8').catch(() => '')
if (!gitignore.includes('.hub')) {
await writeFile(gitignorePath, `${gitignore ? gitignore + '\n' : gitignore}.hub`, 'utf-8')
if (!gitignore.includes('.data')) {
await writeFile(gitignorePath, `${gitignore ? gitignore + '\n' : gitignore}.data`, 'utf-8')
}

// Generate the wrangler.toml file
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/server/api/_hub/analytics/index.put.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { AnalyticsEngineDataPoint } from '@cloudflare/workers-types/experimental'
import { eventHandler, readValidatedBody } from 'h3'
import { z } from 'zod'
import { useAnalytics } from '../../../utils/analytics'
import { hubAnalytics } from '../../../utils/analytics'

export default eventHandler(async (event) => {
const { data } = await readValidatedBody(event, z.object({
data: z.custom<AnalyticsEngineDataPoint>()
}).parse)

await useAnalytics().put(data)
await hubAnalytics().put(data)

return true
})
6 changes: 3 additions & 3 deletions src/runtime/server/utils/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ function _useDataset() {
throw createError(`Missing Cloudflare ${name} binding (Analytics Engine)`)
}

export function useAnalytics() {
export function hubAnalytics() {
const hub = useRuntimeConfig().hub
if (import.meta.dev && hub.projectUrl) {
return useProxyAnalytics(hub.projectUrl, hub.projectSecretKey || hub.userToken)
return proxyHubAnalytics(hub.projectUrl, hub.projectSecretKey || hub.userToken)
}
const dataset = _useDataset()

Expand All @@ -36,7 +36,7 @@ export function useAnalytics() {
}
}

export function useProxyAnalytics(projectUrl: string, secretKey?: string) {
export function proxyHubAnalytics(projectUrl: string, secretKey?: string) {
const analyticsAPI = ofetch.create({
baseURL: joinURL(projectUrl, '/api/_hub/analytics'),
headers: {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/server/utils/blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function _useBucket() {
export function hubBlob() {
const hub = useRuntimeConfig().hub
if (import.meta.dev && hub.projectUrl) {
return useProxyBlob(hub.projectUrl, hub.projectSecretKey || hub.userToken)
return proxyHubBlob(hub.projectUrl, hub.projectSecretKey || hub.userToken)
}
const bucket = _useBucket()

Expand Down Expand Up @@ -132,7 +132,7 @@ export function hubBlob() {
}
}

export function useProxyBlob(projectUrl: string, secretKey?: string) {
export function proxyHubBlob(projectUrl: string, secretKey?: string) {
const blobAPI = ofetch.create({
baseURL: joinURL(projectUrl, '/api/_hub/blob'),
headers: {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/server/utils/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function hubDatabase(): D1Database {
}
const hub = useRuntimeConfig().hub
if (import.meta.dev && hub.projectUrl) {
_db = useProxyDatabase(hub.projectUrl, hub.projectSecretKey || hub.userToken)
_db = proxyHubDatabase(hub.projectUrl, hub.projectSecretKey || hub.userToken)
return _db
}
// @ts-ignore
Expand All @@ -25,7 +25,7 @@ export function hubDatabase(): D1Database {
throw createError('Missing Cloudflare DB binding (D1)')
}

export function useProxyDatabase(projectUrl: string, secretKey?: string): D1Database {
export function proxyHubDatabase(projectUrl: string, secretKey?: string): D1Database {
const d1API = ofetch.create({
baseURL: joinURL(projectUrl, '/api/_hub/database'),
method: 'POST',
Expand Down
37 changes: 31 additions & 6 deletions src/runtime/server/utils/kv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,61 @@ import { joinURL } from 'ufo'
import { createError } from 'h3'
import { useRuntimeConfig } from '#imports'

let _kv: Storage
interface KV extends Storage {
keys: Storage['getKeys']
get: Storage['getItem']
set: Storage['setItem']
has: Storage['hasItem']
del: Storage['removeItem']
}

let _kv: KV

export function hubKV(): Storage {
export function hubKV(): KV {
if (_kv) {
return _kv
}
const hub = useRuntimeConfig().hub
if (import.meta.dev && hub.projectUrl) {
return useProxyKV(hub.projectUrl, hub.projectSecretKey || hub.userToken)
return proxyHubKV(hub.projectUrl, hub.projectSecretKey || hub.userToken)
}
// @ts-ignore
const binding = process.env.KV || globalThis.__env__?.KV || globalThis.KV
if (binding) {
_kv = createStorage({
const storage = createStorage({
driver: cloudflareKVBindingDriver({
binding
})
})
_kv = {
keys: storage.getKeys,
get: storage.getItem,
set: storage.setItem,
has: storage.hasItem,
del: storage.removeItem,
...storage,
}
return _kv
}
throw createError('Missing Cloudflare KV binding (KV)')
}

export function useProxyKV(projectUrl: string, secretKey?: string): Storage {
return createStorage({
export function proxyHubKV(projectUrl: string, secretKey?: string): KV {
const storage = createStorage({
driver: httpDriver({
base: joinURL(projectUrl, '/api/_hub/kv/'),
headers: {
Authorization: `Bearer ${secretKey}`
}
})
})

return {
keys: storage.getKeys,
get: storage.getItem,
set: storage.setItem,
has: storage.hasItem,
del: storage.removeItem,
...storage,
}
}

0 comments on commit 5d73b42

Please sign in to comment.