Skip to content

Commit

Permalink
repalce upstash SDK wtih withRedis util and ioredis
Browse files Browse the repository at this point in the history
  • Loading branch information
juandjara committed Oct 16, 2023
1 parent bdce92f commit e9d3563
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 107 deletions.
32 changes: 21 additions & 11 deletions app/lib/cache.server.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
import { Redis } from "@upstash/redis"
import type { ParsedFile, TreeItem } from "./github"

const db = Redis.fromEnv()
import { withRedis } from "./redis.server"

export async function getTreeCache(repo: string, sha: string) {
return await db.get(`tree:${repo}:${sha}`) as TreeItem[]
return withRedis<TreeItem[]>(async (db) => {
const data = await db.get(`tree:${repo}:${sha}`)
return data && JSON.parse(data)
})
}

export async function setTreeCache(repo: string, sha: string, tree: TreeItem[]) {
await db.set(`tree:${repo}:${sha}`, tree, {
ex: 60 * 60 * 24 // 1 day
return withRedis(async (db) => {
const oneDay = 60 * 60 * 24
await db.setex(`tree:${repo}:${sha}`, oneDay, JSON.stringify(tree))
})
}

export async function deleteTreeCache(repo: string, sha: string) {
await db.del(`tree:${repo}:${sha}`)
return withRedis(async (db) => {
await db.del(`tree:${repo}:${sha}`)
})
}

export async function getFileCache(repo: string, branch: string, path: string) {
return await db.get(`file:${repo}/${branch}/${path}`) as ParsedFile
return withRedis<ParsedFile>(async (db) => {
const data = await db.get(`file:${repo}/${branch}/${path}`)
return data && JSON.parse(data)
})
}

export async function setFileCache(repo: string, branch: string, path: string, file: ParsedFile) {
await db.set(`file:${repo}/${branch}/${path}`, file, {
ex: 60 * 60 * 24 // 1 day
return withRedis(async (db) => {
const oneDay = 60 * 60 * 24
await db.setex(`file:${repo}/${branch}/${path}`, oneDay, JSON.stringify(file))
})
}

export async function deleteFileCache(repo: string, branch: string, path: string) {
await db.del(`file:${repo}/${branch}/${path}`)
return withRedis(async (db) => {
await db.del(`file:${repo}/${branch}/${path}`)
})
}
105 changes: 65 additions & 40 deletions app/lib/projects.server.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Redis } from "@upstash/redis"
import type { ParsedFile } from "./github"
import { FileMode, commitAndPush, deleteFile, getFileContent, getRepoFiles, saveFile } from "./github"
import { getBasename, getDirname, isMarkdown } from "./pathUtils"
import matter from 'front-matter'
import { deleteFileCache } from "./cache.server"

const db = Redis.fromEnv()
import { withRedis } from "./redis.server"

export type Project = {
id: number
Expand Down Expand Up @@ -44,44 +42,61 @@ export type ProjectConfig = {
const NEXT_PROJECT_KEY = 'next_project_id'

export async function getUserProjects(user: string) {
const ids = await db.smembers(`projects:${user}`)
if (ids.length === 0) {
return []
}

const projects = await db.mget(...ids.map(id => `project:${id}`)) as Project[]
return projects.sort((a, b) => a.title.localeCompare(b.title))
return withRedis(async (db) => {
const ids = await db.smembers(`projects:${user}`)
if (ids.length === 0) {
return []
}

const projects = await db.mget(...ids.map(id => `project:${id}`))
return projects
.filter((p) => p)
.map((p) => JSON.parse(p!) as Project)
.sort((a, b) => a.title.localeCompare(b.title))
})
}

export async function getProject(id: number) {
return await db.get(`project:${id}`) as Project
return withRedis<Project>(async (db) => {
const data = await db.get(`project:${id}`)
return data && JSON.parse(data)
})
}

export async function getIdForRepo(repo: string) {
return await db.get(`repo:${repo}`)
return withRedis(async (db) => {
const data = await db.get(`repo:${repo}`)
return data ? Number(data) : null
})
}

export async function createProject(project: Omit<Project, 'id'>) {
const id = await db.incr(NEXT_PROJECT_KEY)
await Promise.all([
db.sadd(`projects:${project.user}`, id),
db.set(`project:${id}`, { ...project, id }),
db.set(`repo:${project.repo}`, id)
])

return id
return withRedis(async (db) => {
const id = await db.incr(NEXT_PROJECT_KEY)
await Promise.all([
db.sadd(`projects:${project.user}`, id),
db.set(`project:${id}`, JSON.stringify({ ...project, id })),
db.set(`repo:${project.repo}`, id)
])

return id
})
}

export async function updateProject(project: Project) {
return db.set(`project:${project.id}`, project)
return withRedis(async (db) => {
await db.set(`project:${project.id}`, JSON.stringify(project))
})
}

export async function deleteProject(project: Project) {
return Promise.all([
db.srem(`projects:${project.user}`, project.id),
db.del(`project:${project.id}`),
db.del(`repo:${project.repo}`)
])
return withRedis(async (db) => {
await Promise.all([
db.srem(`projects:${project.user}`, project.id),
db.del(`project:${project.id}`),
db.del(`repo:${project.repo}`)
])
})
}

export const CONFIG_FILE_NAME = 'pressunto.config.json'
Expand Down Expand Up @@ -250,27 +265,37 @@ type SaveDraftParams = {
}

export async function saveDraft(params: SaveDraftParams) {
const { project, file } = params
const key = `draft:${project.id}:${encodeURIComponent(file.path)}`
await db.set(key, file)
return key
return withRedis(async (db) => {
const { project, file } = params
const key = `draft:${project.id}:${encodeURIComponent(file.path)}`
await db.set(key, JSON.stringify(file))
return key
})
}

export async function getDraft(projectId: number, path: string) {
const draft = await db.get<CollectionFile>(`draft:${projectId}:${encodeURIComponent(path)}`)
return draft
return withRedis<CollectionFile>(async (db) => {
const data = await db.get(`draft:${projectId}:${encodeURIComponent(path)}`)
return data && JSON.parse(data)
})
}

export async function deleteDraft(projectId: number, path: string) {
await db.del(`draft:${projectId}:${encodeURIComponent(path)}`)
return withRedis(async (db) => {
await db.del(`draft:${projectId}:${encodeURIComponent(path)}`)
})
}

export async function renameDraft(projectId: number, oldPath: string, newPath: string) {
const draft = await getDraft(projectId, oldPath)
if (!draft) {
return
}

await db.set(`draft:${projectId}:${encodeURIComponent(newPath)}`, draft)
await db.del(`draft:${projectId}:${encodeURIComponent(oldPath)}`)
return withRedis(async (db) => {
const draft = await getDraft(projectId, oldPath)
if (!draft) {
return
}

await Promise.all([
db.set(`draft:${projectId}:${encodeURIComponent(newPath)}`, JSON.stringify(draft)),
db.del(`draft:${projectId}:${encodeURIComponent(oldPath)}`),
])
})
}
22 changes: 22 additions & 0 deletions app/lib/redis.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Redis } from 'ioredis'

const REDIS_URL = process.env.REDIS_URL

/**
* Create a redis instance only for the duration of the function call
*/
export async function withRedis<T>(fn: (redis: Redis) => Promise<T>) {
if (!REDIS_URL) {
throw new Error('REDIS_URL not found in process.env')
}
const redis = new Redis(
REDIS_URL,
{ family: 6, reconnectOnError: () => 1 }
)
redis.on('error', err => {
console.error('Redis error: ', err)
})
const result = await fn(redis)
await redis.quit()
return result
}
Loading

0 comments on commit e9d3563

Please sign in to comment.