Documentação Técnica

Guia completo de arquitetura, modelos, rotas, helpers e configuração do LiveMoments. Referência para desenvolvedores que precisam entender, estender ou manter o sistema.

Visão Geral

LiveMoments é uma plataforma social para eventos (casamentos, formaturas, aniversários, etc.). Convidados acessam via QR Code, enviam fotos e vídeos em tempo real e acompanham um feed ao vivo.

O sistema é monolítico em Flask: um único ponto de entrada (app.py) carrega configurações, inicializa extensões, registra rotas e dispara eventos WebSocket via Flask-SocketIO com eventlet.

Stack & Dependências

CamadaTecnologiaOBS
BackendPython 3.10+, Flask 3, Flask-SocketIO, SQLAlchemyCore da aplicação
Real-timeSocket.IO 4 + eventletBroadcast de mídia, likes, comentários e recados
FrontendBootstrap 5.3, Font Awesome 6, Google FontsTemplates Jinja2 server-side
Banco de dadosSQLite (dev) / PostgreSQL (prod)Via SQLAlchemy + Flask-Migrate
UploadCanvas API + multipart/form-dataFiltros, crop e emojis no client
E-mailsmtplibSMTP próprio do usuário
QR Codeqrcode (Python)Gerado automaticamente por evento

Estrutura de Pastas

livemoments/
├── app.py # Aplicação principal + todas as rotas
├── models.py # Modelos SQLAlchemy
├── extensions.py # Instâncias globais (db, login_manager, socketio)
├── config.py # Configurações por ambiente
├── requirements.txt # Dependências Python
├── .env.example # Exemplo de variáveis de ambiente
├── static/
│ ├── css/style.css # Estilos globais, feed, admin, footer
│ ├── js/feed.js # Feed em tempo real
│ ├── js/upload-modal.js # Editor de fotos
│ ├── uploads/ # Mídias enviadas
│ └── qrcodes/ # QR Codes gerados
├── templates/
│ ├── base.html # Layout base com footer
│ ├── index.html # Landing page
│ ├── feed.html # Feed público
│ ├── galeria.html # Galeria por postador
│ ├── recados.html # Mural de recados
│ ├── videos.html # Galeria de vídeos
│ ├── capsulas.html # Cápsulas do Tempo
│ ├── upload.html # Página de upload
│ ├── auth/ # Login, cadastro, recuperação de senha
│ ├── admin/ # Painel administrativo
│ └── emails/ # Templates HTML de e-mail
└── capsule_videos/ # Vídeos das Cápsulas do Tempo (não público via static)

Configuração

Arquivo config.py define a classe base Config e as subclasses DevelopmentConfig e ProductionConfig.

Principais variáveis

ChaveOrigemDescrição
SECRET_KEYSECRET_KEY ou fallback devChave secreta do Flask
SQLALCHEMY_DATABASE_URIDATABASE_URL ou SQLite localURI do banco
UPLOAD_FOLDERUPLOAD_FOLDER ou static/uploadsPasta de mídias
CAPSULE_FOLDERCAPSULE_FOLDER ou capsule_videosPasta de vídeos de cápsulas
QR_FOLDERstatic/qrcodesPasta de QR Codes
MAX_CONTENT_LENGTH500 MBLimite de upload
ALLOWED_IMAGE_EXTENSIONSsetjpg, jpeg, png, gif, webp, heic, heif
ALLOWED_VIDEO_EXTENSIONSsetmp4, mov, avi, webm, mkv, 3gp
THUMBNAIL_SIZE(600, 600)Tamanho das thumbnails
RATELIMIT_STORAGE_URLREDIS_URL ou memory://Storage do rate limiting
APP_BASE_URLAPP_BASE_URLURL pública para QR Code

Extensões

extensions.py cria três instâncias globais (padrão Flask) para evitar import circular:

  • db = SQLAlchemy() — ORM e banco de dados
  • login_manager = LoginManager() — gerenciamento de sessão/login
  • socketio = SocketIO() — WebSocket / Socket.IO

Modelos

Os modelos SQLAlchemy estão em models.py e representam todas as entidades do sistema.

User

Herda de UserMixin (Flask-Login). Armazena credenciais, dados pessoais e configurações SMTP.

ColunaTipoDescrição
idInteger PKIdentificador
emailString(255), uniqueE-mail de login
nicknameString(100), uniqueApelido
password_hashString(255)Hash bcrypt
full_nameString(255)Nome completo
is_superadminBooleanSuper administrador
is_activeBooleanConta ativa
created_atDateTimeData de criação
smtp_host / smtp_port / smtp_username / smtp_passwordVáriosConfiguração SMTP
smtp_from_email / smtp_from_name / smtp_use_tlsVáriosRemetente e TLS

Métodos: set_password(password), check_password(password), has_smtp().

Event

Representa um evento/álbum. Cada evento pertence a um User e possui muitas Media.

ColunaTipoDescrição
idInteger PKIdentificador
user_idFK users.idDono do evento
nameString(255)Nome do evento
slugString(120), unique, indexSlug para URLs
couple_namesString(255)Nomes dos noivos, etc.
event_dateDateData do evento
locationString(255)Local
descriptionTextDescrição
cover_imageString(255)Imagem de capa
is_activeBooleanEvento ativo
allow_videosBooleanPermitir vídeos
require_guest_nameBooleanExigir nome do convidado
moderationBooleanModeração ativa
primary_color / secondary_colorString(20)Cores do tema
theme_configText (JSON)Configuração completa do tema
created_atDateTimeData de criação

Propriedades: approved_media, media_count, photo_count, video_count.

Media

Foto ou vídeo enviado por um convidado.

ColunaTipoDescrição
idInteger PKIdentificador
event_idFK events.idEvento
filenameString(255)Caminho relativo do arquivo
original_filenameString(255)Nome original
file_typeString(20)image ou video
mime_typeString(100)MIME type
file_sizeIntegerTamanho em bytes
uploader_nameString(255)Nome do convidado
is_approvedBooleanAprovado na moderação
thumbnailString(255)Thumbnail
captionTextLegenda
created_atDateTimeData de envio

Propriedade: size_mb — retorna tamanho em MB.

Like, Comment, Recado, PasswordResetToken, TimeCapsule

ModeloDescriçãoDestaque
LikeLike de uma mídiaConstraint única media_id + session_id
CommentComentário de uma mídiato_dict() formatado
RecadoMensagem no mural do eventoto_dict()
PasswordResetTokenToken de redefiniçãois_valid() verifica expiração
TimeCapsuleCápsula do Tempoto_dict(), is_unlocked(), reaction_count()

Helpers

Funções utilitárias definidas em app.py (seção Helpers) e usadas pelas rotas.

FunçãoDescrição
allowed_file(filename, file_type='image')Valida extensão contra as listas permitidas
get_file_type(filename)Retorna image ou video
slugify(text)Normaliza acentos e símbolos para slug
make_unique_slug(base_slug)Garante slug único com sufixo numérico
create_thumbnail(filepath, thumb_path, size=(600,600))Gera thumbnail JPG com Pillow
send_email(to, subject, html_body, user=None)Envia e-mail via SMTP (próprio do usuário ou .env)
send_welcome_email(user)E-mail de boas-vindas
send_reset_email(user, token)E-mail de redefinição de senha
get_theme(event)Monta dicionário de cores/fontes do tema
_generate_qrcode(event)Gera QR Code PNG do feed do evento
_get_session_id()Gera/retorna ID de sessão anônima

Rotas

Todas as rotas são registradas dentro de _register_routes(app) em app.py.

Públicas

RotaMétodoDescrição
/GETLanding page com eventos ativos
/e/<slug>GETFeed público do evento
/e/<slug>/uploadGETPOSTUpload de mídias
/e/<slug>/recadosGETPOSTMural de recados
/e/<slug>/galeriaGETGaleria agrupada por postador
/e/<slug>/galeria/<uploader>GETÁlbum individual
/e/<slug>/videosGETGaleria de vídeos
/e/<slug>/capsulasGETCápsulas do Tempo
/e/<slug>/capsulas/novaPOSTCriar nova cápsula

Autenticação

RotaMétodoDescrição
/registerGETPOSTCadastro
/loginGETPOSTLogin
/logoutGETLogout
/forgot-passwordGETPOSTRecuperação de senha
/reset-password/<token>GETPOSTRedefinir senha

Admin

RotaMétodoDescrição
/adminGETDashboard
/admin/events/newGETPOSTCriar evento
/admin/events/<id>GETDetalhes do evento
/admin/events/<id>/editGETPOSTEditar evento
/admin/events/<id>/deletePOSTExcluir evento
/admin/events/<id>/capsulasGETListar cápsulas
/admin/events/<id>/capsulas/<cid>/deletePOSTExcluir cápsula
/admin/events/<id>/downloadGETBaixar ZIP com mídias
/admin/events/<id>/qrcodeGETBaixar QR Code
/admin/events/<id>/qrcode/regeneratePOSTRegenerar QR Code
/admin/events/<id>/moderateGETModeração
/admin/events/<id>/moderate/delete-post/<mid>POSTExcluir post
/admin/events/<id>/moderate/delete-comment/<cid>POSTExcluir comentário
/admin/events/<id>/moderate/delete-recado/<rid>POSTExcluir recado
/admin/media/<id>/deletePOSTExcluir mídia
/admin/media/<id>/approvePOSTAprovar/reprovar mídia
/admin/settingsGETPOSTConfigurações de perfil, senha e SMTP

API

RotaMétodoDescrição
/api/media/<id>/likePOSTLike / unlike
/api/media/<id>/commentsGETListar comentários
/api/media/<id>/commentPOSTComentar
/api/capsulas/<id>/reacaoPOSTReagir a cápsula
/api/capsulas/<id>/videoGETStream do vídeo da cápsula
/api/events/<slug>/feedGETJSON do feed (lazy load)

Socket.IO

Eventos de WebSocket para atualização em tempo real.

EventoTipoDescrição
join_eventRecebeCliente entra na sala event_<id>
new_mediaEmiteNova mídia aprovada no feed
like_updateEmiteAtualização de contagem de likes
new_commentEmiteNovo comentário em mídia
new_recadoEmiteNovo recado no mural

Templates

Renderização server-side com Jinja2. base.html define o layout, footer e assets globais.

TemplateFunção
base.htmlLayout base, footer, toasts, CSS/JS globais
index.htmlLanding page
feed.htmlFeed do evento com stories, ações rápidas e bottom nav
upload.htmlUpload de arquivos
galeria.htmlGaleria por postador
videos.htmlGaleria de vídeos
recados.htmlMural de recados
capsulas.htmlCápsulas do Tempo
_upload_modal.htmlModal de upload/editor
auth/*.htmlLogin, registro, forgot/reset password
admin/*.htmlDashboard, formulário, detalhes, moderação, configurações
emails/*.htmlTemplates de e-mail transacional

Frontend

feed.js

  • Inicializa Socket.IO e entra na sala do evento
  • Recebe new_media e adiciona ao feed/grid
  • Atualiza likes via like_update
  • Carrega comentários e envia novos comentários
  • Lazy load via scroll com /api/events/<slug>/feed

upload-modal.js

  • Canvas-based editor: crop, filtros e emojis
  • Upload multipart com barra de progresso
  • Pré-visualização de arquivos antes do envio

Deploy

Fluxo recomendado para Ubuntu + Nginx + Gunicorn:

  1. Instalar dependências e criar venv
  2. Configurar .env (DATABASE_URL, SECRET_KEY, SMTP, APP_BASE_URL)
  3. Assegurar permissões de www-data em static/uploads, static/qrcodes e capsule_videos
  4. Configurar livemoments.service com gunicorn + eventlet
  5. Configurar Nginx com proxy e upgrade para WebSocket
  6. Certbot para SSL

Ver README.md para exemplos completos de configuração.

Segurança

  • CSRF: Sessão Flask; forms POST validados com contexto de sessão.
  • Upload: validação de extensão, tamanho e content-type; nomes normalizados com secure_filename + UUID.
  • Vídeos de cápsulas: armazenados fora de static e servidos apenas via endpoint autenticado por desbloqueio.
  • Rate limiting: Flask-Limiter protege login, registro, upload e criação de cápsulas.
  • Autenticação: Flask-Login com hash Werkzeug (bcrypt-like).
  • Moderação: mídias podem exigir aprovação antes de aparecerem no feed.

Referência da API

Feed paginado

GET /api/events/<slug>/feed?after=<id>

Retorna até 20 mídias aprovadas com id maior que after, ordenadas por data decrescente.

Like

POST /api/media/<id>/like

Alterna like para a sessão atual. Retorna { liked: bool, count: int }.

Comentários

GET /api/media/<id>/comments
POST /api/media/<id>/comment { "author_name": "...", "content": "..." }

Cápsula do Tempo

POST /api/capsulas/<id>/reacao { "reacao": "love" } // love | funny | cry | clap
GET /api/capsulas/<id>/video // stream do vídeo após desbloqueio