Initial upload
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ title or "ihasmail" }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' https://unpkg.com;">
|
||||
<link rel="icon" href="/static/img/logo.png">
|
||||
<link rel="preconnect" href="https://unpkg.com">
|
||||
<script defer src="https://unpkg.com/[email protected]"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="brand">
|
||||
<a href="/" class="brand-link"><img src="/static/img/logo.png" alt="ihasmail" class="logo"> <strong>ihasmail</strong></a>
|
||||
</div>
|
||||
<nav>
|
||||
{% if user %}
|
||||
<span class="muted">Signed in as {{ user.get("username") }}</span>
|
||||
<a class="btn" href="/mail">Inbox</a>
|
||||
<a class="btn" href="/compose">Compose</a>
|
||||
<a class="btn" href="/calendar">Calendar</a>
|
||||
<a class="btn" href="/contacts">Contacts</a>
|
||||
<a class="btn" href="/webdav">WebDAV</a>
|
||||
<a class="btn" href="/logout">Logout</a>
|
||||
{% else %}
|
||||
<a class="btn" href="/login">Login</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<footer class="muted">ihasmail • JMAP • Sieve • DAV • FastAPI • reverse-proxy ready</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Calendar (JMAP & CalDAV)</h1>
|
||||
<p class="muted">Listing upcoming events via JMAP. CalDAV endpoints available for DAV clients.</p>
|
||||
<table>
|
||||
<tr><th>When</th><th>Summary</th><th>Where</th></tr>
|
||||
{% for e in events %}
|
||||
<tr>
|
||||
<td class="nowrap">{{ e.start }} – {{ e.end }}</td>
|
||||
<td>{{ e.title }}</td>
|
||||
<td>{{ e.loc or "" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Compose</h1>
|
||||
<form method="post" action="/compose">
|
||||
<input type="hidden" name="csrf" value="{{ csrf }}">
|
||||
<div class="row"><label>To</label><input name="to" required></div>
|
||||
<div class="row"><label>Subject</label><input name="subject"></div>
|
||||
<div class="row"><label>Body</label><textarea name="body" rows="14"></textarea></div>
|
||||
<button class="btn">Send</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Contacts (Directory via JMAP)</h1>
|
||||
<div class="toolbar">
|
||||
<form>
|
||||
<input name="q" value="{{ q or '' }}" placeholder="Search name/email…">
|
||||
</form>
|
||||
</div>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Email</th><th>Org</th></tr>
|
||||
{% for c in contacts %}
|
||||
<tr>
|
||||
<td>{{ c.name }}</td>
|
||||
<td>{{ c.email }}</td>
|
||||
<td>{{ c.org or "" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="center"><div class="card" style="min-width:320px; max-width:420px;">
|
||||
<div style="text-align:center;margin-bottom:8px"><img class="logo-lg" src="/static/img/logo.png" alt="ihasmail"></div>
|
||||
<h2 style="text-align:center;margin-top:0">Sign in</h2>
|
||||
<form method="post" action="/login">
|
||||
<input type="hidden" name="csrf" value="{{ csrf }}">
|
||||
<div class="row">
|
||||
<label>Username</label>
|
||||
<input name="username" autocomplete="username" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Password</label>
|
||||
<input type="password" name="password" autocomplete="current-password" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>JMAP Base</label>
|
||||
<input name="jmap_base" value="{{ jmap_base }}">
|
||||
</div>
|
||||
<button class="btn" type="submit">Sign in</button>
|
||||
</form>
|
||||
</div></div>
|
||||
<p class="muted">Credentials are sent to your JMAP server to obtain a session/auth token; they are not stored on the server.</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Inbox</h1>
|
||||
<div class="toolbar">
|
||||
<form method="get" action="/mail">
|
||||
<select name="mailbox" onchange="this.form.submit()">
|
||||
{% for b in mailboxes %}
|
||||
<option value="{{ b.id }}" {% if b.id == selected %}selected{% endif %}>{{ b.name }}{% if b.unread %} ({{ b.unread }}){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input name="q" placeholder="Search (from, subject, text…)" value="{{ q or '' }}">
|
||||
</form>
|
||||
<a class="btn" href="/compose">Compose</a>
|
||||
</div>
|
||||
<table>
|
||||
<tr><th class="nowrap">When</th><th>From</th><th>Subject</th><th class="right">Size</th></tr>
|
||||
{% for m in messages %}
|
||||
<tr class="msg" onclick="location.href='/mail/{{ m.id }}'">
|
||||
<td class="nowrap">{{ m.when }}</td>
|
||||
<td>{{ m.from }}</td>
|
||||
<td>{{ m.subject }}</td>
|
||||
<td class="right">{{ m.size }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>{{ msg.subject or "(no subject)" }}</h1>
|
||||
<p><span class="pill">From</span> {{ msg.from }} <span class="pill">To</span> {{ msg.to|join(", ") }}</p>
|
||||
<p class="muted">{{ msg.when }}</p>
|
||||
{% if msg.htmlBody %}
|
||||
<div class="panel">{{ (msg.htmlBody | safe) }}</div>
|
||||
{% elif msg.textBody %}
|
||||
<div class="panel">{{ msg.textBody }}</div>
|
||||
{% else %}
|
||||
<div class="panel muted">(no body)</div>
|
||||
{% endif %}
|
||||
<div class="toolbar">
|
||||
<a class="btn" href="/compose?reply={{ msg.id }}">Reply</a>
|
||||
<a class="btn" href="/compose?forward={{ msg.id }}">Forward</a>
|
||||
</div>
|
||||
{% if msg.attachments %}
|
||||
<h3>Attachments</h3>
|
||||
<ul>
|
||||
{% for a in msg.attachments %}
|
||||
<li>{{ a.name }} ({{ a.type }}, {{ a.size }} bytes)</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>WebDAV</h1>
|
||||
<p class="muted">Browsing {{ base }}</p>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Type</th><th class="right">Size</th></tr>
|
||||
{% for i in items %}
|
||||
<tr>
|
||||
<td>{{ i.name }}</td>
|
||||
<td>{{ i.type }}</td>
|
||||
<td class="right">{% if i.size is not none %}{{ i.size }}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user