Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
170b147
PROD DEPLOY 8/6/25 (#2116)
tevko Aug 6, 2025
55286fd
clean up delphi root.
colinmegill Jul 26, 2025
aaad1d2
repair toipcMapNarrativeReport import
colinmegill Jul 27, 2025
1ccb867
topic stats rendering
colinmegill Jul 27, 2025
9bc3b76
reset conversation script
colinmegill Jul 27, 2025
64b8fb6
looking at cross topic gic measures
colinmegill Jul 27, 2025
ba584c0
reset jobs uitl
colinmegill Jul 27, 2025
39e92a7
reset batch util
colinmegill Jul 27, 2025
74f68b2
rename notebooks folder
colinmegill Jul 27, 2025
400a4d2
move markdown files to docs
colinmegill Jul 31, 2025
a797d8c
batch complete job
colinmegill Jul 31, 2025
d3cb69e
revert dynamo scan
colinmegill Jul 31, 2025
77630a7
show button on low divisive statemtns
colinmegill Aug 1, 2025
36cd792
collective statements render
colinmegill Aug 1, 2025
6d6d650
table sort
colinmegill Aug 1, 2025
90e7bb6
plotly scatterplot topic metrics
colinmegill Aug 2, 2025
4478856
update prompt
colinmegill Aug 2, 2025
57c66e3
avg group aware consensus
colinmegill Aug 3, 2025
54c3567
blazing fast, great.
colinmegill Aug 3, 2025
e93738c
green yellow red
colinmegill Aug 3, 2025
daeaf65
all comments plot
colinmegill Aug 3, 2025
7ead8af
sort!
colinmegill Aug 3, 2025
0429b24
collective statement modal
colinmegill Aug 3, 2025
9f0c2ca
refactor componetize
colinmegill Aug 3, 2025
0355dcc
three modals.
colinmegill Aug 3, 2025
ece08c5
modals.
colinmegill Aug 4, 2025
2539e2e
topic page.
colinmegill Aug 4, 2025
00cd031
topic page
colinmegill Aug 4, 2025
cc66592
typography
colinmegill Aug 4, 2025
dba4a5e
button cleanup
colinmegill Aug 4, 2025
20120ff
button color
colinmegill Aug 4, 2025
43991d1
disable topic mod link for now
colinmegill Aug 4, 2025
9791073
topic prioritize delete prototypte
colinmegill Aug 5, 2025
435911c
remove topicAgenda since now copied to ptpt alpha
colinmegill Aug 5, 2025
7d7f9b4
put back participnat topic prioritize
colinmegill Aug 5, 2025
c677471
lint hierarchy
colinmegill Aug 5, 2025
6cb1551
Normalized group aware consensus calculation
colinmegill Aug 6, 2025
b027fc2
thresholding .05 3@.8
colinmegill Aug 7, 2025
8ac7998
url format, prompt tweak, copywrite,
colinmegill Aug 7, 2025
9a71e26
broken collective statement carousel alpha
colinmegill Aug 7, 2025
2a835ba
collective statemetn carousel
colinmegill Aug 7, 2025
a89a191
carousel
colinmegill Aug 7, 2025
57853bf
embed logic
colinmegill Aug 7, 2025
6005e61
clean up collective statmetns before rerun
colinmegill Aug 7, 2025
87bed41
Te client participation 2 (#2098)
tevko Aug 7, 2025
a7de50c
Merge branch 'stable' into edge
tevko Aug 8, 2025
1bb5dc5
update compose-test
tevko Aug 8, 2025
16ef887
fix test compose network name
tevko Aug 8, 2025
b9cb2a9
compose test cert volume
tevko Aug 8, 2025
0e49340
update test action
tevko Aug 8, 2025
283f174
add cpa to test file
tevko Aug 8, 2025
c068bc0
gh actions copy certs for tests
tevko Aug 8, 2025
87328b7
PROD DEPLOY 8/7 (#2120)
tevko Aug 8, 2025
a710ff9
only show most recent version in carousel
colinmegill Aug 8, 2025
8b566b0
fix token in header
tevko Aug 8, 2025
1b39bb3
Merge branch 'stable' into edge
tevko Aug 8, 2025
aba3e8b
update token logic
tevko Aug 8, 2025
5a31863
PROD DEPLOY 2 8/7 (#2121)
tevko Aug 8, 2025
f7bc9ff
change token process
tevko Aug 8, 2025
f75e337
Merge branch 'stable' into edge
tevko Aug 8, 2025
dd4b9ff
Merge pull request #2122 from compdemocracy/edge
colinmegill Aug 8, 2025
231b70d
token cleanup and load topic fix (#2123)
tevko Aug 8, 2025
5317cb2
Merge branch 'edge' into colinmegill/collectiveStatement
colinmegill Aug 8, 2025
2409fdb
Merge remote-tracking branch 'origin/edge' into colinmegill/collectiv…
colinmegill Aug 8, 2025
3e6ea9c
Topic Selection and New Comment Routing (#2117)
ballPointPenguin Aug 8, 2025
6747019
Bump astro from 5.11.0 to 5.12.8 in /client-participation-alpha (#2119)
dependabot[bot] Aug 8, 2025
9fa3751
Merge branch 'stable' into edge
tevko Aug 9, 2025
1995694
PROD DEPLOY 8/8 (#2124)
ballPointPenguin Aug 9, 2025
5289db5
fix stray line in app.js
colinmegill Aug 9, 2025
8c30256
GHA Testing update (#2125)
ballPointPenguin Aug 9, 2025
7c95294
delete duplicate script
colinmegill Aug 9, 2025
0d76f95
update to query command, delete unused files
tevko Aug 9, 2025
e967d11
make PCA optional
tevko Aug 9, 2025
15530aa
Merge branch 'edge' into colinmegill/collectiveStatement
tevko Aug 9, 2025
509f8d1
don't load pca immediately
tevko Aug 9, 2025
ee29b70
linting fixes
tevko Aug 9, 2025
13e0516
Collective Statements, Topics, Topic (#2118)
colinmegill Aug 9, 2025
91e1ded
Merge branch 'stable' into edge
tevko Aug 9, 2025
15d9e00
fix jest failure
tevko Aug 9, 2025
80bc164
Merge branch 'edge' into colinmegill/collectiveStatement
tevko Aug 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/cypress-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
mkcert -cert-file localhost.pem -key-file localhost-key.pem \
localhost 127.0.0.1 ::1 oidc-simulator host.docker.internal

# Copy mkcert root CA to the certs directory for Docker containers
# Copy mkcert root CA for other containers
cp "$(mkcert -CAROOT)/rootCA.pem" ./rootCA.pem

# List generated files for debugging
Expand Down Expand Up @@ -89,11 +89,14 @@ jobs:
if: always()
run: |
# Show logs from critical services for debugging
echo "=== NGINX Proxy logs ==="
docker compose -f docker-compose.test.yml logs nginx-proxy || true

echo "=== OIDC Simulator logs ==="
docker compose -f docker-compose.test.yml logs oidc-simulator | tail -100
docker compose -f docker-compose.test.yml logs oidc-simulator || true

echo "=== Server logs ==="
docker compose -f docker-compose.test.yml logs server | tail -100
docker compose -f docker-compose.test.yml logs server || true

- name: Run auth setup test
run: |
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/jest-server-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ jobs:
run: |
# Build all services except server (tests run on host)
docker compose -f docker-compose.test.yml --env-file test.env build \
math postgres file-server maildev oidc-simulator
math postgres file-server maildev oidc-simulator dynamodb

- name: Start services
run: |
# Start only required services in detached mode (exclude server)
docker compose -f docker-compose.test.yml --env-file test.env up -d \
math postgres file-server maildev oidc-simulator
math postgres file-server maildev oidc-simulator dynamodb

# Wait for services to be ready
echo "Waiting for services to start..."
Expand All @@ -88,6 +88,11 @@ jobs:

echo "Checking oidc-simulator..."
curl -k -f https://localhost:3000/.well-known/jwks.json || true

echo "Checking DynamoDB..."
curl -f http://localhost:8000 || true
# Alternative check using AWS CLI if available
aws dynamodb list-tables --endpoint-url http://localhost:8000 --region us-east-1 2>/dev/null || true

- name: Setup Node.js
uses: actions/setup-node@v4
Expand All @@ -114,6 +119,7 @@ jobs:
export DATABASE_URL=postgres://postgres:PdwPNS2mDN73Vfbc@localhost:5432/polis-test
export MAILDEV_HOST=localhost
export STATIC_FILES_HOST=localhost
export DYNAMODB_ENDPOINT=http://localhost:8000

# Run the tests on the host machine
npm test -- --ci --coverage --maxWorkers=2
Expand All @@ -138,6 +144,9 @@ jobs:

echo "=== Math logs ==="
docker compose -f docker-compose.test.yml logs math | tail -50

echo "=== DynamoDB logs ==="
docker compose -f docker-compose.test.yml logs dynamodb | tail -100

- name: Clean up
if: always()
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ start-FULL-REBUILD: echo_vars stop rm-ALL ## Remove and restart all Docker conta
docker compose ${COMPOSE_FILE_ARGS} --env-file ${ENV_FILE} build --no-cache
docker compose ${COMPOSE_FILE_ARGS} --env-file ${ENV_FILE} up ${DETACH_ARG}

rebuild-web: echo_vars ## Rebuild and restart just the file-server container and its static assets
docker compose ${COMPOSE_FILE_ARGS} --env-file ${ENV_FILE} up ${DETACH_ARG} --build --force-recreate file-server
rebuild-web: echo_vars ## Rebuild and restart just the file-server container and its static assets, and client-participation-alpha
docker compose ${COMPOSE_FILE_ARGS} --env-file ${ENV_FILE} up ${DETACH_ARG} --build --force-recreate file-server client-participation-alpha

build-web-assets: ## Build and extract static web assets for cloud deployment to `build` dir
docker compose ${COMPOSE_FILE_ARGS} --env-file ${ENV_FILE} create --build --force-recreate file-server
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const ModerateCommentsSeed = ({ params }) => {
Upload a CSV of seed comments
</Heading>
<input onChange={handleFileChange} type="file" id="csvFile" accept=".csv"></input>
<Button onClick={handleSubmitSeedBulk}>{getButtonText()}</Button>
<Button onClick={handleSubmitSeedBulk} data-testid="upload-csv-button">{getButtonText()}</Button>
</Box>
</Box>
)
Expand Down
2 changes: 1 addition & 1 deletion client-participation-alpha/.astro/data-store.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.11.0","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/node\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/Users/colinmegill/polis/client-participation-alpha/node_modules/.astro/sessions\"}}}"]
[["Map",1,2],"meta::meta",["Map",3,4,5,6],"astro-version","5.11.0","astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"server\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"0.0.0.0\",\"port\":4321,\"streaming\":true,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\",\"entrypoint\":\"astro/assets/endpoint/node\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"gfm\":true,\"smartypants\":true},\"security\":{\"checkOrigin\":true},\"env\":{\"schema\":{},\"validateSecrets\":false},\"experimental\":{\"clientPrerender\":false,\"contentIntellisense\":false,\"headingIdCompat\":false,\"preserveScriptOrder\":false,\"liveContentCollections\":false,\"csp\":false},\"legacy\":{\"collections\":false},\"session\":{\"driver\":\"fs-lite\",\"options\":{\"base\":\"/Users/bennie/code/src/github.com/compdemocracy/polis/client-participation-alpha/node_modules/.astro/sessions\"}}}"]
2 changes: 1 addition & 1 deletion client-participation-alpha/.astro/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1753121227723
"lastUpdateCheck": 1754517865792
}
}
1 change: 0 additions & 1 deletion client-participation-alpha/.astro/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
/// <reference types="astro/client" />
/// <reference path="content.d.ts" />
191 changes: 191 additions & 0 deletions client-participation-alpha/README-AUTH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Authentication and JWT Handling in Client-Participation-Alpha

## Overview

The alpha client now has centralized JWT and authentication handling that mirrors the legacy client's approach but with modern patterns. All JWT tokens are automatically extracted and stored from API responses, and authentication headers are automatically added to requests.

## Key Components

### 1. `auth.ts` - Authentication Management

- `setJwtToken(token)` - Stores JWT tokens per conversation (extracts conversation_id from token)
- `getConversationToken(conversation_id)` - Retrieves stored JWT for a specific conversation
- `handleJwtFromResponse(response)` - Automatically extracts and stores JWT from API responses
- `getConversationIdFromUrl()` - Extracts conversation_id from URL path (e.g., /alpha/2demo → "2demo")

### 2. `net.js` - Network Layer with Auto-Auth

- Automatically adds authentication headers to all requests
- Automatically extracts and stores JWT tokens from all responses
- Handles both OIDC tokens and conversation-specific JWTs
- Provides consistent error handling with auth error details

## Usage Examples

### Simple API Call (JWT handled automatically)

```javascript
import PolisNet from '../lib/net';

// Make an API call - JWT will be automatically added to headers if available
// and automatically extracted/stored from response
const result = await PolisNet.polisPost('/api/v3/votes', {
conversation_id: 'abc123',
tid: 42,
vote: 1
});
// No need to manually handle result.auth.token - it's done automatically!
```

### Component Example

```javascript
import PolisNet from '../lib/net';
import { getConversationToken } from '../lib/auth';

export function MyComponent({ conversation_id }) {
const submitData = async (data) => {
// Get current participant info if needed
const token = getConversationToken(conversation_id);
const pid = token?.pid;

try {
// Make API call - auth headers added automatically
const response = await PolisNet.polisPost('/api/v3/comments', {
conversation_id,
pid,
txt: data.text
});

// JWT token from response.auth.token is automatically stored
// No manual handling needed!

return response;
} catch (error) {
// Error includes responseText for server error messages
console.error('API Error:', error.responseText || error.message);
throw error;
}
};
}
```

## URL Structure and Conversation ID

The conversation_id is extracted from the URL path, not query parameters:

- `https://pol.is/alpha/2demo` → conversation_id: "2demo"
- `http://localhost:4321/3xyz` → conversation_id: "3xyz"
- `https://edge.pol.is/alpha/4abc` → conversation_id: "4abc"

The pattern matches:

- `/alpha/:conversation_id` (production)
- `/:conversation_id` (development)

Conversation IDs always start with a digit followed by alphanumeric characters.

## Authentication Flow

1. **Initial Page Load (SSR)**
- Server fetches initial data with participationInit
- JWT token included in response
- Client-side script stores token on hydration

2. **Subsequent API Calls**
- `net.js` checks for OIDC token first
- Falls back to conversation-specific JWT (extracted from URL path)
- Automatically adds to Authorization header
- Server responds with updated JWT if needed
- `net.js` automatically stores new JWT

3. **Token Priority**
- OIDC tokens (if available) take precedence
- Conversation-specific JWTs used as fallback (based on current URL)
- No token sent if neither available (anonymous access)

## Migration from Manual JWT Handling

### Before (Manual JWT Handling)

```javascript
// DON'T DO THIS ANYMORE
const response = await fetch('/api/v3/votes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});

const result = await response.json();

// Manual JWT extraction and storage
if (result?.auth?.token) {
const token = result.auth.token;
const parts = token.split('.');
if (parts.length === 3) {
const payload = JSON.parse(atob(parts[1]));
if (payload.conversation_id) {
localStorage.setItem('participant_token_' + payload.conversation_id, token);
}
}
}
```

### After (Centralized Handling)

```javascript
// DO THIS INSTEAD
import PolisNet from '../lib/net';

const result = await PolisNet.polisPost('/api/v3/votes', data);
// JWT handling is automatic - no manual code needed!
```

## Benefits

1. **DRY Principle** - JWT handling logic in one place
2. **Consistency** - All API calls handle auth the same way
3. **Error Handling** - Centralized auth error handling
4. **Maintainability** - Easy to update auth logic globally
5. **Type Safety** - TypeScript interfaces for auth objects
6. **Automatic Token Refresh** - Server can send new tokens anytime

## Comparison with Legacy Client

| Feature | Legacy Client | Alpha Client |
|---------|--------------|--------------|
| JWT Storage | `polisStorage.js` | `auth.ts` |
| Auto JWT Extraction | ✅ `net.js`, `main.js` | ✅ `net.js` |
| Auto Auth Headers | ✅ `backbonePolis.js` | ✅ `net.js` |
| OIDC Support | ❌ | ✅ |
| TypeScript | ❌ | ✅ |
| Centralized | Partial (3 files) | ✅ (2 files) |

## Testing

When testing components that use the network layer:

```javascript
// Mock the net module
jest.mock('../lib/net', () => ({
polisPost: jest.fn(),
polisGet: jest.fn(),
polisPut: jest.fn()
}));

// In tests
import PolisNet from '../lib/net';

PolisNet.polisPost.mockResolvedValue({
success: true,
auth: { token: 'mock-jwt-token' }
});
```

## Troubleshooting

1. **No auth header sent**: Check if token exists for conversation
2. **401 errors**: Token may be expired or invalid
3. **403 errors**: User lacks permission for action
4. **Token not stored**: Check browser storage settings
5. **OIDC vs JWT conflicts**: OIDC takes precedence when available
7 changes: 6 additions & 1 deletion client-participation-alpha/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ export default defineConfig({
server: {
host: '0.0.0.0'
},
integrations: [react()]
integrations: [
react({
experimentalDisableStreaming: true,
experimentalReactChildren: true
})
]
});
4 changes: 4 additions & 0 deletions client-participation-alpha/example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
INTERNAL_SERVICE_URL=http://localhost/api/v3
PUBLIC_SERVICE_URL=http://localhost/api/v3
PUBLIC_OIDC_CACHE_KEY_ID_TOKEN_SUFFIX=@@user@@
PUBLIC_OIDC_CACHE_KEY_PREFIX=oidc.user
Loading
Loading