Firmy a prava: tenance napric portalem

Portal nemel zadnou tenanci. Kterykoliv prihlaseny uzivatel videl vsechny
tickety vsech firem i cely seznam resitelu, requireRole se nikde nevolal.

Tenant je hranice viditelnosti, tenantId na ticketu, resiteli i automatizaci.
Uzivatel muze patrit do vic firem, v kazde s jinou roli. Pristup napric firmami
je zvlast jako platformAdmin.

Tri pohledy na tickety: all, tenant, mine. Admin mezi nimi prepina vcetne
vyberu firmy. O pravech rozhoduje jedine data/access.ts, klient si nic
nedovozuje a bere je z GET /api/dashboard/access.

Filtr na firmu je v ulozistich povinny argument, takze zapomenuty filtr
neznamena vse, ale nezkompiluje se. Cizi firma vraci 403 nebo 404, nikdy
tise zuzeny vysledek.

Prirazeni jen v ramci firmy. Prehazovat praci mezi lidmi smi jen admin,
agent si smi vzit ticket na sebe.

Zmena prihlasovani: ucet klient@firma.cz zanikl, demo ucty jsou nove.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-08-03 12:59:30 +02:00
parent dd021b5f69
commit 2a3d75c85e
25 changed files with 997 additions and 163 deletions
+76 -4
View File
@@ -49,12 +49,53 @@ export function buildOpenApiDocument() {
},
User: {
type: 'object',
description:
'Uzivatel muze patrit do vic firem. Role je vzdy az uvnitr firmy, ' +
'pristup napric firmami je zvlast jako platformAdmin.',
properties: {
id: { type: 'string', example: 'usr_1' },
email: { type: 'string', example: 'admin@automia.cz' },
name: { type: 'string', example: 'Jiri Uhlir' },
role: { type: 'string', enum: ['admin', 'client'] },
company: { type: 'string', example: 'Automia' },
platformAdmin: {
type: 'boolean',
description: 'Vidi napric vsemi firmami a muze mezi nimi prepinat.',
},
memberships: {
type: 'array',
items: {
type: 'object',
properties: {
tenantId: { type: 'string', example: 'tnt_automia' },
role: { type: 'string', enum: ['admin', 'agent'] },
},
},
},
},
},
Access: {
type: 'object',
description: 'Co uzivatel smi. Klient podle toho kresli prepinac pohledu.',
properties: {
scopes: {
type: 'array',
items: { type: 'string', enum: ['all', 'tenant', 'mine'] },
},
tenants: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string', example: 'tnt_automia' },
name: { type: 'string', example: 'Automia' },
},
},
},
defaultTenantId: { type: 'string', nullable: true },
canAssignOthers: {
type: 'boolean',
description: 'Smi prehazovat tickety mezi lidmi, ne jen brat na sebe.',
},
personId: { type: 'string', nullable: true },
},
},
LoginRequest: {
@@ -419,6 +460,23 @@ export function buildOpenApiDocument() {
responses: { '200': { description: 'Souhrnne metriky a casova rada' } },
},
},
'/api/dashboard/access': {
get: {
tags: ['Dashboard'],
summary: 'Co uzivatel smi videt',
description:
'Povolene pohledy, firmy k prepinani a prava. Klient si to nesmi dovozovat sam.',
security: [{ bearerAuth: [] }],
responses: {
'200': {
description: 'Opravneni',
content: {
'application/json': { schema: { $ref: '#/components/schemas/Access' } },
},
},
},
},
},
'/api/dashboard/people': {
get: {
tags: ['Tickety'],
@@ -452,13 +510,27 @@ export function buildOpenApiDocument() {
description: 'Neznama hodnota filtru se ignoruje a zaloguje, seznam se nezuzi.',
security: [{ bearerAuth: [] }],
parameters: [
{
name: 'scope',
in: 'query',
schema: { type: 'string', enum: ['all', 'tenant', 'mine'] },
description:
'`all` napric firmami (jen platformni admin), `tenant` cela firma, ' +
'`mine` jen moje tickety. Nepovoleny pohled vraci 403, nikdy se tise nezuzi.',
},
{
name: 'tenantId',
in: 'query',
schema: { type: 'string' },
description: 'Firma u pohledu `tenant` a `mine`. Bez clenstvi vraci 404.',
example: 'tnt_automia',
},
{
name: 'assignee',
in: 'query',
schema: { type: 'string' },
description:
'ID resitele, `me` pro prihlaseneho uzivatele, nebo `unassigned` pro frontu.',
example: 'me',
'ID resitele nebo `unassigned` pro frontu. U pohledu `mine` se ignoruje.',
},
{
name: 'status',