fix(psa): move CW clientId to server config, remove from user input

ClientId is a product-level GUID registered at developer.connectwise.com,
not per-MSP. Moved to settings.CW_CLIENT_ID env var. MSPs now only
provide site URL, company ID, public key, and private key.

Also added newline handling note to post_note() — CW Developer Guide
states \n is unsupported in JSON bodies. Needs sandbox testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-15 00:15:04 -04:00
parent db305f54e7
commit 7f3f054583
7 changed files with 26 additions and 28 deletions

View File

@@ -28,6 +28,7 @@ from app.schemas.psa_connection import (
PsaMemberResponse,
AutoMatchResult,
)
from app.core.config import settings
from app.services.psa.encryption import (
decrypt_credentials,
encrypt_credentials,
@@ -130,6 +131,9 @@ async def create_connection(
if not current_user.account_id:
raise HTTPException(status.HTTP_400_BAD_REQUEST, "No account associated with user")
if not settings.CW_CLIENT_ID:
raise HTTPException(status.HTTP_503_SERVICE_UNAVAILABLE, "ConnectWise integration is not configured on this server")
# Check for existing connection
existing = await _get_connection(current_user.account_id, db)
if existing:
@@ -145,7 +149,7 @@ async def create_connection(
company_id=data.company_id,
public_key=data.public_key,
private_key=data.private_key,
client_id=data.client_id,
client_id=settings.CW_CLIENT_ID,
)
if not test_result.success:
raise HTTPException(
@@ -156,7 +160,6 @@ async def create_connection(
credentials = {
"public_key": data.public_key,
"private_key": data.private_key,
"client_id": data.client_id,
}
conn = PsaConnection(
@@ -189,7 +192,7 @@ async def update_connection(
creds = decrypt_credentials(conn.credentials_encrypted)
# Track whether credential fields changed
cred_fields = {"public_key", "private_key", "client_id"}
cred_fields = {"public_key", "private_key"}
cred_changed = False
# Apply updates
@@ -213,7 +216,7 @@ async def update_connection(
company_id=company_id_val,
public_key=creds["public_key"],
private_key=creds["private_key"],
client_id=creds["client_id"],
client_id=settings.CW_CLIENT_ID or "",
)
if not test_result.success:
raise HTTPException(
@@ -263,7 +266,7 @@ async def test_connection(
company_id=conn.company_id,
public_key=creds["public_key"],
private_key=creds["private_key"],
client_id=creds["client_id"],
client_id=settings.CW_CLIENT_ID or "",
)
if result.success: