A first-party Rust clan is a Team (R5). This module becomes the site's Team provider and answers core from the plugin's `clans` board. Design of record: docs/modules/rust/PLAN.md §24, D47-D58. - The store: rust_clans, rust_clan_members and rust_clan_boards. A clan's identity is <serverId>:<clanId>:<createdMs> (D52), because the game restarts clan ids whenever its clan database version changes. - The provider (D53): getTeams is complete only when every server's board is fresh, supported and untruncated. It is partial when some are, and refuses when none are. Freshness is judged by the website's clock, from when the board's `t` last advanced. - Only a complete board may mark a clan gone. A board at the game's 100-clan ceiling (D55), or one with an unreadable row, proves nothing about what it leaves out. - Leadership is diffed board to board and published (D54). The five clan events are published as team.* kinds, and written to the Team feed as members-only lines (D49). - Core only writes feed items for a Team it already holds. So the last 10 minutes of clan events are re-offered on each board refresh, deduped by a sha1 key: core clamps a dedupeKey to 40 characters, and a readable key would be truncated into collisions. - projectRoster and the clan page share one audience rule (D48): the clan's linked members and staff by default, re-read from the users row. The setting lives on Admin > Rust visibility, which also warns about uMod Clans (D47) and the ceiling. - Public: GET servers/:id/clans (the list is public, D58) and GET clans/:externalId. The client adds a Clans tab and /rust/clans/:externalId, with three module slots for core's notify, activity and forum contributions (D56). - Linking and unlinking an account ask core to reconcile Teams (D57). - The clan kinds are staff-class in the public feed allowlist. - PROTOCOL_VERSION is now 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
54 lines
3.6 KiB
JavaScript
54 lines
3.6 KiB
JavaScript
// ── Admin · Rust · Visibility ─────────────────────────────────────────────
|
||
//
|
||
// Mounted under the admin tier's `/rust` prefix, so every path here is
|
||
// `/api/v1/admin/rust/visibility`. Who may see what the servers say about the
|
||
// people on them — a fourth subject beside the bridge, the permissions and the
|
||
// mod configuration.
|
||
//
|
||
// **Every route is `requireRole('admin')`.** The tier's own gate admits editors
|
||
// and moderators, and a moderator widening the roll call to the public is the
|
||
// decision the org lead settled should be deliberate. Reading is gated the same
|
||
// as writing: the screen is one form, and a view of the settings without the
|
||
// power to change them is not something anybody has asked for.
|
||
|
||
const core = require('../../core')
|
||
|
||
const express = core.express
|
||
const visibility = require('./visibility.controller')
|
||
const { requireRole, validate } = core.middleware
|
||
const { body } = core.validator
|
||
|
||
const visibilityRouter = express.Router()
|
||
|
||
const AUDIENCES = ['staff', 'signed_in', 'public']
|
||
const CLAN_AUDIENCES = ['members', 'signed_in', 'public']
|
||
|
||
visibilityRouter.get(
|
||
'/',
|
||
// #swagger.tags = ['Admin · Rust']
|
||
// #swagger.summary = 'Who may see who is online, and who may see a clan roster'
|
||
// #swagger.description = 'The presence fleet default and every server’s optional override. It governs the Online list, every feed item that names a player who was on the server (connects, respawns, deaths, chat, tallies) and the leaderboard’s `lastSeen`. The default is `staff`: nothing names who is online until an operator widens it. The player count is public at every setting. `clans` carries the clan roster audience (default `members`: the clan’s own linked members, and staff) and each server’s clan board — whether it is current, at the game’s 100-clan ceiling, or running the uMod Clans plugin, whose clans are not Teams.'
|
||
/* #swagger.responses[200] = { description: 'The fleet default and each server', content: { "application/json": { schema: { $ref: "#/components/schemas/RustVisibility" } } } } */
|
||
requireRole('admin'),
|
||
visibility.read,
|
||
)
|
||
|
||
visibilityRouter.put(
|
||
'/',
|
||
// #swagger.tags = ['Admin · Rust']
|
||
// #swagger.summary = 'Change who may see who is online, or who may see a clan roster'
|
||
// #swagger.description = 'Sets the presence fleet default, one or more server overrides, the clan roster audience, or any of them together. A server set to `null` follows the fleet default again. Validated whole before anything is written: a request naming a server that does not exist changes nothing. Widening the clan roster audience also shows which members are online to that audience, because a roster row carries it.'
|
||
/* #swagger.requestBody = { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/RustVisibilityUpdate" } } } } */
|
||
/* #swagger.responses[200] = { description: 'Saved; answers the new state', content: { "application/json": { schema: { $ref: "#/components/schemas/RustVisibility" } } } } */
|
||
/* #swagger.responses[400] = { description: 'An audience that does not exist' } */
|
||
/* #swagger.responses[404] = { description: 'A server that does not exist' } */
|
||
requireRole('admin'),
|
||
body('fleet').optional().isIn(AUDIENCES).withMessage(`fleet must be one of ${AUDIENCES.join(', ')}`),
|
||
body('servers').optional().isObject().withMessage('servers maps a server id to an audience or null'),
|
||
body('clanRoster').optional().isIn(CLAN_AUDIENCES).withMessage(`clanRoster must be one of ${CLAN_AUDIENCES.join(', ')}`),
|
||
validate,
|
||
visibility.update,
|
||
)
|
||
|
||
module.exports = visibilityRouter
|