@riligar/agents-kit 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/skills/riligar-design-system/SKILL.md +1 -0
- package/.agent/skills/riligar-design-system/references/design-system.md +0 -7
- package/.agent/skills/riligar-dev-auth-elysia/SKILL.md +2 -2
- package/.agent/skills/riligar-dev-dashboard/SKILL.md +331 -1
- package/.agent/skills/riligar-dev-dashboard/references/dependencies.md +44 -0
- package/.agent/skills/riligar-dev-database/SKILL.md +45 -0
- package/.agent/skills/riligar-dev-database/references/connection.md +74 -0
- package/.agent/skills/riligar-dev-database/references/migrations.md +70 -0
- package/.agent/skills/riligar-dev-database/references/queries.md +173 -0
- package/.agent/skills/riligar-dev-database/references/schema.md +106 -0
- package/.agent/skills/riligar-dev-manager/SKILL.md +13 -8
- package/.agent/skills/riligar-dev-website/SKILL.md +1 -1
- package/.agent/skills/riligar-dev-website-seo/SKILL.md +1 -1
- package/.agent/skills/riligar-infra-cloudfare/SKILL.md +350 -0
- package/.agent/skills/riligar-infra-cloudfare/references/cloudflare-api.md +139 -0
- package/.agent/skills/riligar-infra-cloudfare/references/email-routing.md +262 -0
- package/.agent/skills/riligar-infra-cloudfare/references/r2-storage.md +333 -0
- package/.agent/skills/riligar-infra-fly/SKILL.md +38 -1
- package/.agent/skills/riligar-infra-stripe/SKILL.md +2 -3
- package/.agent/skills/riligar-marketing-email/SKILL.md +0 -6
- package/.agent/skills/riligar-marketing-seo/SKILL.md +1 -7
- package/package.json +1 -1
- package/.agent/skills/riligar-dev-stack/SKILL.md +0 -110
- package/.agent/skills/riligar-dev-stack/references/tech-stack.md +0 -131
- package/.agent/skills/riligar-infra-cloudfare/.gitkeep +0 -0
- /package/.agent/skills/riligar-dev-auth-elysia/assets/{server-snippets.ts → server-snippets.js} +0 -0
|
@@ -23,6 +23,7 @@ Para atingir a excelência:
|
|
|
23
23
|
- **[Theme Config](assets/theme.js)**: Configuração do tema com suporte a Dark Mode automático.
|
|
24
24
|
- **[Visual references](references/visual-references.md)**: Mapeamento de nuances estéticas.
|
|
25
25
|
- **[Anti-patterns](references/anti-patterns.md)**: O que evitar (especialmente CSS inline).
|
|
26
|
+
- **[Design Guidelines](references/design-system.md)**: Tipografia, cores e hierarquia visual detalhada.
|
|
26
27
|
|
|
27
28
|
## Checklist de Lapidação (RiLiGar Excellence)
|
|
28
29
|
|
|
@@ -104,10 +104,3 @@ Ao pedir código ou design para uma IA, use este formato:
|
|
|
104
104
|
- **Modais:** Devem ter um backdrop com blur (`backdrop-blur-sm`) e fundo branco sólido. Sem cabeçalhos coloridos.
|
|
105
105
|
- **Dashboards:** Devem privilegiar números grandes (Big Numbers) em monocromia, com rótulos pequenos abaixo. Gráficos devem ser limpos, sem grid lines pesadas.
|
|
106
106
|
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 🤖 Integração com Gemini CLI
|
|
110
|
-
|
|
111
|
-
Este repositório possui uma **Skill do Gemini CLI** configurada em [`riligar-design-system/SKILL.md`](../SKILL.md). Ao usar o Gemini CLI neste repositório, o agente aplicará automaticamente estas diretrizes a qualquer código ou design gerado.
|
|
112
|
-
|
|
113
|
-
Para saber como usar, veja o [README principal](../../README.md).
|
|
@@ -14,7 +14,7 @@ This skill provides a backend workflow for integrating authentication and permis
|
|
|
14
14
|
Install the backend SDK:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
|
|
17
|
+
bun add @riligar/auth-elysia
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
### 2. Environment Variables
|
|
@@ -55,7 +55,7 @@ const app = new Elysia()
|
|
|
55
55
|
.listen(3000)
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
For more patterns, see [server-snippets.
|
|
58
|
+
For more patterns, see [server-snippets.js](assets/server-snippets.js).
|
|
59
59
|
|
|
60
60
|
## Specialized Guides
|
|
61
61
|
|
|
@@ -15,6 +15,7 @@ description: Padrões React específicos do RiLiGar. Zustand, i18n, estrutura de
|
|
|
15
15
|
> Sempre respeite também:
|
|
16
16
|
> - @[.agent/skills/riligar-design-system] — UI exclusivo via Mantine, zero CSS
|
|
17
17
|
> - Rules em `.agent/rules/` — clean-code, naming-conventions, code-style, javascript-only
|
|
18
|
+
> - [references/dependencies.md](references/dependencies.md) — Pacotes e versões do frontend, config Vite
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
|
@@ -246,7 +247,336 @@ export const WHATSAPP_SUPPORT_MESSAGE = '...' // ou via i18n se traduzível
|
|
|
246
247
|
|
|
247
248
|
---
|
|
248
249
|
|
|
250
|
+
## 8. Padrões reutilizáveis
|
|
251
|
+
|
|
252
|
+
Estruturas que repetem pela codebase. Copie o esqueleto, ajuste apenas o conteúdo domínio-específico.
|
|
253
|
+
|
|
254
|
+
### 8.1 Page Header
|
|
255
|
+
|
|
256
|
+
Presente em **todas** as pages. Estrutura idêntica sempre:
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
<Box py="xl">
|
|
260
|
+
<Group justify="space-between" align="flex-end" mb="xl">
|
|
261
|
+
<Stack gap={0}>
|
|
262
|
+
<Text size="xs" fw={700} c="dimmed" tt="uppercase" lts="0.1em">
|
|
263
|
+
{t('namespace.subtitle')}
|
|
264
|
+
</Text>
|
|
265
|
+
<Title order={1} style={{ letterSpacing: '-0.04em' }}>
|
|
266
|
+
{t('namespace.title')}
|
|
267
|
+
</Title>
|
|
268
|
+
</Stack>
|
|
269
|
+
{/* CTA opcional — ex: <Button leftSection={<IconPlus size={16} />}> */}
|
|
270
|
+
</Group>
|
|
271
|
+
{/* Conteúdo da página */}
|
|
272
|
+
</Box>
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 8.2 Empty State
|
|
276
|
+
|
|
277
|
+
Usado quando uma lista está vazia. Card com borda dashed, icone grande, texto e CTA:
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
<Card padding="xl" radius="md" withBorder style={{ borderStyle: 'dashed', textAlign: 'center' }}>
|
|
281
|
+
<Stack align="center" py="xl">
|
|
282
|
+
<IconDominio size={48} stroke={1} color="var(--mantine-color-gray-2)" />
|
|
283
|
+
<Text c="dimmed" size="sm">{t('namespace.emptyMessage')}</Text>
|
|
284
|
+
<Button onClick={handleCreate} leftSection={<IconPlus size={16} />}>
|
|
285
|
+
{t('namespace.createFirst')}
|
|
286
|
+
</Button>
|
|
287
|
+
</Stack>
|
|
288
|
+
</Card>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 8.3 Loading Guard
|
|
292
|
+
|
|
293
|
+
Loader só aparece quando não há dados ainda (não sobrescreve lista existente):
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
{loading && data.length === 0 ? (
|
|
297
|
+
<Center style={{ height: 300 }}>
|
|
298
|
+
<Loader />
|
|
299
|
+
</Center>
|
|
300
|
+
) : (
|
|
301
|
+
/* conteúdo normal */
|
|
302
|
+
)}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 8.4 Card Grid
|
|
306
|
+
|
|
307
|
+
Layout responsivo padrão para listas de cards:
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
<SimpleGrid cols={{ base: 1, sm: 2, md: 3 }}>
|
|
311
|
+
{items.map((item) => (
|
|
312
|
+
<Card key={item.id} padding="lg" radius="md" withBorder>
|
|
313
|
+
{/* conteúdo do card */}
|
|
314
|
+
</Card>
|
|
315
|
+
))}
|
|
316
|
+
</SimpleGrid>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Para galerias (mais itens pequenos): `cols={{ base: 1, sm: 2, md: 3, lg: 4 }}`
|
|
320
|
+
|
|
321
|
+
### 8.5 Modal
|
|
322
|
+
|
|
323
|
+
Sempre usa `useDisclosure`. Header com borderBottom específico:
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
const [opened, { open, close }] = useDisclosure(false)
|
|
327
|
+
|
|
328
|
+
<Modal
|
|
329
|
+
opened={opened}
|
|
330
|
+
onClose={close}
|
|
331
|
+
centered
|
|
332
|
+
radius="md"
|
|
333
|
+
padding="xl"
|
|
334
|
+
title={<Text fw={700}>{t('namespace.modalTitle')}</Text>}
|
|
335
|
+
styles={{ header: { borderBottom: '1px solid var(--mantine-color-gray-2)', marginBottom: 20 } }}
|
|
336
|
+
>
|
|
337
|
+
{/* corpo do modal */}
|
|
338
|
+
</Modal>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Para múltiplos modais na mesma page, renomeia as funções: `{ open: openEdit, close: closeEdit }`
|
|
342
|
+
|
|
343
|
+
### 8.6 Status Badge
|
|
344
|
+
|
|
345
|
+
Mapeia status → configuração visual via função que recebe `t`:
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
const getStatusConfig = (t) => ({
|
|
349
|
+
draft: { color: 'gray', icon: <IconCircleDotted size={16} />, label: t('posts.status.draft') },
|
|
350
|
+
scheduled: { color: 'blue', icon: <IconClock size={16} />, label: t('posts.status.scheduled') },
|
|
351
|
+
published: { color: 'green', icon: <IconCircleCheck size={16} />, label: t('posts.status.published') },
|
|
352
|
+
failed: { color: 'red', icon: <IconCircleX size={16} />, label: t('posts.status.failed') },
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// Uso
|
|
356
|
+
const config = getStatusConfig(t)[status]
|
|
357
|
+
<Badge variant="dot" color={config.color}>{config.label}</Badge>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 8.7 Search / Filter
|
|
361
|
+
|
|
362
|
+
Filtro local sem chamada de API — estado local + filter inline:
|
|
363
|
+
|
|
364
|
+
```javascript
|
|
365
|
+
const [search, setSearch] = useState('')
|
|
366
|
+
|
|
367
|
+
const filtered = items.filter((item) =>
|
|
368
|
+
item.name.toLowerCase().includes(search.toLowerCase())
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
<TextInput
|
|
372
|
+
placeholder={t('common.search')}
|
|
373
|
+
leftSection={<IconSearch size={16} />}
|
|
374
|
+
value={search}
|
|
375
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
376
|
+
/>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 9. Padrões de dados e lógica
|
|
382
|
+
|
|
383
|
+
### 9.1 Store — async action
|
|
384
|
+
|
|
385
|
+
Template exato que todas as actions seguem. Imports do service como namespace:
|
|
386
|
+
|
|
387
|
+
```javascript
|
|
388
|
+
import { create } from 'zustand'
|
|
389
|
+
import * as feedsService from '../services/feeds.js'
|
|
390
|
+
|
|
391
|
+
export const useFeedStore = create((set) => ({
|
|
392
|
+
feeds: [],
|
|
393
|
+
loading: false,
|
|
394
|
+
error: null,
|
|
395
|
+
|
|
396
|
+
fetchFeeds: async () => {
|
|
397
|
+
set({ loading: true, error: null })
|
|
398
|
+
try {
|
|
399
|
+
const feeds = await feedsService.getAll()
|
|
400
|
+
set({ feeds, loading: false })
|
|
401
|
+
} catch (error) {
|
|
402
|
+
set({ error: error.message, loading: false })
|
|
403
|
+
throw error
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
}))
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Nota: services são importados como `import * as service` (namespace), não como objeto exportado.
|
|
410
|
+
|
|
411
|
+
### 9.2 Store — mutação de listas
|
|
412
|
+
|
|
413
|
+
Atualizações imutáveis via `set(state => ...)` com spread + map/filter:
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
// Atualizar item na lista
|
|
417
|
+
updateFeed: (id, data) => set((state) => ({
|
|
418
|
+
feeds: state.feeds.map((f) => (f.id === id ? { ...f, ...data } : f))
|
|
419
|
+
})),
|
|
420
|
+
|
|
421
|
+
// Remover item
|
|
422
|
+
removeFeed: (id) => set((state) => ({
|
|
423
|
+
feeds: state.feeds.filter((f) => f.id !== id)
|
|
424
|
+
})),
|
|
425
|
+
|
|
426
|
+
// Adicionar item
|
|
427
|
+
addFeed: (feed) => set((state) => ({
|
|
428
|
+
feeds: [...state.feeds, feed]
|
|
429
|
+
})),
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### 9.3 Notifications
|
|
433
|
+
|
|
434
|
+
Shape e convenção de cores consistente em toda a app:
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
import { notifications } from '@mantine/notifications'
|
|
438
|
+
import { IconCheck, IconX, IconAlertCircle } from '@tabler/icons-react'
|
|
439
|
+
|
|
440
|
+
// ✅ Sucesso
|
|
441
|
+
notifications.show({
|
|
442
|
+
title: t('common.success'),
|
|
443
|
+
message: t('namespace.savedMessage'),
|
|
444
|
+
color: 'green',
|
|
445
|
+
icon: <IconCheck size={18} />,
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
// ✅ Erro
|
|
449
|
+
notifications.show({
|
|
450
|
+
title: t('common.error'),
|
|
451
|
+
message: error.message,
|
|
452
|
+
color: 'red',
|
|
453
|
+
icon: <IconX size={18} />,
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
// ✅ Warning
|
|
457
|
+
notifications.show({
|
|
458
|
+
title: t('common.warning'),
|
|
459
|
+
message: t('namespace.warningMessage'),
|
|
460
|
+
color: 'yellow',
|
|
461
|
+
icon: <IconAlertCircle size={18} />,
|
|
462
|
+
})
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Icones de notification: sempre `size={18}`. Mensagens de erro: usa `error.message` diretamente (já vem traduzido do backend).
|
|
466
|
+
|
|
467
|
+
### 9.4 dayjs + i18n
|
|
468
|
+
|
|
469
|
+
Locale do dayjs sincroniza com o idioma do i18n:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
import dayjs from 'dayjs'
|
|
473
|
+
import relativeTime from 'dayjs/plugin/relativeTime'
|
|
474
|
+
import { useTranslation } from 'react-i18next'
|
|
475
|
+
|
|
476
|
+
dayjs.extend(relativeTime)
|
|
477
|
+
|
|
478
|
+
const MyComponent = () => {
|
|
479
|
+
const { i18n } = useTranslation()
|
|
480
|
+
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
dayjs.locale(i18n.language)
|
|
483
|
+
}, [i18n.language])
|
|
484
|
+
|
|
485
|
+
// Formatos usados no projeto:
|
|
486
|
+
// dayjs(date).format('DD MMM, HH:mm') — compacto com hora
|
|
487
|
+
// dayjs(date).format('DD/MM/YYYY [at] HH:mm') — completo
|
|
488
|
+
// dayjs(date).fromNow() — relativo ("há 2 dias")
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## 10. Padrões de fluxo
|
|
495
|
+
|
|
496
|
+
### 10.1 Route Guard (wrapper)
|
|
497
|
+
|
|
498
|
+
Componente que protege rotas verificando estado do store:
|
|
499
|
+
|
|
500
|
+
```javascript
|
|
501
|
+
import { Navigate } from 'react-router-dom'
|
|
502
|
+
import { useFeedStore } from '../store/feed-store.js'
|
|
503
|
+
|
|
504
|
+
const RequireFeed = ({ children }) => {
|
|
505
|
+
const activeFeed = useFeedStore((s) => s.activeFeed)
|
|
506
|
+
if (!activeFeed) return <Navigate to="/" />
|
|
507
|
+
return children
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Usado na definição de rotas: `<RequireFeed><EditorPage /></RequireFeed>`
|
|
512
|
+
|
|
513
|
+
### 10.2 Notificação via URL params
|
|
514
|
+
|
|
515
|
+
Após redirects externos (OAuth, Stripe checkout), status vem via query params:
|
|
516
|
+
|
|
517
|
+
```javascript
|
|
518
|
+
import { useSearchParams } from 'react-router-dom'
|
|
519
|
+
|
|
520
|
+
const SubscriptionPage = () => {
|
|
521
|
+
const [searchParams, setSearchParams] = useSearchParams()
|
|
522
|
+
const { t } = useTranslation()
|
|
523
|
+
|
|
524
|
+
useEffect(() => {
|
|
525
|
+
if (searchParams.get('success')) {
|
|
526
|
+
notifications.show({ title: t('common.success'), message: t('subscription.successMessage'), color: 'green', icon: <IconCheck size={18} /> })
|
|
527
|
+
setSearchParams({})
|
|
528
|
+
} else if (searchParams.get('canceled')) {
|
|
529
|
+
notifications.show({ title: t('common.warning'), message: t('subscription.canceledMessage'), color: 'yellow', icon: <IconAlertCircle size={18} /> })
|
|
530
|
+
setSearchParams({})
|
|
531
|
+
}
|
|
532
|
+
}, [searchParams])
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### 10.3 Autosave
|
|
537
|
+
|
|
538
|
+
Padrão usado no editor — debounce com state machine de status:
|
|
539
|
+
|
|
540
|
+
```javascript
|
|
541
|
+
const [saveStatus, setSaveStatus] = useState('idle') // 'idle' | 'saving' | 'saved'
|
|
542
|
+
|
|
543
|
+
useEffect(() => {
|
|
544
|
+
if (!content) return
|
|
545
|
+
const timeout = setTimeout(async () => {
|
|
546
|
+
setSaveStatus('saving')
|
|
547
|
+
try {
|
|
548
|
+
await postsService.update(postId, { content })
|
|
549
|
+
setSaveStatus('saved')
|
|
550
|
+
// Reset para idle após 3s
|
|
551
|
+
setTimeout(() => setSaveStatus('idle'), 3000)
|
|
552
|
+
} catch {
|
|
553
|
+
setSaveStatus('idle')
|
|
554
|
+
}
|
|
555
|
+
}, 2000) // debounce de 2s
|
|
556
|
+
|
|
557
|
+
return () => clearTimeout(timeout)
|
|
558
|
+
}, [content, postId])
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 11. Convenção de tamanhos de icones
|
|
564
|
+
|
|
565
|
+
Hierarquia consistente — sempre de `@tabler/icons-react`:
|
|
566
|
+
|
|
567
|
+
| Contexto | Size | Exemplo |
|
|
568
|
+
|---|---|---|
|
|
569
|
+
| Menu items / nav | 14 | Sidebar links |
|
|
570
|
+
| Inline / badges | 16 | Botões, labels, leftSection |
|
|
571
|
+
| Notifications | 18 | Icons nas notifications |
|
|
572
|
+
| Card headers | 20 | Ação principal do card |
|
|
573
|
+
| Feature cards | 24 | Cards de destaque |
|
|
574
|
+
| Empty states | 48 | Icone do empty state (com `stroke={1}`) |
|
|
575
|
+
|
|
576
|
+
Empty states usam `stroke={1}` para parecer mais leve. Icones decorativos genéricos usam `stroke={1.5}`.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
249
580
|
## Related Skills
|
|
250
581
|
|
|
251
582
|
- @[.agent/skills/riligar-design-system]
|
|
252
|
-
- @[.agent/skills/riligar-dev-stack]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Dependências Frontend
|
|
2
|
+
|
|
3
|
+
## Core
|
|
4
|
+
|
|
5
|
+
| Pacote | Versão | Descrição |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `react` | ^19.x | Biblioteca UI |
|
|
8
|
+
| `react-dom` | ^19.x | React DOM renderer |
|
|
9
|
+
| `react-router-dom` | ^7.x | Roteamento |
|
|
10
|
+
| `vite` | ^5.x | Build tool |
|
|
11
|
+
| `zustand` | ^5.x | Gerenciamento de estado |
|
|
12
|
+
| `ky` | ^1.x | HTTP client |
|
|
13
|
+
|
|
14
|
+
## UI
|
|
15
|
+
|
|
16
|
+
| Pacote | Versão | Descrição |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `@mantine/core` | ^8.x | Componentes UI |
|
|
19
|
+
| `@mantine/hooks` | ^8.x | Hooks utilitários |
|
|
20
|
+
| `@mantine/form` | ^8.x | Gerenciamento de formulários |
|
|
21
|
+
| `@mantine/notifications` | ^8.x | Sistema de notificações |
|
|
22
|
+
| `@tabler/icons-react` | ^3.x | Iconografia |
|
|
23
|
+
|
|
24
|
+
> Use apenas Mantine para estilização. Sem CSS Modules, Custom CSS ou CSS In-line.
|
|
25
|
+
|
|
26
|
+
## Configuração Vite
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { defineConfig } from 'vite'
|
|
30
|
+
import react from '@vitejs/plugin-react'
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
plugins: [react()],
|
|
34
|
+
build: {
|
|
35
|
+
lib: {
|
|
36
|
+
entry: 'src/index.js',
|
|
37
|
+
formats: ['es', 'cjs'],
|
|
38
|
+
},
|
|
39
|
+
rollupOptions: {
|
|
40
|
+
external: ['react', 'react-dom', '@mantine/core'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: riligar-dev-database
|
|
3
|
+
description: Database patterns for RiLiGar using Drizzle ORM + bun:sqlite. Use when setting up database connections, defining schemas, creating migrations, or writing queries. Covers SQLite on Fly.io volumes with the drizzle-kit workflow.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Database — Drizzle + bun:sqlite
|
|
7
|
+
|
|
8
|
+
> SQLite nativo no Bun. Zero drivers externos. Base de dados no volume do Fly.io (`/app/data`).
|
|
9
|
+
|
|
10
|
+
## Referências
|
|
11
|
+
|
|
12
|
+
| Arquivo | Quando usar |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| [connection.md](references/connection.md) | Setup inicial: instalação, db.js, drizzle.config |
|
|
15
|
+
| [schema.md](references/schema.md) | Definir tabelas, tipos de colunas, relações |
|
|
16
|
+
| [migrations.md](references/migrations.md) | Criar e executar migrations com drizzle-kit |
|
|
17
|
+
| [queries.md](references/queries.md) | Select, insert, update, delete, queries com relações |
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
// database/db.js
|
|
23
|
+
import { drizzle } from 'drizzle-orm/bun-sqlite'
|
|
24
|
+
import Database from 'bun:sqlite'
|
|
25
|
+
|
|
26
|
+
const sqlite = new Database(process.env.DB_PATH ?? './data/database.db')
|
|
27
|
+
const db = drizzle({ client: sqlite })
|
|
28
|
+
|
|
29
|
+
export { db }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Regras
|
|
33
|
+
|
|
34
|
+
- **Caminho do banco:** `/app/data/database.db` em produção (volume Fly.io). `./data/database.db` em desenvolvimento.
|
|
35
|
+
- **Migrations sempre:** Use `drizzle-kit generate` + `drizzle-kit migrate`. Nunca edite migrations à mão.
|
|
36
|
+
- **Schema único:** Todas as tabelas em `database/schema.js`.
|
|
37
|
+
- **Migrations no startup:** Use `migrate()` no `index.js` antes de `.listen()`.
|
|
38
|
+
|
|
39
|
+
## Related Skills
|
|
40
|
+
|
|
41
|
+
| Need | Skill |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| **Backend (Elysia)** | @[.agent/skills/riligar-dev-manager] |
|
|
44
|
+
| **Payments (Stripe)** | @[.agent/skills/riligar-infra-stripe] |
|
|
45
|
+
| **Infrastructure** | @[.agent/skills/riligar-infra-fly] |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Database Connection
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun add drizzle-orm
|
|
7
|
+
bun add -d drizzle-kit
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
No additional drivers needed — `bun:sqlite` is built into Bun.
|
|
11
|
+
|
|
12
|
+
## db.js
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
// database/db.js
|
|
16
|
+
import { drizzle } from 'drizzle-orm/bun-sqlite'
|
|
17
|
+
import Database from 'bun:sqlite'
|
|
18
|
+
|
|
19
|
+
const sqlite = new Database(process.env.DB_PATH ?? './data/database.db')
|
|
20
|
+
const db = drizzle({ client: sqlite })
|
|
21
|
+
|
|
22
|
+
export { db }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- Em **desenvolvimento**: `DB_PATH` não é definido → usa `./data/database.db`
|
|
26
|
+
- Em **produção** (Fly.io): `fly secrets set DB_PATH=/app/data/database.db`
|
|
27
|
+
|
|
28
|
+
## drizzle.config.js
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// drizzle.config.js (na raiz do projeto)
|
|
32
|
+
import { defineConfig } from 'drizzle-kit'
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
dialect: 'sqlite',
|
|
36
|
+
schema: './database/schema.js',
|
|
37
|
+
out: './database/migrations',
|
|
38
|
+
dbCredentials: {
|
|
39
|
+
url: process.env.DB_PATH ?? './data/database.db',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Migrations no Startup
|
|
45
|
+
|
|
46
|
+
No `index.js`, execute migrations antes de iniciar o servidor:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// index.js
|
|
50
|
+
import { migrate } from 'drizzle-orm/bun-sqlite/migrator'
|
|
51
|
+
import { db } from './database/db'
|
|
52
|
+
|
|
53
|
+
await migrate(db, { migrationsFolder: './database/migrations' })
|
|
54
|
+
|
|
55
|
+
// ... resto do setup
|
|
56
|
+
const app = new Elysia()
|
|
57
|
+
.use(routes)
|
|
58
|
+
.listen(3000)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Isso garante que o banco esteja sempre atualizado quando o servidor inicia no Fly.io.
|
|
62
|
+
|
|
63
|
+
## Estrutura de Arquivos
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
database/
|
|
67
|
+
├── db.js # Conexão (drizzle + bun:sqlite)
|
|
68
|
+
├── schema.js # Todas as tabelas e relações
|
|
69
|
+
└── migrations/
|
|
70
|
+
├── 0000_*.sql # Migrations geradas pelo drizzle-kit
|
|
71
|
+
├── 0001_*.sql
|
|
72
|
+
└── meta/
|
|
73
|
+
└── _journal.json
|
|
74
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Migrations — drizzle-kit
|
|
2
|
+
|
|
3
|
+
## Fluxo Padrão
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
1. Edite database/schema.js
|
|
7
|
+
2. bun run db:generate → gera migration SQL
|
|
8
|
+
3. bun run db:migrate → aplica no banco
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Scripts no package.json
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
{
|
|
15
|
+
"scripts": {
|
|
16
|
+
"db:generate": "drizzle-kit generate",
|
|
17
|
+
"db:migrate": "drizzle-kit migrate",
|
|
18
|
+
"db:push": "drizzle-kit push",
|
|
19
|
+
"db:studio": "drizzle-kit studio"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Comandos
|
|
25
|
+
|
|
26
|
+
### Gerar migration
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun run db:generate
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Compara o schema atual com o último snapshot. Gera um arquivo `.sql` em `database/migrations/`. Pergunta se algum campo foi renomeado (vs criado novo + deletado).
|
|
33
|
+
|
|
34
|
+
### Aplicar migrations
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bun run db:migrate
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Lê as migrations não aplicadas, executa em ordem. Mantém histórico no banco.
|
|
41
|
+
|
|
42
|
+
### Push (apenas desenvolvimento)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bun run db:push
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Aplica mudanças diretamente no banco sem gerar arquivos de migration. Útil durante prototipage rápido. **Nunca use em produção.**
|
|
49
|
+
|
|
50
|
+
### Studio (GUI)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bun run db:studio
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Abre uma interface visual para explorar e editar dados no banco.
|
|
57
|
+
|
|
58
|
+
## Migrations Programáticas (Startup)
|
|
59
|
+
|
|
60
|
+
Em produção, as migrations são executadas automaticamente no startup da aplicação:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// index.js
|
|
64
|
+
import { migrate } from 'drizzle-orm/bun-sqlite/migrator'
|
|
65
|
+
import { db } from './database/db'
|
|
66
|
+
|
|
67
|
+
await migrate(db, { migrationsFolder: './database/migrations' })
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Isso garante que o banco esteja sempre atualizado quando o servidor inicia no Fly.io.
|