/opt/canhelp/apps/api/src/routes
Edit: /opt/canhelp/apps/api/src/routes/locations.ts (1781B)
import { Hono } from 'hono'
import { eq, asc } from 'drizzle-orm'
import { db } from '../db.js'
import { locations } from '@canhelp/db'
import { redis } from '../redis.js'
const CACHE_KEY = 'locations:tree:v2'
const CACHE_TTL = 600
const app = new Hono()
function rowToLocation(row: typeof locations.$inferSelect) {
return {
id: row.id,
slug: row.slug,
names: { el: row.nameEl, en: row.nameEn, ru: row.nameRu, uk: row.nameUk ?? row.nameRu },
parentId: row.parentId,
order: row.order,
isActive: row.isActive,
}
}
function buildTree(rows: typeof locations.$inferSelect[]) {
const map: Record
= {}
const roots: any[] = []
for (const row of rows) {
map[row.id] = { ...rowToLocation(row), children: [] }
}
for (const row of rows) {
if (row.parentId && map[row.parentId]) {
map[row.parentId].children.push(map[row.id])
} else {
roots.push(map[row.id])
}
}
return roots
}
// GET /locations — tree of active locations
app.get('/', async (c) => {
const cached = await redis.get(CACHE_KEY).catch(() => null)
if (cached) return c.json(JSON.parse(cached))
const rows = await db
.select()
.from(locations)
.where(eq(locations.isActive, true))
.orderBy(asc(locations.order), asc(locations.nameEl))
const tree = buildTree(rows)
await redis.set(CACHE_KEY, JSON.stringify(tree), 'EX', CACHE_TTL).catch(() => {})
return c.json(tree)
})
// GET /locations/flat — flat list for dropdowns
app.get('/flat', async (c) => {
const rows = await db
.select()
.from(locations)
.where(eq(locations.isActive, true))
.orderBy(asc(locations.order), asc(locations.nameEl))
return c.json(rows.map(rowToLocation))
})
export { buildTree, rowToLocation }
export default app