FireChan es un imageboard estilo 4chan/2chan desarrollado con tecnologías web modernas. Permite a los usuarios crear tablones temáticos, publicar, responder con imágenes y texto, todo almacenado en Firebase.
- Múltiples tablones temáticos: Anime, Tecnología, Videojuegos, Política, Paranormal y más
- Sistema de publicaciones y respuestas: Publica contenido y responde a otros usuarios
- Soporte de imágenes: Almacenamiento local en el servidor con nombres estilo 4chan
- Formato de texto enriquecido: Soporta greentext, citas, enlaces y referencias cruzadas
- Diseño responsive: Funciona en dispositivos móviles y escritorio
- Lightbox para imágenes: Visualiza imágenes en pantalla completa
- Panel de administración completo: Gestiona publicaciones, respuestas y reportes
- Firebase Authentication: Sistema de autenticación seguro para administradores
- Sistema de reportes: Los usuarios pueden reportar contenido inapropiado
- Sistema de baneos por IP: Banea IPs con duración configurable y razones específicas
- Visualización de IPs: Los administradores pueden ver y copiar IPs de usuarios con ipify
- Gestión de baneos: Interface para ver, agregar y remover baneos activos
- Estadísticas del sitio: Métricas completas de uso y actividad
- Sistema CAPTCHA matemático: Verificación anti-spam simple pero efectiva
- Validación doble: Cliente + servidor con tokens únicos Firebase
- Sistema de baneos automático: Ban por múltiples fallos de CAPTCHA
- Prevención de ataques: Registro de intentos fallidos por IP
- Overlay anti-duplicados: Sistema mejorado de notificaciones de ban
- Arquitectura modular: Código refactorizado con utilidades centralizadas
- Sistema de integración de baneos: Verificación automática antes de cada acción
- Overlay completo para usuarios baneados: Interface profesional para usuarios restringidos
- Firebase Auth integrado: Autenticación segura reemplaza sistema hardcodeado
- Frontend: HTML5, CSS3, JavaScript (ES6+ con módulos)
- Base de datos: Firebase Firestore con transacciones y queries optimizados
- Autenticación: Firebase Authentication para administradores
- Almacenamiento: Sistema local de imágenes en el servidor (PHP)
- Arquitectura: Sistema modular con utilidades centralizadas
- Seguridad: Sistema CAPTCHA matemático con validación dual
- APIs externas:
- ipify.org para detección de IP
- Firebase Auth para autenticación segura
- Diseño: Inspirado en imageboards clásicos con mejoras modernas
Antes de comenzar, necesitas:
- Una cuenta de Firebase
- Un servidor web con PHP 7.0+ (como XAMPP, WAMP, o hosting con PHP)
- Permisos de escritura en la carpeta
img/board/
git clone https://github.com/0x230797/FireChan.git
cd FireChan-
Ve a Firebase Console
-
Crea un nuevo proyecto
-
Habilita Firestore Database:
- Ve a "Firestore Database"
- Crea una base de datos en modo de producción
- Configura las reglas de seguridad según tus necesidades
-
Habilita Firebase Authentication:
- Ve a "Authentication" > "Sign-in method"
- Habilita "Email/Password"
- Agrega dominios autorizados si es necesario
-
Obtén las credenciales de tu proyecto:
- Ve a Configuración del proyecto > Tus aplicaciones
- Registra una aplicación web
- Copia la configuración de Firebase
-
Configura el archivo
js/firebase-config.jscon tus credenciales:
const firebaseConfig = {
apiKey: "TU_API_KEY",
authDomain: "tu-proyecto.firebaseapp.com",
projectId: "tu-proyecto-id",
storageBucket: "tu-proyecto.appspot.com",
messagingSenderId: "123456789",
appId: "tu-app-id"
};Las imágenes se almacenan localmente en img/board/. Asegúrate de que el servidor web tenga permisos de escritura:
En Windows (XAMPP):
icacls "c:\xampp\htdocs\FireChan\img\board" /grant "Everyone:(OI)(CI)F" /TEn Linux:
chmod -R 755 img/board/
chown -R www-data:www-data img/board/- Abre el archivo
js/firebase-auth.js - Actualiza la lista de emails autorizados como administradores:
const AUTHORIZED_ADMINS = [
'tu-email@dominio.com', // Agrega tu email aquí
// Agregar más emails de administradores aquí
];- Crear primera cuenta de administrador:
- Accede a
admin.html
- Accede a
Seguro: Las credenciales se manejan completamente por Firebase Authentication.
Asegúrate de que:
- PHP 7.0 o superior está instalado
- Apache está configurado para procesar archivos .php
- El módulo
mod_rewriteestá habilitado (opcional, para .htaccess)
En XAMPP: Todo viene preconfigurado, solo asegúrate de iniciar Apache.
Puedes desplegar en:
- Servidor local: XAMPP, WAMP, MAMP con soporte PHP
- Hosting compartido: Cualquier hosting con PHP 7.0+ y MySQL/Firebase
- VPS/Cloud: AWS, DigitalOcean, Linode con LAMP stack
- Firebase Hosting: Requiere configuración adicional para PHP (usar Cloud Functions)
Nota: El sistema de imágenes requiere PHP, por lo que no funciona en hosting puramente estático como GitHub Pages.
FireChan/
├── index.html # Página principal con tablones
├── thread.html # Vista de publicaciones de un tablón
├── reply.html # Vista de publicación individual con respuestas
├── admin.html # Panel de administración completo
├── rules.html # Reglas del sitio
├── upload.php # Script de subida de imágenes
├── delete-image.php # Script de eliminación de imágenes
├── css/
│ ├── style.css # Estilos principales
│ └── reset.css # Reset CSS
├── js/
│ ├── firebase-config.js # Configuración de Firebase
│ ├── firebase-auth.js # Sistema de autenticación Firebase
│ ├── config.js # Configuración general
│ ├── utils.js # Utilidades centralizadas
│ ├── upload-system.js # Sistema de subida local de imágenes
│ ├── thread.js # Lógica de publicaciones
│ ├── reply.js # Lógica de respuestas
│ ├── admin.js # Panel admin con gestión de IPs
│ ├── text-processor.js # Procesamiento de texto y referencias
│ ├── ip-ban-system.js # Sistema de baneos por IP
│ ├── ban-integration.js # Integración automática de baneos
│ ├── captcha-system.js # Sistema CAPTCHA matemático anti-spam
│ └── stats.js # Estadísticas del sitio
├── img/
│ └── board/ # Carpeta de imágenes organizadas por board
│ ├── a/ # Imágenes de /a/
│ ├── b/ # Imágenes de /b/
│ ├── g/ # Imágenes de /g/
│ ├── v/ # Imágenes de /v/
│ ├── pol/ # Imágenes de /pol/
│ ├── x/ # Imágenes de /x/
│ ├── me/ # Imágenes de /me/
│ └── .htaccess # Protección de seguridad
├── README.md
- Navegar tablones: Accede a
index.htmly selecciona un tablón - Crear publicación: En un tablón, llena el formulario y haz clic en "Publicar"
- Responder: Abre una publicación y usa el formulario de respuesta
- Reportar contenido: Usa el botón "Reportar" en cualquier publicación
- Acceso al panel: Accede a
admin.html - Iniciar sesión: Usa Firebase Authentication (email/password)
- Gestión de contenido:
- Ver y eliminar publicaciones y respuestas
- Gestionar reportes de usuarios
- Ver estadísticas completas del sitio
- Sistema de baneos:
- Ver IPs de todos los usuarios (clic para copiar)
- Banear IPs con duración configurable (1 hora - permanente)
- Gestionar lista de IPs baneadas
- Razones predefinidas: Spam, Contenido inapropiado, Trolling, Flood, etc.
- Sistema CAPTCHA:
- Automáticamente protege contra spam
- Baneos automáticos por múltiples fallos
- Sin configuración adicional requerida
Edita el archivos index.html para agregar o quitar tablones:
<a href="thread.html?board=tu-tablón" class="board-card">
<div class="board-title">/tu/ - Tu Tablón</div>
<div class="board-desc">Descripción de tu tablón</div>
</a>En ip-ban-system.js puedes modificar:
// Duraciones predefinidas (en milisegundos)
export const BanDurations = {
HOUR_1: 60 * 60 * 1000,
DAY_1: 24 * 60 * 60 * 1000,
WEEK_1: 7 * 24 * 60 * 60 * 1000,
MONTH_1: 30 * 24 * 60 * 60 * 1000,
PERMANENT: null
};Modifica css/style.css para personalizar colores, fuentes y diseño.
El sistema de baneos incluye estilos específicos para overlays y notificaciones.
Para un entorno de producción más seguro y optimizado:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ================ FUNCIONES DE UTILIDAD ================
// Verificar si el usuario es administrador autenticado
function isAdmin() {
return request.auth != null &&
request.auth.token.email in [
'admin@firechan.org',
// Agregar emails de admins autorizados aquí
];
}
// Validar tamaño y formato de contenido
function isValidContent(data) {
return data.comment != null &&
data.comment is string &&
data.comment.size() > 0 &&
data.comment.size() <= 2000 &&
data.name.size() <= 50 &&
(!('subject' in data) || data.subject.size() <= 100);
}
// Validar estructura de thread
function isValidThread(data) {
return isValidContent(data) &&
data.board != null &&
data.board is string &&
data.board.size() <= 20 &&
data.postId != null &&
data.postId is int &&
data.timestamp != null;
}
// Validar estructura de reply
function isValidReply(data) {
return isValidContent(data) &&
data.threadId != null &&
data.threadId is string &&
data.postId != null &&
data.postId is int &&
data.timestamp != null;
}
// Validar que la IP no esté baneada (simplificado, mejor en backend)
function isNotBanned() {
// Esta validación es básica. La verdadera validación
// debe hacerse en el cliente antes de enviar
return true;
}
// ================ COLECCIONES ================
// THREADS - Posts principales
match /threads/{threadId} {
// Lectura pública
allow read: if true;
// Creación solo con contenido válido
allow create: if isValidThread(request.resource.data) &&
isNotBanned();
// Actualización solo para incrementar contador de respuestas
// o admins para cualquier cambio
allow update: if isAdmin() ||
(request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['replyCount']));
// Eliminación solo admins
allow delete: if isAdmin();
}
// REPLIES - Respuestas a threads
match /replies/{replyId} {
// Lectura pública
allow read: if true;
// Creación solo con contenido válido
allow create: if isValidReply(request.resource.data) &&
isNotBanned() &&
// Verificar que el thread padre existe
exists(/databases/$(database)/documents/threads/$(request.resource.data.threadId));
// Sin actualizaciones de usuarios normales
allow update: if isAdmin();
// Eliminación solo admins
allow delete: if isAdmin();
}
// REPORTS - Sistema de reportes
match /reports/{reportId} {
// Lectura solo admins
allow read: if isAdmin();
// Crear reporte con validación
allow create: if request.resource.data.contentId != null &&
request.resource.data.contentType != null &&
request.resource.data.reason != null &&
request.resource.data.reason is string &&
request.resource.data.reason.size() > 0 &&
request.resource.data.reason.size() <= 200 &&
request.resource.data.timestamp != null;
// Solo admins pueden actualizar/eliminar
allow update, delete: if isAdmin();
}
// IP_BANS - Sistema de baneos
match /ip_bans/{banId} {
// Lectura solo para verificar si están baneados (limitada)
allow read: if true;
// Escritura solo admins o backend
allow write: if isAdmin();
}
// NEWS - Noticias del sitio
match /news/{newsId} {
// Lectura pública
allow read: if true;
// Solo admins pueden gestionar noticias
allow write: if isAdmin();
}
// CAPTCHA_CHALLENGES - Sistema anti-spam
match /captcha_challenges/{challengeId} {
// Lectura para validar respuestas
allow read: if true;
// Creación para generar desafíos
allow create: if request.resource.data.token != null &&
request.resource.data.answer != null &&
request.resource.data.timestamp != null;
// Actualización para marcar como usado/verificado
allow update: if request.resource.data.verified != null ||
request.resource.data.attempts != null;
// Eliminación automática después de TTL (mejor con Cloud Functions)
allow delete: if true;
}
// COUNTERS - Sistema de IDs secuenciales
match /counters/{counterId} {
// Lectura pública
allow read: if true;
// Escritura controlada (crear contador inicial)
allow create: if counterId == 'postIdCounter' &&
request.resource.data.value != null &&
request.resource.data.value is int;
// Actualización con incremento atómico
allow update: if counterId == 'postIdCounter' &&
request.resource.data.value == resource.data.value + 1;
// Sin eliminación
allow delete: if false;
}
}
}// functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Validar y limpiar contenido antes de guardar
exports.validatePost = functions.firestore
.document('{collection}/{docId}')
.onCreate(async (snap, context) => {
const data = snap.data();
// Verificar IP baneada
const ipBansRef = admin.firestore().collection('ip_bans');
const banQuery = await ipBansRef
.where('ip', '==', data.userIP)
.where('active', '==', true)
.get();
if (!banQuery.empty) {
// Eliminar el post de usuario baneado
await snap.ref.delete();
console.log(`Post bloqueado de IP baneada: ${data.userIP}`);
}
// Validar contenido (anti-spam, palabras prohibidas, etc.)
// ...
});
// Limpiar CAPTCHA challenges expirados
exports.cleanupExpiredCaptchas = functions.pubsub
.schedule('every 1 hours')
.onRun(async (context) => {
const now = admin.firestore.Timestamp.now();
const expiredCaptchas = await admin.firestore()
.collection('captcha_challenges')
.where('timestamp', '<', new Date(now.toMillis() - 3600000))
.get();
const batch = admin.firestore().batch();
expiredCaptchas.docs.forEach(doc => batch.delete(doc.ref));
await batch.commit();
console.log(`Eliminados ${expiredCaptchas.size} CAPTCHAs expirados`);
});Crea estos índices en Firebase Console para optimizar queries:
Collection: threads
- board (Ascending) + timestamp (Descending)
Collection: replies
- threadId (Ascending) + timestamp (Ascending)
- threadId (Ascending) + timestamp (Descending)
Collection: reports
- timestamp (Descending)
Collection: ip_bans
- ip (Ascending) + active (Ascending)
- active (Ascending) + expiresAt (Ascending)
Collection: captcha_challenges
- token (Ascending) + verified (Ascending)
- ip (Ascending) + timestamp (Descending)
Habilita Firebase App Check para proteger contra abuso:
// En firebase-config.js, agregar:
import { initializeAppCheck, ReCaptchaV3Provider } from "firebase/app-check";
const appCheck = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_V3_SITE_KEY'),
isTokenAutoRefreshEnabled: true
});rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /images/{imageId} {
// Solo lectura pública
allow read: if true;
// Solo escritura con validación de tamaño
allow write: if request.resource.size < 10 * 1024 * 1024 && // 10MB máx
request.resource.contentType.matches('image/.*');
}
}
}Las contribuciones son bienvenidas. Por favor:
- Haz fork del proyecto
- Crea una rama para tu feature (
git checkout -b feature/NuevaCaracteristica) - Commit tus cambios (
git commit -m 'Agrega nueva característica') - Push a la rama (
git push origin feature/NuevaCaracteristica) - Abre un Pull Request
Este proyecto es de código abierto bajo MIT License.
FireChan es una plataforma de entretenimiento y discusión. Todo el contenido es responsabilidad de los usuarios. Los administradores se reservan el derecho de moderar contenido según las reglas establecidas. El sistema de baneos está implementado para mantener un ambiente seguro y respetuoso.
FireChan - Desarrollado con ❤️ por 0x230797