feat(dev): add scripts/dev-seed to populate sample test data
New scripts/dev-seed.sh (+ .ps1), run after dev-server-init: seeds the test server with Users (alice, bob, carol — spanning User/Admin roles, group membership, an alias), Groups (engineering, marketing), Mailing Lists (newsletter, support), and custom Roles (Support Agent, Read-only Auditor), so each admin panel screen has real data to test against instead of an empty list. Idempotent (skips if 'alice' already exists), documented as workflow step 3 in DEVELOPMENT.md (renumbering the steps after it) and listed in AGENTS.md. Verified end-to-end with both scripts against a fresh server, including idempotency and the resulting UI (Accounts, Groups, Mailing Lists, Roles all populated as expected).
This commit is contained in:
@@ -28,6 +28,8 @@ scripts/*
|
|||||||
!scripts/dev-token.sh
|
!scripts/dev-token.sh
|
||||||
!scripts/dev-server-init.ps1
|
!scripts/dev-server-init.ps1
|
||||||
!scripts/dev-server-init.sh
|
!scripts/dev-server-init.sh
|
||||||
|
!scripts/dev-seed.ps1
|
||||||
|
!scripts/dev-seed.sh
|
||||||
*.md
|
*.md
|
||||||
!README.md
|
!README.md
|
||||||
!CHANGELOG.md
|
!CHANGELOG.md
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ Community fork of [stalwartlabs/webui](https://github.com/stalwartlabs/webui), a
|
|||||||
|
|
||||||
- `npm run dev:server` - Start a local disposable Stalwart test server (Docker); `npm run dev:server:down` to stop it
|
- `npm run dev:server` - Start a local disposable Stalwart test server (Docker); `npm run dev:server:down` to stop it
|
||||||
- `bash scripts/dev-server-init.sh` (or `pwsh ./scripts/dev-server-init.ps1`) - One-time setup of that server (bootstrap, dev admin account, 3h default token lifetime); idempotent
|
- `bash scripts/dev-server-init.sh` (or `pwsh ./scripts/dev-server-init.ps1`) - One-time setup of that server (bootstrap, dev admin account, 3h default token lifetime); idempotent
|
||||||
|
- `bash scripts/dev-seed.sh` (or `pwsh ./scripts/dev-seed.ps1`) - Optional: seed sample Users/Groups/Mailing Lists/Roles for testing; idempotent
|
||||||
- `bash scripts/dev-token.sh [duration_seconds]` (or `pwsh ./scripts/dev-token.ps1 [-DurationSeconds N]`) - Get a dev access token from that server, 3h by default
|
- `bash scripts/dev-token.sh [duration_seconds]` (or `pwsh ./scripts/dev-token.ps1 [-DurationSeconds N]`) - Get a dev access token from that server, 3h by default
|
||||||
- `npm run dev` - Dev server (proxies `/api` and `/jmap` to the test server)
|
- `npm run dev` - Dev server (proxies `/api` and `/jmap` to the test server)
|
||||||
- `npm run typecheck` - `tsc --noEmit`
|
- `npm run typecheck` - `tsc --noEmit`
|
||||||
|
|||||||
+35
-7
@@ -65,7 +65,33 @@ idempotent — safe to re-run, it no-ops once the server is already
|
|||||||
bootstrapped. You only need to re-run it after `docker compose down -v`
|
bootstrapped. You only need to re-run it after `docker compose down -v`
|
||||||
(which wipes the volumes).
|
(which wipes the volumes).
|
||||||
|
|
||||||
## 3. Get an access token
|
## 3. Seed sample data (optional)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows / PowerShell
|
||||||
|
pwsh ./scripts/dev-seed.ps1
|
||||||
|
|
||||||
|
# Linux / macOS / any POSIX shell (including most AI agent sandboxes)
|
||||||
|
bash ./scripts/dev-seed.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Populates Users, Groups, Mailing Lists, and Roles with sample data so
|
||||||
|
every admin panel screen has something to test against:
|
||||||
|
|
||||||
|
- **Users**: `alice@example.org` / `AlicePass123!` (User role, in
|
||||||
|
engineering, 1 alias), `bob@example.org` / `BobPass123!` (User role, in
|
||||||
|
engineering + marketing), `carol@example.org` / `CarolPass123!` (Admin
|
||||||
|
role, in marketing) — alongside the `devadmin`/`admin` accounts from
|
||||||
|
step 2.
|
||||||
|
- **Groups**: `engineering`, `marketing`.
|
||||||
|
- **Mailing lists**: `newsletter@example.org`, `support@example.org`.
|
||||||
|
- **Roles**: `Support Agent`, `Read-only Auditor` (custom, alongside the
|
||||||
|
built-in System Administrator/Tenant Administrator/Group/User roles).
|
||||||
|
|
||||||
|
Idempotent — does nothing if `alice` already exists. Only needed once per
|
||||||
|
fresh volume, same as step 2.
|
||||||
|
|
||||||
|
## 4. Get an access token
|
||||||
|
|
||||||
For local development it's simpler to skip interactive login and use a
|
For local development it's simpler to skip interactive login and use a
|
||||||
bearer token directly via `VITE_ACCESS_TOKEN` (see `.env.development`).
|
bearer token directly via `VITE_ACCESS_TOKEN` (see `.env.development`).
|
||||||
@@ -92,7 +118,7 @@ The `STALWART_RECOVERY_ADMIN` account is intentionally not used here: it's
|
|||||||
a break-glass credential and its tokens always expire in a fixed 1 hour
|
a break-glass credential and its tokens always expire in a fixed 1 hour
|
||||||
regardless of server configuration, so it can't honor a custom duration.
|
regardless of server configuration, so it can't honor a custom duration.
|
||||||
|
|
||||||
## 4. Run the WebUI
|
## 5. Run the WebUI
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install # first time only
|
npm install # first time only
|
||||||
@@ -102,7 +128,7 @@ npm run dev
|
|||||||
Open `http://localhost:5173`. You should land directly in the admin panel
|
Open `http://localhost:5173`. You should land directly in the admin panel
|
||||||
(no login screen) since `VITE_ACCESS_TOKEN` is set.
|
(no login screen) since `VITE_ACCESS_TOKEN` is set.
|
||||||
|
|
||||||
## 5. Verify your change
|
## 6. Verify your change
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run typecheck
|
npm run typecheck
|
||||||
@@ -125,6 +151,7 @@ npm run dev:server:down
|
|||||||
docker compose down -v # also removes the stalwart-etc/stalwart-data volumes
|
docker compose down -v # also removes the stalwart-etc/stalwart-data volumes
|
||||||
npm run dev:server
|
npm run dev:server
|
||||||
bash ./scripts/dev-server-init.sh # re-run: fresh volume needs bootstrapping again
|
bash ./scripts/dev-server-init.sh # re-run: fresh volume needs bootstrapping again
|
||||||
|
bash ./scripts/dev-seed.sh # optional: re-seed sample data too
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
@@ -148,11 +175,12 @@ bash ./scripts/dev-server-init.sh # re-run: fresh volume needs bootstrapping a
|
|||||||
|
|
||||||
## Notes for AI agents
|
## Notes for AI agents
|
||||||
|
|
||||||
- This whole workflow (steps 1–4) is scriptable end-to-end without a
|
- This whole workflow (steps 1–5) is scriptable end-to-end without a
|
||||||
browser: `npm run dev:server`, then `bash scripts/dev-server-init.sh`
|
browser: `npm run dev:server`, then `bash scripts/dev-server-init.sh`
|
||||||
(first time only), then `bash scripts/dev-token.sh`, then the app is
|
and `bash scripts/dev-seed.sh` (first time only), then
|
||||||
reachable at `http://localhost:5173` with `VITE_ACCESS_TOKEN` already
|
`bash scripts/dev-token.sh`, then the app is reachable at
|
||||||
set. Verify backend connectivity directly with `curl`, e.g.
|
`http://localhost:5173` with `VITE_ACCESS_TOKEN` already set. Verify
|
||||||
|
backend connectivity directly with `curl`, e.g.
|
||||||
`curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/jmap/session`.
|
`curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/jmap/session`.
|
||||||
- Read [AGENTS.md](AGENTS.md) before touching anything under `src/` —
|
- Read [AGENTS.md](AGENTS.md) before touching anything under `src/` —
|
||||||
the schema-fidelity rule applies to all development, local test server
|
the schema-fidelity rule applies to all development, local test server
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#Requires -Version 5.1
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Populates the disposable Stalwart test server with sample Users, Groups,
|
||||||
|
Mailing Lists, and Roles, so each admin panel screen has something to
|
||||||
|
click through and test against.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Local development only. Run after scripts/dev-server-init.ps1.
|
||||||
|
Idempotent: does nothing if the seed users already exist. Only a fresh
|
||||||
|
volume (`docker compose down -v`) clears this data.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$ApiBaseUrl = "http://localhost:8080"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$DevAdminAccount = "devadmin@example.org"
|
||||||
|
$DevAdminSecret = "DevAdminPass123!"
|
||||||
|
|
||||||
|
$creds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$($DevAdminAccount):$($DevAdminSecret)"))
|
||||||
|
$authHeader = @{ Authorization = "Basic $creds" }
|
||||||
|
|
||||||
|
function Invoke-Jmap($body) {
|
||||||
|
Invoke-RestMethod -Uri "$ApiBaseUrl/jmap/" -Method Post -ContentType "application/json" -Headers $authHeader -Body ($body | ConvertTo-Json -Depth 10 -Compress) -TimeoutSec 15
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$session = Invoke-RestMethod -Uri "$ApiBaseUrl/jmap/session" -Headers $authHeader -TimeoutSec 15
|
||||||
|
} catch {
|
||||||
|
throw "Could not reach $ApiBaseUrl as $DevAdminAccount. Run scripts/dev-server-init.ps1 first."
|
||||||
|
}
|
||||||
|
$accountId = $session.primaryAccounts.'urn:stalwart:jmap'
|
||||||
|
|
||||||
|
$existing = Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Account/query", @{ accountId = $accountId; filter = @{ text = "alice" } }, "0"))
|
||||||
|
}
|
||||||
|
if ($existing.methodResponses[0][1].ids.Count -gt 0) {
|
||||||
|
Write-Host "Seed data already present (found 'alice'), skipping. Use 'docker compose down -v' + dev-server-init.ps1 to start fresh."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$domainResult = Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Domain/query", @{ accountId = $accountId }, "0"))
|
||||||
|
}
|
||||||
|
$domainId = $domainResult.methodResponses[0][1].ids[0]
|
||||||
|
|
||||||
|
Write-Host "Creating groups..."
|
||||||
|
$groupsResult = Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Account/set", @{
|
||||||
|
accountId = $accountId
|
||||||
|
create = @{
|
||||||
|
g_eng = @{ "@type" = "Group"; name = "engineering"; domainId = $domainId; description = "Engineering team" }
|
||||||
|
g_mkt = @{ "@type" = "Group"; name = "marketing"; domainId = $domainId; description = "Marketing team" }
|
||||||
|
}
|
||||||
|
}, "0"))
|
||||||
|
}
|
||||||
|
$groupEngId = $groupsResult.methodResponses[0][1].created.g_eng.id
|
||||||
|
$groupMktId = $groupsResult.methodResponses[0][1].created.g_mkt.id
|
||||||
|
|
||||||
|
Write-Host "Creating users..."
|
||||||
|
$usersResult = Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Account/set", @{
|
||||||
|
accountId = $accountId
|
||||||
|
create = @{
|
||||||
|
u_alice = @{
|
||||||
|
"@type" = "User"; name = "alice"; domainId = $domainId; description = "Alice Smith"
|
||||||
|
roles = @{ "@type" = "User" }
|
||||||
|
credentials = @{ "0" = @{ "@type" = "Password"; secret = "AlicePass123!" } }
|
||||||
|
memberGroupIds = @{ $groupEngId = $true }
|
||||||
|
}
|
||||||
|
u_bob = @{
|
||||||
|
"@type" = "User"; name = "bob"; domainId = $domainId; description = "Bob Jones"
|
||||||
|
roles = @{ "@type" = "User" }
|
||||||
|
credentials = @{ "0" = @{ "@type" = "Password"; secret = "BobPass123!" } }
|
||||||
|
memberGroupIds = @{ $groupEngId = $true; $groupMktId = $true }
|
||||||
|
}
|
||||||
|
u_carol = @{
|
||||||
|
"@type" = "User"; name = "carol"; domainId = $domainId; description = "Carol Diaz"
|
||||||
|
roles = @{ "@type" = "Admin" }
|
||||||
|
credentials = @{ "0" = @{ "@type" = "Password"; secret = "CarolPass123!" } }
|
||||||
|
memberGroupIds = @{ $groupMktId = $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "0"))
|
||||||
|
}
|
||||||
|
$userAliceId = $usersResult.methodResponses[0][1].created.u_alice.id
|
||||||
|
|
||||||
|
Write-Host "Adding an alias to alice (for the Aliases column)..."
|
||||||
|
Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Account/set", @{
|
||||||
|
accountId = $accountId
|
||||||
|
update = @{ $userAliceId = @{ aliases = @{ "0" = @{ name = "a.smith"; domainId = $domainId } } } }
|
||||||
|
}, "0"))
|
||||||
|
} | Out-Null
|
||||||
|
|
||||||
|
Write-Host "Creating mailing lists..."
|
||||||
|
Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:MailingList/set", @{
|
||||||
|
accountId = $accountId
|
||||||
|
create = @{
|
||||||
|
m_news = @{
|
||||||
|
name = "newsletter"; domainId = $domainId; description = "Company newsletter"
|
||||||
|
recipients = @{ "alice@example.org" = $true; "bob@example.org" = $true; "carol@example.org" = $true }
|
||||||
|
}
|
||||||
|
m_support = @{
|
||||||
|
name = "support"; domainId = $domainId; description = "Support queue"
|
||||||
|
recipients = @{ "bob@example.org" = $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "0"))
|
||||||
|
} | Out-Null
|
||||||
|
|
||||||
|
Write-Host "Creating roles..."
|
||||||
|
Invoke-Jmap @{
|
||||||
|
using = @("urn:ietf:params:jmap:core", "urn:stalwart:jmap")
|
||||||
|
methodCalls = @(, @("x:Role/set", @{
|
||||||
|
accountId = $accountId
|
||||||
|
create = @{
|
||||||
|
r_support = @{ description = "Support Agent"; enabledPermissions = @{ authenticate = $true; emailSend = $true; emailReceive = $true } }
|
||||||
|
r_audit = @{ description = "Read-only Auditor"; enabledPermissions = @{ authenticate = $true } }
|
||||||
|
}
|
||||||
|
}, "0"))
|
||||||
|
} | Out-Null
|
||||||
|
|
||||||
|
Write-Host "Done. Seeded:"
|
||||||
|
Write-Host " Users: alice@example.org / AlicePass123! (User role, in engineering, 1 alias)"
|
||||||
|
Write-Host " bob@example.org / BobPass123! (User role, in engineering + marketing)"
|
||||||
|
Write-Host " carol@example.org / CarolPass123! (Admin role, in marketing)"
|
||||||
|
Write-Host " Groups: engineering, marketing"
|
||||||
|
Write-Host " Mailing lists: newsletter@example.org, support@example.org"
|
||||||
|
Write-Host " Roles: Support Agent, Read-only Auditor"
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Local development only. Populates the disposable Stalwart test server
|
||||||
|
# (docker-compose.yml) with sample data across Users, Groups, Mailing Lists,
|
||||||
|
# and Roles, so each admin panel screen has something to click through and
|
||||||
|
# test against. Run after scripts/dev-server-init.sh.
|
||||||
|
#
|
||||||
|
# Idempotent: does nothing if the seed users already exist (re-running
|
||||||
|
# scripts/dev-server-init.sh does not wipe this data; only a fresh volume
|
||||||
|
# via `docker compose down -v` does).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
API_BASE_URL="${1:-http://localhost:8080}"
|
||||||
|
DEVADMIN_ACCOUNT="devadmin@example.org"
|
||||||
|
DEVADMIN_SECRET="DevAdminPass123!"
|
||||||
|
|
||||||
|
jmap() {
|
||||||
|
curl -sf --compressed -u "$DEVADMIN_ACCOUNT:$DEVADMIN_SECRET" \
|
||||||
|
-X POST -H "Content-Type: application/json" -d "$1" "$API_BASE_URL/jmap/"
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_id() {
|
||||||
|
# $1 = JSON response, $2 = create-id key (e.g. "u1")
|
||||||
|
printf '%s' "$1" | grep -o "\"$2\":{\"id\":\"[^\"]*\"" | head -1 | grep -o '"id":"[^"]*"' | cut -d'"' -f4
|
||||||
|
}
|
||||||
|
|
||||||
|
SESSION=$(curl -sf --compressed -u "$DEVADMIN_ACCOUNT:$DEVADMIN_SECRET" "$API_BASE_URL/jmap/session") || {
|
||||||
|
echo "Could not reach $API_BASE_URL as $DEVADMIN_ACCOUNT. Run scripts/dev-server-init.sh first." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
ACCOUNT_ID=$(printf '%s' "$SESSION" | grep -o '"urn:stalwart:jmap":"[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
||||||
|
EXISTING=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/query\",{\"accountId\":\"$ACCOUNT_ID\",\"filter\":{\"text\":\"alice\"}},\"0\"]]}")
|
||||||
|
if printf '%s' "$EXISTING" | grep -q '"ids":\["'; then
|
||||||
|
echo "Seed data already present (found 'alice'), skipping. Use 'docker compose down -v' + dev-server-init.sh to start fresh."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOMAIN_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Domain/query\",{\"accountId\":\"$ACCOUNT_ID\"},\"0\"]]}")
|
||||||
|
DOMAIN_ID=$(printf '%s' "$DOMAIN_RESULT" | grep -o '"ids":\["[^"]*"' | cut -d'"' -f4)
|
||||||
|
|
||||||
|
echo "Creating groups..."
|
||||||
|
GROUPS_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{
|
||||||
|
\"g_eng\":{\"@type\":\"Group\",\"name\":\"engineering\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Engineering team\"},
|
||||||
|
\"g_mkt\":{\"@type\":\"Group\",\"name\":\"marketing\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Marketing team\"}
|
||||||
|
}},\"0\"]]}")
|
||||||
|
GROUP_ENG_ID=$(extract_id "$GROUPS_RESULT" g_eng)
|
||||||
|
GROUP_MKT_ID=$(extract_id "$GROUPS_RESULT" g_mkt)
|
||||||
|
|
||||||
|
echo "Creating users..."
|
||||||
|
USERS_RESULT=$(jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{
|
||||||
|
\"u_alice\":{\"@type\":\"User\",\"name\":\"alice\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Alice Smith\",\"roles\":{\"@type\":\"User\"},\"credentials\":{\"0\":{\"@type\":\"Password\",\"secret\":\"AlicePass123!\"}},\"memberGroupIds\":{\"$GROUP_ENG_ID\":true}},
|
||||||
|
\"u_bob\":{\"@type\":\"User\",\"name\":\"bob\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Bob Jones\",\"roles\":{\"@type\":\"User\"},\"credentials\":{\"0\":{\"@type\":\"Password\",\"secret\":\"BobPass123!\"}},\"memberGroupIds\":{\"$GROUP_ENG_ID\":true,\"$GROUP_MKT_ID\":true}},
|
||||||
|
\"u_carol\":{\"@type\":\"User\",\"name\":\"carol\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Carol Diaz\",\"roles\":{\"@type\":\"Admin\"},\"credentials\":{\"0\":{\"@type\":\"Password\",\"secret\":\"CarolPass123!\"}},\"memberGroupIds\":{\"$GROUP_MKT_ID\":true}}
|
||||||
|
}},\"0\"]]}")
|
||||||
|
USER_ALICE_ID=$(extract_id "$USERS_RESULT" u_alice)
|
||||||
|
|
||||||
|
echo "Adding an alias to alice (for the Aliases column)..."
|
||||||
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Account/set\",{\"accountId\":\"$ACCOUNT_ID\",\"update\":{\"$USER_ALICE_ID\":{\"aliases\":{\"0\":{\"name\":\"a.smith\",\"domainId\":\"$DOMAIN_ID\"}}}}},\"0\"]]}" > /dev/null
|
||||||
|
|
||||||
|
echo "Creating mailing lists..."
|
||||||
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:MailingList/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{
|
||||||
|
\"m_news\":{\"name\":\"newsletter\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Company newsletter\",\"recipients\":{\"alice@example.org\":true,\"bob@example.org\":true,\"carol@example.org\":true}},
|
||||||
|
\"m_support\":{\"name\":\"support\",\"domainId\":\"$DOMAIN_ID\",\"description\":\"Support queue\",\"recipients\":{\"bob@example.org\":true}}
|
||||||
|
}},\"0\"]]}" > /dev/null
|
||||||
|
|
||||||
|
echo "Creating roles..."
|
||||||
|
jmap "{\"using\":[\"urn:ietf:params:jmap:core\",\"urn:stalwart:jmap\"],\"methodCalls\":[[\"x:Role/set\",{\"accountId\":\"$ACCOUNT_ID\",\"create\":{
|
||||||
|
\"r_support\":{\"description\":\"Support Agent\",\"enabledPermissions\":{\"authenticate\":true,\"emailSend\":true,\"emailReceive\":true}},
|
||||||
|
\"r_audit\":{\"description\":\"Read-only Auditor\",\"enabledPermissions\":{\"authenticate\":true}}
|
||||||
|
}},\"0\"]]}" > /dev/null
|
||||||
|
|
||||||
|
echo "Done. Seeded:"
|
||||||
|
echo " Users: alice@example.org / AlicePass123! (User role, in engineering, 1 alias)"
|
||||||
|
echo " bob@example.org / BobPass123! (User role, in engineering + marketing)"
|
||||||
|
echo " carol@example.org / CarolPass123! (Admin role, in marketing)"
|
||||||
|
echo " Groups: engineering, marketing"
|
||||||
|
echo " Mailing lists: newsletter@example.org, support@example.org"
|
||||||
|
echo " Roles: Support Agent, Read-only Auditor"
|
||||||
Reference in New Issue
Block a user