almeira-web/scripts/rebuild_almeira_cloud.js

431 lines
21 KiB
JavaScript

const sdk = require('node-appwrite');
const axios = require('axios');
require('dotenv').config({ path: '.env.local' });
const ENDPOINT = process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || 'https://nyc.cloud.appwrite.io/v1';
const PROJECT_ID = process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID;
const API_KEY = process.env.APPWRITE_API_KEY;
const DB_ID = process.env.APPWRITE_DATABASE_ID || 'almeira';
const BUCKET_ID = process.env.NEXT_PUBLIC_APPWRITE_MEDIA_BUCKET_ID || 'media_bucket';
if (!PROJECT_ID || !API_KEY) {
console.error('❌ Faltan NEXT_PUBLIC_APPWRITE_PROJECT_ID o APPWRITE_API_KEY en .env.local');
process.exit(1);
}
const client = new sdk.Client()
.setEndpoint(ENDPOINT)
.setProject(PROJECT_ID)
.setKey(API_KEY);
const databases = new sdk.Databases(client);
const storage = new sdk.Storage(client);
const api = axios.create({
baseURL: ENDPOINT,
headers: {
'X-Appwrite-Project': PROJECT_ID,
'X-Appwrite-Key': API_KEY,
'Content-Type': 'application/json',
},
});
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
const PUBLIC_READ_WRITE = [
sdk.Permission.read(sdk.Role.any()),
sdk.Permission.create(sdk.Role.any()),
sdk.Permission.update(sdk.Role.any()),
sdk.Permission.delete(sdk.Role.any()),
];
async function ensureCollection(colId, name, permissions = PUBLIC_READ_WRITE) {
try {
await api.get(`/tablesdb/${DB_ID}/tables/${colId}`);
console.log(` ✓ Tabla/Colección '${colId}' ya existe.`);
} catch (e) {
if (e.response?.status === 404) {
try {
await api.post(`/tablesdb/${DB_ID}/tables`, {
tableId: colId,
name,
permissions,
rowSecurity: false,
enabled: true,
});
console.log(` + Tabla/Colección '${colId}' (${name}) creada.`);
await delay(300);
} catch (err) {
console.error(` ! Error creando '${colId}':`, err.response?.data?.message || err.message);
}
} else {
console.error(` ! Error verificando '${colId}':`, e.response?.data?.message || e.message);
}
}
}
async function ensureAttr(colId, type, key, opts = {}) {
try {
const payload = {
key,
required: opts.required || false,
default: opts.default !== undefined ? opts.default : null,
array: opts.array || false,
};
let routeType = type;
if (type === 'string') {
payload.size = opts.size || 255;
} else if (type === 'integer' || type === 'float') {
if (opts.min !== undefined) payload.min = opts.min;
if (opts.max !== undefined) payload.max = opts.max;
}
await api.post(`/tablesdb/${DB_ID}/tables/${colId}/columns/${routeType}`, payload);
console.log(` + Columna ${colId}.${key} (${type})`);
await delay(200);
} catch (e) {
if (e.response?.status !== 409) {
console.warn(` ! Aviso en ${colId}.${key}:`, e.response?.data?.message || e.message);
}
}
}
async function ensureDoc(colId, docId, data) {
try {
await api.post(`/tablesdb/${DB_ID}/tables/${colId}/rows`, {
rowId: docId,
data,
});
console.log(` + Registro sembrado: ${colId}/${docId}`);
} catch (e) {
if (e.response?.status !== 409) {
console.warn(` ! Aviso sembrando ${colId}/${docId}:`, e.response?.data?.message || e.message);
}
}
}
async function run() {
console.log('===================================================================');
console.log('🚀 RECONSTRUCCIÓN INTEGRAL DE ALMEIRA PRODUCCIONES EN APPWRITE');
console.log(`Endpoint: ${ENDPOINT} | Project: ${PROJECT_ID}`);
console.log('===================================================================\n');
// 1. Crear Base de Datos
try {
await databases.get(DB_ID);
console.log(`✓ Base de datos '${DB_ID}' ya existe.`);
} catch (e) {
if (e.code === 404) {
await databases.create(DB_ID, 'Almeira Producciones');
console.log(`+ Base de datos '${DB_ID}' creada exitosamente.`);
} else {
throw e;
}
}
// 2. Crear Bucket de Medios
try {
await storage.getBucket(BUCKET_ID);
console.log(`✓ Bucket '${BUCKET_ID}' ya existe.`);
} catch (e) {
if (e.code === 404) {
await storage.createBucket(
BUCKET_ID,
'Biblioteca de Medios',
PUBLIC_READ_WRITE,
false,
true,
50000000,
['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'webm', 'mov', 'jfif']
);
console.log(`+ Bucket '${BUCKET_ID}' creado exitosamente.`);
} else {
console.warn(`! Aviso en Bucket '${BUCKET_ID}':`, e.message);
}
}
// 3. Crear todas las colecciones y atributos
console.log('\n--- Configurando Colecciones y Atributos ---');
// posts
await ensureCollection('posts', 'Publicaciones');
await ensureAttr('posts', 'string', 'titulo', { size: 255, required: true });
await ensureAttr('posts', 'string', 'contenido', { size: 50000, required: true });
await ensureAttr('posts', 'string', 'resumen_redes', { size: 1000, required: false });
await ensureAttr('posts', 'string', 'tipo', { size: 50, required: false, default: 'noticia' });
await ensureAttr('posts', 'string', 'tipo_publicacion_id', { size: 255, required: false });
await ensureAttr('posts', 'string', 'estado', { size: 50, required: true });
await ensureAttr('posts', 'string', 'imagen_destacada_id', { size: 255, required: false });
await ensureAttr('posts', 'string', 'video_url', { size: 2048, required: false });
await ensureAttr('posts', 'string', 'cliente_id', { size: 255, required: false });
await ensureAttr('posts', 'datetime', 'fecha_publicacion', { required: true });
await ensureAttr('posts', 'datetime', 'fecha_finalizacion', { required: false });
await ensureAttr('posts', 'string', 'url_destino', { size: 2048, required: false });
await ensureAttr('posts', 'string', 'slug', { size: 255, required: true });
await ensureAttr('posts', 'string', 'seo_titulo', { size: 120, required: false });
await ensureAttr('posts', 'string', 'seo_descripcion', { size: 300, required: false });
await ensureAttr('posts', 'string', 'seo_keywords', { size: 500, required: false });
await ensureAttr('posts', 'string', 'og_image_url', { size: 2048, required: false });
// categories & posts_categories
await ensureCollection('categories', 'Categorías');
await ensureAttr('categories', 'string', 'nombre', { size: 100, required: true });
await ensureAttr('categories', 'string', 'slug', { size: 100, required: true });
await ensureCollection('posts_categories', 'Publicaciones Categorías');
await ensureAttr('posts_categories', 'string', 'post_id', { size: 255, required: true });
await ensureAttr('posts_categories', 'string', 'categoria_id', { size: 255, required: true });
// publication_types
await ensureCollection('publication_types', 'Tipos de Publicación');
await ensureAttr('publication_types', 'string', 'codigo', { size: 50, required: true });
await ensureAttr('publication_types', 'string', 'descripcion', { size: 255, required: true });
// client_types
await ensureCollection('client_types', 'Tipos de Cliente');
await ensureAttr('client_types', 'string', 'codigo', { size: 50, required: true });
await ensureAttr('client_types', 'string', 'descripcion', { size: 100, required: true });
// clients
await ensureCollection('clients', 'Clientes');
await ensureAttr('clients', 'string', 'nombre_entidad', { size: 255, required: true });
await ensureAttr('clients', 'string', 'contacto_nombre', { size: 255, required: true });
await ensureAttr('clients', 'string', 'numero_whatsapp', { size: 50, required: true });
await ensureAttr('clients', 'boolean', 'estado', { required: true });
await ensureAttr('clients', 'string', 'slug', { size: 255, required: true });
await ensureAttr('clients', 'string', 'seo_descripcion', { size: 255, required: false });
await ensureAttr('clients', 'string', 'nit', { size: 50, required: false });
await ensureAttr('clients', 'string', 'tipo_cliente_id', { size: 255, required: false });
await ensureAttr('clients', 'string', 'direccion', { size: 500, required: false });
await ensureAttr('clients', 'string', 'ciudad_id', { size: 255, required: false });
await ensureAttr('clients', 'string', 'correo_electronico', { size: 255, required: false });
await ensureAttr('clients', 'string', 'sitio_web', { size: 2048, required: false });
// countries, departments, cities
await ensureCollection('countries', 'Países');
await ensureAttr('countries', 'string', 'codigo', { size: 10, required: true });
await ensureAttr('countries', 'string', 'descripcion', { size: 100, required: true });
await ensureCollection('departments', 'Departamentos');
await ensureAttr('departments', 'string', 'codigo', { size: 10, required: true });
await ensureAttr('departments', 'string', 'descripcion', { size: 100, required: true });
await ensureAttr('departments', 'string', 'pais_id', { size: 255, required: true });
await ensureCollection('cities', 'Ciudades');
await ensureAttr('cities', 'string', 'codigo', { size: 10, required: true });
await ensureAttr('cities', 'string', 'descripcion', { size: 100, required: true });
await ensureAttr('cities', 'string', 'departamento_id', { size: 255, required: true });
// payment_methods, contracts, contract_details
await ensureCollection('payment_methods', 'Formas de Pago');
await ensureAttr('payment_methods', 'string', 'codigo', { size: 50, required: true });
await ensureAttr('payment_methods', 'string', 'descripcion', { size: 255, required: true });
await ensureAttr('payment_methods', 'integer', 'numero_dias', { required: true });
await ensureCollection('contracts', 'Contratos');
await ensureAttr('contracts', 'string', 'numero_contrato', { size: 100, required: true });
await ensureAttr('contracts', 'string', 'eslogan', { size: 255, required: false });
await ensureAttr('contracts', 'string', 'estado', { size: 50, required: true });
await ensureAttr('contracts', 'string', 'cliente_id', { size: 255, required: true });
await ensureAttr('contracts', 'string', 'descripcion', { size: 2000, required: true });
await ensureAttr('contracts', 'datetime', 'fecha_inicial', { required: true });
await ensureAttr('contracts', 'datetime', 'fecha_final', { required: true });
await ensureAttr('contracts', 'float', 'valor_contrato', { required: true });
await ensureAttr('contracts', 'string', 'forma_pago_id', { size: 255, required: true });
await ensureCollection('contract_details', 'Detalles de Contrato');
await ensureAttr('contract_details', 'string', 'contrato_id', { size: 255, required: true });
await ensureAttr('contract_details', 'string', 'tipo_publicacion_id', { size: 255, required: true });
await ensureAttr('contract_details', 'string', 'cantidad_periodo', { size: 50, required: true });
await ensureAttr('contract_details', 'integer', 'cantidad_total', { required: true });
// media
await ensureCollection('media', 'Medios y SEO');
await ensureAttr('media', 'string', 'file_id', { size: 255, required: true });
await ensureAttr('media', 'string', 'bucket_id', { size: 255, required: true });
await ensureAttr('media', 'string', 'titulo', { size: 255, required: true });
await ensureAttr('media', 'string', 'alt_text', { size: 500, required: false });
await ensureAttr('media', 'string', 'descripcion_seo', { size: 2000, required: false });
await ensureAttr('media', 'string', 'tipo', { size: 50, required: true });
// settings & entidad
await ensureCollection('settings', 'Configuración del Medio');
await ensureAttr('settings', 'string', 'radio_stream_url', { size: 2048, required: false });
await ensureAttr('settings', 'string', 'tv_stream_url', { size: 2048, required: false });
await ensureAttr('settings', 'string', 'whatsapp_recepcion', { size: 50, required: false });
await ensureAttr('settings', 'string', 'seo_site_name', { size: 255, required: false });
await ensureAttr('settings', 'string', 'seo_default_image', { size: 2048, required: false });
await ensureAttr('settings', 'integer', 'ticker_count', { required: false, default: 10 });
await ensureAttr('settings', 'integer', 'rotation_seconds', { required: false, default: 5 });
await ensureCollection('entidad', 'Configuración de Entidad');
await ensureAttr('entidad', 'string', 'logotipo', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'razon_social', { size: 255, required: true });
await ensureAttr('entidad', 'string', 'direccion', { size: 255, required: false });
await ensureAttr('entidad', 'string', 'numero_contacto', { size: 50, required: false });
await ensureAttr('entidad', 'string', 'numero_whatsapp', { size: 50, required: false });
await ensureAttr('entidad', 'string', 'ciudad', { size: 100, required: false });
await ensureAttr('entidad', 'string', 'sitio_web', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'email', { size: 255, required: false });
await ensureAttr('entidad', 'string', 'facebook_url', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'youtube_url', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'instagram_url', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'x_url', { size: 500, required: false });
await ensureAttr('entidad', 'string', 'telegram_url', { size: 500, required: false });
await ensureAttr('entidad', 'boolean', 'mostrar_texto_logo', { required: false, default: true });
// metrics, programming_matrices, whatsapp_messages, whatsapp_chats, live_chat, stream_state
await ensureCollection('metrics', 'Métricas');
await ensureAttr('metrics', 'string', 'publicacion_id', { size: 255, required: false });
await ensureAttr('metrics', 'string', 'post_id', { size: 255, required: false });
await ensureAttr('metrics', 'string', 'plataforma', { size: 50, required: false });
await ensureAttr('metrics', 'string', 'tipo_metrica', { size: 50, required: false });
await ensureAttr('metrics', 'integer', 'vistas', { required: false });
await ensureAttr('metrics', 'integer', 'interacciones', { required: false });
await ensureAttr('metrics', 'datetime', 'timestamp', { required: false });
await ensureCollection('programming_matrices', 'Matrices de Programación');
await ensureAttr('programming_matrices', 'string', 'media_type', { size: 50, required: true });
await ensureAttr('programming_matrices', 'string', 'matrix_data', { size: 100000, required: true });
await ensureCollection('whatsapp_messages', 'Mensajes WhatsApp');
await ensureAttr('whatsapp_messages', 'string', 'sender_name', { size: 255, required: true });
await ensureAttr('whatsapp_messages', 'string', 'phone_number', { size: 50, required: true });
await ensureAttr('whatsapp_messages', 'string', 'content', { size: 5000, required: true });
await ensureAttr('whatsapp_messages', 'datetime', 'timestamp', { required: true });
await ensureAttr('whatsapp_messages', 'string', 'status', { size: 50, required: false, default: 'pending' });
await ensureAttr('whatsapp_messages', 'string', 'platform', { size: 50, required: false, default: 'whatsapp' });
await ensureAttr('whatsapp_messages', 'string', 'avatar', { size: 1000, required: false });
await ensureCollection('whatsapp_chats', 'Chats Autorizados WhatsApp');
await ensureAttr('whatsapp_chats', 'string', 'name', { size: 255, required: true });
await ensureAttr('whatsapp_chats', 'string', 'jid', { size: 255, required: false });
await ensureAttr('whatsapp_chats', 'boolean', 'enabled', { required: false, default: true });
await ensureAttr('whatsapp_chats', 'string', 'notes', { size: 1000, required: false });
await ensureCollection('live_chat', 'Live Chat');
await ensureAttr('live_chat', 'string', 'user_id', { size: 50, required: true });
await ensureAttr('live_chat', 'string', 'user_name', { size: 100, required: true });
await ensureAttr('live_chat', 'string', 'message', { size: 500, required: true });
await ensureAttr('live_chat', 'datetime', 'timestamp', { required: true });
await ensureCollection('stream_state', 'Stream State');
await ensureAttr('stream_state', 'string', 'active_overlays', { size: 50000, required: false });
await ensureCollection('entity_config', 'Configuración de Entidad');
for (const col of ['razon_social', 'logotipo', 'ciudad', 'direccion', 'numero_contacto', 'numero_whatsapp', 'email', 'sitio_web', 'tv_url', 'radio_url', 'facebook_url', 'instagram_url', 'x_url', 'youtube_url', 'telegram_url']) {
await ensureAttr('entity_config', 'string', col, { size: 500, required: false });
}
await ensureAttr('entity_config', 'boolean', 'mostrar_texto_logo', { required: false, default: true });
await ensureCollection('verification_tokens', 'Tokens de Verificación');
await ensureAttr('verification_tokens', 'string', 'user_id', { size: 255, required: true });
await ensureAttr('verification_tokens', 'string', 'otp_code', { size: 50, required: true });
await ensureAttr('verification_tokens', 'string', 'expires_at', { size: 100, required: true });
await ensureAttr('verification_tokens', 'string', 'pending_data', { size: 5000, required: true });
console.log('\n⏳ Esperando 4 segundos para que Appwrite indexe todas las columnas...');
await delay(4000);
// 4. Sembrar Catálogos Base
console.log('\n--- Sembrando Catálogos Base de Almeira Producciones ---');
const tiposPublicacion = [
{ id: '69c2b91400362d5d52b6', codigo: 'NOTICIA', descripcion: 'Noticia' },
{ id: 't_evento', codigo: 'EVENTO', descripcion: 'Evento' },
{ id: 't_clasif', codigo: 'CLASIFICADO', descripcion: 'Clasificado' },
{ id: 't_dbanner', codigo: 'DISPLAY_BANNER', descripcion: 'Display Banner' },
{ id: 't_dslider', codigo: 'DISPLAY_SLIDER', descripcion: 'Display Slider' },
];
for (const t of tiposPublicacion) {
await ensureDoc('publication_types', t.id, { codigo: t.codigo, descripcion: t.descripcion });
}
await ensureDoc('client_types', 'ct_institucional', { codigo: 'INSTITUCIONAL', descripcion: 'Institucional' });
await ensureDoc('client_types', 'ct_comercial', { codigo: 'COMERCIAL', descripcion: 'Comercial' });
await ensureDoc('countries', 'co_colombia', { codigo: 'CO', descripcion: 'Colombia' });
await ensureDoc('departments', 'dep_cauca', { codigo: '19', descripcion: 'Cauca', pais_id: 'co_colombia' });
await ensureDoc('cities', 'cit_popayan', { codigo: '19001', descripcion: 'Popayán', departamento_id: 'dep_cauca' });
await ensureDoc('cities', 'cit_quilichao', { codigo: '19698', descripcion: 'Santander de Quilichao', departamento_id: 'dep_cauca' });
await ensureDoc('payment_methods', 'pm_contado', { codigo: 'CONTADO', descripcion: 'Contado', numero_dias: 0 });
await ensureDoc('payment_methods', 'pm_credito30', { codigo: 'CREDITO30', descripcion: 'Crédito 30 Días', numero_dias: 30 });
await ensureDoc('clients', 'cli_gob_cauca', {
nombre_entidad: 'Gobernación del Cauca',
nit: '891580016-8',
slug: 'gobernacion-del-cauca',
tipo_cliente_id: 'ct_institucional',
contacto_nombre: 'Oficina Asesora de Comunicaciones',
numero_whatsapp: '573134848203',
ciudad_id: 'cit_popayan',
estado: true,
sitio_web: 'https://www.cauca.gov.co',
});
await ensureDoc('clients', 'cli_alcaldia_quilichao', {
nombre_entidad: 'Alcaldía de Santander de Quilichao',
slug: 'alcaldia-santander-de-quilichao',
tipo_cliente_id: 'ct_institucional',
contacto_nombre: 'Prensa Alcaldía',
numero_whatsapp: '573167455877',
ciudad_id: 'cit_quilichao',
estado: true,
});
await ensureDoc('entidad', 'default', {
razon_social: 'Almeira Producciones',
ciudad: 'Santander de Quilichao - Cauca',
numero_whatsapp: '573167455877',
sitio_web: 'https://almeiraproducciones.com',
mostrar_texto_logo: true,
});
await ensureDoc('entity_config', 'main_profile_singleton', {
razon_social: 'Almeira Producciones',
ciudad: 'Santander de Quilichao - Cauca',
numero_contacto: '3167455877',
numero_whatsapp: '573167455877',
sitio_web: 'https://almeiraproducciones.com',
mostrar_texto_logo: true,
});
await ensureDoc('settings', 'global_config', {
seo_site_name: 'Almeira Producciones',
ticker_count: 10,
rotation_seconds: 5,
});
await ensureDoc('whatsapp_chats', 'wc_almeira', {
name: 'Publicaciones Almeira',
jid: '120363419329355281@g.us',
enabled: true,
notes: 'Grupo oficial de publicaciones de Almeira',
});
await ensureDoc('whatsapp_chats', 'wc_gob_cauca', {
name: 'Medios y Prensa Gob Cauca',
jid: '573134848203-1408546044@g.us',
enabled: true,
notes: 'Boletines de prensa y noticias de la Gobernación del Cauca',
});
await ensureDoc('programming_matrices', 'matrix_radio', {
media_type: 'radio',
matrix_data: '{}',
});
await ensureDoc('programming_matrices', 'matrix_tv', {
media_type: 'tv',
matrix_data: '{}',
});
console.log('\n✅ ¡Estructura y catálogos base de Almeira Producciones listos en Appwrite!');
}
run().catch((e) => {
console.error('❌ Error fatal:', e);
process.exit(1);
});