@qubiit/lmagent 2.5.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/.editorconfig +18 -0
- package/AGENTS.md +169 -0
- package/CLAUDE.md +122 -0
- package/CONTRIBUTING.md +90 -0
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/config/commands.yaml +194 -0
- package/config/levels.yaml +135 -0
- package/config/models.yaml +192 -0
- package/config/settings.yaml +405 -0
- package/config/tools-extended.yaml +534 -0
- package/config/tools.yaml +437 -0
- package/docs/assets/logo.png +0 -0
- package/docs/commands.md +132 -0
- package/docs/customization-guide.md +445 -0
- package/docs/getting-started.md +154 -0
- package/docs/how-to-start.md +242 -0
- package/docs/navigation-index.md +227 -0
- package/docs/usage-guide.md +113 -0
- package/install.js +1044 -0
- package/package.json +35 -0
- package/pyproject.toml +182 -0
- package/rules/_bootstrap.md +138 -0
- package/rules/agents-ia.md +607 -0
- package/rules/api-design.md +337 -0
- package/rules/automations-n8n.md +646 -0
- package/rules/code-style.md +570 -0
- package/rules/documentation.md +98 -0
- package/rules/security.md +316 -0
- package/rules/stack.md +395 -0
- package/rules/testing.md +326 -0
- package/rules/workflow.md +353 -0
- package/scripts/create_skill.js +300 -0
- package/scripts/validate_skills.js +283 -0
- package/skills/ai-agent-engineer/SKILL.md +394 -0
- package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
- package/skills/api-designer/SKILL.md +429 -0
- package/skills/api-designer/references/api-standards.md +13 -0
- package/skills/architect/SKILL.md +285 -0
- package/skills/architect/references/c4-model.md +133 -0
- package/skills/automation-engineer/SKILL.md +352 -0
- package/skills/automation-engineer/references/n8n-patterns.md +127 -0
- package/skills/backend-engineer/SKILL.md +261 -0
- package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
- package/skills/backend-engineer/references/debugging-guide.md +174 -0
- package/skills/backend-engineer/references/design-patterns.md +208 -0
- package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
- package/skills/bmad-methodology/SKILL.md +202 -0
- package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
- package/skills/browser-agent/SKILL.md +502 -0
- package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
- package/skills/code-reviewer/SKILL.md +306 -0
- package/skills/code-reviewer/references/code-review-checklist.md +16 -0
- package/skills/data-engineer/SKILL.md +474 -0
- package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
- package/skills/data-engineer/references/index-strategy.md +128 -0
- package/skills/data-engineer/scripts/backup_postgres.py +221 -0
- package/skills/devops-engineer/SKILL.md +547 -0
- package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
- package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
- package/skills/document-generator/SKILL.md +746 -0
- package/skills/document-generator/references/pdf-generation.md +22 -0
- package/skills/frontend-engineer/SKILL.md +532 -0
- package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
- package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
- package/skills/git-workflow/SKILL.md +374 -0
- package/skills/git-workflow/references/git-flow.md +25 -0
- package/skills/mcp-builder/SKILL.md +471 -0
- package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
- package/skills/mobile-engineer/SKILL.md +502 -0
- package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
- package/skills/orchestrator/SKILL.md +246 -0
- package/skills/orchestrator/references/methodology-routing.md +117 -0
- package/skills/orchestrator/references/persona-mapping.md +85 -0
- package/skills/orchestrator/references/routing-logic.md +110 -0
- package/skills/performance-engineer/SKILL.md +549 -0
- package/skills/performance-engineer/references/caching-patterns.md +181 -0
- package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
- package/skills/product-manager/SKILL.md +488 -0
- package/skills/product-manager/references/prioritization-frameworks.md +126 -0
- package/skills/prompt-engineer/SKILL.md +433 -0
- package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
- package/skills/qa-engineer/SKILL.md +441 -0
- package/skills/qa-engineer/references/testing-strategy.md +166 -0
- package/skills/qa-engineer/scripts/run_coverage.py +147 -0
- package/skills/scrum-master/SKILL.md +225 -0
- package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
- package/skills/security-analyst/SKILL.md +390 -0
- package/skills/security-analyst/references/owasp-top10.md +188 -0
- package/skills/security-analyst/scripts/audit_security.py +242 -0
- package/skills/seo-auditor/SKILL.md +523 -0
- package/skills/seo-auditor/references/seo-checklist.md +17 -0
- package/skills/spec-driven-dev/SKILL.md +342 -0
- package/skills/spec-driven-dev/references/phase-gates.md +107 -0
- package/skills/supabase-expert/SKILL.md +602 -0
- package/skills/supabase-expert/references/supabase-patterns.md +19 -0
- package/skills/swe-agent/SKILL.md +311 -0
- package/skills/swe-agent/references/trajectory-format.md +134 -0
- package/skills/systematic-debugger/SKILL.md +512 -0
- package/skills/systematic-debugger/references/debugging-guide.md +12 -0
- package/skills/tech-lead/SKILL.md +409 -0
- package/skills/tech-lead/references/code-review-checklist.md +111 -0
- package/skills/technical-writer/SKILL.md +631 -0
- package/skills/technical-writer/references/doc-templates.md +218 -0
- package/skills/testing-strategist/SKILL.md +476 -0
- package/skills/testing-strategist/references/testing-pyramid.md +16 -0
- package/skills/ux-ui-designer/SKILL.md +419 -0
- package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
- package/skills_overview.txt +94 -0
- package/templates/PROJECT_KICKOFF.md +284 -0
- package/templates/SKILL_TEMPLATE.md +131 -0
- package/templates/USAGE.md +95 -0
- package/templates/agent-python/README.md +71 -0
- package/templates/agent-python/agent.py +272 -0
- package/templates/agent-python/config.yaml +76 -0
- package/templates/agent-python/prompts/system.md +109 -0
- package/templates/agent-python/requirements.txt +7 -0
- package/templates/automation-n8n/README.md +14 -0
- package/templates/automation-n8n/webhook-handler.json +57 -0
- package/templates/backend-node/Dockerfile +12 -0
- package/templates/backend-node/README.md +15 -0
- package/templates/backend-node/package.json +30 -0
- package/templates/backend-node/src/index.ts +19 -0
- package/templates/backend-node/src/routes.ts +7 -0
- package/templates/backend-node/tsconfig.json +22 -0
- package/templates/backend-python/Dockerfile +11 -0
- package/templates/backend-python/README.md +78 -0
- package/templates/backend-python/app/core/config.py +12 -0
- package/templates/backend-python/app/core/database.py +12 -0
- package/templates/backend-python/app/main.py +17 -0
- package/templates/backend-python/app/routers/__init__.py +1 -0
- package/templates/backend-python/app/routers/health.py +7 -0
- package/templates/backend-python/requirements-dev.txt +6 -0
- package/templates/backend-python/requirements.txt +4 -0
- package/templates/backend-python/tests/test_health.py +9 -0
- package/templates/checkpoint.yaml +117 -0
- package/templates/database/README.md +474 -0
- package/templates/frontend-react/README.md +446 -0
- package/templates/plan.yaml +320 -0
- package/templates/session.yaml +125 -0
- package/templates/spec.yaml +229 -0
- package/templates/tasks.yaml +330 -0
- package/workflows/bugfix-backend.md +380 -0
- package/workflows/documentation.md +232 -0
- package/workflows/generate-prd.md +320 -0
- package/workflows/ideation.md +396 -0
- package/workflows/new-agent-ia.md +497 -0
- package/workflows/new-automation.md +374 -0
- package/workflows/new-feature.md +290 -0
- package/workflows/optimize-performance.md +373 -0
- package/workflows/resolve-github-issue.md +524 -0
- package/workflows/security-review.md +291 -0
- package/workflows/spec-driven.md +476 -0
- package/workflows/testing-strategy.md +296 -0
- package/workflows/third-party-integration.md +277 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# LMAgent Database Template
|
|
2
|
+
|
|
3
|
+
Template para diseño e implementación de bases de datos PostgreSQL con migraciones.
|
|
4
|
+
|
|
5
|
+
## Estructura del Proyecto
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
database/
|
|
9
|
+
├── migrations/ # Migraciones versionadas
|
|
10
|
+
│ ├── 001_initial_schema.sql
|
|
11
|
+
│ ├── 002_add_users.sql
|
|
12
|
+
│ └── 003_add_indexes.sql
|
|
13
|
+
│
|
|
14
|
+
├── schemas/ # Definiciones de schema
|
|
15
|
+
│ ├── tables/ # Definiciones de tablas
|
|
16
|
+
│ │ ├── users.sql
|
|
17
|
+
│ │ ├── roles.sql
|
|
18
|
+
│ │ └── audit_logs.sql
|
|
19
|
+
│ ├── views/ # Views
|
|
20
|
+
│ │ └── active_users_v.sql
|
|
21
|
+
│ ├── functions/ # Stored functions
|
|
22
|
+
│ │ └── update_timestamp.sql
|
|
23
|
+
│ └── triggers/ # Triggers
|
|
24
|
+
│ └── audit_trigger.sql
|
|
25
|
+
│
|
|
26
|
+
├── seeds/ # Datos iniciales
|
|
27
|
+
│ ├── 01_roles.sql
|
|
28
|
+
│ └── 02_admin_user.sql
|
|
29
|
+
│
|
|
30
|
+
├── queries/ # Queries comunes documentadas
|
|
31
|
+
│ ├── reports/
|
|
32
|
+
│ │ └── user_activity.sql
|
|
33
|
+
│ └── maintenance/
|
|
34
|
+
│ └── vacuum_analyze.sql
|
|
35
|
+
│
|
|
36
|
+
├── diagrams/ # Diagramas ERD
|
|
37
|
+
│ └── erd.mmd # Mermaid diagram
|
|
38
|
+
│
|
|
39
|
+
├── scripts/ # Scripts de utilidad
|
|
40
|
+
│ ├── backup.sh
|
|
41
|
+
│ ├── restore.sh
|
|
42
|
+
│ └── migrate.py
|
|
43
|
+
│
|
|
44
|
+
└── README.md
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Schema Base
|
|
48
|
+
|
|
49
|
+
### Tabla de Usuarios
|
|
50
|
+
|
|
51
|
+
```sql
|
|
52
|
+
-- schemas/tables/users.sql
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
55
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
56
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
57
|
+
email_verified BOOLEAN DEFAULT FALSE,
|
|
58
|
+
password_hash VARCHAR(255),
|
|
59
|
+
|
|
60
|
+
-- Profile
|
|
61
|
+
first_name VARCHAR(100),
|
|
62
|
+
last_name VARCHAR(100),
|
|
63
|
+
avatar_url TEXT,
|
|
64
|
+
|
|
65
|
+
-- Status
|
|
66
|
+
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'suspended', 'deleted')),
|
|
67
|
+
role_id UUID REFERENCES roles(id),
|
|
68
|
+
|
|
69
|
+
-- Metadata
|
|
70
|
+
metadata JSONB DEFAULT '{}',
|
|
71
|
+
preferences JSONB DEFAULT '{}',
|
|
72
|
+
|
|
73
|
+
-- Timestamps
|
|
74
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
75
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
76
|
+
last_login_at TIMESTAMPTZ,
|
|
77
|
+
deleted_at TIMESTAMPTZ,
|
|
78
|
+
|
|
79
|
+
-- Constraints
|
|
80
|
+
CONSTRAINT email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
-- Índices
|
|
84
|
+
CREATE INDEX idx_users_email ON users(email);
|
|
85
|
+
CREATE INDEX idx_users_status ON users(status) WHERE status != 'deleted';
|
|
86
|
+
CREATE INDEX idx_users_role ON users(role_id);
|
|
87
|
+
CREATE INDEX idx_users_created ON users(created_at DESC);
|
|
88
|
+
|
|
89
|
+
-- Índice para búsqueda full-text
|
|
90
|
+
CREATE INDEX idx_users_fulltext ON users USING GIN (
|
|
91
|
+
to_tsvector('spanish', coalesce(first_name, '') || ' ' || coalesce(last_name, '') || ' ' || email)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
COMMENT ON TABLE users IS 'Tabla principal de usuarios del sistema';
|
|
95
|
+
COMMENT ON COLUMN users.metadata IS 'Datos adicionales flexibles en formato JSON';
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Tabla de Roles
|
|
99
|
+
|
|
100
|
+
```sql
|
|
101
|
+
-- schemas/tables/roles.sql
|
|
102
|
+
|
|
103
|
+
CREATE TABLE IF NOT EXISTS roles (
|
|
104
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
105
|
+
name VARCHAR(50) NOT NULL UNIQUE,
|
|
106
|
+
display_name VARCHAR(100) NOT NULL,
|
|
107
|
+
description TEXT,
|
|
108
|
+
|
|
109
|
+
-- Permisos como JSONB
|
|
110
|
+
permissions JSONB NOT NULL DEFAULT '[]',
|
|
111
|
+
|
|
112
|
+
-- Flags
|
|
113
|
+
is_system BOOLEAN DEFAULT FALSE, -- No editable por usuarios
|
|
114
|
+
is_default BOOLEAN DEFAULT FALSE, -- Rol por defecto para nuevos usuarios
|
|
115
|
+
|
|
116
|
+
-- Timestamps
|
|
117
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
118
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
-- Índices
|
|
122
|
+
CREATE INDEX idx_roles_name ON roles(name);
|
|
123
|
+
CREATE INDEX idx_roles_is_default ON roles(is_default) WHERE is_default = TRUE;
|
|
124
|
+
|
|
125
|
+
-- Datos iniciales
|
|
126
|
+
INSERT INTO roles (name, display_name, description, permissions, is_system, is_default) VALUES
|
|
127
|
+
('admin', 'Administrador', 'Acceso completo al sistema', '["*"]', TRUE, FALSE),
|
|
128
|
+
('user', 'Usuario', 'Usuario estándar', '["read", "write"]', TRUE, TRUE),
|
|
129
|
+
('viewer', 'Visor', 'Solo lectura', '["read"]', TRUE, FALSE);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Tabla de Auditoría
|
|
133
|
+
|
|
134
|
+
```sql
|
|
135
|
+
-- schemas/tables/audit_logs.sql
|
|
136
|
+
|
|
137
|
+
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
138
|
+
id BIGSERIAL PRIMARY KEY,
|
|
139
|
+
|
|
140
|
+
-- Qué cambió
|
|
141
|
+
table_name VARCHAR(100) NOT NULL,
|
|
142
|
+
record_id UUID NOT NULL,
|
|
143
|
+
action VARCHAR(10) NOT NULL CHECK (action IN ('INSERT', 'UPDATE', 'DELETE')),
|
|
144
|
+
|
|
145
|
+
-- Datos del cambio
|
|
146
|
+
old_values JSONB,
|
|
147
|
+
new_values JSONB,
|
|
148
|
+
changed_fields TEXT[],
|
|
149
|
+
|
|
150
|
+
-- Quién y cuándo
|
|
151
|
+
user_id UUID REFERENCES users(id),
|
|
152
|
+
ip_address INET,
|
|
153
|
+
user_agent TEXT,
|
|
154
|
+
|
|
155
|
+
-- Timestamp
|
|
156
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
-- Particionamiento por mes para mejor performance
|
|
160
|
+
CREATE INDEX idx_audit_created ON audit_logs(created_at);
|
|
161
|
+
CREATE INDEX idx_audit_table_record ON audit_logs(table_name, record_id);
|
|
162
|
+
CREATE INDEX idx_audit_user ON audit_logs(user_id);
|
|
163
|
+
|
|
164
|
+
-- Partición automática (PostgreSQL 14+)
|
|
165
|
+
-- ALTER TABLE audit_logs PARTITION BY RANGE (created_at);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Functions y Triggers
|
|
169
|
+
|
|
170
|
+
### Update Timestamp Automático
|
|
171
|
+
|
|
172
|
+
```sql
|
|
173
|
+
-- schemas/functions/update_timestamp.sql
|
|
174
|
+
|
|
175
|
+
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
176
|
+
RETURNS TRIGGER AS $$
|
|
177
|
+
BEGIN
|
|
178
|
+
NEW.updated_at = NOW();
|
|
179
|
+
RETURN NEW;
|
|
180
|
+
END;
|
|
181
|
+
$$ LANGUAGE plpgsql;
|
|
182
|
+
|
|
183
|
+
-- Aplicar a todas las tablas con updated_at
|
|
184
|
+
CREATE TRIGGER tr_users_updated_at
|
|
185
|
+
BEFORE UPDATE ON users
|
|
186
|
+
FOR EACH ROW
|
|
187
|
+
EXECUTE FUNCTION update_updated_at();
|
|
188
|
+
|
|
189
|
+
CREATE TRIGGER tr_roles_updated_at
|
|
190
|
+
BEFORE UPDATE ON roles
|
|
191
|
+
FOR EACH ROW
|
|
192
|
+
EXECUTE FUNCTION update_updated_at();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Trigger de Auditoría
|
|
196
|
+
|
|
197
|
+
```sql
|
|
198
|
+
-- schemas/triggers/audit_trigger.sql
|
|
199
|
+
|
|
200
|
+
CREATE OR REPLACE FUNCTION audit_trigger_func()
|
|
201
|
+
RETURNS TRIGGER AS $$
|
|
202
|
+
DECLARE
|
|
203
|
+
old_data JSONB;
|
|
204
|
+
new_data JSONB;
|
|
205
|
+
changed_cols TEXT[];
|
|
206
|
+
BEGIN
|
|
207
|
+
IF TG_OP = 'DELETE' THEN
|
|
208
|
+
old_data = to_jsonb(OLD);
|
|
209
|
+
|
|
210
|
+
INSERT INTO audit_logs (table_name, record_id, action, old_values, user_id)
|
|
211
|
+
VALUES (TG_TABLE_NAME, OLD.id, 'DELETE', old_data, current_setting('app.current_user_id', TRUE)::UUID);
|
|
212
|
+
|
|
213
|
+
RETURN OLD;
|
|
214
|
+
|
|
215
|
+
ELSIF TG_OP = 'UPDATE' THEN
|
|
216
|
+
old_data = to_jsonb(OLD);
|
|
217
|
+
new_data = to_jsonb(NEW);
|
|
218
|
+
|
|
219
|
+
-- Detectar columnas cambiadas
|
|
220
|
+
SELECT array_agg(key) INTO changed_cols
|
|
221
|
+
FROM jsonb_each(new_data)
|
|
222
|
+
WHERE new_data -> key IS DISTINCT FROM old_data -> key;
|
|
223
|
+
|
|
224
|
+
INSERT INTO audit_logs (table_name, record_id, action, old_values, new_values, changed_fields, user_id)
|
|
225
|
+
VALUES (TG_TABLE_NAME, NEW.id, 'UPDATE', old_data, new_data, changed_cols, current_setting('app.current_user_id', TRUE)::UUID);
|
|
226
|
+
|
|
227
|
+
RETURN NEW;
|
|
228
|
+
|
|
229
|
+
ELSIF TG_OP = 'INSERT' THEN
|
|
230
|
+
new_data = to_jsonb(NEW);
|
|
231
|
+
|
|
232
|
+
INSERT INTO audit_logs (table_name, record_id, action, new_values, user_id)
|
|
233
|
+
VALUES (TG_TABLE_NAME, NEW.id, 'INSERT', new_data, current_setting('app.current_user_id', TRUE)::UUID);
|
|
234
|
+
|
|
235
|
+
RETURN NEW;
|
|
236
|
+
END IF;
|
|
237
|
+
|
|
238
|
+
RETURN NULL;
|
|
239
|
+
END;
|
|
240
|
+
$$ LANGUAGE plpgsql;
|
|
241
|
+
|
|
242
|
+
-- Aplicar a tablas que requieren auditoría
|
|
243
|
+
CREATE TRIGGER tr_users_audit
|
|
244
|
+
AFTER INSERT OR UPDATE OR DELETE ON users
|
|
245
|
+
FOR EACH ROW
|
|
246
|
+
EXECUTE FUNCTION audit_trigger_func();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Migraciones
|
|
250
|
+
|
|
251
|
+
### Ejemplo de Migración
|
|
252
|
+
|
|
253
|
+
```sql
|
|
254
|
+
-- migrations/001_initial_schema.sql
|
|
255
|
+
-- Migration: Initial Schema
|
|
256
|
+
-- Created: 2024-01-01
|
|
257
|
+
-- Author: DBA Engineer
|
|
258
|
+
|
|
259
|
+
BEGIN;
|
|
260
|
+
|
|
261
|
+
-- Verificar que no existe
|
|
262
|
+
DO $$
|
|
263
|
+
BEGIN
|
|
264
|
+
IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'users') THEN
|
|
265
|
+
RAISE EXCEPTION 'Migration already applied';
|
|
266
|
+
END IF;
|
|
267
|
+
END $$;
|
|
268
|
+
|
|
269
|
+
-- Crear extensiones necesarias
|
|
270
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
271
|
+
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
272
|
+
|
|
273
|
+
-- Crear tablas (ejecutar en orden de dependencias)
|
|
274
|
+
\i schemas/tables/roles.sql
|
|
275
|
+
\i schemas/tables/users.sql
|
|
276
|
+
\i schemas/tables/audit_logs.sql
|
|
277
|
+
|
|
278
|
+
-- Crear functions y triggers
|
|
279
|
+
\i schemas/functions/update_timestamp.sql
|
|
280
|
+
\i schemas/triggers/audit_trigger.sql
|
|
281
|
+
|
|
282
|
+
-- Registrar migración
|
|
283
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
284
|
+
id SERIAL PRIMARY KEY,
|
|
285
|
+
name VARCHAR(255) NOT NULL UNIQUE,
|
|
286
|
+
applied_at TIMESTAMPTZ DEFAULT NOW()
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
INSERT INTO _migrations (name) VALUES ('001_initial_schema');
|
|
290
|
+
|
|
291
|
+
COMMIT;
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Script de Migración (Python)
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
#!/usr/bin/env python3
|
|
298
|
+
# scripts/migrate.py
|
|
299
|
+
|
|
300
|
+
"""
|
|
301
|
+
Database migration script for LMAgent.
|
|
302
|
+
|
|
303
|
+
Usage:
|
|
304
|
+
python migrate.py up # Apply all pending migrations
|
|
305
|
+
python migrate.py down # Rollback last migration
|
|
306
|
+
python migrate.py status # Show migration status
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
import os
|
|
310
|
+
import sys
|
|
311
|
+
from pathlib import Path
|
|
312
|
+
import psycopg2
|
|
313
|
+
from psycopg2.extras import RealDictCursor
|
|
314
|
+
|
|
315
|
+
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://localhost/lmagent")
|
|
316
|
+
MIGRATIONS_DIR = Path(__file__).parent.parent / "migrations"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def get_connection():
|
|
320
|
+
return psycopg2.connect(DATABASE_URL, cursor_factory=RealDictCursor)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def get_applied_migrations(conn):
|
|
324
|
+
with conn.cursor() as cur:
|
|
325
|
+
cur.execute("""
|
|
326
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
327
|
+
id SERIAL PRIMARY KEY,
|
|
328
|
+
name VARCHAR(255) NOT NULL UNIQUE,
|
|
329
|
+
applied_at TIMESTAMPTZ DEFAULT NOW()
|
|
330
|
+
)
|
|
331
|
+
""")
|
|
332
|
+
cur.execute("SELECT name FROM _migrations ORDER BY id")
|
|
333
|
+
return {row['name'] for row in cur.fetchall()}
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def get_pending_migrations(applied):
|
|
337
|
+
all_migrations = sorted(MIGRATIONS_DIR.glob("*.sql"))
|
|
338
|
+
return [m for m in all_migrations if m.stem not in applied]
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def apply_migration(conn, migration_file):
|
|
342
|
+
print(f"Applying: {migration_file.name}")
|
|
343
|
+
|
|
344
|
+
with open(migration_file) as f:
|
|
345
|
+
sql = f.read()
|
|
346
|
+
|
|
347
|
+
with conn.cursor() as cur:
|
|
348
|
+
cur.execute(sql)
|
|
349
|
+
cur.execute(
|
|
350
|
+
"INSERT INTO _migrations (name) VALUES (%s)",
|
|
351
|
+
(migration_file.stem,)
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
conn.commit()
|
|
355
|
+
print(f"✓ Applied: {migration_file.name}")
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def main():
|
|
359
|
+
if len(sys.argv) < 2:
|
|
360
|
+
print("Usage: migrate.py [up|down|status]")
|
|
361
|
+
sys.exit(1)
|
|
362
|
+
|
|
363
|
+
command = sys.argv[1]
|
|
364
|
+
|
|
365
|
+
with get_connection() as conn:
|
|
366
|
+
applied = get_applied_migrations(conn)
|
|
367
|
+
pending = get_pending_migrations(applied)
|
|
368
|
+
|
|
369
|
+
if command == "status":
|
|
370
|
+
print(f"Applied: {len(applied)}")
|
|
371
|
+
print(f"Pending: {len(pending)}")
|
|
372
|
+
for m in pending:
|
|
373
|
+
print(f" - {m.name}")
|
|
374
|
+
|
|
375
|
+
elif command == "up":
|
|
376
|
+
if not pending:
|
|
377
|
+
print("No pending migrations")
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
for migration in pending:
|
|
381
|
+
apply_migration(conn, migration)
|
|
382
|
+
|
|
383
|
+
print(f"\n✓ Applied {len(pending)} migration(s)")
|
|
384
|
+
|
|
385
|
+
elif command == "down":
|
|
386
|
+
print("Rollback not implemented - use manual SQL")
|
|
387
|
+
|
|
388
|
+
else:
|
|
389
|
+
print(f"Unknown command: {command}")
|
|
390
|
+
sys.exit(1)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
if __name__ == "__main__":
|
|
394
|
+
main()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Diagrama ERD
|
|
398
|
+
|
|
399
|
+
```mermaid
|
|
400
|
+
erDiagram
|
|
401
|
+
USERS ||--o{ AUDIT_LOGS : generates
|
|
402
|
+
ROLES ||--o{ USERS : has
|
|
403
|
+
|
|
404
|
+
USERS {
|
|
405
|
+
uuid id PK
|
|
406
|
+
varchar email UK
|
|
407
|
+
boolean email_verified
|
|
408
|
+
varchar password_hash
|
|
409
|
+
varchar first_name
|
|
410
|
+
varchar last_name
|
|
411
|
+
text avatar_url
|
|
412
|
+
varchar status
|
|
413
|
+
uuid role_id FK
|
|
414
|
+
jsonb metadata
|
|
415
|
+
jsonb preferences
|
|
416
|
+
timestamptz created_at
|
|
417
|
+
timestamptz updated_at
|
|
418
|
+
timestamptz last_login_at
|
|
419
|
+
timestamptz deleted_at
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
ROLES {
|
|
423
|
+
uuid id PK
|
|
424
|
+
varchar name UK
|
|
425
|
+
varchar display_name
|
|
426
|
+
text description
|
|
427
|
+
jsonb permissions
|
|
428
|
+
boolean is_system
|
|
429
|
+
boolean is_default
|
|
430
|
+
timestamptz created_at
|
|
431
|
+
timestamptz updated_at
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
AUDIT_LOGS {
|
|
435
|
+
bigint id PK
|
|
436
|
+
varchar table_name
|
|
437
|
+
uuid record_id
|
|
438
|
+
varchar action
|
|
439
|
+
jsonb old_values
|
|
440
|
+
jsonb new_values
|
|
441
|
+
text[] changed_fields
|
|
442
|
+
uuid user_id FK
|
|
443
|
+
inet ip_address
|
|
444
|
+
text user_agent
|
|
445
|
+
timestamptz created_at
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Best Practices
|
|
450
|
+
|
|
451
|
+
### Naming Conventions
|
|
452
|
+
|
|
453
|
+
| Tipo | Convención | Ejemplo |
|
|
454
|
+
|------|------------|---------|
|
|
455
|
+
| Tablas | snake_case, plural | `users`, `audit_logs` |
|
|
456
|
+
| Columnas | snake_case | `first_name`, `created_at` |
|
|
457
|
+
| Índices | `idx_{table}_{columns}` | `idx_users_email` |
|
|
458
|
+
| Foreign Keys | `fk_{table}_{ref_table}` | `fk_users_roles` |
|
|
459
|
+
| Constraints | `chk_{table}_{column}` | `chk_users_status` |
|
|
460
|
+
| Triggers | `tr_{table}_{action}` | `tr_users_audit` |
|
|
461
|
+
| Functions | verb_noun | `update_timestamp()` |
|
|
462
|
+
|
|
463
|
+
### Checklist de Diseño
|
|
464
|
+
|
|
465
|
+
- [ ] Usar UUID para PKs (mejor para distribución)
|
|
466
|
+
- [ ] Incluir `created_at`, `updated_at` en todas las tablas
|
|
467
|
+
- [ ] Soft delete con `deleted_at` en vez de DELETE físico
|
|
468
|
+
- [ ] Índices en FKs y columnas de búsqueda frecuente
|
|
469
|
+
- [ ] JSONB para datos flexibles, no para datos relacionales
|
|
470
|
+
- [ ] Constraints CHECK para validaciones de dominio
|
|
471
|
+
- [ ] Comentarios en tablas y columnas importantes
|
|
472
|
+
- [ ] Migraciones versionadas y atómicas
|
|
473
|
+
- [ ] Auditoría para datos sensibles
|
|
474
|
+
- [ ] Particionamiento para tablas de alto volumen
|