feat(campaigns): campaign start date — scheduled activation, Jira start_date

DB: migration b047 adds start_date (DateTime nullable) + index to campaigns.

Backend:
- Campaign model: start_date field
- CampaignCreate/Update schemas: accept start_date (ISO string)
- CRUD service: persist + serialize start_date in both serializers
- Activation endpoint: blocks manual activation if start_date is in the future
  (campaign will auto-activate via scheduler)
- Scheduler: new hourly job _run_scheduled_campaign_activation — finds draft
  campaigns with start_date <= now, activates them, creates Jira tickets,
  notifies red_tech team
- Jira: campaign + test tickets now include JIRA_START_DATE_FIELD (configurable,
  default customfield_10015). Campaign uses start_date if set, else created_at.
  Tests inherit campaign start_date.
- config.py: JIRA_START_DATE_FIELD setting

Frontend:
- Campaign type: start_date field on Campaign + CampaignSummary
- CampaignCreatePayload: start_date optional field
- Create form: date picker with min=today, warning message explaining behavior
- Campaign detail header: start_date badge showing days remaining or started date
This commit is contained in:
kitos
2026-06-03 16:57:06 +02:00
parent 9fb84fa65c
commit 92e8ff7aff
10 changed files with 218 additions and 2 deletions
+3
View File
@@ -31,6 +31,7 @@ export interface Campaign {
threat_actor_id: string | null;
threat_actor_name: string | null;
created_by: string | null;
start_date: string | null;
scheduled_at: string | null;
completed_at: string | null;
target_platform: string | null;
@@ -55,6 +56,7 @@ export interface CampaignSummary {
threat_actor_name: string | null;
target_platform: string | null;
tags: string[];
start_date: string | null;
created_at: string | null;
test_count: number;
completion_pct: number;
@@ -63,6 +65,7 @@ export interface CampaignSummary {
export interface CampaignCreatePayload {
name: string;
description?: string;
start_date?: string; // ISO date YYYY-MM-DD — campaign won't activate before this
type?: string;
threat_actor_id?: string;
target_platform?: string;