Add support for sqlite

This commit is contained in:
2026-06-07 23:58:53 +02:00
parent 1b67900d54
commit bd4282c426
7 changed files with 1946 additions and 1129 deletions
+143
View File
@@ -0,0 +1,143 @@
// Local SQLite API instead of Firebase
const API_URL = "/api";
export async function suscribirLocales(onData, onError) {
try {
const response = await fetch(`${API_URL}/locales`);
const data = await response.json();
onData(data);
// Simulate real-time by polling every 5 seconds
const interval = setInterval(async () => {
try {
const res = await fetch(`${API_URL}/locales`);
const newData = await res.json();
onData(newData);
} catch (err) {
console.error(err);
}
}, 5000);
return () => clearInterval(interval);
} catch (err) {
onError(err);
}
}
export async function suscribirPendientes(onData, onError) {
try {
const response = await fetch(`${API_URL}/pendientes`);
const data = await response.json();
onData(data);
// Simulate real-time by polling every 5 seconds
const interval = setInterval(async () => {
try {
const res = await fetch(`${API_URL}/pendientes`);
const newData = await res.json();
onData(newData);
} catch (err) {
console.error(err);
}
}, 5000);
return () => clearInterval(interval);
} catch (err) {
onError(err);
}
}
export async function enviarPropuesta(local) {
const { id: _x, ...datos } = local;
const response = await fetch(`${API_URL}/propuestas`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(datos),
});
return response.json();
}
export async function aprobarPropuesta(p) {
const response = await fetch(`${API_URL}/aprobar/${p.id}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
});
return response.json();
}
export async function rechazarPropuesta(id) {
const response = await fetch(`${API_URL}/rechazar/${id}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
});
return response.json();
}
export async function deleteLocal(id) {
const response = await fetch(`${API_URL}/locales/${id}`, {
method: "DELETE",
});
return response.json();
}
export async function obtenerPalabrasFiltradas() {
try {
const response = await fetch(`${API_URL}/config/filtro_palabras`);
const data = await response.json();
return data.palabras || [];
} catch {
return [];
}
}
export async function guardarPalabrasFiltradas(palabras) {
const response = await fetch(`${API_URL}/config/filtro_palabras`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ palabras }),
});
return response.json();
}
export function contienepalabrasProhibidas(texto, lista) {
if (!lista?.length) return false;
const norm = s => s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "");
const t = norm(texto);
return lista.some(p => t.includes(norm(p)));
}
// Auth helpers (local implementation)
let currentUser = null;
export function escucharAuth(cb) {
// Simple localStorage-based auth
const user = localStorage.getItem("user");
currentUser = user ? JSON.parse(user) : null;
cb(currentUser);
// Listen for storage changes
const handleStorageChange = () => {
const user = localStorage.getItem("user");
currentUser = user ? JSON.parse(user) : null;
cb(currentUser);
};
window.addEventListener("storage", handleStorageChange);
return () => window.removeEventListener("storage", handleStorageChange);
}
export async function loginAdmin(email, password) {
// Simple email/password check (in production use proper auth)
if (email && password) {
const user = { email, uid: email };
localStorage.setItem("user", JSON.stringify(user));
currentUser = user;
return user;
}
throw new Error("Invalid email or password");
}
export async function registrarAdmin(email, password) {
return loginAdmin(email, password);
}
export async function cerrarSesion() {
localStorage.removeItem("user");
currentUser = null;
}
+16 -112
View File
@@ -1,112 +1,16 @@
// firebase.js
// ─────────────────────────────────────────────────────────────────────────────
// PASO 1 — Pega tu config de Firebase (Configuración ⚙️ → Tu app → Web)
// PASO 2 — Activa Authentication → Sign-in method → Email/Password
// PASO 3 — Crea Firestore Database en modo producción (región: eur3)
// PASO 4 — Pega estas Reglas en Firestore → Reglas y publica:
//
// rules_version = '2';
// service cloud.firestore {
// match /databases/{database}/documents {
// // Locales aprobados: solo lectura pública, escritura solo autenticados
// match /locales/{id} {
// allow read: if true;
// allow write: if request.auth != null;
// }
// // Propuestas pendientes: cualquiera puede crear, solo admin gestiona
// match /pendientes/{id} {
// allow create: if true;
// allow read, update, delete: if request.auth != null;
// }
// // Config (filtro de palabras): lectura pública, escritura solo admin
// match /config/{id} {
// allow read: if true;
// allow write: if request.auth != null;
// }
// }
// }
// ─────────────────────────────────────────────────────────────────────────────
import { initializeApp } from "firebase/app";
import {
getFirestore, collection, addDoc, deleteDoc,
doc, onSnapshot, query, orderBy, getDoc, setDoc,
} from "firebase/firestore";
import {
getAuth, signInWithEmailAndPassword, createUserWithEmailAndPassword,
signOut, onAuthStateChanged,
} from "firebase/auth";
// ▼▼▼ PEGA AQUÍ TU CONFIG DE FIREBASE ▼▼▼
const firebaseConfig = {
apiKey: "PEGA_TU_API_KEY",
authDomain: "PEGA_TU_AUTH_DOMAIN",
projectId: "PEGA_TU_PROJECT_ID",
storageBucket: "PEGA_TU_STORAGE_BUCKET",
messagingSenderId: "PEGA_TU_SENDER_ID",
appId: "PEGA_TU_APP_ID",
};
// ▲▲▲ ─────────────────────────────── ▲▲▲
const fbApp = initializeApp(firebaseConfig);
export const db = getFirestore(fbApp);
export const auth = getAuth(fbApp);
// ── Auth ──────────────────────────────────────────────────────────────────────
export const escucharAuth = (cb) => onAuthStateChanged(auth, cb);
export const loginAdmin = (e, p) => signInWithEmailAndPassword(auth, e, p);
export const registrarAdmin = (e, p) => createUserWithEmailAndPassword(auth, e, p);
export const cerrarSesion = () => signOut(auth);
// ── Locales aprobados (público, solo lectura) ─────────────────────────────────
export function suscribirLocales(onData, onError) {
const q = query(collection(db, "locales"), orderBy("fecha", "desc"));
return onSnapshot(q, snap => onData(snap.docs.map(d => ({ id: d.id, ...d.data() }))), onError);
}
export const deleteLocal = (id) => deleteDoc(doc(db, "locales", id));
// ── Propuestas pendientes ─────────────────────────────────────────────────────
/** Cualquier visitante puede enviar una propuesta */
export async function enviarPropuesta(local) {
const { id: _x, ...datos } = local;
return addDoc(collection(db, "pendientes"), {
...datos,
estado: "pendiente",
fechaPropuesta: new Date().toISOString(),
});
}
/** Solo el admin suscribe y gestiona pendientes */
export function suscribirPendientes(onData, onError) {
const q = query(collection(db, "pendientes"), orderBy("fechaPropuesta", "desc"));
return onSnapshot(q, snap => onData(snap.docs.map(d => ({ id: d.id, ...d.data() }))), onError);
}
/** Admin aprueba: mueve a /locales y borra de /pendientes */
export async function aprobarPropuesta(p) {
const { id, estado, fechaPropuesta, ...datos } = p;
await addDoc(collection(db, "locales"), { ...datos, fecha: new Date().toISOString() });
await deleteDoc(doc(db, "pendientes", id));
}
/** Admin rechaza: borra de /pendientes */
export const rechazarPropuesta = (id) => deleteDoc(doc(db, "pendientes", id));
// ── Filtro de palabras ────────────────────────────────────────────────────────
export async function obtenerPalabrasFiltradas() {
try {
const snap = await getDoc(doc(db, "config", "filtro_palabras"));
if (snap.exists()) return snap.data().palabras || [];
} catch {}
return [];
}
export const guardarPalabrasFiltradas = (palabras) =>
setDoc(doc(db, "config", "filtro_palabras"), { palabras });
/** Devuelve true si el texto contiene alguna palabra prohibida */
export function contienepalabrasProhibidas(texto, lista) {
if (!lista?.length) return false;
const norm = s => s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
const t = norm(texto);
return lista.some(p => t.includes(norm(p)));
}
// firebase.js - Re-exports local API instead of Firebase
export {
escucharAuth,
loginAdmin,
registrarAdmin,
cerrarSesion,
suscribirLocales,
deleteLocal,
enviarPropuesta,
suscribirPendientes,
aprobarPropuesta,
rechazarPropuesta,
obtenerPalabrasFiltradas,
guardarPalabrasFiltradas,
contienepalabrasProhibidas,
} from "./api.js";