Login: bare sign-in page at /login; Telegram bridge connection guide
This commit is contained in:
parent
d313ce0bec
commit
22f9e12141
2 changed files with 157 additions and 5 deletions
|
|
@ -1,9 +1,71 @@
|
|||
import { redirect } from "next/navigation";
|
||||
"use client";
|
||||
|
||||
import { useState, type FormEvent } from "react";
|
||||
import Link from "next/link";
|
||||
import { Button, Card, Input } from "../../components/ui/primitives";
|
||||
import { api, errorMessage } from "../../lib/api-client";
|
||||
import { useAuth } from "../../lib/auth-store";
|
||||
|
||||
/**
|
||||
* /login — the sign-in door. The onboarding page holds both forms; this route
|
||||
* deep-links into sign-in mode (register stays the default at /signup).
|
||||
* /login — bare sign-in: username and password, nothing else. Account creation
|
||||
* lives at /signup; collective joining happens with an invite link.
|
||||
*/
|
||||
export default function LoginRedirect(): React.ReactElement {
|
||||
redirect("/onboarding?mode=signin");
|
||||
export default function LoginPage(): React.ReactElement {
|
||||
const { login } = useAuth();
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
async function submit(e: FormEvent): Promise<void> {
|
||||
e.preventDefault();
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await api.post<unknown>("/auth/login", { username: username.trim(), password }, { redirectOn401: false });
|
||||
window.location.assign("/streams");
|
||||
} catch (err) {
|
||||
setError(errorMessage(err));
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full items-center justify-center bg-bg p-6">
|
||||
<Card className="w-[420px] max-w-full p-6">
|
||||
<div className="mb-5 text-center">
|
||||
<h1 className="text-xl font-semibold tracking-tight">Sign in</h1>
|
||||
<p className="mt-1 text-2xs text-text-faint">Tactical Media Broadcast Hub</p>
|
||||
</div>
|
||||
<form onSubmit={(e) => void submit(e)}>
|
||||
<div className="space-y-2">
|
||||
<Input
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="username"
|
||||
autoComplete="username"
|
||||
autoFocus
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Password"
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
{error !== null && <p className="mt-2 text-xs text-danger">{error}</p>}
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<Link href="/signup" className="text-xs text-text-faint underline hover:text-text">
|
||||
Create an account
|
||||
</Link>
|
||||
<Button type="submit" variant="primary" disabled={busy || username.trim() === "" || password === ""}>
|
||||
{busy ? "Signing in…" : "Sign in"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
90
docs/telegram-bridge-guide.md
Normal file
90
docs/telegram-bridge-guide.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Telegram bridge — connection and admin guide
|
||||
|
||||
Status: bridge live on the chat CT (mautrix-telegram, appservice registered with
|
||||
tuwunel). Bot: **@occupymedia_bot**. This page is the operator + end-user guide.
|
||||
|
||||
## What it does
|
||||
|
||||
Each collective's **open room** can be attached to a **Telegram group**.
|
||||
Outsiders talk from Telegram with no Matrix account; collective members answer
|
||||
from Element. Everything in the open room still flows through the archive
|
||||
(shares into the group's files folder, indexed, searchable).
|
||||
|
||||
Closed production rooms are **never** bridged — they are end-to-end encrypted
|
||||
and the bridge has no keys. This is by design.
|
||||
|
||||
## For collective admins
|
||||
|
||||
1. Create the Telegram group (public link recommended — it's the outsiders'
|
||||
door) or use an existing one.
|
||||
2. Add **@occupymedia_bot** to the group and **promote it to admin**.
|
||||
3. In BotFather: `/setprivacy` → @occupymedia_bot → **Disable** — without this
|
||||
the bot cannot read group messages.
|
||||
4. From Matrix (Element), in the collective's open room, a bridge admin sends
|
||||
the bridge command to link the room. The bridge bot account
|
||||
(@telegrambot:chat.occupymedia.cc) then relays both ways.
|
||||
|
||||
Contribution policy reminder (disclosed at join time): messages and files that
|
||||
outsiders post through the open room are archived into the collective's
|
||||
library the same as member posts. Telegram participants are subject to the
|
||||
same moderation (redaction propagates; archive entries get marked
|
||||
unavailable and links die).
|
||||
|
||||
## For outsiders (Telegram side)
|
||||
|
||||
- Join the collective's public Telegram group.
|
||||
- Everything you post is visible to the collective and archived in their
|
||||
public library. Same rules as posting to any public channel of the
|
||||
collective.
|
||||
- You do not need a Matrix account. No account is created on this server for
|
||||
you beyond a relay placeholder.
|
||||
|
||||
## How it works (for operators)
|
||||
|
||||
- The bridge is mautrix-telegram on CT 152, compose project `occ-chat`,
|
||||
service `mautrix-telegram` (profile `bridges`), config + database in the
|
||||
`occ-chat_mautrix_telegram_data` volume.
|
||||
- It registers with tuwunel as an appservice (inline declaration in
|
||||
`/opt/occupymedia/tuwunel.toml`, `[appservice.telegram]` — as_token,
|
||||
hs_token, sender_localpart `telegrambot`, user namespaces for
|
||||
`@telegrambot` and `@telegram_*` ghosts).
|
||||
- Telegram API credentials: `api_id` / `api_hash` from the operator's
|
||||
my.telegram.org app "OccupyMediaBridge" + the BotFather token for
|
||||
@occupymedia_bot, all in the bridge's `config.yaml` (mode 600, in the data
|
||||
volume — never in git).
|
||||
- Appservice registration file: `/opt/occupymedia/appservices/registration.yaml`
|
||||
(mounted read-only into tuwunel at `/appservices`, loaded via
|
||||
`appservice_dir`).
|
||||
|
||||
## Operations
|
||||
|
||||
| Task | How |
|
||||
|---|---|
|
||||
| Restart bridge | `docker restart occ-chat-mautrix-telegram-1` (CT 152) |
|
||||
| Logs | `docker logs -f occ-chat-mautrix-telegram-1` |
|
||||
| Config | volume `occ-chat_mautrix_telegram_data` → `config.yaml` |
|
||||
| Re-register appservice with tuwunel | admin room: `!admin appservices list` / `register` (or the inline TOML section — both load) |
|
||||
| Known-good boot | logs end with the bridge serving on :29317 and no `Configuration error` |
|
||||
|
||||
## Failure modes seen in practice
|
||||
|
||||
- `as_token was not accepted` → the appservice registration is not loaded by
|
||||
tuwunel (check the inline section and `/appservices` mount), or the tokens
|
||||
were regenerated on one side only.
|
||||
- `invalid permissions level X` → this mautrix generation only accepts
|
||||
`relay`, `commands`, `user`, `admin` in `bridge.permissions` — "relaybot"
|
||||
and "full" are not valid.
|
||||
- `disk quota exceeded` at container start → host kernel keyring quota
|
||||
(LXC+Docker); raised + persisted on the host in
|
||||
`/etc/sysctl.d/99-lxc-keys.conf`.
|
||||
- oCIS `strconv.ParseInt ""` on uploads → a client sent a chunked PUT without
|
||||
Content-Length; fixed in the archive bot (buffer + explicit
|
||||
content-length).
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Telegram login (userbot) for the operator's own account — richer bridging
|
||||
(history, DMs), at the cost of operating a userbot (ban risk if it behaves
|
||||
spammy). Not enabled; the bot path covers the collective use case.
|
||||
- Per-collective Telegram groups auto-created at collective creation.
|
||||
- Contribution-policy banners posted into bridged groups by the guide bot.
|
||||
Loading…
Add table
Add a link
Reference in a new issue