Guia completo de arquitetura, modelos, rotas, helpers e configuração do LiveMoments. Referência para desenvolvedores que precisam entender, estender ou manter o sistema.
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.
| Camada | Tecnologia | OBS |
|---|---|---|
| Backend | Python 3.10+, Flask 3, Flask-SocketIO, SQLAlchemy | Core da aplicação |
| Real-time | Socket.IO 4 + eventlet | Broadcast de mídia, likes, comentários e recados |
| Frontend | Bootstrap 5.3, Font Awesome 6, Google Fonts | Templates Jinja2 server-side |
| Banco de dados | SQLite (dev) / PostgreSQL (prod) | Via SQLAlchemy + Flask-Migrate |
| Upload | Canvas API + multipart/form-data | Filtros, crop e emojis no client |
| smtplib | SMTP próprio do usuário | |
| QR Code | qrcode (Python) | Gerado automaticamente por evento |
Arquivo config.py define a classe base Config e as subclasses DevelopmentConfig e ProductionConfig.
| Chave | Origem | Descrição |
|---|---|---|
SECRET_KEY | SECRET_KEY ou fallback dev | Chave secreta do Flask |
SQLALCHEMY_DATABASE_URI | DATABASE_URL ou SQLite local | URI do banco |
UPLOAD_FOLDER | UPLOAD_FOLDER ou static/uploads | Pasta de mídias |
CAPSULE_FOLDER | CAPSULE_FOLDER ou capsule_videos | Pasta de vídeos de cápsulas |
QR_FOLDER | static/qrcodes | Pasta de QR Codes |
MAX_CONTENT_LENGTH | 500 MB | Limite de upload |
ALLOWED_IMAGE_EXTENSIONS | set | jpg, jpeg, png, gif, webp, heic, heif |
ALLOWED_VIDEO_EXTENSIONS | set | mp4, mov, avi, webm, mkv, 3gp |
THUMBNAIL_SIZE | (600, 600) | Tamanho das thumbnails |
RATELIMIT_STORAGE_URL | REDIS_URL ou memory:// | Storage do rate limiting |
APP_BASE_URL | APP_BASE_URL | URL pública para QR Code |
extensions.py cria três instâncias globais (padrão Flask) para evitar import circular:
db = SQLAlchemy() — ORM e banco de dadoslogin_manager = LoginManager() — gerenciamento de sessão/loginsocketio = SocketIO() — WebSocket / Socket.IOOs modelos SQLAlchemy estão em models.py e representam todas as entidades do sistema.
Herda de UserMixin (Flask-Login). Armazena credenciais, dados pessoais e configurações SMTP.
| Coluna | Tipo | Descrição |
|---|---|---|
id | Integer PK | Identificador |
email | String(255), unique | E-mail de login |
nickname | String(100), unique | Apelido |
password_hash | String(255) | Hash bcrypt |
full_name | String(255) | Nome completo |
is_superadmin | Boolean | Super administrador |
is_active | Boolean | Conta ativa |
created_at | DateTime | Data de criação |
smtp_host / smtp_port / smtp_username / smtp_password | Vários | Configuração SMTP |
smtp_from_email / smtp_from_name / smtp_use_tls | Vários | Remetente e TLS |
Métodos: set_password(password), check_password(password), has_smtp().
Representa um evento/álbum. Cada evento pertence a um User e possui muitas Media.
| Coluna | Tipo | Descrição |
|---|---|---|
id | Integer PK | Identificador |
user_id | FK users.id | Dono do evento |
name | String(255) | Nome do evento |
slug | String(120), unique, index | Slug para URLs |
couple_names | String(255) | Nomes dos noivos, etc. |
event_date | Date | Data do evento |
location | String(255) | Local |
description | Text | Descrição |
cover_image | String(255) | Imagem de capa |
is_active | Boolean | Evento ativo |
allow_videos | Boolean | Permitir vídeos |
require_guest_name | Boolean | Exigir nome do convidado |
moderation | Boolean | Moderação ativa |
primary_color / secondary_color | String(20) | Cores do tema |
theme_config | Text (JSON) | Configuração completa do tema |
created_at | DateTime | Data de criação |
Propriedades: approved_media, media_count, photo_count, video_count.
Foto ou vídeo enviado por um convidado.
| Coluna | Tipo | Descrição |
|---|---|---|
id | Integer PK | Identificador |
event_id | FK events.id | Evento |
filename | String(255) | Caminho relativo do arquivo |
original_filename | String(255) | Nome original |
file_type | String(20) | image ou video |
mime_type | String(100) | MIME type |
file_size | Integer | Tamanho em bytes |
uploader_name | String(255) | Nome do convidado |
is_approved | Boolean | Aprovado na moderação |
thumbnail | String(255) | Thumbnail |
caption | Text | Legenda |
created_at | DateTime | Data de envio |
Propriedade: size_mb — retorna tamanho em MB.
| Modelo | Descrição | Destaque |
|---|---|---|
Like | Like de uma mídia | Constraint única media_id + session_id |
Comment | Comentário de uma mídia | to_dict() formatado |
Recado | Mensagem no mural do evento | to_dict() |
PasswordResetToken | Token de redefinição | is_valid() verifica expiração |
TimeCapsule | Cápsula do Tempo | to_dict(), is_unlocked(), reaction_count() |
Funções utilitárias definidas em app.py (seção Helpers) e usadas pelas rotas.
| Função | Descriçã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 |
Todas as rotas são registradas dentro de _register_routes(app) em app.py.
| Rota | Método | Descrição |
|---|---|---|
/ | GET | Landing page com eventos ativos |
/e/<slug> | GET | Feed público do evento |
/e/<slug>/upload | GETPOST | Upload de mídias |
/e/<slug>/recados | GETPOST | Mural de recados |
/e/<slug>/galeria | GET | Galeria agrupada por postador |
/e/<slug>/galeria/<uploader> | GET | Álbum individual |
/e/<slug>/videos | GET | Galeria de vídeos |
/e/<slug>/capsulas | GET | Cápsulas do Tempo |
/e/<slug>/capsulas/nova | POST | Criar nova cápsula |
| Rota | Método | Descrição |
|---|---|---|
/register | GETPOST | Cadastro |
/login | GETPOST | Login |
/logout | GET | Logout |
/forgot-password | GETPOST | Recuperação de senha |
/reset-password/<token> | GETPOST | Redefinir senha |
| Rota | Método | Descrição |
|---|---|---|
/admin | GET | Dashboard |
/admin/events/new | GETPOST | Criar evento |
/admin/events/<id> | GET | Detalhes do evento |
/admin/events/<id>/edit | GETPOST | Editar evento |
/admin/events/<id>/delete | POST | Excluir evento |
/admin/events/<id>/capsulas | GET | Listar cápsulas |
/admin/events/<id>/capsulas/<cid>/delete | POST | Excluir cápsula |
/admin/events/<id>/download | GET | Baixar ZIP com mídias |
/admin/events/<id>/qrcode | GET | Baixar QR Code |
/admin/events/<id>/qrcode/regenerate | POST | Regenerar QR Code |
/admin/events/<id>/moderate | GET | Moderação |
/admin/events/<id>/moderate/delete-post/<mid> | POST | Excluir post |
/admin/events/<id>/moderate/delete-comment/<cid> | POST | Excluir comentário |
/admin/events/<id>/moderate/delete-recado/<rid> | POST | Excluir recado |
/admin/media/<id>/delete | POST | Excluir mídia |
/admin/media/<id>/approve | POST | Aprovar/reprovar mídia |
/admin/settings | GETPOST | Configurações de perfil, senha e SMTP |
| Rota | Método | Descrição |
|---|---|---|
/api/media/<id>/like | POST | Like / unlike |
/api/media/<id>/comments | GET | Listar comentários |
/api/media/<id>/comment | POST | Comentar |
/api/capsulas/<id>/reacao | POST | Reagir a cápsula |
/api/capsulas/<id>/video | GET | Stream do vídeo da cápsula |
/api/events/<slug>/feed | GET | JSON do feed (lazy load) |
Eventos de WebSocket para atualização em tempo real.
| Evento | Tipo | Descrição |
|---|---|---|
join_event | Recebe | Cliente entra na sala event_<id> |
new_media | Emite | Nova mídia aprovada no feed |
like_update | Emite | Atualização de contagem de likes |
new_comment | Emite | Novo comentário em mídia |
new_recado | Emite | Novo recado no mural |
Renderização server-side com Jinja2. base.html define o layout, footer e assets globais.
| Template | Função |
|---|---|
base.html | Layout base, footer, toasts, CSS/JS globais |
index.html | Landing page |
feed.html | Feed do evento com stories, ações rápidas e bottom nav |
upload.html | Upload de arquivos |
galeria.html | Galeria por postador |
videos.html | Galeria de vídeos |
recados.html | Mural de recados |
capsulas.html | Cápsulas do Tempo |
_upload_modal.html | Modal de upload/editor |
auth/*.html | Login, registro, forgot/reset password |
admin/*.html | Dashboard, formulário, detalhes, moderação, configurações |
emails/*.html | Templates de e-mail transacional |
new_media e adiciona ao feed/gridlike_update/api/events/<slug>/feedFluxo recomendado para Ubuntu + Nginx + Gunicorn:
.env (DATABASE_URL, SECRET_KEY, SMTP, APP_BASE_URL)www-data em static/uploads, static/qrcodes e capsule_videoslivemoments.service com gunicorn + eventletVer README.md para exemplos completos de configuração.
secure_filename + UUID.static e servidos apenas via endpoint autenticado por desbloqueio.Retorna até 20 mídias aprovadas com id maior que after, ordenadas por data decrescente.
Alterna like para a sessão atual. Retorna { liked: bool, count: int }.