Tidy up rustdocs

axum-login-upgrade
Manos Pitsidianakis 2023-04-25 17:02:24 +03:00
parent 21c9fb9586
commit cfd55d3e4a
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
24 changed files with 759 additions and 312 deletions

97
.github/doc_extra.html vendored 100644
View File

@ -0,0 +1,97 @@
<style>
.rustdoc { flex-wrap: wrap; }
@media (prefers-color-scheme: light) {
:root {
--border-primary: #cdcdcd;
--border-secondary: #cdcdcd;
--a-normal-text: #034575;
--a-normal-underline: #bbb;
--a-visited-underline: #707070;
--a-hover-bg: #bfbfbf40;
--a-active-text: #c00;
--a-active-underline: #c00;
--accent-primary: #0085f2;
--accent-primary-engage: #0085f21a;
--accent-secondary: #0085f2;
--accent-tertiary: #0085f21a;
color-scheme: light;
}
}
@media (prefers-color-scheme: dark) {
:root {
--border-primary: #858585;
--border-secondary: #696969;
--a-normal-text: #4db4ff;
--a-normal-underline: #8b8b8b;
--a-visited-underline: #707070;
--a-hover-bg: #bfbfbf40;
--a-active-text: #c00;
--a-active-underline: #c00;
--accent-primary: #5e9eff;
--accent-primary-engage: #5e9eff1a;
--accent-secondary: #5e9eff;
--accent-tertiary: #0085f21a;
color-scheme: dark;
}
}
nav.main-nav {
padding: 0rem 1rem;
border: 0.1rem solid var(--border-secondary);
border-left: none;
border-right: none;
border-radius: 2px;
padding: 10px 14px 10px 10px;
margin-bottom: 10px;
/*! width: 100%; */
height: max-content;
width: 100vw;
}
nav.main-nav * {
padding: 0;
margin: 0;
}
nav.main-nav > ul {
display: flex;
flex-wrap: wrap;
row-gap: 1rem;
list-style: none;
}
nav.main-nav > ul > li > a {
padding: 1rem;
}
nav.main-nav a[href] {
text-decoration: underline;
text-decoration-color: currentcolor;
color: #034575;
color: var(--a-normal-text);
text-decoration-color: #707070;
text-decoration-color: var(--accent-secondary);
text-decoration-skip-ink: none;
font-synthesis: none;
font-feature-settings: "onum" 1;
text-rendering: optimizeLegibility;
font-family: -apple-system,BlinkMacSystemFont,Roboto,Roboto Slab,Droid Serif,Segoe UI,system-ui,Arial,sans-serif;
font-size: 1.125em;
}
nav.main-nav > ul > li > a:hover {
outline: 0.1rem solid;
outline-offset: -0.5rem;
}
a[href]:focus, a[href]:hover {
text-decoration-thickness: 2px;
text-decoration-skip-ink: none;
}
</style>
<nav class="main-nav">
<ul>
<li><a href="https://git.meli.delivery/meli/mailpot">Gitea Repository</a></li>
<li><a href="https://github.com/meli/mailpot">Github Repository</a></li>
<li><a href="https://lists.meli.delivery/">Official Instance</a></li>
<li><a href="https://meli.github.io/mailpot/lists/2/">Static Demo</a></li>
</ul>
</nav>

View File

@ -84,8 +84,8 @@ jobs:
run: ls -R
- name: Push
run: |
git config --global user.name 'Github Actions'
git config --global user.email 'actions@users.noreply.github.com'
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git show-ref
git add coverage
git commit -m "Update grcov report"

81
.github/workflows/rustdoc.yml vendored 100644
View File

@ -0,0 +1,81 @@
name: Build rustdoc for Github Pages
env:
RUST_BACKTRACE: 1
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
on:
workflow_dispatch:
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- id: cache-rustup
name: Cache Rust toolchain
uses: actions/cache@v3
with:
path: ~/.rustup
key: toolchain-grcov
- id: cache-cargo
name: Cache Cargo
uses: actions/cache@v3
with:
path: ~/.cargo
key: cargo-grcov
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Make rustdocs
run: |
make rustdoc
rm -rf docs
ls -R
mv target/doc docs
- name: Upload report artifacts
uses: actions/upload-artifact@v3
with:
name: docs
path: docs
deploy-docs:
needs: build-docs
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/checkout@v3
with:
ref: 'gh-pages'
token: ${{ secrets.GRCOVGHPAGES }}
- name: Download docs
id: download
uses: actions/download-artifact@v3
with:
name: docs
path: docs
- name: 'Echo download path'
run: echo ${{steps.download.outputs.download-path}}
- name: Display structure of downloaded files
run: ls -R
- name: Push
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git show-ref
git add docs
git commit -m "Update rustdoc"
git show-ref
git branch --verbose
git remote set-url origin "https://${{github.actor}}:${{ secrets.GRCOVGHPAGES }}@github.com/${{github.repository}}.git"
git push

View File

@ -21,3 +21,7 @@ lint:
.PHONY: test
test: check lint
@cargo test --all --no-fail-fast --all-features
.PHONY: rustdoc
rustdoc:
@RUSTDOCFLAGS="--html-before-content ./.github/doc_extra.html" cargo doc --workspace --all-features --no-deps --document-private-items

View File

@ -31,7 +31,7 @@ use chrono::*;
///
/// # Examples
/// ```
/// use calendarize::calendarize;
/// use mailpot_archives::cal::calendarize;
/// use chrono::*;
///
/// let date = NaiveDate::parse_from_str("2021-01-02", "%Y-%m-%d").unwrap();
@ -59,7 +59,7 @@ pub fn calendarize(date: NaiveDate) -> Vec<[u32; 7]> {
///
/// # Examples
/// ```
/// use calendarize::calendarize_with_offset;
/// use mailpot_archives::cal::calendarize_with_offset;
/// use chrono::*;
///
/// let date = NaiveDate::parse_from_str("2021-01-02", "%Y-%m-%d").unwrap();

View File

@ -17,19 +17,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use chrono::Datelike;
use std::{fs::OpenOptions, io::Write};
mod cal;
mod utils;
use std::{borrow::Cow, fs::OpenOptions, io::Write};
pub use mailpot::{models::DbVal, *};
use minijinja::{
value::{Object, Value},
Environment, Error, Source, State,
};
use utils::*;
use mailpot::*;
use mailpot_archives::utils::*;
use minijinja::value::Value;
fn run_app() -> std::result::Result<(), Box<dyn std::error::Error>> {
let args = std::env::args().collect::<Vec<_>>();

View File

@ -0,0 +1,21 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pub mod cal;
pub mod utils;

View File

@ -17,20 +17,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use chrono::Datelike;
mod cal;
mod utils;
use std::borrow::Cow;
pub use mailpot::{models::DbVal, *};
use minijinja::{
value::{Object, Value},
Environment, Error, Source, State,
};
use mailpot::*;
use mailpot_archives::utils::*;
use minijinja::value::Value;
use percent_encoding::percent_decode_str;
use utils::*;
use warp::Filter;
#[tokio::main]

View File

@ -17,7 +17,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use super::*;
use std::borrow::Cow;
use chrono::{Datelike, Month};
use mailpot::{models::DbVal, *};
use minijinja::{
value::{Object, Value},
Environment, Error, Source, State,
};
lazy_static::lazy_static! {
pub static ref TEMPLATES: Environment<'static> = {
@ -131,8 +138,6 @@ impl minijinja::value::StructObject for MailingList {
}
pub fn calendarize(_state: &State, args: Value, hists: Value) -> std::result::Result<Value, Error> {
use chrono::Month;
macro_rules! month {
($int:expr) => {{
let int = $int;
@ -175,7 +180,7 @@ pub fn calendarize(_state: &State, args: Value, hists: Value) -> std::result::Re
month => month,
month_int => date.month() as usize,
year => date.year(),
weeks => cal::calendarize_with_offset(date, 1),
weeks => crate::cal::calendarize_with_offset(date, 1),
hist => hist,
sum => sum,
})

View File

@ -27,10 +27,10 @@ use clap::ArgAction;
use clap_mangen::{roff, Man};
use roff::{bold, italic, roman, Inline, Roff};
include!("src/args.rs");
include!("src/lib.rs");
fn main() -> std::io::Result<()> {
println!("cargo:rerun-if-changed=./src/args.rs");
println!("cargo:rerun-if-changed=./src/lib.rs");
println!("cargo:rerun-if-changed=./build.rs");
std::env::set_current_dir("..").expect("could not chdir('..')");

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::path::PathBuf;
pub use std::path::PathBuf;
pub use clap::{Args, CommandFactory, Parser, Subcommand};

View File

@ -24,15 +24,12 @@ use std::{
process::Stdio,
};
pub use mailpot::{
mail::*,
use mailpot::{
melib::{backends::maildir::MaildirPathTrait, smol, Envelope, EnvelopeHash},
models::{changesets::*, *},
*,
};
mod args;
use args::*;
use mailpot_cli::*;
macro_rules! list {
($db:ident, $list_id:expr) => {{

View File

@ -21,6 +21,7 @@
use assert_cmd::assert::OutputAssertExt;
use mailpot::{
melib,
models::{changesets::ListSubscriptionChangeset, *},
Configuration, Connection, Queue, SendMail,
};

View File

@ -29,6 +29,12 @@ fn main() {
.arg("./src/schema.sql.m4")
.output()
.unwrap();
if String::from_utf8_lossy(&output.stdout).trim().is_empty() {
panic!(
"m4 output is empty. stderr was {}",
String::from_utf8_lossy(&output.stderr)
);
}
let mut verify = Command::new("sqlite3")
.stdin(Stdio::piped())
.stdout(Stdio::piped())

View File

@ -131,6 +131,13 @@ fn user_authorizer_callback(
}
impl Connection {
/// The database schema.
///
/// ```sql
#[doc = include_str!("./schema.sql")]
/// ```
pub const SCHEMA: &str = include_str!("./schema.sql");
/// Creates a new database connection.
///
/// `Connection` supports a limited subset of operations by default (see

View File

@ -48,6 +48,18 @@
//! Mailing list manager library.
//!
//! Data is stored in a `sqlite3` database.
//! You can inspect the schema in [`SCHEMA`](crate::Connection::SCHEMA).
//!
//! # Usage
//!
//! `mailpot` can be used with the CLI tool in [`mailpot-cli`](mailpot-cli),
//! and/or in the web interface of the [`mailpot-web`](mailpot-web) crate.
//!
//! You can also directly use this crate as a library.
//!
//! # Example
//!
//! ```
//! use mailpot::{models::*, Configuration, Connection, SendMail};
//! # use tempfile::TempDir;

View File

@ -2,19 +2,19 @@ PRAGMA foreign_keys = true;
PRAGMA encoding = 'UTF-8';
CREATE TABLE IF NOT EXISTS list (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
id TEXT NOT NULL UNIQUE,
address TEXT NOT NULL UNIQUE,
owner_local_part TEXT,
request_local_part TEXT,
archive_url TEXT,
description TEXT,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
verify BOOLEAN CHECK (verify in (0, 1)) NOT NULL DEFAULT 1,
hidden BOOLEAN CHECK (hidden in (0, 1)) NOT NULL DEFAULT 0,
enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
id TEXT NOT NULL UNIQUE,
address TEXT NOT NULL UNIQUE,
owner_local_part TEXT,
request_local_part TEXT,
archive_url TEXT,
description TEXT,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
verify BOOLEAN CHECK (verify IN (0, 1)) NOT NULL DEFAULT 1,
hidden BOOLEAN CHECK (hidden IN (0, 1)) NOT NULL DEFAULT 0,
enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS owner (
@ -28,100 +28,243 @@ CREATE TABLE IF NOT EXISTS owner (
);
CREATE TABLE IF NOT EXISTS post_policy (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
announce_only BOOLEAN CHECK (announce_only in (0, 1)) NOT NULL DEFAULT 0,
subscription_only BOOLEAN CHECK (subscription_only in (0, 1)) NOT NULL DEFAULT 0,
approval_needed BOOLEAN CHECK (approval_needed in (0, 1)) NOT NULL DEFAULT 0,
open BOOLEAN CHECK (open in (0, 1)) NOT NULL DEFAULT 0,
custom BOOLEAN CHECK (custom in (0, 1)) NOT NULL DEFAULT 0,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
CHECK(((custom) OR (((open) OR (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))) AND NOT ((open) AND (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))))) AND NOT ((custom) AND (((open) OR (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))))) AND NOT ((open) AND (((approval_needed) OR (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only)))) AND NOT ((approval_needed) AND (((announce_only) OR (subscription_only)) AND NOT ((announce_only) AND (subscription_only))))))))),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
announce_only BOOLEAN CHECK (announce_only IN (0, 1)) NOT NULL
DEFAULT 0,
subscription_only BOOLEAN CHECK (subscription_only IN (0, 1)) NOT NULL
DEFAULT 0,
approval_needed BOOLEAN CHECK (approval_needed IN (0, 1)) NOT NULL
DEFAULT 0,
open BOOLEAN CHECK (open IN (0, 1)) NOT NULL DEFAULT 0,
custom BOOLEAN CHECK (custom IN (0, 1)) NOT NULL DEFAULT 0,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
CHECK((
(custom) OR ((
(open) OR ((
(approval_needed) OR ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
)
AND NOT
(
(approval_needed) AND ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
))
)
AND NOT
(
(open) AND ((
(approval_needed) OR ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
)
AND NOT
(
(approval_needed) AND ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
))
))
)
AND NOT
(
(custom) AND ((
(open) OR ((
(approval_needed) OR ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
)
AND NOT
(
(approval_needed) AND ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
))
)
AND NOT
(
(open) AND ((
(approval_needed) OR ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
)
AND NOT
(
(approval_needed) AND ((
(announce_only) OR (subscription_only)
)
AND NOT
(
(announce_only) AND (subscription_only)
))
))
))
)),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS subscription_policy (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
send_confirmation BOOLEAN CHECK (send_confirmation in (0, 1)) NOT NULL DEFAULT 1,
open BOOLEAN CHECK (open in (0, 1)) NOT NULL DEFAULT 0,
manual BOOLEAN CHECK (manual in (0, 1)) NOT NULL DEFAULT 0,
request BOOLEAN CHECK (request in (0, 1)) NOT NULL DEFAULT 0,
custom BOOLEAN CHECK (custom in (0, 1)) NOT NULL DEFAULT 0,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
CHECK(((open) OR (((manual) OR (((request) OR (custom)) AND NOT ((request) AND (custom)))) AND NOT ((manual) AND (((request) OR (custom)) AND NOT ((request) AND (custom)))))) AND NOT ((open) AND (((manual) OR (((request) OR (custom)) AND NOT ((request) AND (custom)))) AND NOT ((manual) AND (((request) OR (custom)) AND NOT ((request) AND (custom))))))),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
send_confirmation BOOLEAN CHECK (send_confirmation IN (0, 1)) NOT NULL
DEFAULT 1,
open BOOLEAN CHECK (open IN (0, 1)) NOT NULL DEFAULT 0,
manual BOOLEAN CHECK (manual IN (0, 1)) NOT NULL DEFAULT 0,
request BOOLEAN CHECK (request IN (0, 1)) NOT NULL DEFAULT 0,
custom BOOLEAN CHECK (custom IN (0, 1)) NOT NULL DEFAULT 0,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
CHECK((
(open) OR ((
(manual) OR ((
(request) OR (custom)
)
AND NOT
(
(request) AND (custom)
))
)
AND NOT
(
(manual) AND ((
(request) OR (custom)
)
AND NOT
(
(request) AND (custom)
))
))
)
AND NOT
(
(open) AND ((
(manual) OR ((
(request) OR (custom)
)
AND NOT
(
(request) AND (custom)
))
)
AND NOT
(
(manual) AND ((
(request) OR (custom)
)
AND NOT
(
(request) AND (custom)
))
))
)),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS subscription (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
account INTEGER,
enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1,
verified BOOLEAN CHECK (verified in (0, 1)) NOT NULL DEFAULT 1,
digest BOOLEAN CHECK (digest in (0, 1)) NOT NULL DEFAULT 0,
hide_address BOOLEAN CHECK (hide_address in (0, 1)) NOT NULL DEFAULT 0,
receive_duplicates BOOLEAN CHECK (receive_duplicates in (0, 1)) NOT NULL DEFAULT 1,
receive_own_posts BOOLEAN CHECK (receive_own_posts in (0, 1)) NOT NULL DEFAULT 0,
receive_confirmation BOOLEAN CHECK (receive_confirmation in (0, 1)) NOT NULL DEFAULT 1,
last_digest INTEGER NOT NULL DEFAULT (unixepoch()),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
account INTEGER,
enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL
DEFAULT 1,
verified BOOLEAN CHECK (verified IN (0, 1)) NOT NULL
DEFAULT 1,
digest BOOLEAN CHECK (digest IN (0, 1)) NOT NULL
DEFAULT 0,
hide_address BOOLEAN CHECK (hide_address IN (0, 1)) NOT NULL
DEFAULT 0,
receive_duplicates BOOLEAN CHECK (receive_duplicates IN (0, 1)) NOT NULL
DEFAULT 1,
receive_own_posts BOOLEAN CHECK (receive_own_posts IN (0, 1)) NOT NULL
DEFAULT 0,
receive_confirmation BOOLEAN CHECK (receive_confirmation IN (0, 1)) NOT NULL
DEFAULT 1,
last_digest INTEGER NOT NULL DEFAULT (unixepoch()),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
FOREIGN KEY (account) REFERENCES account(pk) ON DELETE SET NULL,
UNIQUE (list, address) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS account (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT,
address TEXT NOT NULL UNIQUE,
public_key TEXT,
password TEXT NOT NULL,
enabled BOOLEAN CHECK (enabled in (0, 1)) NOT NULL DEFAULT 1,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT,
address TEXT NOT NULL UNIQUE,
public_key TEXT,
password TEXT NOT NULL,
enabled BOOLEAN CHECK (enabled IN (0, 1)) NOT NULL DEFAULT 1,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE TABLE IF NOT EXISTS candidate_subscription (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
accepted INTEGER UNIQUE,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
accepted INTEGER UNIQUE,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
FOREIGN KEY (accepted) REFERENCES subscription(pk) ON DELETE CASCADE,
UNIQUE (list, address) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS post (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
envelope_from TEXT,
address TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
headers_json TEXT,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
created INTEGER NOT NULL DEFAULT (unixepoch())
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
envelope_from TEXT,
address TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
headers_json TEXT,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
created INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE TABLE IF NOT EXISTS templates (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
list INTEGER,
subject TEXT,
headers_json TEXT,
body TEXT NOT NULL,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
list INTEGER,
subject TEXT,
headers_json TEXT,
body TEXT NOT NULL,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
UNIQUE (list, name) ON CONFLICT ROLLBACK
);
@ -130,47 +273,56 @@ CREATE TABLE IF NOT EXISTS templates (
--
-- ## The "maildrop" queue
--
-- Messages that have been submitted but not yet processed, await processing in
-- the "maildrop" queue. Messages can be added to the "maildrop" queue even when
-- mailpot is not running.
-- Messages that have been submitted but not yet processed, await processing
-- in the "maildrop" queue. Messages can be added to the "maildrop" queue
-- even when mailpot is not running.
--
-- ## The "deferred" queue
--
-- When all the deliverable recipients for a message are delivered, and for some
-- recipients delivery failed for a transient reason (it might succeed later), the
-- message is placed in the "deferred" queue.
-- When all the deliverable recipients for a message are delivered, and for
-- some recipients delivery failed for a transient reason (it might succeed
-- later), the message is placed in the "deferred" queue.
--
-- ## The "hold" queue
--
-- List administrators may introduce rules for emails to be placed indefinitely in
-- the "hold" queue. Messages placed in the "hold" queue stay there until the
-- administrator intervenes. No periodic delivery attempts are made for messages
-- in the "hold" queue.
-- List administrators may introduce rules for emails to be placed
-- indefinitely in the "hold" queue. Messages placed in the "hold" queue stay
-- there until the administrator intervenes. No periodic delivery attempts
-- are made for messages in the "hold" queue.
-- ## The "out" queue
--
-- Emails that must be sent as soon as possible.
CREATE TABLE IF NOT EXISTS queue (
pk INTEGER PRIMARY KEY NOT NULL,
which TEXT CHECK (which IN ('maildrop', 'hold', 'deferred', 'corrupt', 'error', 'out')) NOT NULL,
list INTEGER,
comment TEXT,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
pk INTEGER PRIMARY KEY NOT NULL,
which TEXT
CHECK (
which IN
('maildrop',
'hold',
'deferred',
'corrupt',
'error',
'out')
) NOT NULL,
list INTEGER,
comment TEXT,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
UNIQUE (to_addresses, message_id) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS bounce (
pk INTEGER PRIMARY KEY NOT NULL,
subscription INTEGER NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 0,
last_bounce TEXT NOT NULL DEFAULT (datetime()),
pk INTEGER PRIMARY KEY NOT NULL,
subscription INTEGER NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 0,
last_bounce TEXT NOT NULL DEFAULT (datetime()),
FOREIGN KEY (subscription) REFERENCES subscription(pk) ON DELETE CASCADE
);
@ -179,7 +331,8 @@ CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
CREATE INDEX IF NOT EXISTS list_idx ON list(id);
CREATE INDEX IF NOT EXISTS subscription_idx ON subscription(address);
-- [tag:accept_candidate]: Update candidacy with 'subscription' foreign key on 'subscription' insert.
-- [tag:accept_candidate]: Update candidacy with 'subscription' foreign key on
-- 'subscription' insert.
CREATE TRIGGER IF NOT EXISTS accept_candidate AFTER INSERT ON subscription
FOR EACH ROW
BEGIN
@ -187,13 +340,18 @@ BEGIN
WHERE candidate_subscription.list = NEW.list AND candidate_subscription.address = NEW.address;
END;
-- [tag:verify_subscription_email]: If list settings require e-mail to be verified,
-- update new subscription's 'verify' column value.
-- [tag:verify_subscription_email]: If list settings require e-mail to be
-- verified, update new subscription's 'verify' column value.
CREATE TRIGGER IF NOT EXISTS verify_subscription_email AFTER INSERT ON subscription
FOR EACH ROW
BEGIN
UPDATE subscription SET verified = 0, last_modified = unixepoch()
WHERE subscription.pk = NEW.pk AND EXISTS (SELECT 1 FROM list WHERE pk = NEW.list AND verify = 1);
UPDATE subscription
SET verified = 0, last_modified = unixepoch()
WHERE
subscription.pk = NEW.pk
AND
EXISTS
(SELECT 1 FROM list WHERE pk = NEW.list AND verify = 1);
END;
-- [tag:add_account]: Update list subscription entries with 'account' foreign
@ -206,11 +364,14 @@ BEGIN
END;
-- [tag:add_account_to_subscription]: When adding a new 'subscription', auto
-- set 'account' value if there already exists an 'account' entry with the same
-- address.
CREATE TRIGGER IF NOT EXISTS add_account_to_subscription AFTER INSERT ON subscription
-- set 'account' value if there already exists an 'account' entry with the
-- same address.
CREATE TRIGGER IF NOT EXISTS add_account_to_subscription
AFTER INSERT ON subscription
FOR EACH ROW
WHEN NEW.account IS NULL AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address)
WHEN
NEW.account IS NULL
AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address)
BEGIN
UPDATE subscription
SET account = (SELECT pk FROM account WHERE address = NEW.address),
@ -218,8 +379,11 @@ BEGIN
WHERE subscription.pk = NEW.pk;
END;
-- [tag:last_modified_list] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_list AFTER UPDATE ON list
-- [tag:last_modified_list]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_list
AFTER UPDATE ON list
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -227,8 +391,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_owner] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_owner AFTER UPDATE ON owner
-- [tag:last_modified_owner]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_owner
AFTER UPDATE ON owner
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -236,8 +402,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_post_policy] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_post_policy AFTER UPDATE ON post_policy
-- [tag:last_modified_post_policy]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_post_policy
AFTER UPDATE ON post_policy
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -245,8 +413,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_subscription_policy] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_subscription_policy AFTER UPDATE ON subscription_policy
-- [tag:last_modified_subscription_policy]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_subscription_policy
AFTER UPDATE ON subscription_policy
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -254,8 +424,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_subscription] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_subscription AFTER UPDATE ON subscription
-- [tag:last_modified_subscription]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_subscription
AFTER UPDATE ON subscription
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -263,8 +435,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_account] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_account AFTER UPDATE ON account
-- [tag:last_modified_account]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_account
AFTER UPDATE ON account
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -272,8 +446,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_candidate_subscription] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_candidate_subscription AFTER UPDATE ON candidate_subscription
-- [tag:last_modified_candidate_subscription]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_candidate_subscription
AFTER UPDATE ON candidate_subscription
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
@ -281,8 +457,10 @@ BEGIN
WHERE pk = NEW.pk;
END;
-- [tag:last_modified_templates] update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_templates AFTER UPDATE ON templates
-- [tag:last_modified_templates]: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_templates
AFTER UPDATE ON templates
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN

View File

@ -1,34 +1,54 @@
define(xor, `(($1) OR ($2)) AND NOT (($1) AND ($2))')dnl
define(BOOLEAN_TYPE, `$1 BOOLEAN CHECK ($1 in (0, 1)) NOT NULL')dnl
define(xor, `dnl
(
($1) OR ($2)
)
AND NOT
(
($1) AND ($2)
)')dnl
dnl
dnl # Define boolean column types and defaults
define(BOOLEAN_TYPE, `BOOLEAN CHECK ($1 IN (0, 1)) NOT NULL')dnl
define(BOOLEAN_FALSE, `0')dnl
define(BOOLEAN_TRUE, `1')dnl
define(__TAG, `tag')dnl # Write the string '['+'tag'+':'+... with a macro so that tagref check doesn't pick up on it as a duplicate.
dnl
dnl # defile comment functions
dnl
dnl # Write the string '['+'tag'+':'+... with a macro so that tagref check
dnl # doesn't pick up on it as a duplicate.
define(__TAG, `tag')dnl
define(TAG, `['__TAG()`:$1]')dnl
define(update_last_modified, `-- 'TAG(last_modified_$1)` update last_modified on every change.
CREATE TRIGGER IF NOT EXISTS last_modified_$1 AFTER UPDATE ON $1
dnl
dnl # define triggers
define(update_last_modified, `
-- 'TAG(last_modified_$1)`: update last_modified on every change.
CREATE TRIGGER
IF NOT EXISTS last_modified_$1
AFTER UPDATE ON $1
FOR EACH ROW
WHEN NEW.last_modified != OLD.last_modified
BEGIN
UPDATE $1 SET last_modified = unixepoch()
WHERE pk = NEW.pk;
END;')dnl
dnl
PRAGMA foreign_keys = true;
PRAGMA encoding = 'UTF-8';
CREATE TABLE IF NOT EXISTS list (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
id TEXT NOT NULL UNIQUE,
address TEXT NOT NULL UNIQUE,
owner_local_part TEXT,
request_local_part TEXT,
archive_url TEXT,
description TEXT,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
BOOLEAN_TYPE(verify) DEFAULT BOOLEAN_TRUE(),
BOOLEAN_TYPE(hidden) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE()
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
id TEXT NOT NULL UNIQUE,
address TEXT NOT NULL UNIQUE,
owner_local_part TEXT,
request_local_part TEXT,
archive_url TEXT,
description TEXT,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
verify BOOLEAN_TYPE(verify) DEFAULT BOOLEAN_TRUE(),
hidden BOOLEAN_TYPE(hidden) DEFAULT BOOLEAN_FALSE(),
enabled BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE()
);
CREATE TABLE IF NOT EXISTS owner (
@ -42,100 +62,111 @@ CREATE TABLE IF NOT EXISTS owner (
);
CREATE TABLE IF NOT EXISTS post_policy (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
BOOLEAN_TYPE(announce_only) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(subscription_only) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(approval_needed) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
announce_only BOOLEAN_TYPE(announce_only)
DEFAULT BOOLEAN_FALSE(),
subscription_only BOOLEAN_TYPE(subscription_only)
DEFAULT BOOLEAN_FALSE(),
approval_needed BOOLEAN_TYPE(approval_needed)
DEFAULT BOOLEAN_FALSE(),
open BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(),
custom BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
CHECK(xor(custom, xor(open, xor(approval_needed, xor(announce_only, subscription_only))))),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS subscription_policy (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
BOOLEAN_TYPE(send_confirmation) DEFAULT BOOLEAN_TRUE(),
BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(manual) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(request) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL UNIQUE,
send_confirmation BOOLEAN_TYPE(send_confirmation)
DEFAULT BOOLEAN_TRUE(),
open BOOLEAN_TYPE(open) DEFAULT BOOLEAN_FALSE(),
manual BOOLEAN_TYPE(manual) DEFAULT BOOLEAN_FALSE(),
request BOOLEAN_TYPE(request) DEFAULT BOOLEAN_FALSE(),
custom BOOLEAN_TYPE(custom) DEFAULT BOOLEAN_FALSE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
CHECK(xor(open, xor(manual, xor(request, custom)))),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS subscription (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
account INTEGER,
BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(),
BOOLEAN_TYPE(verified) DEFAULT BOOLEAN_TRUE(),
BOOLEAN_TYPE(digest) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(hide_address) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(receive_duplicates) DEFAULT BOOLEAN_TRUE(),
BOOLEAN_TYPE(receive_own_posts) DEFAULT BOOLEAN_FALSE(),
BOOLEAN_TYPE(receive_confirmation) DEFAULT BOOLEAN_TRUE(),
last_digest INTEGER NOT NULL DEFAULT (unixepoch()),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
account INTEGER,
enabled BOOLEAN_TYPE(enabled)
DEFAULT BOOLEAN_TRUE(),
verified BOOLEAN_TYPE(verified)
DEFAULT BOOLEAN_TRUE(),
digest BOOLEAN_TYPE(digest)
DEFAULT BOOLEAN_FALSE(),
hide_address BOOLEAN_TYPE(hide_address)
DEFAULT BOOLEAN_FALSE(),
receive_duplicates BOOLEAN_TYPE(receive_duplicates)
DEFAULT BOOLEAN_TRUE(),
receive_own_posts BOOLEAN_TYPE(receive_own_posts)
DEFAULT BOOLEAN_FALSE(),
receive_confirmation BOOLEAN_TYPE(receive_confirmation)
DEFAULT BOOLEAN_TRUE(),
last_digest INTEGER NOT NULL DEFAULT (unixepoch()),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
FOREIGN KEY (account) REFERENCES account(pk) ON DELETE SET NULL,
UNIQUE (list, address) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS account (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT,
address TEXT NOT NULL UNIQUE,
public_key TEXT,
password TEXT NOT NULL,
BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT,
address TEXT NOT NULL UNIQUE,
public_key TEXT,
password TEXT NOT NULL,
enabled BOOLEAN_TYPE(enabled) DEFAULT BOOLEAN_TRUE(),
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE TABLE IF NOT EXISTS candidate_subscription (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
accepted INTEGER UNIQUE,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
address TEXT NOT NULL,
name TEXT,
accepted INTEGER UNIQUE,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
FOREIGN KEY (accepted) REFERENCES subscription(pk) ON DELETE CASCADE,
UNIQUE (list, address) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS post (
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
envelope_from TEXT,
address TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
headers_json TEXT,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
created INTEGER NOT NULL DEFAULT (unixepoch())
pk INTEGER PRIMARY KEY NOT NULL,
list INTEGER NOT NULL,
envelope_from TEXT,
address TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
headers_json TEXT,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
created INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE TABLE IF NOT EXISTS templates (
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
list INTEGER,
subject TEXT,
headers_json TEXT,
body TEXT NOT NULL,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
pk INTEGER PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
list INTEGER,
subject TEXT,
headers_json TEXT,
body TEXT NOT NULL,
created INTEGER NOT NULL DEFAULT (unixepoch()),
last_modified INTEGER NOT NULL DEFAULT (unixepoch()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
UNIQUE (list, name) ON CONFLICT ROLLBACK
);
@ -144,47 +175,56 @@ CREATE TABLE IF NOT EXISTS templates (
--
-- ## The "maildrop" queue
--
-- Messages that have been submitted but not yet processed, await processing in
-- the "maildrop" queue. Messages can be added to the "maildrop" queue even when
-- mailpot is not running.
-- Messages that have been submitted but not yet processed, await processing
-- in the "maildrop" queue. Messages can be added to the "maildrop" queue
-- even when mailpot is not running.
--
-- ## The "deferred" queue
--
-- When all the deliverable recipients for a message are delivered, and for some
-- recipients delivery failed for a transient reason (it might succeed later), the
-- message is placed in the "deferred" queue.
-- When all the deliverable recipients for a message are delivered, and for
-- some recipients delivery failed for a transient reason (it might succeed
-- later), the message is placed in the "deferred" queue.
--
-- ## The "hold" queue
--
-- List administrators may introduce rules for emails to be placed indefinitely in
-- the "hold" queue. Messages placed in the "hold" queue stay there until the
-- administrator intervenes. No periodic delivery attempts are made for messages
-- in the "hold" queue.
-- List administrators may introduce rules for emails to be placed
-- indefinitely in the "hold" queue. Messages placed in the "hold" queue stay
-- there until the administrator intervenes. No periodic delivery attempts
-- are made for messages in the "hold" queue.
-- ## The "out" queue
--
-- Emails that must be sent as soon as possible.
CREATE TABLE IF NOT EXISTS queue (
pk INTEGER PRIMARY KEY NOT NULL,
which TEXT CHECK (which IN ('maildrop', 'hold', 'deferred', 'corrupt', 'error', 'out')) NOT NULL,
list INTEGER,
comment TEXT,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
pk INTEGER PRIMARY KEY NOT NULL,
which TEXT
CHECK (
which IN
('maildrop',
'hold',
'deferred',
'corrupt',
'error',
'out')
) NOT NULL,
list INTEGER,
comment TEXT,
to_addresses TEXT NOT NULL,
from_address TEXT NOT NULL,
subject TEXT NOT NULL,
message_id TEXT NOT NULL,
message BLOB NOT NULL,
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
datetime TEXT NOT NULL DEFAULT (datetime()),
FOREIGN KEY (list) REFERENCES list(pk) ON DELETE CASCADE,
UNIQUE (to_addresses, message_id) ON CONFLICT ROLLBACK
);
CREATE TABLE IF NOT EXISTS bounce (
pk INTEGER PRIMARY KEY NOT NULL,
subscription INTEGER NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 0,
last_bounce TEXT NOT NULL DEFAULT (datetime()),
pk INTEGER PRIMARY KEY NOT NULL,
subscription INTEGER NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 0,
last_bounce TEXT NOT NULL DEFAULT (datetime()),
FOREIGN KEY (subscription) REFERENCES subscription(pk) ON DELETE CASCADE
);
@ -193,7 +233,8 @@ CREATE INDEX IF NOT EXISTS post_msgid_idx ON post(message_id);
CREATE INDEX IF NOT EXISTS list_idx ON list(id);
CREATE INDEX IF NOT EXISTS subscription_idx ON subscription(address);
-- TAG(accept_candidate): Update candidacy with 'subscription' foreign key on 'subscription' insert.
-- TAG(accept_candidate): Update candidacy with 'subscription' foreign key on
-- 'subscription' insert.
CREATE TRIGGER IF NOT EXISTS accept_candidate AFTER INSERT ON subscription
FOR EACH ROW
BEGIN
@ -201,13 +242,18 @@ BEGIN
WHERE candidate_subscription.list = NEW.list AND candidate_subscription.address = NEW.address;
END;
-- TAG(verify_subscription_email): If list settings require e-mail to be verified,
-- update new subscription's 'verify' column value.
-- TAG(verify_subscription_email): If list settings require e-mail to be
-- verified, update new subscription's 'verify' column value.
CREATE TRIGGER IF NOT EXISTS verify_subscription_email AFTER INSERT ON subscription
FOR EACH ROW
BEGIN
UPDATE subscription SET verified = BOOLEAN_FALSE(), last_modified = unixepoch()
WHERE subscription.pk = NEW.pk AND EXISTS (SELECT 1 FROM list WHERE pk = NEW.list AND verify = BOOLEAN_TRUE());
UPDATE subscription
SET verified = BOOLEAN_FALSE(), last_modified = unixepoch()
WHERE
subscription.pk = NEW.pk
AND
EXISTS
(SELECT 1 FROM list WHERE pk = NEW.list AND verify = BOOLEAN_TRUE());
END;
-- TAG(add_account): Update list subscription entries with 'account' foreign
@ -220,11 +266,14 @@ BEGIN
END;
-- TAG(add_account_to_subscription): When adding a new 'subscription', auto
-- set 'account' value if there already exists an 'account' entry with the same
-- address.
CREATE TRIGGER IF NOT EXISTS add_account_to_subscription AFTER INSERT ON subscription
-- set 'account' value if there already exists an 'account' entry with the
-- same address.
CREATE TRIGGER IF NOT EXISTS add_account_to_subscription
AFTER INSERT ON subscription
FOR EACH ROW
WHEN NEW.account IS NULL AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address)
WHEN
NEW.account IS NULL
AND EXISTS (SELECT 1 FROM account WHERE address = NEW.address)
BEGIN
UPDATE subscription
SET account = (SELECT pk FROM account WHERE address = NEW.address),
@ -233,17 +282,10 @@ BEGIN
END;
update_last_modified(`list')
update_last_modified(`owner')
update_last_modified(`post_policy')
update_last_modified(`subscription_policy')
update_last_modified(`subscription')
update_last_modified(`account')
update_last_modified(`candidate_subscription')
update_last_modified(`templates')

View File

@ -18,7 +18,7 @@
*/
use log::{trace, warn};
use mailpot::{melib, Configuration, Connection, Queue, SendMail};
use mailpot::{melib, models::*, Configuration, Connection, Queue, SendMail};
use mailpot_tests::*;
use melib::smol;
use tempfile::TempDir;

View File

@ -33,11 +33,6 @@ use mailin_embedded::{
response::{INTERNAL_ERROR, OK},
Handler, Response, Server,
};
pub use mailpot::{
melib::{self, smol, smtp::SmtpServerConf},
models::{changesets::ListSubscriptionChangeset, *},
Configuration, Connection, Queue, SendMail,
};
pub use predicates;
pub use tempfile::{self, TempDir};
@ -84,9 +79,9 @@ pub struct TestSmtpHandler {
address: Cow<'static, str>,
ssl: SslConfig,
envelope_from: Cow<'static, str>,
auth: melib::smtp::SmtpAuth,
auth: mailpot::melib::smtp::SmtpAuth,
pub messages: Arc<Mutex<Vec<((IpAddr, String), Message)>>>,
pub stored: Arc<Mutex<Vec<(String, melib::Envelope)>>>,
pub stored: Arc<Mutex<Vec<(String, mailpot::melib::Envelope)>>>,
}
impl Handler for TestSmtpHandler {
@ -181,7 +176,7 @@ impl Handler for TestSmtpHandler {
let last = self.messages.lock().unwrap().pop();
if let Some(((ip, domain), Message::Data { from: _, to, buf })) = last {
for to in to {
match melib::Envelope::from_bytes(&buf, None) {
match mailpot::melib::Envelope::from_bytes(&buf, None) {
Ok(env) => {
self.stored.lock().unwrap().push((to.clone(), env));
}
@ -202,8 +197,8 @@ impl Handler for TestSmtpHandler {
impl TestSmtpHandler {
#[inline]
pub fn smtp_conf(&self) -> melib::smtp::SmtpServerConf {
use melib::smtp::*;
pub fn smtp_conf(&self) -> mailpot::melib::smtp::SmtpServerConf {
use mailpot::melib::smtp::*;
let sockaddr = self
.address
.as_ref()
@ -234,7 +229,7 @@ impl TestSmtpHandler {
pub struct TestSmtpHandlerBuilder {
address: Cow<'static, str>,
ssl: SslConfig,
auth: melib::smtp::SmtpAuth,
auth: mailpot::melib::smtp::SmtpAuth,
envelope_from: Cow<'static, str>,
}
@ -243,7 +238,7 @@ impl TestSmtpHandlerBuilder {
Self {
address: ADDRESS.into(),
ssl: SslConfig::None,
auth: melib::smtp::SmtpAuth::None,
auth: mailpot::melib::smtp::SmtpAuth::None,
envelope_from: "foo-chat@example.com".into(),
}
}

View File

@ -0,0 +1,18 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pub use mailpot::{models::*, *};
use mailpot::*;
use warp::Filter;
#[tokio::main]

View File

@ -74,7 +74,7 @@ use std::{borrow::Cow, collections::HashMap, sync::Arc};
use chrono::Datelike;
pub use http::{Request, Response, StatusCode};
pub use mailpot::{models::DbVal, rusqlite::OptionalExtension, *};
use mailpot::{models::DbVal, rusqlite::OptionalExtension, *};
use minijinja::{
value::{Object, Value},
Environment, Error,

View File

@ -19,6 +19,7 @@
use std::{collections::HashMap, sync::Arc};
use mailpot::{Configuration, Connection};
use mailpot_web::*;
use minijinja::value::Value;
use rand::Rng;