@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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Queries — Drizzle
|
|
2
|
+
|
|
3
|
+
## Imports Comuns
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
import { db } from '../database/db'
|
|
7
|
+
import { users, posts } from '../database/schema'
|
|
8
|
+
import { eq, and, or, desc, asc } from 'drizzle-orm'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Select
|
|
12
|
+
|
|
13
|
+
### Básico
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// Todos os registros
|
|
17
|
+
const allUsers = await db.select().from(users)
|
|
18
|
+
|
|
19
|
+
// Campos específicos
|
|
20
|
+
const names = await db.select({
|
|
21
|
+
id: users.id,
|
|
22
|
+
name: users.name,
|
|
23
|
+
}).from(users)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Filtrar
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
// Por campo único
|
|
30
|
+
const [user] = await db.select().from(users).where(eq(users.id, userId)).limit(1)
|
|
31
|
+
|
|
32
|
+
// Múltiplos filtros (AND)
|
|
33
|
+
const results = await db.select().from(users).where(
|
|
34
|
+
and(
|
|
35
|
+
eq(users.active, true),
|
|
36
|
+
eq(users.plan, 'pro')
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
// OR
|
|
41
|
+
const results = await db.select().from(users).where(
|
|
42
|
+
or(eq(users.id, '1'), eq(users.id, '2'))
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Ordenar e Paginar
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const page = await db.select().from(posts)
|
|
50
|
+
.orderBy(desc(posts.createdAt))
|
|
51
|
+
.limit(10)
|
|
52
|
+
.offset(pageIndex * 10)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Insert
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Básico
|
|
59
|
+
await db.insert(users).values({ name: 'Dan', email: 'dan@email.com' })
|
|
60
|
+
|
|
61
|
+
// Com retorno
|
|
62
|
+
const [user] = await db.insert(users)
|
|
63
|
+
.values({ name: 'Dan', email: 'dan@email.com' })
|
|
64
|
+
.returning()
|
|
65
|
+
|
|
66
|
+
// Múltiplos
|
|
67
|
+
await db.insert(users).values([
|
|
68
|
+
{ name: 'Dan', email: 'dan@email.com' },
|
|
69
|
+
{ name: 'Ana', email: 'ana@email.com' },
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
// Upsert (conflict handling)
|
|
73
|
+
await db.insert(users)
|
|
74
|
+
.values({ id: '1', name: 'Dan' })
|
|
75
|
+
.onConflictDoUpdate({
|
|
76
|
+
target: users.id,
|
|
77
|
+
set: { name: 'Dan', updatedAt: new Date() },
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Update
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
await db.update(users)
|
|
85
|
+
.set({ name: 'Mr. Dan', updatedAt: new Date() })
|
|
86
|
+
.where(eq(users.id, userId))
|
|
87
|
+
|
|
88
|
+
// Com retorno
|
|
89
|
+
const [updated] = await db.update(users)
|
|
90
|
+
.set({ plan: 'pro' })
|
|
91
|
+
.where(eq(users.id, userId))
|
|
92
|
+
.returning()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Delete
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
await db.delete(users).where(eq(users.id, userId))
|
|
99
|
+
|
|
100
|
+
// Com retorno
|
|
101
|
+
const [deleted] = await db.delete(users)
|
|
102
|
+
.where(eq(users.id, userId))
|
|
103
|
+
.returning()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Queries com Relações
|
|
107
|
+
|
|
108
|
+
Quando tiver relações definidas no schema, use `db.query` em vez de `db.select`:
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Usuário com todos os posts
|
|
112
|
+
const user = await db.query.users.findOne({
|
|
113
|
+
where: eq(users.id, userId),
|
|
114
|
+
with: {
|
|
115
|
+
posts: true,
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Posts com autor
|
|
120
|
+
const posts = await db.query.posts.findMany({
|
|
121
|
+
with: {
|
|
122
|
+
author: true,
|
|
123
|
+
},
|
|
124
|
+
orderBy: desc(posts.createdAt),
|
|
125
|
+
limit: 10,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Nesting profundo
|
|
129
|
+
const user = await db.query.users.findOne({
|
|
130
|
+
where: eq(users.id, userId),
|
|
131
|
+
with: {
|
|
132
|
+
posts: {
|
|
133
|
+
with: {
|
|
134
|
+
comments: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Padrão de Serviço
|
|
142
|
+
|
|
143
|
+
Sempre encapsule queries em serviços:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// services/user.js
|
|
147
|
+
import { db } from '../database/db'
|
|
148
|
+
import { users } from '../database/schema'
|
|
149
|
+
import { eq } from 'drizzle-orm'
|
|
150
|
+
|
|
151
|
+
export async function getUserById(id) {
|
|
152
|
+
const [user] = await db.select().from(users).where(eq(users.id, id)).limit(1)
|
|
153
|
+
return user ?? null
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function createUser(data) {
|
|
157
|
+
const [user] = await db.insert(users).values(data).returning()
|
|
158
|
+
return user
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function updateUser(id, data) {
|
|
162
|
+
const [user] = await db.update(users)
|
|
163
|
+
.set({ ...data, updatedAt: new Date() })
|
|
164
|
+
.where(eq(users.id, id))
|
|
165
|
+
.returning()
|
|
166
|
+
return user ?? null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function deleteUser(id) {
|
|
170
|
+
const [user] = await db.delete(users).where(eq(users.id, id)).returning()
|
|
171
|
+
return user ?? null
|
|
172
|
+
}
|
|
173
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Schema — Drizzle SQLite
|
|
2
|
+
|
|
3
|
+
## Definição Básica
|
|
4
|
+
|
|
5
|
+
```javascript
|
|
6
|
+
// database/schema.js
|
|
7
|
+
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
|
|
8
|
+
import { relations } from 'drizzle-orm'
|
|
9
|
+
|
|
10
|
+
export const users = sqliteTable('users', {
|
|
11
|
+
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
12
|
+
email: text('email').notNull().unique(),
|
|
13
|
+
name: text('name'),
|
|
14
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
15
|
+
updatedAt: integer('updated_at', { mode: 'timestamp' }),
|
|
16
|
+
})
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Tipos de Colunas
|
|
20
|
+
|
|
21
|
+
| Tipo | Uso | Exemplo |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| `text()` | Strings, UUIDs, JSON | `text('name').notNull()` |
|
|
24
|
+
| `integer()` | Números, booleans, timestamps | `integer('age')` |
|
|
25
|
+
| `real()` | Decimais (float) | `real('price')` |
|
|
26
|
+
| `blob()` | Dados binários | `blob('avatar')` |
|
|
27
|
+
| `numeric()` | Valores numéricos precisos | `numeric('amount')` |
|
|
28
|
+
|
|
29
|
+
### Modes do `integer()`
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
integer('count') // número
|
|
33
|
+
integer('active', { mode: 'boolean' }) // boolean (0/1)
|
|
34
|
+
integer('created_at', { mode: 'timestamp' }) // Date (segundos)
|
|
35
|
+
integer('created_at', { mode: 'timestamp_ms' }) // Date (milissegundos)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### JSON via `text()`
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
text('metadata', { mode: 'json' }) // armazena JSON como text
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Primary Key
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// UUID (padrão RiLiGar)
|
|
48
|
+
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
49
|
+
|
|
50
|
+
// Auto-increment (quando necessário)
|
|
51
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Foreign Keys
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
export const posts = sqliteTable('posts', {
|
|
58
|
+
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
|
|
59
|
+
authorId: text('author_id').notNull().references(() => users.id),
|
|
60
|
+
title: text('title').notNull(),
|
|
61
|
+
body: text('body'),
|
|
62
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Relações
|
|
67
|
+
|
|
68
|
+
Relações são definidas separadamente do schema — são apenas para queries, não afetam o banco:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
72
|
+
posts: many(posts),
|
|
73
|
+
}))
|
|
74
|
+
|
|
75
|
+
export const postsRelations = relations(posts, ({ one }) => ({
|
|
76
|
+
author: one(users, {
|
|
77
|
+
fields: [posts.authorId],
|
|
78
|
+
references: [users.id],
|
|
79
|
+
}),
|
|
80
|
+
}))
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Many-to-Many (via junction table)
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
export const usersToGroups = sqliteTable('users_to_groups', {
|
|
87
|
+
userId: text('user_id').notNull().references(() => users.id),
|
|
88
|
+
groupId: text('group_id').notNull().references(() => groups.id),
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
export const usersRelations = relations(users, ({ many }) => ({
|
|
92
|
+
groups: many(usersToGroups),
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
export const groupsRelations = relations(groups, ({ many }) => ({
|
|
96
|
+
users: many(usersToGroups),
|
|
97
|
+
}))
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Defaults e Constraints
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
plan: text('plan').default('free'),
|
|
104
|
+
active: integer('active', { mode: 'boolean' }).default(true),
|
|
105
|
+
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
106
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: riligar-dev-
|
|
2
|
+
name: riligar-dev-manager
|
|
3
3
|
description: Elysia backend development patterns for Bun. Use when building APIs, routes, plugins, validation, middleware, and error handling with Elysia framework.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -10,10 +10,7 @@ description: Elysia backend development patterns for Bun. Use when building APIs
|
|
|
10
10
|
## Mandatory Guidelines
|
|
11
11
|
|
|
12
12
|
> [!IMPORTANT]
|
|
13
|
-
> All work in this skill MUST adhere to
|
|
14
|
-
>
|
|
15
|
-
> - @[.agent/skills/riligar-dev-clean-code] (Clean Code Standards)
|
|
16
|
-
> - @[.agent/skills/riligar-tech-stack] (Tech Stack - Bun, Elysia, SQLite, Drizzle)
|
|
13
|
+
> All work in this skill MUST adhere to rules em `.agent/rules/` — clean-code, code-style, javascript-only, naming-conventions.
|
|
17
14
|
|
|
18
15
|
## Quick Reference
|
|
19
16
|
|
|
@@ -62,6 +59,16 @@ src/
|
|
|
62
59
|
└── logger.js # Request logging
|
|
63
60
|
```
|
|
64
61
|
|
|
62
|
+
## Dependencies
|
|
63
|
+
|
|
64
|
+
| Pacote | Versão | Descrição |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `bun` | latest | Runtime |
|
|
67
|
+
| `elysia` | latest | Framework HTTP |
|
|
68
|
+
| `bun:sqlite` | builtin | SQLite driver |
|
|
69
|
+
| `drizzle-orm` | latest | ORM |
|
|
70
|
+
| `bun:s3` | latest | S3/R2 Storage |
|
|
71
|
+
|
|
65
72
|
## Core Patterns
|
|
66
73
|
|
|
67
74
|
### Route Plugin
|
|
@@ -109,9 +116,7 @@ console.log(`Server running at ${app.server?.url}`)
|
|
|
109
116
|
| --- | --- |
|
|
110
117
|
| **Authentication** | @[.agent/skills/riligar-dev-auth-elysia] |
|
|
111
118
|
| **Database** | @[.agent/skills/riligar-dev-database] |
|
|
112
|
-
| **
|
|
113
|
-
| **Clean Code** | @[.agent/skills/riligar-dev-clean-code] |
|
|
114
|
-
| **Infrastructure** | @[.agent/skills/riligar-infrastructure] |
|
|
119
|
+
| **Infrastructure** | @[.agent/skills/riligar-infra-fly] |
|
|
115
120
|
|
|
116
121
|
## Decision Checklist
|
|
117
122
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: riligar-dev-
|
|
2
|
+
name: riligar-dev-website
|
|
3
3
|
description: Specialist in High-Conversion Landing Pages using RiLiGar Design System. Use for: (1) Creating marketing/sales pages, (2) Structuring conversion flows (AIDA/PAS), (3) Implementing high-trust components (Hero, Social Proof, Pricing), (4) Writing persuasive copy.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: riligar-dev-seo
|
|
2
|
+
name: riligar-dev-website-seo
|
|
3
3
|
description: Implementação de infraestrutura de SEO técnico seguindo a stack RiLiGar (React, Vite, Bun, Elysia). Use para configurar sitemaps, robots.txt, meta tags, OpenGraph, dados estruturados (JSON-LD) e URLs canônicas.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: riligar-infra-cloudfare
|
|
3
|
+
description: "Setup domains in Cloudflare with DNS, email routing, and R2 storage. Use when adding new domains, configuring DNS records, or setting up email redirects."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cloudflare Setup
|
|
7
|
+
|
|
8
|
+
Automate Cloudflare workflows: DNS setup, email routing, and R2 storage.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
### Authentication (Choose One)
|
|
13
|
+
|
|
14
|
+
**Option 1: API Token (Recommended)**
|
|
15
|
+
```bash
|
|
16
|
+
# Add to .env.local
|
|
17
|
+
CLOUDFLARE_API_TOKEN="your-api-token"
|
|
18
|
+
CLOUDFLARE_ACCOUNT_ID="your-account-id"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Create token at: https://dash.cloudflare.com/profile/api-tokens
|
|
22
|
+
Required permissions:
|
|
23
|
+
- Zone:DNS:Edit
|
|
24
|
+
- Zone:Zone:Read
|
|
25
|
+
- Email Routing Addresses:Edit
|
|
26
|
+
- Email Routing Rules:Edit
|
|
27
|
+
- Account:R2:Edit (for R2 storage)
|
|
28
|
+
|
|
29
|
+
**Option 2: Wrangler CLI**
|
|
30
|
+
```bash
|
|
31
|
+
# Install wrangler
|
|
32
|
+
bun add -g wrangler
|
|
33
|
+
|
|
34
|
+
# Login (opens browser)
|
|
35
|
+
wrangler login
|
|
36
|
+
|
|
37
|
+
# Verify
|
|
38
|
+
wrangler whoami
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Workflow
|
|
42
|
+
|
|
43
|
+
When setting up a new domain, follow these steps:
|
|
44
|
+
|
|
45
|
+
### Step 1: Gather Information
|
|
46
|
+
|
|
47
|
+
Ask the user for:
|
|
48
|
+
1. **Domain name** (e.g., `example.com`)
|
|
49
|
+
2. **Email addresses** to create (e.g., `contact`, `support`)
|
|
50
|
+
3. **Redirect target email** (e.g., `me@gmail.com`)
|
|
51
|
+
|
|
52
|
+
### Step 2: Get Zone ID
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# If using API token
|
|
56
|
+
curl -X GET "https://api.cloudflare.com/client/v4/zones?name=DOMAIN" \
|
|
57
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
58
|
+
-H "Content-Type: application/json" | jq '.result[0].id'
|
|
59
|
+
|
|
60
|
+
# If using wrangler
|
|
61
|
+
wrangler pages project list # Shows associated zones
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: Setup Email Routing
|
|
65
|
+
|
|
66
|
+
First, enable email routing for the zone (do this in Cloudflare dashboard first time).
|
|
67
|
+
|
|
68
|
+
Then create routing rules:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Create destination address (must be verified first)
|
|
72
|
+
curl -X POST "https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/email/routing/addresses" \
|
|
73
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
74
|
+
-H "Content-Type: application/json" \
|
|
75
|
+
--data '{
|
|
76
|
+
"email": "your-main-email@gmail.com"
|
|
77
|
+
}'
|
|
78
|
+
|
|
79
|
+
# Create routing rule for contact@domain.com
|
|
80
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/email/routing/rules" \
|
|
81
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
82
|
+
-H "Content-Type: application/json" \
|
|
83
|
+
--data '{
|
|
84
|
+
"name": "Forward contact",
|
|
85
|
+
"enabled": true,
|
|
86
|
+
"matchers": [{"type": "literal", "field": "to", "value": "contact@DOMAIN"}],
|
|
87
|
+
"actions": [{"type": "forward", "value": ["your-main-email@gmail.com"]}]
|
|
88
|
+
}'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Required MX records for email routing:
|
|
92
|
+
```bash
|
|
93
|
+
# MX records for Cloudflare Email Routing
|
|
94
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
95
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
96
|
+
-H "Content-Type: application/json" \
|
|
97
|
+
--data '{
|
|
98
|
+
"type": "MX",
|
|
99
|
+
"name": "@",
|
|
100
|
+
"content": "route1.mx.cloudflare.net",
|
|
101
|
+
"priority": 69,
|
|
102
|
+
"ttl": 1
|
|
103
|
+
}'
|
|
104
|
+
|
|
105
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
106
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
--data '{
|
|
109
|
+
"type": "MX",
|
|
110
|
+
"name": "@",
|
|
111
|
+
"content": "route2.mx.cloudflare.net",
|
|
112
|
+
"priority": 46,
|
|
113
|
+
"ttl": 1
|
|
114
|
+
}'
|
|
115
|
+
|
|
116
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
117
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
118
|
+
-H "Content-Type: application/json" \
|
|
119
|
+
--data '{
|
|
120
|
+
"type": "MX",
|
|
121
|
+
"name": "@",
|
|
122
|
+
"content": "route3.mx.cloudflare.net",
|
|
123
|
+
"priority": 89,
|
|
124
|
+
"ttl": 1
|
|
125
|
+
}'
|
|
126
|
+
|
|
127
|
+
# TXT record for SPF
|
|
128
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
129
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
130
|
+
-H "Content-Type: application/json" \
|
|
131
|
+
--data '{
|
|
132
|
+
"type": "TXT",
|
|
133
|
+
"name": "@",
|
|
134
|
+
"content": "v=spf1 include:_spf.mx.cloudflare.net ~all",
|
|
135
|
+
"ttl": 1
|
|
136
|
+
}'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Step 4: Verification Checklist
|
|
140
|
+
|
|
141
|
+
After setup, verify:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# List all DNS records
|
|
145
|
+
curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
146
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq '.result[] | {type, name, content}'
|
|
147
|
+
|
|
148
|
+
# Test email routing (send test email to contact@DOMAIN)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Interactive Prompts Template
|
|
152
|
+
|
|
153
|
+
When running `/cloudflare`, ask:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
What domain are you setting up?
|
|
157
|
+
> example.com
|
|
158
|
+
|
|
159
|
+
What email addresses should I create? (comma-separated)
|
|
160
|
+
> contact, support, hello
|
|
161
|
+
|
|
162
|
+
What email should these redirect to?
|
|
163
|
+
> myemail@gmail.com
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Common DNS Record Types
|
|
167
|
+
|
|
168
|
+
| Type | Use Case | Proxied |
|
|
169
|
+
|------|----------|---------|
|
|
170
|
+
| A | Root domain to IP | Yes (production) |
|
|
171
|
+
| CNAME | Subdomain to hostname | Yes (production) |
|
|
172
|
+
| TXT | Verification, SPF | N/A |
|
|
173
|
+
| MX | Email routing | N/A |
|
|
174
|
+
|
|
175
|
+
## Troubleshooting
|
|
176
|
+
|
|
177
|
+
| Issue | Solution |
|
|
178
|
+
|-------|----------|
|
|
179
|
+
| Zone not found | Domain must be added to Cloudflare first |
|
|
180
|
+
| DNS propagation slow | Wait 5-10 minutes, check with `dig` |
|
|
181
|
+
| Email not forwarding | Verify destination email first |
|
|
182
|
+
|
|
183
|
+
## Useful Commands
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Check DNS propagation
|
|
187
|
+
dig DOMAIN +short
|
|
188
|
+
dig DOMAIN MX +short
|
|
189
|
+
dig DOMAIN TXT +short
|
|
190
|
+
|
|
191
|
+
# List zones in account
|
|
192
|
+
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
|
|
193
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq '.result[] | {name, id}'
|
|
194
|
+
|
|
195
|
+
# Delete a DNS record
|
|
196
|
+
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records/RECORD_ID" \
|
|
197
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
# R2 Storage Setup
|
|
203
|
+
|
|
204
|
+
Setup R2 buckets for file storage: user uploads, static assets, backups.
|
|
205
|
+
|
|
206
|
+
## R2 Workflow
|
|
207
|
+
|
|
208
|
+
### Step 1: Determine Use Case
|
|
209
|
+
|
|
210
|
+
Ask the user:
|
|
211
|
+
```
|
|
212
|
+
What do you want to do with R2?
|
|
213
|
+
1. Create new bucket (full setup)
|
|
214
|
+
2. Configure existing bucket (CORS, public access)
|
|
215
|
+
3. Setup custom domain for bucket
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Step 2: Gather Bucket Info
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
Bucket name?
|
|
222
|
+
> my-app-uploads
|
|
223
|
+
|
|
224
|
+
What will this bucket store?
|
|
225
|
+
1. User uploads (images, files) - needs CORS + presigned URLs
|
|
226
|
+
2. Static assets (public CDN) - needs public access
|
|
227
|
+
3. Backups (private) - no public access
|
|
228
|
+
> 1
|
|
229
|
+
|
|
230
|
+
Custom domain? (optional, press enter to skip)
|
|
231
|
+
> uploads.myapp.com
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Step 3: Create Bucket
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Create bucket via wrangler
|
|
238
|
+
wrangler r2 bucket create my-app-uploads
|
|
239
|
+
|
|
240
|
+
# Or via API
|
|
241
|
+
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/r2/buckets" \
|
|
242
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
243
|
+
-H "Content-Type: application/json" \
|
|
244
|
+
--data '{"name": "my-app-uploads", "locationHint": "wnam"}'
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Step 4: Configure CORS (for user uploads)
|
|
248
|
+
|
|
249
|
+
Create `cors.json`:
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"corsRules": [
|
|
253
|
+
{
|
|
254
|
+
"allowedOrigins": ["https://myapp.com", "http://localhost:3000"],
|
|
255
|
+
"allowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
|
|
256
|
+
"allowedHeaders": ["*"],
|
|
257
|
+
"exposeHeaders": ["ETag", "Content-Length"],
|
|
258
|
+
"maxAgeSeconds": 3600
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Apply CORS:
|
|
265
|
+
```bash
|
|
266
|
+
wrangler r2 bucket cors put my-app-uploads --file=cors.json
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Step 5: Setup Public Access (for static assets)
|
|
270
|
+
|
|
271
|
+
Option A: Enable R2.dev subdomain (via dashboard)
|
|
272
|
+
- Go to R2 > Bucket > Settings > Public access
|
|
273
|
+
|
|
274
|
+
Option B: Custom domain:
|
|
275
|
+
```bash
|
|
276
|
+
# Add CNAME record
|
|
277
|
+
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
|
|
278
|
+
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
|
|
279
|
+
-H "Content-Type: application/json" \
|
|
280
|
+
--data '{
|
|
281
|
+
"type": "CNAME",
|
|
282
|
+
"name": "uploads",
|
|
283
|
+
"content": "{account_id}.r2.cloudflarestorage.com",
|
|
284
|
+
"ttl": 1,
|
|
285
|
+
"proxied": true
|
|
286
|
+
}'
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Then enable custom domain in R2 bucket settings.
|
|
290
|
+
|
|
291
|
+
### Step 6: Generate S3 API Credentials (for SDK access)
|
|
292
|
+
|
|
293
|
+
1. Go to R2 > Manage R2 API Tokens
|
|
294
|
+
2. Create token with Object Read & Write
|
|
295
|
+
3. Add to `.env.local`:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
R2_ACCESS_KEY_ID="your-access-key"
|
|
299
|
+
R2_SECRET_ACCESS_KEY="your-secret-key"
|
|
300
|
+
R2_ENDPOINT="https://{account_id}.r2.cloudflarestorage.com"
|
|
301
|
+
R2_BUCKET_NAME="my-app-uploads"
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## R2 Quick Commands
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# List buckets
|
|
308
|
+
wrangler r2 bucket list
|
|
309
|
+
|
|
310
|
+
# Create bucket
|
|
311
|
+
wrangler r2 bucket create BUCKET_NAME
|
|
312
|
+
|
|
313
|
+
# Delete bucket
|
|
314
|
+
wrangler r2 bucket delete BUCKET_NAME
|
|
315
|
+
|
|
316
|
+
# List objects
|
|
317
|
+
wrangler r2 object list BUCKET_NAME
|
|
318
|
+
|
|
319
|
+
# Upload file
|
|
320
|
+
wrangler r2 object put BUCKET_NAME/path/file.png --file=./local.png
|
|
321
|
+
|
|
322
|
+
# View CORS config
|
|
323
|
+
wrangler r2 bucket cors get BUCKET_NAME
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## R2 Use Case Presets
|
|
327
|
+
|
|
328
|
+
| Use Case | CORS | Public | Custom Domain |
|
|
329
|
+
|----------|------|--------|---------------|
|
|
330
|
+
| User uploads | Yes | No | Optional |
|
|
331
|
+
| Static assets/CDN | No | Yes | Recommended |
|
|
332
|
+
| Backups | No | No | No |
|
|
333
|
+
| Public downloads | No | Yes | Optional |
|
|
334
|
+
|
|
335
|
+
## R2 Troubleshooting
|
|
336
|
+
|
|
337
|
+
| Issue | Solution |
|
|
338
|
+
|-------|----------|
|
|
339
|
+
| CORS error in browser | Add domain to allowedOrigins |
|
|
340
|
+
| 403 Forbidden | Check API token has R2:Edit permission |
|
|
341
|
+
| Custom domain not working | Ensure CNAME is proxied (orange cloud) |
|
|
342
|
+
| Upload fails | Verify Content-Type header matches file |
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## References
|
|
347
|
+
|
|
348
|
+
- **[Cloudflare API Reference](references/cloudflare-api.md)**: Auth tokens, zone operations, DNS endpoints
|
|
349
|
+
- **[Email Routing](references/email-routing.md)**: Email forwarding setup and configuration
|
|
350
|
+
- **[R2 Storage](references/r2-storage.md)**: Bucket management, CORS, presigned URLs
|