Skip to content

Commit 99c8983

Browse files
authored
Merge pull request #15 from baseVISION/sync/upstream-2026-07-13
Sync/upstream 2026 07 13
2 parents d59fd3e + a61da26 commit 99c8983

43 files changed

Lines changed: 5317 additions & 380 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ certificates/web_certificates/*.key
3434
local_dev/
3535
.serena/
3636
.tokensave
37+
.claude/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ git clone https://github.com/dfir-iris/iris-web.git
5656
cd iris-web
5757

5858
# Checkout to the last tagged version
59-
git checkout v2.4.20
59+
git checkout v2.5.0-beta.1
6060
# Copy the environment file
6161
cp .env.model .env
6262

docker-compose.new-ui.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ services:
112112
# install` on boot — the image carries everything it needs.
113113
build:
114114
context: ${IRIS_SVELTEKIT_FRONTEND_DIR:-../iris-frontend}
115-
dockerfile: ${IRIS_FRONTEND_DOCKERFILE:-../iris-web/docker/frontend/Dockerfile}
115+
dockerfile: ${IRIS_FRONTEND_DOCKERFILE:-Dockerfile}
116116
image: iris_frontend:newui
117117
profiles: ["new-ui"]
118118
container_name: iris_sveltekit_frontend

docker-compose.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ services:
2727
extends:
2828
file: docker-compose.base.yml
2929
service: db
30-
image: ${DB_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_db}:${DB_IMAGE_TAG:-v2.4.20}
30+
image: ${DB_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_db}:${DB_IMAGE_TAG:-v2.5.0-beta.1}
3131
build: docker/db/
3232
restart: always
3333

@@ -36,7 +36,7 @@ services:
3636
extends:
3737
file: docker-compose.base.yml
3838
service: app
39-
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.20}
39+
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.5.0-beta.1}
4040
build:
4141
context: .
4242
dockerfile: docker/webApp/Dockerfile
@@ -47,7 +47,7 @@ services:
4747
extends:
4848
file: docker-compose.base.yml
4949
service: worker
50-
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.4.20}
50+
image: ${APP_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_app}:${APP_IMAGE_TAG:-v2.5.0-beta.1}
5151
build:
5252
context: .
5353
dockerfile: docker/webApp/Dockerfile
@@ -58,7 +58,7 @@ services:
5858
extends:
5959
file: docker-compose.base.yml
6060
service: nginx
61-
image: ${NGINX_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_nginx}:${NGINX_IMAGE_TAG:-v2.4.20}
61+
image: ${NGINX_IMAGE_NAME:-ghcr.io/dfir-iris/iriswebapp_nginx}:${NGINX_IMAGE_TAG:-v2.5.0-beta.1}
6262
build:
6363
context: docker/nginx/
6464
args:

docker/frontend/Dockerfile

Lines changed: 0 additions & 61 deletions
This file was deleted.

docker/nginx/nginx-newui.conf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ http {
2828
map $request_uri $csp_header {
2929
# `blob:` is required so authenticated avatars (fetched as
3030
# bytes and exposed via URL.createObjectURL) can render.
31-
default "default-src 'self' https://analytics.dfir-iris.org https://127.0.0.1 http://app:8000; script-src 'self' 'unsafe-inline' https://analytics.dfir-iris.org; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:;";
31+
# `connect-src` must be explicit — otherwise browsers fall back
32+
# to `default-src` for XHR + WebSocket connections, and `default-src`
33+
# doesn't cover the `ws:` / `wss:` schemes Socket.IO uses for
34+
# the /collab namespace (real-time markdown collab in case notes,
35+
# war-room notes, and sitreps). Without `ws: wss:` here, every
36+
# collab editor hangs on its sync-init handshake.
37+
default "default-src 'self' https://analytics.dfir-iris.org https://127.0.0.1 http://app:8000; connect-src 'self' https://analytics.dfir-iris.org https://127.0.0.1 http://app:8000 ws: wss:; script-src 'self' 'unsafe-inline' https://analytics.dfir-iris.org; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:;";
3238
}
3339
include /etc/nginx/mime.types;
3440

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Merge dashboard/collab merge head and war_room_teams head
2+
3+
The upstream sync merge brought in war_room_teams_and_chat_attachments
4+
(f7a3b9c1d02e), based on d5e6f7a8b9c0, on a sibling branch to the
5+
dashboard/collab merge head (b5c6d7e8f9a0), re-forking the alembic
6+
head into two.
7+
8+
Revision ID: 0508647a855c
9+
Revises: b5c6d7e8f9a0, f7a3b9c1d02e
10+
Create Date: 2026-07-13 00:00:00.000000
11+
12+
"""
13+
from alembic import op
14+
import sqlalchemy as sa
15+
16+
# revision identifiers, used by Alembic.
17+
revision = '0508647a855c'
18+
down_revision = ('b5c6d7e8f9a0', 'f7a3b9c1d02e')
19+
branch_labels = None
20+
depends_on = None
21+
22+
23+
def upgrade():
24+
pass
25+
26+
27+
def downgrade():
28+
pass
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Add war_room_topic table + topic_id column on war_room_chat_message.
2+
3+
Topics are top-level partitions of the chat stream — each war room
4+
has one non-archivable "Main" topic (created lazily by the business
5+
layer on first read) plus any number of operator-created ones. A
6+
message with `topic_id IS NULL` is on Main; the read path treats
7+
NULL and the Main topic row as equivalent so pre-migration rows keep
8+
working without a backfill.
9+
10+
Archive is soft (`archived_at`). No hard delete — archived topics
11+
still render (read-only) in the sidebar.
12+
13+
Idempotent via `_has_table` / `_table_has_column` so re-running on
14+
environments that already applied the migration is a no-op.
15+
16+
Revision ID: c3d4e5f6a9b0
17+
Revises: f3c8d2a1b47e
18+
Create Date: 2026-07-09 10:00:00.000000
19+
"""
20+
import sqlalchemy as sa
21+
from alembic import op
22+
23+
from app.alembic.alembic_utils import _has_table, _table_has_column
24+
25+
26+
revision = 'c3d4e5f6a9b0'
27+
down_revision = 'f3c8d2a1b47e'
28+
branch_labels = None
29+
depends_on = None
30+
31+
32+
def upgrade():
33+
if not _has_table('war_room_topic'):
34+
op.create_table(
35+
'war_room_topic',
36+
sa.Column('topic_id', sa.BigInteger(), primary_key=True),
37+
sa.Column(
38+
'war_room_id', sa.BigInteger(),
39+
sa.ForeignKey('war_room.war_room_id', ondelete='CASCADE'),
40+
nullable=False,
41+
),
42+
sa.Column('name', sa.String(length=80), nullable=False),
43+
sa.Column(
44+
'is_main', sa.Boolean(), nullable=False,
45+
server_default=sa.text('false'),
46+
),
47+
sa.Column(
48+
'created_by_id', sa.BigInteger(),
49+
sa.ForeignKey('user.id'), nullable=True,
50+
),
51+
sa.Column(
52+
'created_at', sa.DateTime(), nullable=False,
53+
server_default=sa.text('now()'),
54+
),
55+
sa.Column('archived_at', sa.DateTime(), nullable=True),
56+
sa.UniqueConstraint('war_room_id', 'name',
57+
name='uq_war_room_topic_name'),
58+
)
59+
op.create_index(
60+
'ix_war_room_topic_war_room_id',
61+
'war_room_topic',
62+
['war_room_id'],
63+
)
64+
65+
if _has_table('war_room_chat_message'):
66+
if not _table_has_column('war_room_chat_message', 'topic_id'):
67+
op.add_column(
68+
'war_room_chat_message',
69+
sa.Column(
70+
'topic_id', sa.BigInteger(),
71+
sa.ForeignKey(
72+
'war_room_topic.topic_id', ondelete='SET NULL'
73+
),
74+
nullable=True,
75+
),
76+
)
77+
op.create_index(
78+
'ix_war_room_chat_message_topic_id',
79+
'war_room_chat_message',
80+
['topic_id'],
81+
)
82+
83+
84+
def downgrade():
85+
if _has_table('war_room_chat_message'):
86+
if _table_has_column('war_room_chat_message', 'topic_id'):
87+
op.drop_index(
88+
'ix_war_room_chat_message_topic_id',
89+
table_name='war_room_chat_message',
90+
)
91+
op.drop_column('war_room_chat_message', 'topic_id')
92+
93+
if _has_table('war_room_topic'):
94+
op.drop_index(
95+
'ix_war_room_topic_war_room_id',
96+
table_name='war_room_topic',
97+
)
98+
op.drop_table('war_room_topic')
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""Add parent_task_id + search indexes on war_room_task.
2+
3+
Subtasks are single-level: a war-room task may point at another
4+
war-room task in the same room via `parent_task_id`. Enforcement of
5+
the "no grand-children" rule lives in the business layer (a task
6+
that is itself a child cannot be given children) so the DB stays
7+
simple. `ON DELETE CASCADE` on the FK: removing a parent removes its
8+
subtasks, matching the UX expectation ("delete a task, delete its
9+
subtasks").
10+
11+
Also adds three helper indexes used by the new search/filter list:
12+
- `parent_task_id` for expand-children lookups
13+
- `status_id` for status-filter scans
14+
- `war_room_id, parent_task_id` composite to cheaply pull the
15+
top-level tree on the tasks page (parent_task_id IS NULL).
16+
17+
Idempotent via `_has_table` / `_table_has_column` so re-running on
18+
environments that already applied the migration is a no-op.
19+
20+
Revision ID: d5e6f7a8b9c0
21+
Revises: c3d4e5f6a9b0
22+
Create Date: 2026-07-13 10:00:00.000000
23+
"""
24+
import sqlalchemy as sa
25+
from alembic import op
26+
27+
from app.alembic.alembic_utils import _has_table, _table_has_column
28+
29+
30+
revision = 'd5e6f7a8b9c0'
31+
down_revision = 'c3d4e5f6a9b0'
32+
branch_labels = None
33+
depends_on = None
34+
35+
36+
def upgrade():
37+
if not _has_table('war_room_task'):
38+
return
39+
40+
if not _table_has_column('war_room_task', 'parent_task_id'):
41+
op.add_column(
42+
'war_room_task',
43+
sa.Column(
44+
'parent_task_id', sa.BigInteger(),
45+
sa.ForeignKey('war_room_task.task_id', ondelete='CASCADE'),
46+
nullable=True,
47+
),
48+
)
49+
op.create_index(
50+
'ix_war_room_task_parent_task_id',
51+
'war_room_task',
52+
['parent_task_id'],
53+
)
54+
55+
op.execute(
56+
'CREATE INDEX IF NOT EXISTS ix_war_room_task_status_id '
57+
'ON war_room_task (status_id)'
58+
)
59+
op.execute(
60+
'CREATE INDEX IF NOT EXISTS ix_war_room_task_war_room_parent '
61+
'ON war_room_task (war_room_id, parent_task_id)'
62+
)
63+
64+
65+
def downgrade():
66+
if not _has_table('war_room_task'):
67+
return
68+
69+
op.execute('DROP INDEX IF EXISTS ix_war_room_task_war_room_parent')
70+
op.execute('DROP INDEX IF EXISTS ix_war_room_task_status_id')
71+
72+
if _table_has_column('war_room_task', 'parent_task_id'):
73+
op.execute('DROP INDEX IF EXISTS ix_war_room_task_parent_task_id')
74+
op.drop_column('war_room_task', 'parent_task_id')

0 commit comments

Comments
 (0)