mirror of
https://github.com/LogicLabs-OU/OpenArchiver.git
synced 2026-04-06 00:31:57 +02:00
Idendity inboxes in org
This commit is contained in:
@@ -0,0 +1 @@
|
||||
ALTER TABLE "archived_emails" ADD COLUMN "user_email" text NOT NULL;
|
||||
832
packages/backend/src/database/migrations/meta/0008_snapshot.json
Normal file
832
packages/backend/src/database/migrations/meta/0008_snapshot.json
Normal file
@@ -0,0 +1,832 @@
|
||||
{
|
||||
"id": "86b6960e-1936-4543-846f-a2d24d6dc5d1",
|
||||
"prevId": "2a68f80f-b233-43bd-8280-745bee76ca3e",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.archived_emails": {
|
||||
"name": "archived_emails",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"ingestion_source_id": {
|
||||
"name": "ingestion_source_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"message_id_header": {
|
||||
"name": "message_id_header",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"sent_at": {
|
||||
"name": "sent_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"subject": {
|
||||
"name": "subject",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"sender_name": {
|
||||
"name": "sender_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"sender_email": {
|
||||
"name": "sender_email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"recipients": {
|
||||
"name": "recipients",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"storage_path": {
|
||||
"name": "storage_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"storage_hash_sha256": {
|
||||
"name": "storage_hash_sha256",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"size_bytes": {
|
||||
"name": "size_bytes",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"is_indexed": {
|
||||
"name": "is_indexed",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"has_attachments": {
|
||||
"name": "has_attachments",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"is_on_legal_hold": {
|
||||
"name": "is_on_legal_hold",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"archived_at": {
|
||||
"name": "archived_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"archived_emails_ingestion_source_id_ingestion_sources_id_fk": {
|
||||
"name": "archived_emails_ingestion_source_id_ingestion_sources_id_fk",
|
||||
"tableFrom": "archived_emails",
|
||||
"tableTo": "ingestion_sources",
|
||||
"columnsFrom": [
|
||||
"ingestion_source_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.attachments": {
|
||||
"name": "attachments",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"filename": {
|
||||
"name": "filename",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"mime_type": {
|
||||
"name": "mime_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"size_bytes": {
|
||||
"name": "size_bytes",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content_hash_sha256": {
|
||||
"name": "content_hash_sha256",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"storage_path": {
|
||||
"name": "storage_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"attachments_content_hash_sha256_unique": {
|
||||
"name": "attachments_content_hash_sha256_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"content_hash_sha256"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.email_attachments": {
|
||||
"name": "email_attachments",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"email_id": {
|
||||
"name": "email_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"attachment_id": {
|
||||
"name": "attachment_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"email_attachments_email_id_archived_emails_id_fk": {
|
||||
"name": "email_attachments_email_id_archived_emails_id_fk",
|
||||
"tableFrom": "email_attachments",
|
||||
"tableTo": "archived_emails",
|
||||
"columnsFrom": [
|
||||
"email_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"email_attachments_attachment_id_attachments_id_fk": {
|
||||
"name": "email_attachments_attachment_id_attachments_id_fk",
|
||||
"tableFrom": "email_attachments",
|
||||
"tableTo": "attachments",
|
||||
"columnsFrom": [
|
||||
"attachment_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "restrict",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"email_attachments_email_id_attachment_id_pk": {
|
||||
"name": "email_attachments_email_id_attachment_id_pk",
|
||||
"columns": [
|
||||
"email_id",
|
||||
"attachment_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.audit_logs": {
|
||||
"name": "audit_logs",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "bigserial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"timestamp": {
|
||||
"name": "timestamp",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"actor_identifier": {
|
||||
"name": "actor_identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"action": {
|
||||
"name": "action",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"target_type": {
|
||||
"name": "target_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"target_id": {
|
||||
"name": "target_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"details": {
|
||||
"name": "details",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"is_tamper_evident": {
|
||||
"name": "is_tamper_evident",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.ediscovery_cases": {
|
||||
"name": "ediscovery_cases",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'open'"
|
||||
},
|
||||
"created_by_identifier": {
|
||||
"name": "created_by_identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"ediscovery_cases_name_unique": {
|
||||
"name": "ediscovery_cases_name_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.export_jobs": {
|
||||
"name": "export_jobs",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"case_id": {
|
||||
"name": "case_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"format": {
|
||||
"name": "format",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'pending'"
|
||||
},
|
||||
"query": {
|
||||
"name": "query",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"file_path": {
|
||||
"name": "file_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_by_identifier": {
|
||||
"name": "created_by_identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"completed_at": {
|
||||
"name": "completed_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"export_jobs_case_id_ediscovery_cases_id_fk": {
|
||||
"name": "export_jobs_case_id_ediscovery_cases_id_fk",
|
||||
"tableFrom": "export_jobs",
|
||||
"tableTo": "ediscovery_cases",
|
||||
"columnsFrom": [
|
||||
"case_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.legal_holds": {
|
||||
"name": "legal_holds",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"case_id": {
|
||||
"name": "case_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"custodian_id": {
|
||||
"name": "custodian_id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"hold_criteria": {
|
||||
"name": "hold_criteria",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"reason": {
|
||||
"name": "reason",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"applied_by_identifier": {
|
||||
"name": "applied_by_identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"applied_at": {
|
||||
"name": "applied_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"removed_at": {
|
||||
"name": "removed_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"legal_holds_case_id_ediscovery_cases_id_fk": {
|
||||
"name": "legal_holds_case_id_ediscovery_cases_id_fk",
|
||||
"tableFrom": "legal_holds",
|
||||
"tableTo": "ediscovery_cases",
|
||||
"columnsFrom": [
|
||||
"case_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"legal_holds_custodian_id_custodians_id_fk": {
|
||||
"name": "legal_holds_custodian_id_custodians_id_fk",
|
||||
"tableFrom": "legal_holds",
|
||||
"tableTo": "custodians",
|
||||
"columnsFrom": [
|
||||
"custodian_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.retention_policies": {
|
||||
"name": "retention_policies",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"priority": {
|
||||
"name": "priority",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"retention_period_days": {
|
||||
"name": "retention_period_days",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"action_on_expiry": {
|
||||
"name": "action_on_expiry",
|
||||
"type": "retention_action",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"is_enabled": {
|
||||
"name": "is_enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"conditions": {
|
||||
"name": "conditions",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"retention_policies_name_unique": {
|
||||
"name": "retention_policies_name_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.custodians": {
|
||||
"name": "custodians",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"source_type": {
|
||||
"name": "source_type",
|
||||
"type": "ingestion_provider",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"custodians_email_unique": {
|
||||
"name": "custodians_email_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.ingestion_sources": {
|
||||
"name": "ingestion_sources",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "ingestion_provider",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"credentials": {
|
||||
"name": "credentials",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "ingestion_status",
|
||||
"typeSchema": "public",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'pending_auth'"
|
||||
},
|
||||
"last_sync_started_at": {
|
||||
"name": "last_sync_started_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_sync_finished_at": {
|
||||
"name": "last_sync_finished_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_sync_status_message": {
|
||||
"name": "last_sync_status_message",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"sync_state": {
|
||||
"name": "sync_state",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"public.retention_action": {
|
||||
"name": "retention_action",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"delete_permanently",
|
||||
"notify_admin"
|
||||
]
|
||||
},
|
||||
"public.ingestion_provider": {
|
||||
"name": "ingestion_provider",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"google_workspace",
|
||||
"microsoft_365",
|
||||
"generic_imap"
|
||||
]
|
||||
},
|
||||
"public.ingestion_status": {
|
||||
"name": "ingestion_status",
|
||||
"schema": "public",
|
||||
"values": [
|
||||
"active",
|
||||
"paused",
|
||||
"error",
|
||||
"pending_auth",
|
||||
"syncing",
|
||||
"importing",
|
||||
"auth_success"
|
||||
]
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,13 @@
|
||||
"when": 1753190159356,
|
||||
"tag": "0007_handy_archangel",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1753370737317,
|
||||
"tag": "0008_eminent_the_spike",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export const archivedEmails = pgTable('archived_emails', {
|
||||
ingestionSourceId: uuid('ingestion_source_id')
|
||||
.notNull()
|
||||
.references(() => ingestionSources.id, { onDelete: 'cascade' }),
|
||||
userEmail: text('user_email').notNull(),
|
||||
messageIdHeader: text('message_id_header'),
|
||||
sentAt: timestamp('sent_at', { withTimezone: true }).notNull(),
|
||||
subject: text('subject'),
|
||||
|
||||
@@ -23,7 +23,7 @@ export const processMailboxProcessor = async (job: Job<IProcessMailboxJob, SyncS
|
||||
// Pass the sync state for the entire source, the connector will handle per-user logic if necessary
|
||||
for await (const email of connector.fetchEmails(userEmail, source.syncState)) {
|
||||
if (email) {
|
||||
await ingestionService.processEmail(email, source, storageService);
|
||||
await ingestionService.processEmail(email, source, storageService, userEmail);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,8 @@ export class IngestionService {
|
||||
public async processEmail(
|
||||
email: EmailObject,
|
||||
source: IngestionSource,
|
||||
storage: StorageService
|
||||
storage: StorageService,
|
||||
userEmail: string
|
||||
): Promise<void> {
|
||||
try {
|
||||
console.log('processing email, ', email.id, email.subject);
|
||||
@@ -212,6 +213,7 @@ export class IngestionService {
|
||||
.insert(archivedEmails)
|
||||
.values({
|
||||
ingestionSourceId: source.id,
|
||||
userEmail,
|
||||
messageIdHeader:
|
||||
(email.headers['message-id'] as string) ??
|
||||
`generated-${emailHash}-${source.id}-${email.id}`,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@open-archiver/types": "workspace:*",
|
||||
"d3-shape": "^3.2.0",
|
||||
"jose": "^6.0.1",
|
||||
"lucide-svelte": "^0.525.0",
|
||||
"postal-mime": "^2.4.4",
|
||||
@@ -27,6 +28,7 @@
|
||||
"@sveltejs/kit": "^2.16.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"@types/d3-shape": "^3.1.7",
|
||||
"bits-ui": "^2.8.10",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.0",
|
||||
|
||||
@@ -5,72 +5,72 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.129 0.042 264.695);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.129 0.042 264.695);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.129 0.042 264.695);
|
||||
--primary: oklch(0.208 0.042 265.755);
|
||||
--primary-foreground: oklch(0.984 0.003 247.858);
|
||||
--secondary: oklch(0.968 0.007 247.896);
|
||||
--secondary-foreground: oklch(0.208 0.042 265.755);
|
||||
--muted: oklch(0.968 0.007 247.896);
|
||||
--muted-foreground: oklch(0.554 0.046 257.417);
|
||||
--accent: oklch(0.968 0.007 247.896);
|
||||
--accent-foreground: oklch(0.208 0.042 265.755);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.705 0.213 47.604);
|
||||
--primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.929 0.013 255.508);
|
||||
--input: oklch(0.929 0.013 255.508);
|
||||
--ring: oklch(0.704 0.04 256.788);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.705 0.213 47.604);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.984 0.003 247.858);
|
||||
--sidebar-foreground: oklch(0.129 0.042 264.695);
|
||||
--sidebar-primary: oklch(0.208 0.042 265.755);
|
||||
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||
--sidebar-accent: oklch(0.968 0.007 247.896);
|
||||
--sidebar-accent-foreground: oklch(0.208 0.042 265.755);
|
||||
--sidebar-border: oklch(0.929 0.013 255.508);
|
||||
--sidebar-ring: oklch(0.704 0.04 256.788);
|
||||
--chart-2: oklch(0.705 0.213 47.604);
|
||||
--chart-3: oklch(0.837 0.128 66.29);
|
||||
--chart-4: oklch(0.553 0.195 38.402);
|
||||
--chart-5: oklch(0.47 0.157 37.304);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.705 0.213 47.604);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.705 0.213 47.604);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.129 0.042 264.695);
|
||||
--foreground: oklch(0.984 0.003 247.858);
|
||||
--card: oklch(0.208 0.042 265.755);
|
||||
--card-foreground: oklch(0.984 0.003 247.858);
|
||||
--popover: oklch(0.208 0.042 265.755);
|
||||
--popover-foreground: oklch(0.984 0.003 247.858);
|
||||
--primary: oklch(0.929 0.013 255.508);
|
||||
--primary-foreground: oklch(0.208 0.042 265.755);
|
||||
--secondary: oklch(0.279 0.041 260.031);
|
||||
--secondary-foreground: oklch(0.984 0.003 247.858);
|
||||
--muted: oklch(0.279 0.041 260.031);
|
||||
--muted-foreground: oklch(0.704 0.04 256.788);
|
||||
--accent: oklch(0.279 0.041 260.031);
|
||||
--accent-foreground: oklch(0.984 0.003 247.858);
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.646 0.222 41.116);
|
||||
--primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.551 0.027 264.364);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.208 0.042 265.755);
|
||||
--sidebar-foreground: oklch(0.984 0.003 247.858);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||
--sidebar-accent: oklch(0.279 0.041 260.031);
|
||||
--sidebar-accent-foreground: oklch(0.984 0.003 247.858);
|
||||
--ring: oklch(0.646 0.222 41.116);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.705 0.213 47.604);
|
||||
--chart-3: oklch(0.837 0.128 66.29);
|
||||
--chart-4: oklch(0.553 0.195 38.402);
|
||||
--chart-5: oklch(0.47 0.157 37.304);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.646 0.222 41.116);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.551 0.027 264.364);
|
||||
--sidebar-ring: oklch(0.646 0.222 41.116);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { Button, buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import { type Snippet } from 'svelte';
|
||||
|
||||
let {
|
||||
header,
|
||||
text,
|
||||
buttonText,
|
||||
click
|
||||
}: {
|
||||
header: string;
|
||||
text: string;
|
||||
buttonText?: string;
|
||||
click: () => void;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="space-y-4 rounded-lg border-2 border-dashed border-gray-300 p-6 text-center">
|
||||
<div>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
class="mx-auto size-12 text-gray-400"
|
||||
>
|
||||
<path
|
||||
d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"
|
||||
stroke-width="2"
|
||||
vector-effect="non-scaling-stroke"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-2 text-sm font-semibold text-gray-900">{header}</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">{text}</p>
|
||||
<div>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="cursor-pointer"
|
||||
onclick={() => {
|
||||
click();
|
||||
}}
|
||||
>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<header class="bg-background sticky top-0 z-40 border-b">
|
||||
<div class="container mx-auto flex h-16 flex-row items-center justify-between">
|
||||
<a href="/dashboard" class="text-primary flex flex-row items-center gap-2 font-bold">
|
||||
<a href="/dashboard" class="flex flex-row items-center gap-2 font-bold">
|
||||
<img src="/logos/logo-sq.svg" alt="OpenArchiver Logo" class="h-8 w-8" />
|
||||
<span>Open Archiver</span>
|
||||
</a>
|
||||
|
||||
@@ -1,19 +1,35 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import * as Chart from '$lib/components/ui/chart/index.js';
|
||||
import { BarChart } from 'layerchart';
|
||||
import { LineChart, PieChart, AreaChart } from 'layerchart';
|
||||
import { formatBytes } from '$lib/utils';
|
||||
|
||||
import { curveCatmullRom } from 'd3-shape';
|
||||
import type { ChartConfig } from '$lib/components/ui/chart';
|
||||
import EmptyState from '$lib/components/custom/EmptyState.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Archive, CircleAlert, HardDrive } from 'lucide-svelte';
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
const chartConfig = {
|
||||
const transformedHistory = $derived(
|
||||
data.ingestionHistory?.history.map((item) => ({
|
||||
...item,
|
||||
date: new Date(item.date)
|
||||
})) ?? []
|
||||
);
|
||||
|
||||
const emailIngestedChartConfig = {
|
||||
count: {
|
||||
label: 'Emails Ingested',
|
||||
color: '#2563eb'
|
||||
color: 'var(--chart-1)'
|
||||
}
|
||||
} satisfies Chart.ChartConfig;
|
||||
} satisfies ChartConfig;
|
||||
|
||||
const StorageUsedChartConfig = {
|
||||
storageUsed: {
|
||||
label: 'Storage Used'
|
||||
}
|
||||
} satisfies ChartConfig;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -21,134 +37,152 @@
|
||||
<meta name="description" content="Overview of your email archive." />
|
||||
</svelte:head>
|
||||
|
||||
<div class="flex-1 space-y-4 p-8 pt-6">
|
||||
<div class="flex-1 space-y-4">
|
||||
<div class="flex items-center justify-between space-y-2">
|
||||
<h2 class="text-3xl font-bold tracking-tight">Dashboard</h2>
|
||||
</div>
|
||||
{#if !data.ingestionSources || data.ingestionSources?.length === 0}
|
||||
<div>
|
||||
<EmptyState
|
||||
buttonText="Create an ingestion"
|
||||
header="You don't have any ingestion source set up."
|
||||
text="Add an ingestion source to start archiving your inboxes."
|
||||
click={() => {
|
||||
goto('/dashboard/ingestions');
|
||||
}}
|
||||
></EmptyState>
|
||||
</div>
|
||||
{:else}
|
||||
<!-- show data -->
|
||||
<div class="space-y-4">
|
||||
{#if data.stats}
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card.Root>
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Total Emails Archived</Card.Title>
|
||||
<Archive class="text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-primary text-2xl font-bold">{data.stats.totalEmailsArchived}</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Total Storage Used</Card.Title>
|
||||
<HardDrive class="text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-primary text-2xl font-bold">
|
||||
{formatBytes(data.stats.totalStorageUsed)}
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root class="">
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Failed Ingestions (Last 7 Days)</Card.Title>
|
||||
<CircleAlert class=" text-muted-foreground h-4 w-4" />
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div
|
||||
class=" text-2xl font-bold text-green-500"
|
||||
class:text-destructive={data.stats.failedIngestionsLast7Days > 0}
|
||||
>
|
||||
{data.stats.failedIngestionsLast7Days}
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if data.stats}
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card.Root>
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Total Emails Archived</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-2xl font-bold">{data.stats.totalEmailsArchived}</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Total Storage Used</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-2xl font-bold">{formatBytes(data.stats.totalStorageUsed)}</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<Card.Root>
|
||||
<Card.Header class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<Card.Title class="text-sm font-medium">Failed Ingestions (Last 7 Days)</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="text-2xl font-bold">{data.stats.failedIngestionsLast7Days}</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||||
<div class=" lg:col-span-2">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Ingestion History</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class=" pl-4">
|
||||
{#if transformedHistory.length > 0}
|
||||
<Chart.Container config={emailIngestedChartConfig} class="min-h-[300px] w-full">
|
||||
<AreaChart
|
||||
data={transformedHistory}
|
||||
x="date"
|
||||
y="count"
|
||||
yDomain={[0, Math.max(...transformedHistory.map((d) => d.count)) * 1.1]}
|
||||
axis
|
||||
legend={false}
|
||||
series={[
|
||||
{
|
||||
key: 'count',
|
||||
...emailIngestedChartConfig.count
|
||||
}
|
||||
]}
|
||||
cRange={[
|
||||
'var(--color-chart-1)',
|
||||
'var(--color-chart-2)',
|
||||
'var(--color-chart-3)',
|
||||
'var(--color-chart-4)',
|
||||
'var(--color-chart-5)'
|
||||
]}
|
||||
labels={{}}
|
||||
props={{
|
||||
xAxis: {
|
||||
format: (d) =>
|
||||
new Date(d).toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})
|
||||
},
|
||||
area: { curve: curveCatmullRom }
|
||||
}}
|
||||
>
|
||||
{#snippet tooltip()}
|
||||
<Chart.Tooltip />
|
||||
{/snippet}
|
||||
</AreaChart>
|
||||
</Chart.Container>
|
||||
{:else}
|
||||
<p>No ingestion history available.</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
<div class=" lg:col-span-1">
|
||||
<Card.Root class="h-full">
|
||||
<Card.Header>
|
||||
<Card.Title>Storage by Ingestion Source (Bytes)</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="h-full">
|
||||
{#if data.ingestionSources && data.ingestionSources.length > 0}
|
||||
<Chart.Container
|
||||
config={StorageUsedChartConfig}
|
||||
class="h-full min-h-[300px] w-full"
|
||||
>
|
||||
<PieChart
|
||||
data={data.ingestionSources}
|
||||
key="name"
|
||||
value="storageUsed"
|
||||
label="name"
|
||||
legend={{}}
|
||||
cRange={[
|
||||
'var(--color-chart-1)',
|
||||
'var(--color-chart-2)',
|
||||
'var(--color-chart-3)',
|
||||
'var(--color-chart-4)',
|
||||
'var(--color-chart-5)'
|
||||
]}
|
||||
>
|
||||
{#snippet tooltip()}
|
||||
<Chart.Tooltip></Chart.Tooltip>
|
||||
{/snippet}
|
||||
</PieChart>
|
||||
</Chart.Container>
|
||||
{:else}
|
||||
<p>No ingestion sources available.</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
|
||||
<Card.Root class="col-span-4">
|
||||
<Card.Header>
|
||||
<Card.Title>Ingestion History</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="pl-2">
|
||||
{#if data.ingestionHistory && data.ingestionHistory.history.length > 0}
|
||||
<Chart.Container config={chartConfig} class="min-h-[200px] w-full">
|
||||
<BarChart
|
||||
data={data.ingestionHistory.history}
|
||||
x="date"
|
||||
y="count"
|
||||
axis="x"
|
||||
seriesLayout="group"
|
||||
props={{
|
||||
xAxis: {
|
||||
format: (d) =>
|
||||
new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#snippet tooltip()}
|
||||
<Chart.Tooltip />
|
||||
{/snippet}
|
||||
</BarChart>
|
||||
</Chart.Container>
|
||||
{:else}
|
||||
<p>No ingestion history available.</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<Card.Root class="col-span-3">
|
||||
<Card.Header>
|
||||
<Card.Title>Recent Syncs</Card.Title>
|
||||
<Card.Description>Most recent sync activities.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#if data.recentSyncs && data.recentSyncs.length > 0}
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Source</Table.Head>
|
||||
<Table.Head>Status</Table.Head>
|
||||
<Table.Head>Processed</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each data.recentSyncs as sync}
|
||||
<Table.Row>
|
||||
<Table.Cell class="font-medium">{sync.sourceName}</Table.Cell>
|
||||
<Table.Cell>{sync.status}</Table.Cell>
|
||||
<Table.Cell>{sync.emailsProcessed}</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<p>No recent syncs.</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Ingestion Sources</Card.Title>
|
||||
<Card.Description>Overview of your ingestion sources.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#if data.ingestionSources && data.ingestionSources.length > 0}
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Name</Table.Head>
|
||||
<Table.Head>Provider</Table.Head>
|
||||
<Table.Head>Status</Table.Head>
|
||||
<Table.Head class="text-right">Storage Used</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#each data.ingestionSources as source}
|
||||
<Table.Row>
|
||||
<Table.Cell class="font-medium">{source.name}</Table.Cell>
|
||||
<Table.Cell>{source.provider}</Table.Cell>
|
||||
<Table.Cell>{source.status}</Table.Cell>
|
||||
<Table.Cell class="text-right">{formatBytes(source.storageUsed)}</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
{:else}
|
||||
<p>No ingestion sources found.</p>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.Head>Date</Table.Head>
|
||||
<Table.Head>Inbox</Table.Head>
|
||||
<Table.Head>Subject</Table.Head>
|
||||
<Table.Head>Sender</Table.Head>
|
||||
<Table.Head>Attachments</Table.Head>
|
||||
@@ -106,6 +107,7 @@
|
||||
{#each archivedEmails.items as email (email.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell>{new Date(email.sentAt).toLocaleString()}</Table.Cell>
|
||||
<Table.Cell>{email.userEmail}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<div class="max-w-100 truncate">
|
||||
<a href={`/dashboard/archived-emails/${email.id}`}>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
{#if email}
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<div class="col-span-2">
|
||||
<div class="col-span-3 md:col-span-2">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>{email.subject || 'No Subject'}</Card.Title>
|
||||
@@ -79,7 +79,7 @@
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<div class="col-span-3 md:col-span-1">
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Actions</Card.Title>
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface Attachment {
|
||||
export interface ArchivedEmail {
|
||||
id: string;
|
||||
ingestionSourceId: string;
|
||||
userEmail: string;
|
||||
messageIdHeader: string | null;
|
||||
sentAt: Date;
|
||||
subject: string | null;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user