vienalatina/apps/board/templates/member_new.html
Claude ed6b568d60
Hand the mail settings to the container, and rebuild on deploy
Two silent failures sitting between Phase A and a working invitation.

The compose file never passed MAIL_* through. Compose does not give a
container the contents of .env — it only substitutes into the compose
file — so the settings could be filled in correctly and read by nobody,
with the app reporting mail as unconfigured and the values sitting right
there on disk. test_deployment.py now fails the build whenever
.env.example and docker-compose.yml drift apart, which is how this
happened and how it would happen again.

The app's code is baked into the image (COPY apps /srv/apps), so a pull
followed by --force-recreate runs the old code and says nothing. Added
scripts/deploy-board.sh: rebuild, sync the compose file, restart, print
the log, and name any setting present in .env.example and missing from
the live .env. It never touches .env itself.

Also: "no mail server configured" is now told apart from "the mail
server refused". They want different things done about them, and the
first one sent an admin looking for an SMTP error that never existed.
The new-member form says so before it is filled in rather than after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NizVpJ2dwzCbjCrTLCjeHn
2026-09-25 18:42:39 +00:00

67 lines
2.4 KiB
HTML

{% extends "base.html" %}
{% block title %}Dar de alta{% endblock %}
{% block main %}
<form class="card" method="post" action="{{ url_for('members.new') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<h1>Dar de alta a alguien</h1>
<label for="login">Usuario</label>
<input id="login" name="login" required pattern="[A-Za-z0-9][A-Za-z0-9._\-]*"
placeholder="maria.lopez">
<p class="muted small">Letras, números, punto, guion y guion bajo.</p>
<label for="display_name">Nombre</label>
<input id="display_name" name="display_name" placeholder="María López">
<label for="email">Correo</label>
<input id="email" name="email" type="email" placeholder="maria@ejemplo.com">
{% if can_create_accounts %}
<label class="check">
<input type="checkbox" name="create_account" checked>
Crear también su cuenta (desmarca si ya tiene una)
</label>
{% else %}
{# Disabled rather than hidden: "you cannot do this here" is more use than
an option that quietly is not there. The handler refuses it either way. #}
<label class="check">
<input type="checkbox" name="create_account" disabled>
Crear también su cuenta
</label>
<p class="muted small">
Este servidor no puede crear cuentas (no tiene un token de administración
de Gitea). Crea la cuenta primero en
<a href="{{ gitea_url }}/admin/users/new">Gitea</a> y luego da de alta
aquí ese mismo usuario.
</p>
{% endif %}
{% if can_create_accounts and not can_send_mail %}
{# Not an error: the account is still created and the invitation still
issued. But it leaves by hand, and finding that out after filling the
form is how an invitation ends up believed sent and never delivered. #}
<p class="muted small">
Este servidor todavía no envía correo, así que la invitación no se
enviará sola: al terminar te mostraremos el enlace para que se lo pases.
</p>
{% endif %}
{% if can_make_admin %}
<label for="role">Rol</label>
<select id="role" name="role">
<option value="user">Usuario</option>
<option value="admin">Administrador</option>
</select>
{% else %}
<input type="hidden" name="role" value="user">
<p class="muted small">Los administradores sólo pueden dar de alta usuarios.</p>
{% endif %}
<div class="actions">
<button class="btn" type="submit">Dar de alta</button>
<a class="linkish" href="{{ url_for('members.index') }}">Cancelar</a>
</div>
</form>
{% endblock %}