diff --git a/lib/towerops_web/controllers/api_docs_html/index.html.heex b/lib/towerops_web/controllers/api_docs_html/index.html.heex index 5ec51b0d..35ca220a 100644 --- a/lib/towerops_web/controllers/api_docs_html/index.html.heex +++ b/lib/towerops_web/controllers/api_docs_html/index.html.heex @@ -1420,6 +1420,1588 @@ Content-Disposition: attachment; filename="towerops-data-{user_id}-{timestamp}.j
+ + +
+

+ Alerts +

+

+ Alerts represent monitoring events triggered when a device check fails or recovers. You can list, view, acknowledge, and resolve alerts. +

+ + +
+

The alert model

+
+
+
+
+ id string +
+
Unique identifier (UUID).
+
+
+
+ alert_type string +
+
Type of alert (e.g. "ping_down", "snmp_down").
+
+
+
+ message string +
+
Human-readable alert message.
+
+
+
+ triggered_at timestamp +
+
When the alert was triggered.
+
+
+
+ acknowledged_at timestamp +
+
When the alert was acknowledged (null if not acknowledged).
+
+
+
+ resolved_at timestamp +
+
When the alert was resolved (null if still active).
+
+
+
+ device_id string +
+
The device this alert belongs to.
+
+
+
+ device_name string +
+
Name of the associated device.
+
+
+
+ acknowledged_by_email string +
+
Email of the user who acknowledged this alert.
+
+
+
+ gaiia_impact string +
+
AI-generated impact assessment (if available).
+
+
+
+
+ +
+ + +
+
+

List all alerts

+ + GET + + /api/v1/alerts +
+
+
+

+ Returns all alerts for the authenticated organization. +

+

Optional parameters

+
+
+
+ status string +
+
Filter by status (e.g. "active", "acknowledged", "resolved").
+
+
+
+ device_id string +
+
Filter alerts for a specific device.
+
+
+
+ limit integer +
+
Maximum number of alerts to return.
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/alerts \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -d status=active
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
+      "alert_type": "ping_down",
+      "message": "Device is not responding to ping",
+      "triggered_at": "2026-02-14T10:30:00Z",
+      "acknowledged_at": null,
+      "resolved_at": null,
+      "device_id": "550e8400-e29b-41d4-a716-446655440000",
+      "device_name": "Core Router",
+      "acknowledged_by_email": null,
+      "gaiia_impact": "High - affects 12 downstream devices",
+      "inserted_at": "2026-02-14T10:30:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Retrieve an alert

+ + GET + + /api/v1/alerts/:id +
+
+
+

Retrieves a single alert by its ID.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl https://towerops.net/api/v1/alerts/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+ +
+ + +
+
+

Acknowledge an alert

+ + POST + + /api/v1/alerts/:id/acknowledge +
+
+
+

+ Marks an alert as acknowledged. The acknowledging user is recorded automatically. +

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/alerts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/acknowledge \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": {
+    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
+    "alert_type": "ping_down",
+    "message": "Device is not responding to ping",
+    "triggered_at": "2026-02-14T10:30:00Z",
+    "acknowledged_at": "2026-02-14T11:00:00Z",
+    "resolved_at": null,
+    "device_id": "550e8400-e29b-41d4-a716-446655440000",
+    "device_name": "Core Router",
+    "acknowledged_by_email": "admin@example.com",
+    "gaiia_impact": "High - affects 12 downstream devices",
+    "inserted_at": "2026-02-14T10:30:00Z"
+  }
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Resolve an alert

+ + POST + + /api/v1/alerts/:id/resolve +
+
+
+

+ Manually resolves an alert. Alerts may also be resolved automatically when the underlying condition clears. +

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/alerts/a1b2c3d4-e5f6-7890-abcd-ef1234567890/resolve \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Agents +

+

+ Agents are lightweight monitoring agents that run on your infrastructure to collect device data. Each agent has a token used for authentication. +

+ + +
+

The agent model

+
+
+
+
+ id string +
+
Unique identifier (UUID).
+
+
+
+ name string +
+
Name of the agent.
+
+
+
+ enabled boolean +
+
Whether the agent is enabled.
+
+
+
+ last_seen_at timestamp +
+
When the agent last checked in.
+
+
+
+ last_ip string +
+
Last known IP address of the agent.
+
+
+
+ metadata object +
+
Agent metadata (version, OS, etc.).
+
+
+
+
+ +
+ + +
+
+

List all agents

+ + GET + + /api/v1/agents +
+
+
+

Returns all agent tokens for the authenticated organization.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/agents \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
+      "name": "datacenter-agent-01",
+      "enabled": true,
+      "last_seen_at": "2026-02-14T11:00:00Z",
+      "last_ip": "10.0.1.50",
+      "metadata": {"version": "1.4.2", "os": "linux"},
+      "inserted_at": "2026-01-10T08:00:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Create an agent

+ + POST + + /api/v1/agents +
+
+
+

+ Creates a new agent token. The raw token is returned only once in the response — store it securely. +

+

Required parameters

+
+
+
+ name string +
+
A descriptive name for the agent.
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/agents \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"name": "new-site-agent"}'
+""") %>
+
+
+
+

Response (201 Created)

+
+
<%= raw("""
+{
+  "data": {
+    "id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
+    "name": "new-site-agent",
+    "enabled": true,
+    "last_seen_at": null,
+    "last_ip": null,
+    "metadata": null,
+    "inserted_at": "2026-02-14T11:10:00Z",
+    "token": "tow_agent_abc123..."
+  }
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Retrieve an agent

+ + GET + + /api/v1/agents/:id +
+
+
+

Retrieves a single agent by ID. Includes a count of assigned devices.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl https://towerops.net/api/v1/agents/b2c3d4e5-f6a7-8901-bcde-f12345678901 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": {
+    "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
+    "name": "datacenter-agent-01",
+    "enabled": true,
+    "last_seen_at": "2026-02-14T11:00:00Z",
+    "last_ip": "10.0.1.50",
+    "metadata": {"version": "1.4.2", "os": "linux"},
+    "inserted_at": "2026-01-10T08:00:00Z",
+    "device_count": 24
+  }
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Delete an agent

+ + DELETE + + /api/v1/agents/:id +
+
+
+

Permanently deletes an agent token. Devices assigned to this agent will need to be reassigned.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X DELETE https://towerops.net/api/v1/agents/b2c3d4e5-f6a7-8901-bcde-f12345678901 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response (204 No Content)

+
+
<%= raw("""
+(empty response body)
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Organization +

+

+ Retrieve and update settings for the authenticated organization, including SNMP defaults, MikroTik configuration, and general preferences. +

+ + +
+

The organization model

+
+
+
+
+ id string +
+
Unique identifier (UUID).
+
+
+
+ name string +
+
Organization display name.
+
+
+
+ slug string +
+
URL-friendly identifier.
+
+
+
+ subscription_plan string +
+
Current subscription plan.
+
+
+
+ snmp_version string +
+
Default SNMP version (v2c or v3).
+
+
+
+ snmp_community string +
+
Default SNMP community string.
+
+
+
+ mikrotik_enabled boolean +
+
Whether MikroTik API integration is enabled.
+
+
+
+
+ +
+ + +
+
+

Get organization settings

+ + GET + + /api/v1/organization +
+
+
+

Returns the full settings for the authenticated organization.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl https://towerops.net/api/v1/organization \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": {
+    "id": "org-uuid-here",
+    "name": "Acme Networks",
+    "slug": "acme-networks",
+    "subscription_plan": "pro",
+    "use_sites": true,
+    "snmp_version": "v2c",
+    "snmp_community": "public",
+    "snmp_port": 161,
+    "snmp_transport": "udp",
+    "snmpv3_security_level": null,
+    "snmpv3_username": null,
+    "snmpv3_auth_protocol": null,
+    "snmpv3_priv_protocol": null,
+    "mikrotik_enabled": false,
+    "mikrotik_username": null,
+    "mikrotik_port": null,
+    "mikrotik_ssh_port": null,
+    "mikrotik_use_ssl": false,
+    "default_agent_token_id": null,
+    "inserted_at": "2025-06-01T00:00:00Z",
+    "updated_at": "2026-02-14T10:00:00Z"
+  }
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Update organization settings

+ + PATCH + + /api/v1/organization +
+
+
+

+ Updates organization settings. Only the provided fields are changed. +

+

Parameters (all optional)

+
+
+
+ organization[name] string +
+
Organization display name.
+
+
+
+ organization[snmp_community] string +
+
Default SNMP community string.
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X PATCH https://towerops.net/api/v1/organization \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"organization": {"name": "Acme Networks LLC", "snmp_community": "private"}}'
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Members +

+

+ Manage the users who belong to your organization. You can list members, change roles, and remove members. +

+ + +
+

The member model

+
+
+
+
+ id string +
+
User ID of the member.
+
+
+
+ email string +
+
Email address of the member.
+
+
+
+ role string +
+
Member's role (e.g. "owner", "admin", "member").
+
+
+
+
+ +
+ + +
+
+

List all members

+ + GET + + /api/v1/members +
+
+
+

Returns all members of the authenticated organization.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/members \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "user-uuid-1",
+      "email": "admin@example.com",
+      "role": "owner",
+      "inserted_at": "2025-06-01T00:00:00Z"
+    },
+    {
+      "id": "user-uuid-2",
+      "email": "tech@example.com",
+      "role": "admin",
+      "inserted_at": "2025-08-15T00:00:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Change member role

+ + PATCH + + /api/v1/members/:user_id +
+
+
+

Updates a member's role. Cannot change the owner's role.

+

Required parameters

+
+
+
+ role string +
+
New role for the member (e.g. "admin", "member").
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X PATCH https://towerops.net/api/v1/members/user-uuid-2 \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"role": "member"}'
+""") %>
+
+
+
+
+ +
+ + +
+
+

Remove a member

+ + DELETE + + /api/v1/members/:user_id +
+
+
+

Removes a member from the organization. The organization owner cannot be removed.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X DELETE https://towerops.net/api/v1/members/user-uuid-2 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Invitations +

+

+ Invite new users to your organization. Invitations expire after 7 days. +

+ + +
+

The invitation model

+
+
+
+
+ id string +
+
Unique identifier (UUID).
+
+
+
+ email string +
+
Email address of the invited user.
+
+
+
+ role string +
+
Role the user will receive upon accepting.
+
+
+
+ expires_at timestamp +
+
When the invitation expires.
+
+
+
+ invited_by_email string +
+
Email of the user who sent the invitation.
+
+
+
+
+ +
+ + +
+
+

List pending invitations

+ + GET + + /api/v1/invitations +
+
+
+

Returns all pending invitations for the organization.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/invitations \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "inv-uuid-1",
+      "email": "newuser@example.com",
+      "role": "member",
+      "expires_at": "2026-02-21T11:00:00Z",
+      "invited_by_email": "admin@example.com",
+      "inserted_at": "2026-02-14T11:00:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Create an invitation

+ + POST + + /api/v1/invitations +
+
+
+

Sends an invitation to join the organization.

+

Required parameters

+
+
+
+ email string +
+
Email address to invite.
+
+
+
+ role string +
+
Role to assign (e.g. "admin", "member").
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/invitations \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"email": "newuser@example.com", "role": "member"}'
+""") %>
+
+
+
+
+ +
+ + +
+
+

Cancel an invitation

+ + DELETE + + /api/v1/invitations/:id +
+
+
+

Cancels a pending invitation.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X DELETE https://towerops.net/api/v1/invitations/inv-uuid-1 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Integrations +

+

+ Manage third-party integrations for your organization, such as notification providers and external monitoring systems. +

+ + +
+

The integration model

+
+
+
+
+ id string +
+
Unique identifier (UUID).
+
+
+
+ provider string +
+
Integration provider name (e.g. "slack", "pagerduty", "email").
+
+
+
+ enabled boolean +
+
Whether the integration is active.
+
+
+
+ sync_interval_minutes integer +
+
How often the integration syncs (in minutes).
+
+
+
+ last_synced_at timestamp +
+
When the integration last synced.
+
+
+
+ last_sync_status string +
+
Status of the last sync ("success", "error", etc.).
+
+
+
+
+ +
+ + +
+
+

List all integrations

+ + GET + + /api/v1/integrations +
+
+
+

Returns all integrations for the organization.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/integrations \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "intg-uuid-1",
+      "provider": "slack",
+      "enabled": true,
+      "sync_interval_minutes": 5,
+      "last_synced_at": "2026-02-14T11:00:00Z",
+      "last_sync_status": "success",
+      "inserted_at": "2025-12-01T00:00:00Z",
+      "updated_at": "2026-02-14T11:00:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Create an integration

+ + POST + + /api/v1/integrations +
+
+
+

Creates a new integration.

+

Required parameters

+
+
+
+ integration[provider] string +
+
Provider name (e.g. "slack", "pagerduty").
+
+
+

Optional parameters

+
+
+
+ integration[enabled] boolean +
+
Whether to enable the integration immediately (default: true).
+
+
+
+ integration[sync_interval_minutes] integer +
+
Sync interval in minutes.
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/integrations \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"integration": {"provider": "slack", "enabled": true, "sync_interval_minutes": 5}}'
+""") %>
+
+
+
+
+ +
+ + +
+
+

Retrieve an integration

+ + GET + + /api/v1/integrations/:id +
+
+
+

Retrieves a single integration by ID.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl https://towerops.net/api/v1/integrations/intg-uuid-1 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+ +
+ + +
+
+

Update an integration

+ + PATCH + + /api/v1/integrations/:id +
+
+
+

Updates an integration's settings.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X PATCH https://towerops.net/api/v1/integrations/intg-uuid-1 \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -H "Content-Type: application/json" \\
+  -d '{"integration": {"enabled": false}}'
+""") %>
+
+
+
+
+ +
+ + +
+
+

Delete an integration

+ + DELETE + + /api/v1/integrations/:id +
+
+
+

Permanently deletes an integration.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X DELETE https://towerops.net/api/v1/integrations/intg-uuid-1 \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+
+ +
+ + +
+
+

Test connection

+ + POST + + /api/v1/integrations/:id/test +
+
+
+

Tests the connection for an integration to verify it's properly configured.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -X POST https://towerops.net/api/v1/integrations/intg-uuid-1/test \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": {
+    "status": "ok",
+    "provider": "slack"
+  }
+}
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Check Results & Metrics +

+

+ Access monitoring check results, historical metrics, and SNMP interface data for your devices. +

+ +
+ + +
+
+

List device checks

+ + GET + + /api/v1/devices/:device_id/checks +
+
+
+

Returns all monitoring checks configured for a device, including the latest result for each check.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/devices/550e8400-e29b-41d4-a716-446655440000/checks \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "check-uuid-1",
+      "name": "ICMP Ping",
+      "check_type": "ping",
+      "enabled": true,
+      "interval_seconds": 60,
+      "latest_status": "ok",
+      "latest_value": 12.5,
+      "latest_checked_at": "2026-02-14T11:10:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

Get device metrics

+ + GET + + /api/v1/devices/:device_id/metrics +
+
+
+

Returns historical check results for a device, useful for graphing and trend analysis.

+

Optional parameters

+
+
+
+ hours integer +
+
Number of hours of history to return (default: 24).
+
+
+
+ check_type string +
+
Filter by check type (e.g. "ping", "snmp").
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/devices/550e8400-e29b-41d4-a716-446655440000/metrics \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -d hours=48 \\
+  -d check_type=ping
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "check_id": "check-uuid-1",
+      "check_name": "ICMP Ping",
+      "check_type": "ping",
+      "data": [
+        {"timestamp": "2026-02-14T10:00:00Z", "value": 11.2, "status": "ok"},
+        {"timestamp": "2026-02-14T10:01:00Z", "value": 13.8, "status": "ok"},
+        {"timestamp": "2026-02-14T10:02:00Z", "value": null, "status": "timeout"}
+      ]
+    }
+  ]
+}
+""") %>
+
+
+
+
+ +
+ + +
+
+

List device interfaces

+ + GET + + /api/v1/devices/:device_id/interfaces +
+
+
+

Returns SNMP-discovered network interfaces for a device.

+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/devices/550e8400-e29b-41d4-a716-446655440000/interfaces \\
+  -H "Authorization: Bearer #{@sample_token}"
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "id": "iface-uuid-1",
+      "if_index": 1,
+      "if_name": "GigabitEthernet0/0",
+      "if_descr": "GigabitEthernet0/0",
+      "if_alias": "Uplink to ISP",
+      "if_speed": 1000000000,
+      "if_admin_status": "up",
+      "if_oper_status": "up",
+      "monitored": true
+    }
+  ]
+}
+""") %>
+
+
+
+
+
+ +
+ + +
+

+ Activity Feed +

+

+ View a chronological feed of actions and events across your organization. +

+ +
+ + +
+
+

List organization activity

+ + GET + + /api/v1/activity +
+
+
+

Returns recent activity events for the organization.

+

Optional parameters

+
+
+
+ limit integer +
+
Maximum number of events to return (default: 50).
+
+
+
+ types string +
+
Comma-separated list of event types to filter by.
+
+
+
+
+
+
+

Request

+
+
<%= raw("""
+curl -G https://towerops.net/api/v1/activity \\
+  -H "Authorization: Bearer #{@sample_token}" \\
+  -d limit=20
+""") %>
+
+
+
+

Response

+
+
<%= raw("""
+{
+  "data": [
+    {
+      "type": "device_created",
+      "message": "Device 'Core Router' was added",
+      "user_email": "admin@example.com",
+      "inserted_at": "2026-02-14T10:30:00Z"
+    },
+    {
+      "type": "alert_triggered",
+      "message": "Alert triggered: ping_down on Core Router",
+      "user_email": null,
+      "inserted_at": "2026-02-14T10:35:00Z"
+    }
+  ]
+}
+""") %>
+
+
+
+
+
+ +