Skip to content

Commit

Permalink
chore: remove cloudflare kv requirement for project, single read-only…
Browse files Browse the repository at this point in the history
… file works for me
  • Loading branch information
tjmaynes committed Jan 15, 2024
1 parent 663da7e commit f46cda0
Show file tree
Hide file tree
Showing 15 changed files with 36 additions and 303 deletions.
15 changes: 2 additions & 13 deletions Makefile
Expand Up @@ -24,28 +24,17 @@ performance:

test: performance

ensure_cloudflare_kv_exists:
chmod +x ./script/cloudflare/ensure-cloudflare-kv-exists.sh
./script/cloudflare/ensure-cloudflare-kv-exists.sh "IMAGE_ANALYZER_KV"

ensure_cloudflare_page_exists:
chmod +x ./script/cloudflare/ensure-cloudflare-pages-exists.sh
./script/cloudflare/ensure-cloudflare-pages-exists.sh "image-analyzer-app"

ensure_cloudflare_secrets_exist:
@$(call add_cloudflare_secret,"image-analyzer-app","NODE_VERSION",$(shell cat .node-version))

ensure_cloudflare_infra_exists: ensure_cloudflare_page_exists ensure_cloudflare_kv_exists ensure_cloudflare_secrets_exist

deploy_cloudflare_page:
npm run pages:deploy

deploy: install build ensure_cloudflare_infra_exists deploy_cloudflare_page
deploy: install build ensure_cloudflare_page_exists deploy_cloudflare_page

seed:
node ./script/seed.js \
--cloudflare-kv-binding-id "5df82e748f494385a2aeaf2912cbb359" \
--seed-file "./data/seed.json"
node ./script/data/seed.js --seed-file "./src/data/seed.json"

clean:
rm -rf .next/ .vercel/ build/
Expand Down
2 changes: 1 addition & 1 deletion README.md
@@ -1,5 +1,5 @@
# image-analyzer-app
> a NextJS app that allows users to analyze images using MobileNet (via TensorflowJS), ChatGPT, and Cloudflare Pages
> a NextJS app that allows users to analyze images using MobileNet (via TensorflowJS), image descriptions from ChatGPT, and hosted on Cloudflare Pages
## Requirements

Expand Down
8 changes: 0 additions & 8 deletions next.config.js
@@ -1,12 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}

const {
setupDevBindings,
} = require('@cloudflare/next-on-pages/__experimental__next-dev')

setupDevBindings({
kvNamespaces: ['IMAGE_ANALYZER_KV'],
})

module.exports = nextConfig
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -14,7 +14,7 @@
"pages:build": "npx @cloudflare/next-on-pages",
"pages:deploy": "wrangler pages deploy .vercel/output/static --project-name='image-analyzer-app'",
"pages:watch": "next-on-pages --watch",
"pages:dev": "npm run pages:build && wrangler pages dev .vercel/output/static --compatibility-date=2023-10-30 --compatibility-flag=nodejs_compat --kv IMAGE_ANALYZER_KV"
"pages:dev": "npm run pages:build && wrangler pages dev .vercel/output/static --compatibility-date=2023-10-30 --compatibility-flag=nodejs_compat"
},
"dependencies": {
"@tensorflow-models/mobilenet": "^2.1.1",
Expand Down
40 changes: 0 additions & 40 deletions script/cloudflare/add-cloudflare-kv-value.sh

This file was deleted.

34 changes: 0 additions & 34 deletions script/cloudflare/ensure-cloudflare-kv-exists.sh

This file was deleted.

42 changes: 0 additions & 42 deletions script/cloudflare/ensure-cloudflare-secret-exists.sh

This file was deleted.

File renamed without changes.
75 changes: 16 additions & 59 deletions script/seed.js → script/data/seed.js
@@ -1,5 +1,4 @@
const fs = require('fs/promises')
const { execSync } = require('child_process')
const OpenAI = require('openai')
const imageClasses = require('./image-classes.json')

Expand All @@ -10,30 +9,23 @@ const throwIfEnvVarDoesNotExist = (envVarName) => {
}
}

const cleanDescription = (description) =>
description.replace(/\"/g, "'").replace(/(\r\n|\n|\r)/gm, ' ')
const getFlagValue = (flagName) => {
const flagIndex = process.argv.indexOf(`--${flagName}`)
const flagValue = flagIndex > -1 ? process.argv[flagIndex + 1] : ''

const addKeyValueToCloudflareKV = (cloudflareKVBindingId, key, value) => {
console.log(
`Attempting to add key "${key}" to Cloudflare KV: '${cloudflareKVBindingId}'`
)
if (!flagValue) {
console.error(
`Expected required flag '--${flagName}' to be passed to script!`
)
process.exit(1)
}

execSync(
`bash ./script/cloudflare/add-cloudflare-kv-value.sh "${cloudflareKVBindingId}" "${key}" "${value}"`,
(err, output) => {
if (err) {
console.error('could not execute command: ', err)
return
}
console.log('Output: \n', output)
}
)
return flagValue
}

const batchSeedProcess = async (
currentSeedFileContent,
seedFile,
cloudflareKVBindingId,
limitBatchSize = 5
) => {
const currentClassifications = imageClasses.slice(
Expand Down Expand Up @@ -103,14 +95,6 @@ const batchSeedProcess = async (
flag: 'w',
})

newSeedData.forEach(({ name, description }) => {
addKeyValueToCloudflareKV(
cloudflareKVBindingId,
name,
cleanDescription(description)
)
})

latestSeedFileContent = newSeedFileContent
}

Expand Down Expand Up @@ -139,20 +123,6 @@ const ensureSeedFileExists = (seedFile, onSeedFileConfirmation) =>
}
})

const getFlag = (flagName) => {
const flagIndex = process.argv.indexOf(`--${flagName}`)
const flagValue = flagIndex > -1 ? process.argv[flagIndex + 1] : ''

if (!flagValue) {
console.error(
`Expected required flag '--${flagName}' to be passed to script!`
)
process.exit(1)
}

return flagValue
}

const main = async () => {
;['CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_API_TOKEN', 'OPENAI_API_KEY'].forEach(
(envVarName) => throwIfEnvVarDoesNotExist(envVarName)
Expand All @@ -163,26 +133,13 @@ const main = async () => {
process.exit(1)
}

const [seedFile, cloudflareKVBindingId] = [
getFlag('seed-file'),
getFlag('cloudflare-kv-binding-id'),
]

const updatedSeedData = await ensureSeedFileExists(
seedFile,
(currentSeedData) =>
currentSeedData.data.length !== imageClasses.length
? batchSeedProcess(currentSeedData, seedFile, cloudflareKVBindingId, 5)
: currentSeedData
)
const [seedFile] = [getFlagValue('seed-file')]

updatedSeedData.data.forEach(({ name, description }) => {
addKeyValueToCloudflareKV(
cloudflareKVBindingId,
name,
cleanDescription(description)
)
})
return await ensureSeedFileExists(seedFile, (currentSeedData) =>
currentSeedData.data.length !== imageClasses.length
? batchSeedProcess(currentSeedData, seedFile, 5)
: currentSeedData
)
}

main().then(() => console.log('Done!'))
15 changes: 8 additions & 7 deletions src/app/_components/BackgroundInfo.tsx
Expand Up @@ -10,15 +10,16 @@ const BackgroundInfo = () => (
<strong>
<a href="https://chat.openai.com/chat">ChatGPT</a>
</strong>{' '}
(for generating image descriptions), and{' '}
(for generating image descriptions), and hosted on{' '}
<strong>
<a href="https://developers.cloudflare.com/kv/learning/how-kv-works/">
Cloudflare KV
</a>
<a href="https://pages.cloudflare.com/">Cloudflare Pages</a>
</strong>{' '}
(read-only database pairing &quot;image name&quot; to &quot;image
description&quot;). I built this web application to learn how to run maching
learning models in web browsers. Please contact me on{' '}
(via{' '}
<strong>
<a href="https://github.com/cloudflare/next-on-pages">next-on-pages</a>
</strong>
). I built this web application to learn how to run maching learning models
in web browsers. Please contact me on{' '}
<a href="https://linkedin.com/in/tjmaynes">LinkedIn</a> for any feedback or
concerns. Enjoy! 😀
</p>
Expand Down
25 changes: 8 additions & 17 deletions src/app/api/v1/image/description/route.ts
@@ -1,23 +1,14 @@
import 'server-only'

import { NextRequest, NextResponse } from 'next/server'
import {
CacheClient,
InMemoryCacheClient,
KVCacheClient,
} from '@/lib/cache-client'
import seedData from '@/data/seed.json'

export const runtime = 'edge'

const createCacheClient = (): CacheClient => {
if (process.env.NODE_ENV === 'development') return new InMemoryCacheClient()

const IMAGE_ANALYZER_KV = process.env.IMAGE_ANALYZER_KV
const imageDescriptionData = seedData.data.reduce<Record<string, string>>(
(accum, curr) => ({ ...accum, ...{ [curr.name]: curr.description } }),
{}
)

return new KVCacheClient(IMAGE_ANALYZER_KV)
}

const cacheClient = createCacheClient()
export const runtime = 'edge'

export async function GET(request: NextRequest) {
const thing = request.nextUrl.searchParams.get('thing')
Expand All @@ -28,12 +19,12 @@ export async function GET(request: NextRequest) {
status: 422,
})

const response = await cacheClient.get(thing)
const response = imageDescriptionData[thing]

return response
? NextResponse.json({ message: response, status: 200 })
: NextResponse.json({
message: `Unable to find a description for ${thing}...`,
message: `Unable to find a description for "${thing}"...`,
status: 404,
})
}
File renamed without changes.
12 changes: 0 additions & 12 deletions src/env.d.ts

This file was deleted.

0 comments on commit f46cda0

Please sign in to comment.