Server and contracts: public URL on port 443 (D-124)
Kvitt is served on the default HTTPS port through Nginx Proxy Manager, so the OpenAPI server is https://{host} (contracts/openapi.yaml and openapi.rs PUBLIC_SERVER_URL), examples E-7 public_url and E-12 link are https://kvitt.example.invalid without a port, tools/contracts-check.py expects the portless header, and the server's tests, config message and README name no port. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"kind": "partner",
|
||||
"target_user_id": null,
|
||||
"code": "4N8RVT2KW6ZC",
|
||||
"link": "https://kvitt.example.invalid:8443/i/4N8RVT2KW6ZC?from=Jacob",
|
||||
"link": "https://kvitt.example.invalid/i/4N8RVT2KW6ZC?from=Jacob",
|
||||
"created_at": "2026-10-17T06:30:02.004Z",
|
||||
"expires_at": "2026-10-20T06:30:02.004Z"
|
||||
}
|
||||
|
||||
@@ -39,5 +39,5 @@
|
||||
"server_version": 16
|
||||
}
|
||||
],
|
||||
"public_url": "https://kvitt.example.invalid:8443"
|
||||
"public_url": "https://kvitt.example.invalid"
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ info:
|
||||
`protocol_unsupported`, `rate_limited`, `internal`, `unavailable`; unauthenticated ones `rate_limited`,
|
||||
`internal`, `unavailable`. `not_implemented` (501) exists only during milestones M2a to M4.
|
||||
servers:
|
||||
- url: https://{host}:8443
|
||||
- url: https://{host}
|
||||
variables:
|
||||
host:
|
||||
default: kvitt.example.invalid
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ Configuration is environment variables only (SPEC §10.4, exhaustive):
|
||||
|---|---|---|
|
||||
| `KVITT_DATA_DIR` | `/data` | Holds `kvitt.sqlite` (+ `-wal`, `-shm`), `pictures/`, `apk/`. Created if missing. |
|
||||
| `KVITT_BIND` | `0.0.0.0:8080` | Listen address. |
|
||||
| `KVITT_PUBLIC_URL` | none: required | Base URL in invite links and the landing page, `https://<KVITT_PUBLIC_HOST>:8443` on the Pi (set by compose). Every command refuses to start without it. |
|
||||
| `KVITT_PUBLIC_URL` | none: required | Base URL in invite links and the landing page, `https://<KVITT_PUBLIC_HOST>` on the Pi (set by compose). Every command refuses to start without it. |
|
||||
| `KVITT_FCM_CREDENTIALS` | `/secrets/fcm-service-account.json` | FCM key; missing file = push disabled (warning at start). |
|
||||
| `KVITT_FCM_PROJECT_ID` | `kvitt-1337` | FCM project. |
|
||||
| `KVITT_TRUSTED_PROXY` | `none` | IP literal or host name allowed to set `X-Forwarded-For` (on the Pi `HA_HOST`, NPM's LAN address); `none` or a name that does not resolve means no proxy. |
|
||||
|
||||
@@ -69,7 +69,7 @@ impl Config {
|
||||
AppError::new(
|
||||
crate::error::ErrorCode::Internal,
|
||||
"KVITT_PUBLIC_URL is not set: it must be the Kvitt base URL, \
|
||||
https://<KVITT_PUBLIC_HOST>:8443 (SPEC §10.4)",
|
||||
https://<KVITT_PUBLIC_HOST> (SPEC §10.4)",
|
||||
)
|
||||
})?;
|
||||
Ok(Self {
|
||||
@@ -102,7 +102,7 @@ impl Config {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const PUBLIC_URL: &str = "https://kvitt.example.invalid:8443";
|
||||
const PUBLIC_URL: &str = "https://kvitt.example.invalid";
|
||||
|
||||
#[test]
|
||||
fn defaults_when_unset_or_empty() {
|
||||
|
||||
@@ -37,7 +37,7 @@ strings. Common rules: SPEC §14.3.1.";
|
||||
|
||||
/// The production base URL (SPEC §7, §14.3): the host is the server variable [`HOST_VARIABLE`], because the Kvitt
|
||||
/// host name is owner-chosen (SPEC §10.5).
|
||||
pub const PUBLIC_SERVER_URL: &str = "https://{host}:8443";
|
||||
pub const PUBLIC_SERVER_URL: &str = "https://{host}";
|
||||
/// The server variable in [`PUBLIC_SERVER_URL`].
|
||||
pub const HOST_VARIABLE: &str = "host";
|
||||
/// The default of [`HOST_VARIABLE`]: a reserved name that never resolves (RFC 6761), not a real host.
|
||||
|
||||
@@ -23,7 +23,7 @@ use tower::ServiceExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// `KVITT_PUBLIC_URL` of the harness, on the reserved `.invalid` name of the examples (E-7, E-12).
|
||||
pub const PUBLIC_URL: &str = "https://kvitt.example.invalid:8443";
|
||||
pub const PUBLIC_URL: &str = "https://kvitt.example.invalid";
|
||||
pub const ADMIN_ID: &str = "0192a000-0000-7000-8000-000000000001";
|
||||
pub const MEMBER_ID: &str = "0192a000-0000-7000-8000-000000000002";
|
||||
pub const ADMIN_DEVICE_ID: &str = "0192a001-0000-7000-8000-000000000001";
|
||||
|
||||
@@ -12,7 +12,7 @@ fn run(dir: &std::path::Path, args: &[&str]) -> Output {
|
||||
Command::new(env!("CARGO_BIN_EXE_kvitt-server"))
|
||||
.args(args)
|
||||
.env("KVITT_DATA_DIR", dir)
|
||||
.env("KVITT_PUBLIC_URL", "https://kvitt.example:8443")
|
||||
.env("KVITT_PUBLIC_URL", "https://kvitt.example")
|
||||
.env("RUST_LOG", "warn")
|
||||
.output()
|
||||
.unwrap()
|
||||
@@ -26,7 +26,7 @@ fn printed_code(output: &Output) -> String {
|
||||
.find_map(|l| l.strip_prefix("Link:"))
|
||||
.unwrap()
|
||||
.trim();
|
||||
let code = link.strip_prefix("https://kvitt.example:8443/i/").unwrap();
|
||||
let code = link.strip_prefix("https://kvitt.example/i/").unwrap();
|
||||
assert_eq!(code.len(), 12);
|
||||
let display = stdout
|
||||
.lines()
|
||||
@@ -131,7 +131,7 @@ fn printed_partner_code(output: &Output, from: &str) -> String {
|
||||
.find_map(|l| l.strip_prefix("Link:"))
|
||||
.unwrap()
|
||||
.trim();
|
||||
let rest = link.strip_prefix("https://kvitt.example:8443/i/").unwrap();
|
||||
let rest = link.strip_prefix("https://kvitt.example/i/").unwrap();
|
||||
let (code, query) = rest.split_once('?').unwrap();
|
||||
assert_eq!(query, format!("from={from}"));
|
||||
assert_eq!(code.len(), 12);
|
||||
|
||||
@@ -34,9 +34,9 @@ async fn e4_english_by_default_with_both_links_and_the_grouped_code() {
|
||||
assert!(html.contains(">Open in Kvitt</a>"));
|
||||
assert!(html.contains("7K3M-QX9D-2HPA"));
|
||||
assert!(html.contains(
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid%3A8443&code=7K3MQX9D2HPA\""
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid&code=7K3MQX9D2HPA\""
|
||||
));
|
||||
assert!(html.contains("href=\"https://kvitt.example.invalid:8443/apk/files/kvitt-412.apk\""));
|
||||
assert!(html.contains("href=\"https://kvitt.example.invalid/apk/files/kvitt-412.apk\""));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -90,7 +90,7 @@ async fn e4_passes_the_admin_name_of_an_e12_link_into_the_app_link_only() {
|
||||
assert_eq!(reply.status, 200);
|
||||
let html = reply.text();
|
||||
assert!(html.contains(
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid%3A8443&code=7K3MQX9D2HPA&from=%C3%85sa%20Berg\""
|
||||
"href=\"kvitt://invite?server=https%3A%2F%2Fkvitt%2Eexample%2Einvalid&code=7K3MQX9D2HPA&from=%C3%85sa%20Berg\""
|
||||
), "{html}");
|
||||
// The name is not shown on the page itself.
|
||||
assert!(!html.contains("Åsa"));
|
||||
|
||||
@@ -49,7 +49,7 @@ impl TestApp {
|
||||
let dir = TempDir::new();
|
||||
let config = Config::from_lookup(|k| match k {
|
||||
"KVITT_DATA_DIR" => Some(dir.0.to_string_lossy().into_owned()),
|
||||
"KVITT_PUBLIC_URL" => Some("https://kvitt.example.invalid:8443".to_owned()),
|
||||
"KVITT_PUBLIC_URL" => Some("https://kvitt.example.invalid".to_owned()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -1471,7 +1471,7 @@ def check_openapi():
|
||||
lines = f.read().splitlines()
|
||||
text = "\n".join(lines)
|
||||
head_ok = (re.search(r"^openapi: 3\.1\.0$", text, re.M) and re.search(r"^ title: Kvitt$", text, re.M)
|
||||
and re.search(r"^ version: '1'$", text, re.M) and re.search(r"^ - url: https://\{host\}:8443\n variables:\n host:\n default: kvitt\.example\.invalid$", text, re.M))
|
||||
and re.search(r"^ version: '1'$", text, re.M) and re.search(r"^ - url: https://\{host\}\n variables:\n host:\n default: kvitt\.example\.invalid$", text, re.M))
|
||||
report(bool(head_ok), "openapi header (3.1.0, title Kvitt, version 1, one server)", "" if head_ok else "header fields differ from §14.3")
|
||||
sec_ok = re.search(r"^ device:\n type: http\n scheme: bearer$", text, re.M) is not None
|
||||
report(sec_ok, "openapi security scheme device = HTTP bearer (§8.2)", "" if sec_ok else "components.securitySchemes.device not found")
|
||||
|
||||
Reference in New Issue
Block a user