diff --git a/backend/app/api/v1/attachments.py b/backend/app/api/v1/attachments.py new file mode 100644 index 00000000..8eb8086e --- /dev/null +++ b/backend/app/api/v1/attachments.py @@ -0,0 +1,113 @@ +import os +import uuid +from pathlib import Path + +import aiofiles +from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi.responses import FileResponse +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.deps import get_current_user, get_db_session, require_study_member +from app.crud import attachment as attachment_crud +from app.crud import audit as audit_crud +from app.crud import study as study_crud +from app.schemas.attachment import AttachmentRead + +router = APIRouter() + +UPLOAD_ROOT = Path(__file__).resolve().parent.parent.parent / "uploads" + + +async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID): + study = await study_crud.get(db, study_id) + if not study: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Study not found") + return study + + +@router.post( + "/", + response_model=AttachmentRead, + status_code=status.HTTP_201_CREATED, + dependencies=[Depends(require_study_member())], +) +async def upload_attachment( + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + file: UploadFile = File(...), + db: AsyncSession = Depends(get_db_session), + current_user=Depends(get_current_user), +) -> AttachmentRead: + await _ensure_study_exists(db, study_id) + dest_dir = UPLOAD_ROOT / f"study_{study_id}" / f"{entity_type}_{entity_id}" + dest_dir.mkdir(parents=True, exist_ok=True) + unique_name = f"{uuid.uuid4()}{Path(file.filename).suffix}" + dest_path = dest_dir / unique_name + + content = await file.read() + async with aiofiles.open(dest_path, "wb") as out_file: + await out_file.write(content) + + attachment = await attachment_crud.create_attachment( + db, + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + filename=file.filename, + file_path=str(dest_path), + file_size=len(content), + content_type=file.content_type, + uploaded_by=current_user.id, + ) + await audit_crud.log_action( + db, + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + action="UPLOAD_FILE", + detail=f"File uploaded: {file.filename}", + operator_id=current_user.id, + operator_role=current_user.role, + ) + return attachment + + +@router.get( + "/", + response_model=list[AttachmentRead], + dependencies=[Depends(require_study_member())], +) +async def list_attachments( + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + db: AsyncSession = Depends(get_db_session), +) -> list[AttachmentRead]: + await _ensure_study_exists(db, study_id) + attachments = await attachment_crud.list_attachments(db, study_id, entity_type, entity_id) + return list(attachments) + + +@router.get( + "/{attachment_id}/download", + dependencies=[Depends(require_study_member())], +) +async def download_attachment( + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + attachment_id: uuid.UUID, + db: AsyncSession = Depends(get_db_session), +) -> FileResponse: + await _ensure_study_exists(db, study_id) + attachment = await attachment_crud.get_attachment(db, attachment_id) + if not attachment or attachment.study_id != study_id or attachment.entity_id != entity_id or attachment.entity_type != entity_type: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Attachment not found") + if not os.path.exists(attachment.file_path): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="File missing on server") + return FileResponse( + path=attachment.file_path, + filename=attachment.filename, + media_type=attachment.content_type or "application/octet-stream", + ) diff --git a/backend/app/api/v1/audit_logs.py b/backend/app/api/v1/audit_logs.py new file mode 100644 index 00000000..cd61e238 --- /dev/null +++ b/backend/app/api/v1/audit_logs.py @@ -0,0 +1,36 @@ +import uuid + +from fastapi import APIRouter, Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.deps import get_db_session, require_study_member +from app.crud import audit as audit_crud +from app.schemas.audit import AuditLogRead + +router = APIRouter() + + +@router.get( + "/", + response_model=list[AuditLogRead], + dependencies=[Depends(require_study_member())], +) +async def list_audit_logs( + study_id: uuid.UUID, + entity_type: str | None = None, + entity_id: uuid.UUID | None = None, + action: str | None = None, + skip: int = 0, + limit: int = 100, + db: AsyncSession = Depends(get_db_session), +) -> list[AuditLogRead]: + logs = await audit_crud.list_logs( + db, + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + action=action, + skip=skip, + limit=limit, + ) + return list(logs) diff --git a/backend/app/api/v1/comments.py b/backend/app/api/v1/comments.py new file mode 100644 index 00000000..6ca4f015 --- /dev/null +++ b/backend/app/api/v1/comments.py @@ -0,0 +1,71 @@ +import uuid + +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.deps import get_current_user, get_db_session, require_study_member +from app.crud import comment as comment_crud +from app.crud import audit as audit_crud +from app.crud import study as study_crud +from app.schemas.comment import CommentCreate, CommentRead + +router = APIRouter() + + +async def _ensure_study_exists(db: AsyncSession, study_id: uuid.UUID): + study = await study_crud.get(db, study_id) + if not study: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Study not found") + return study + + +@router.post( + "/", + response_model=CommentRead, + status_code=status.HTTP_201_CREATED, + dependencies=[Depends(require_study_member())], +) +async def create_comment( + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + comment_in: CommentCreate, + db: AsyncSession = Depends(get_db_session), + current_user=Depends(get_current_user), +) -> CommentRead: + await _ensure_study_exists(db, study_id) + comment = await comment_crud.create_comment( + db, + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + comment_in=comment_in, + created_by=current_user.id, + ) + await audit_crud.log_action( + db, + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + action="CREATE_COMMENT", + detail=f"Comment created by {current_user.username}", + operator_id=current_user.id, + operator_role=current_user.role, + ) + return comment + + +@router.get( + "/", + response_model=list[CommentRead], + dependencies=[Depends(require_study_member())], +) +async def list_comments( + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + db: AsyncSession = Depends(get_db_session), +) -> list[CommentRead]: + await _ensure_study_exists(db, study_id) + comments = await comment_crud.list_comments(db, study_id, entity_type, entity_id) + return list(comments) diff --git a/backend/app/api/v1/router.py b/backend/app/api/v1/router.py index 7b77b492..45cc9979 100644 --- a/backend/app/api/v1/router.py +++ b/backend/app/api/v1/router.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from app.api.v1 import auth, users, studies, sites, members +from app.api.v1 import auth, users, studies, sites, members, comments, attachments, audit_logs api_router = APIRouter() api_router.include_router(auth.router, prefix="/auth", tags=["auth"]) @@ -8,3 +8,6 @@ api_router.include_router(users.router, prefix="/users", tags=["users"]) api_router.include_router(studies.router, prefix="/studies", tags=["studies"]) api_router.include_router(sites.router, prefix="/studies/{study_id}/sites", tags=["sites"]) api_router.include_router(members.router, prefix="/studies/{study_id}/members", tags=["study-members"]) +api_router.include_router(comments.router, prefix="/studies/{study_id}/{entity_type}/{entity_id}/comments", tags=["comments"]) +api_router.include_router(attachments.router, prefix="/studies/{study_id}/{entity_type}/{entity_id}/attachments", tags=["attachments"]) +api_router.include_router(audit_logs.router, prefix="/studies/{study_id}/audit-logs", tags=["audit-logs"]) diff --git a/backend/app/crud/attachment.py b/backend/app/crud/attachment.py new file mode 100644 index 00000000..72cf739a --- /dev/null +++ b/backend/app/crud/attachment.py @@ -0,0 +1,59 @@ +import uuid +from typing import Sequence + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.attachment import Attachment + + +async def create_attachment( + db: AsyncSession, + *, + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + filename: str, + file_path: str, + file_size: int, + content_type: str | None, + uploaded_by: uuid.UUID, +) -> Attachment: + attachment = Attachment( + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + filename=filename, + file_path=file_path, + file_size=file_size, + content_type=content_type, + uploaded_by=uploaded_by, + ) + db.add(attachment) + await db.commit() + await db.refresh(attachment) + return attachment + + +async def list_attachments( + db: AsyncSession, + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, +) -> Sequence[Attachment]: + result = await db.execute( + select(Attachment) + .where( + Attachment.study_id == study_id, + Attachment.entity_type == entity_type, + Attachment.entity_id == entity_id, + Attachment.is_deleted.is_(False), + ) + .order_by(Attachment.uploaded_at.desc()) + ) + return result.scalars().all() + + +async def get_attachment(db: AsyncSession, attachment_id: uuid.UUID) -> Attachment | None: + result = await db.execute(select(Attachment).where(Attachment.id == attachment_id, Attachment.is_deleted.is_(False))) + return result.scalar_one_or_none() diff --git a/backend/app/crud/audit.py b/backend/app/crud/audit.py new file mode 100644 index 00000000..b85022ab --- /dev/null +++ b/backend/app/crud/audit.py @@ -0,0 +1,55 @@ +import uuid +from typing import Sequence + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.audit_log import AuditLog + + +async def log_action( + db: AsyncSession, + *, + study_id: uuid.UUID | None, + entity_type: str, + entity_id: uuid.UUID | None, + action: str, + detail: str | None, + operator_id: uuid.UUID, + operator_role: str, +) -> AuditLog: + log = AuditLog( + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + action=action, + detail=detail, + operator_id=operator_id, + operator_role=operator_role, + ) + db.add(log) + await db.commit() + await db.refresh(log) + return log + + +async def list_logs( + db: AsyncSession, + *, + study_id: uuid.UUID, + entity_type: str | None = None, + entity_id: uuid.UUID | None = None, + action: str | None = None, + skip: int = 0, + limit: int = 100, +) -> Sequence[AuditLog]: + stmt = select(AuditLog).where(AuditLog.study_id == study_id) + if entity_type: + stmt = stmt.where(AuditLog.entity_type == entity_type) + if entity_id: + stmt = stmt.where(AuditLog.entity_id == entity_id) + if action: + stmt = stmt.where(AuditLog.action == action) + stmt = stmt.order_by(AuditLog.created_at.desc()).offset(skip).limit(limit) + result = await db.execute(stmt) + return result.scalars().all() diff --git a/backend/app/crud/comment.py b/backend/app/crud/comment.py new file mode 100644 index 00000000..7e5d0f01 --- /dev/null +++ b/backend/app/crud/comment.py @@ -0,0 +1,48 @@ +import uuid +from typing import Sequence + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from app.models.comment import Comment +from app.schemas.comment import CommentCreate + + +async def create_comment( + db: AsyncSession, + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, + comment_in: CommentCreate, + created_by: uuid.UUID, +) -> Comment: + comment = Comment( + study_id=study_id, + entity_type=entity_type, + entity_id=entity_id, + content=comment_in.content, + created_by=created_by, + ) + db.add(comment) + await db.commit() + await db.refresh(comment) + return comment + + +async def list_comments( + db: AsyncSession, + study_id: uuid.UUID, + entity_type: str, + entity_id: uuid.UUID, +) -> Sequence[Comment]: + result = await db.execute( + select(Comment) + .where( + Comment.study_id == study_id, + Comment.entity_type == entity_type, + Comment.entity_id == entity_id, + Comment.is_deleted.is_(False), + ) + .order_by(Comment.created_at.asc()) + ) + return result.scalars().all() diff --git a/backend/app/db/base.py b/backend/app/db/base.py index 0b1a63b7..f8963b28 100644 --- a/backend/app/db/base.py +++ b/backend/app/db/base.py @@ -5,3 +5,6 @@ from app.models.user import User # noqa: F401 from app.models.study import Study # noqa: F401 from app.models.site import Site # noqa: F401 from app.models.study_member import StudyMember # noqa: F401 +from app.models.comment import Comment # noqa: F401 +from app.models.attachment import Attachment # noqa: F401 +from app.models.audit_log import AuditLog # noqa: F401 diff --git a/backend/app/models/attachment.py b/backend/app/models/attachment.py new file mode 100644 index 00000000..5b39ea6b --- /dev/null +++ b/backend/app/models/attachment.py @@ -0,0 +1,24 @@ +import uuid +from datetime import datetime + +from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, func +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Mapped, mapped_column + +from app.db.base_class import Base + + +class Attachment(Base): + __tablename__ = "attachments" + + id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + study_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("studies.id"), nullable=False) + entity_type: Mapped[str] = mapped_column(String(50), nullable=False) + entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False) + filename: Mapped[str] = mapped_column(String(255), nullable=False) + file_path: Mapped[str] = mapped_column(String(500), nullable=False) + file_size: Mapped[int] = mapped_column(Integer, nullable=False) + content_type: Mapped[str | None] = mapped_column(String(100), nullable=True) + uploaded_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) + uploaded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) + is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false") diff --git a/backend/app/models/audit_log.py b/backend/app/models/audit_log.py new file mode 100644 index 00000000..63903eca --- /dev/null +++ b/backend/app/models/audit_log.py @@ -0,0 +1,22 @@ +import uuid +from datetime import datetime + +from sqlalchemy import DateTime, ForeignKey, String, Text, func +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Mapped, mapped_column + +from app.db.base_class import Base + + +class AuditLog(Base): + __tablename__ = "audit_logs" + + id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + study_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("studies.id"), nullable=True) + entity_type: Mapped[str] = mapped_column(String(50), nullable=False) + entity_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True) + action: Mapped[str] = mapped_column(String(50), nullable=False) + detail: Mapped[str | None] = mapped_column(Text, nullable=True) + operator_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) + operator_role: Mapped[str] = mapped_column(String(50), nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) diff --git a/backend/app/models/comment.py b/backend/app/models/comment.py new file mode 100644 index 00000000..8d650b59 --- /dev/null +++ b/backend/app/models/comment.py @@ -0,0 +1,21 @@ +import uuid +from datetime import datetime + +from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text, func +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Mapped, mapped_column + +from app.db.base_class import Base + + +class Comment(Base): + __tablename__ = "comments" + + id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + study_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("studies.id"), index=True, nullable=False) + entity_type: Mapped[str] = mapped_column(String(50), nullable=False) + entity_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False) + content: Mapped[str] = mapped_column(Text, nullable=False) + created_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now()) + is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false") diff --git a/backend/app/schemas/attachment.py b/backend/app/schemas/attachment.py new file mode 100644 index 00000000..c531d5fb --- /dev/null +++ b/backend/app/schemas/attachment.py @@ -0,0 +1,14 @@ +import uuid +from datetime import datetime + +from pydantic import BaseModel, ConfigDict + + +class AttachmentRead(BaseModel): + id: uuid.UUID + filename: str + file_size: int + uploaded_by: uuid.UUID + uploaded_at: datetime + + model_config = ConfigDict(from_attributes=True) diff --git a/backend/app/schemas/audit.py b/backend/app/schemas/audit.py new file mode 100644 index 00000000..a0e1a817 --- /dev/null +++ b/backend/app/schemas/audit.py @@ -0,0 +1,16 @@ +import uuid +from datetime import datetime +from typing import Optional + +from pydantic import BaseModel, ConfigDict + + +class AuditLogRead(BaseModel): + id: uuid.UUID + action: str + detail: Optional[str] + operator_id: uuid.UUID + operator_role: str + created_at: datetime + + model_config = ConfigDict(from_attributes=True) diff --git a/backend/app/schemas/comment.py b/backend/app/schemas/comment.py new file mode 100644 index 00000000..d665cd35 --- /dev/null +++ b/backend/app/schemas/comment.py @@ -0,0 +1,17 @@ +import uuid +from datetime import datetime + +from pydantic import BaseModel, ConfigDict, Field + + +class CommentCreate(BaseModel): + content: str = Field(min_length=1) + + +class CommentRead(BaseModel): + id: uuid.UUID + content: str + created_by: uuid.UUID + created_at: datetime + + model_config = ConfigDict(from_attributes=True) diff --git a/backend/app/uploads/study_021aee05-20e3-4441-890c-6822a718f2f0/study_021aee05-20e3-4441-890c-6822a718f2f0/b6a32901-a168-42b1-9b8a-02cb59380f1e.txt b/backend/app/uploads/study_021aee05-20e3-4441-890c-6822a718f2f0/study_021aee05-20e3-4441-890c-6822a718f2f0/b6a32901-a168-42b1-9b8a-02cb59380f1e.txt new file mode 100644 index 00000000..fa254a0b --- /dev/null +++ b/backend/app/uploads/study_021aee05-20e3-4441-890c-6822a718f2f0/study_021aee05-20e3-4441-890c-6822a718f2f0/b6a32901-a168-42b1-9b8a-02cb59380f1e.txt @@ -0,0 +1 @@ +hello ctms \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index 396db03c..b5051fe4 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -7,4 +7,5 @@ python-jose[cryptography]==3.3.0 passlib[bcrypt]==1.7.4 bcrypt==4.0.1 python-multipart==0.0.6 +aiofiles==23.2.1 debugpy==1.8.0 diff --git a/pg_data/base/16384/1247 b/pg_data/base/16384/1247 index ada60ecd..3f84f3d6 100644 Binary files a/pg_data/base/16384/1247 and b/pg_data/base/16384/1247 differ diff --git a/pg_data/base/16384/1249 b/pg_data/base/16384/1249 index 2dc327b5..231cb025 100644 Binary files a/pg_data/base/16384/1249 and b/pg_data/base/16384/1249 differ diff --git a/pg_data/base/16384/1259 b/pg_data/base/16384/1259 index 8d8b67a2..9ae22a67 100644 Binary files a/pg_data/base/16384/1259 and b/pg_data/base/16384/1259 differ diff --git a/pg_data/base/16384/1259_fsm b/pg_data/base/16384/1259_fsm index 65e1941a..92c04596 100644 Binary files a/pg_data/base/16384/1259_fsm and b/pg_data/base/16384/1259_fsm differ diff --git a/pg_data/base/16384/24584 b/pg_data/base/16384/24584 index e69de29b..6d17cf9d 100644 Binary files a/pg_data/base/16384/24584 and b/pg_data/base/16384/24584 differ diff --git a/pg_data/base/16384/24590 b/pg_data/base/16384/24590 index 4546d145..0bb7cdbd 100644 Binary files a/pg_data/base/16384/24590 and b/pg_data/base/16384/24590 differ diff --git a/pg_data/base/16384/24597 b/pg_data/base/16384/24597 index 1cac0492..5e1452df 100644 Binary files a/pg_data/base/16384/24597 and b/pg_data/base/16384/24597 differ diff --git a/pg_data/base/16384/24633 b/pg_data/base/16384/24633 new file mode 100644 index 00000000..6d17cf9d Binary files /dev/null and b/pg_data/base/16384/24633 differ diff --git a/pg_data/base/16384/24638 b/pg_data/base/16384/24638 new file mode 100644 index 00000000..e69de29b diff --git a/pg_data/base/16384/24639 b/pg_data/base/16384/24639 new file mode 100644 index 00000000..2ff28ec0 Binary files /dev/null and b/pg_data/base/16384/24639 differ diff --git a/pg_data/base/16384/24640 b/pg_data/base/16384/24640 new file mode 100644 index 00000000..4c6ad9c2 Binary files /dev/null and b/pg_data/base/16384/24640 differ diff --git a/pg_data/base/16384/24652 b/pg_data/base/16384/24652 new file mode 100644 index 00000000..45d23f3c Binary files /dev/null and b/pg_data/base/16384/24652 differ diff --git a/pg_data/base/16384/24653 b/pg_data/base/16384/24653 new file mode 100644 index 00000000..6d17cf9d Binary files /dev/null and b/pg_data/base/16384/24653 differ diff --git a/pg_data/base/16384/24658 b/pg_data/base/16384/24658 new file mode 100644 index 00000000..e69de29b diff --git a/pg_data/base/16384/24659 b/pg_data/base/16384/24659 new file mode 100644 index 00000000..137e7773 Binary files /dev/null and b/pg_data/base/16384/24659 differ diff --git a/pg_data/base/16384/24660 b/pg_data/base/16384/24660 new file mode 100644 index 00000000..931abcb0 Binary files /dev/null and b/pg_data/base/16384/24660 differ diff --git a/pg_data/base/16384/24672 b/pg_data/base/16384/24672 new file mode 100644 index 00000000..6d17cf9d Binary files /dev/null and b/pg_data/base/16384/24672 differ diff --git a/pg_data/base/16384/24676 b/pg_data/base/16384/24676 new file mode 100644 index 00000000..e69de29b diff --git a/pg_data/base/16384/24677 b/pg_data/base/16384/24677 new file mode 100644 index 00000000..25da78f2 Binary files /dev/null and b/pg_data/base/16384/24677 differ diff --git a/pg_data/base/16384/24678 b/pg_data/base/16384/24678 new file mode 100644 index 00000000..1efb9c8a Binary files /dev/null and b/pg_data/base/16384/24678 differ diff --git a/pg_data/base/16384/2579 b/pg_data/base/16384/2579 index 23bc6c36..bc92f521 100644 Binary files a/pg_data/base/16384/2579 and b/pg_data/base/16384/2579 differ diff --git a/pg_data/base/16384/2604 b/pg_data/base/16384/2604 index 8b39db42..9870d830 100644 Binary files a/pg_data/base/16384/2604 and b/pg_data/base/16384/2604 differ diff --git a/pg_data/base/16384/2606 b/pg_data/base/16384/2606 index 9d2d69e5..bb28fc17 100644 Binary files a/pg_data/base/16384/2606 and b/pg_data/base/16384/2606 differ diff --git a/pg_data/base/16384/2608 b/pg_data/base/16384/2608 index 915ae595..b846db0a 100644 Binary files a/pg_data/base/16384/2608 and b/pg_data/base/16384/2608 differ diff --git a/pg_data/base/16384/2610 b/pg_data/base/16384/2610 index 7af313f0..0730f687 100644 Binary files a/pg_data/base/16384/2610 and b/pg_data/base/16384/2610 differ diff --git a/pg_data/base/16384/2610_fsm b/pg_data/base/16384/2610_fsm index 766cb70f..d6c59d37 100644 Binary files a/pg_data/base/16384/2610_fsm and b/pg_data/base/16384/2610_fsm differ diff --git a/pg_data/base/16384/2620 b/pg_data/base/16384/2620 index c7494c5c..ec70139f 100644 Binary files a/pg_data/base/16384/2620 and b/pg_data/base/16384/2620 differ diff --git a/pg_data/base/16384/2656 b/pg_data/base/16384/2656 index 804b9c52..3e55a033 100644 Binary files a/pg_data/base/16384/2656 and b/pg_data/base/16384/2656 differ diff --git a/pg_data/base/16384/2657 b/pg_data/base/16384/2657 index 35d0ffe3..591b6232 100644 Binary files a/pg_data/base/16384/2657 and b/pg_data/base/16384/2657 differ diff --git a/pg_data/base/16384/2658 b/pg_data/base/16384/2658 index 0379b3f1..e1244a12 100644 Binary files a/pg_data/base/16384/2658 and b/pg_data/base/16384/2658 differ diff --git a/pg_data/base/16384/2659 b/pg_data/base/16384/2659 index 060726cd..44e9ff5a 100644 Binary files a/pg_data/base/16384/2659 and b/pg_data/base/16384/2659 differ diff --git a/pg_data/base/16384/2662 b/pg_data/base/16384/2662 index e2e5499a..8ad83e84 100644 Binary files a/pg_data/base/16384/2662 and b/pg_data/base/16384/2662 differ diff --git a/pg_data/base/16384/2663 b/pg_data/base/16384/2663 index 5fa7557c..351c46e8 100644 Binary files a/pg_data/base/16384/2663 and b/pg_data/base/16384/2663 differ diff --git a/pg_data/base/16384/2664 b/pg_data/base/16384/2664 index 7c35e2e6..708cbf01 100644 Binary files a/pg_data/base/16384/2664 and b/pg_data/base/16384/2664 differ diff --git a/pg_data/base/16384/2665 b/pg_data/base/16384/2665 index 3c6c216d..83ac8218 100644 Binary files a/pg_data/base/16384/2665 and b/pg_data/base/16384/2665 differ diff --git a/pg_data/base/16384/2666 b/pg_data/base/16384/2666 index 96fb2fa9..7bea4825 100644 Binary files a/pg_data/base/16384/2666 and b/pg_data/base/16384/2666 differ diff --git a/pg_data/base/16384/2667 b/pg_data/base/16384/2667 index 5b3e5eb1..50228a1f 100644 Binary files a/pg_data/base/16384/2667 and b/pg_data/base/16384/2667 differ diff --git a/pg_data/base/16384/2673 b/pg_data/base/16384/2673 index 4939f76e..4725b928 100644 Binary files a/pg_data/base/16384/2673 and b/pg_data/base/16384/2673 differ diff --git a/pg_data/base/16384/2674 b/pg_data/base/16384/2674 index cd8532fe..02fca434 100644 Binary files a/pg_data/base/16384/2674 and b/pg_data/base/16384/2674 differ diff --git a/pg_data/base/16384/2678 b/pg_data/base/16384/2678 index 542e419c..bd0ae35c 100644 Binary files a/pg_data/base/16384/2678 and b/pg_data/base/16384/2678 differ diff --git a/pg_data/base/16384/2679 b/pg_data/base/16384/2679 index 1146f20c..2420ee52 100644 Binary files a/pg_data/base/16384/2679 and b/pg_data/base/16384/2679 differ diff --git a/pg_data/base/16384/2699 b/pg_data/base/16384/2699 index 10bcbc67..42acad65 100644 Binary files a/pg_data/base/16384/2699 and b/pg_data/base/16384/2699 differ diff --git a/pg_data/base/16384/2701 b/pg_data/base/16384/2701 index 1ca69452..d7c6a10b 100644 Binary files a/pg_data/base/16384/2701 and b/pg_data/base/16384/2701 differ diff --git a/pg_data/base/16384/2702 b/pg_data/base/16384/2702 index b2d761d4..5114e8ab 100644 Binary files a/pg_data/base/16384/2702 and b/pg_data/base/16384/2702 differ diff --git a/pg_data/base/16384/2703 b/pg_data/base/16384/2703 index 64b80d5f..a93daba0 100644 Binary files a/pg_data/base/16384/2703 and b/pg_data/base/16384/2703 differ diff --git a/pg_data/base/16384/2704 b/pg_data/base/16384/2704 index bfbde192..46146488 100644 Binary files a/pg_data/base/16384/2704 and b/pg_data/base/16384/2704 differ diff --git a/pg_data/base/16384/3455 b/pg_data/base/16384/3455 index 382a2635..aba8e6d9 100644 Binary files a/pg_data/base/16384/3455 and b/pg_data/base/16384/3455 differ diff --git a/pg_data/base/16384/pg_internal.init b/pg_data/base/16384/pg_internal.init index 41961490..b533d5fa 100644 Binary files a/pg_data/base/16384/pg_internal.init and b/pg_data/base/16384/pg_internal.init differ diff --git a/pg_data/global/pg_control b/pg_data/global/pg_control index 82c5ae15..d75d796a 100644 Binary files a/pg_data/global/pg_control and b/pg_data/global/pg_control differ diff --git a/pg_data/global/pg_internal.init b/pg_data/global/pg_internal.init index 3bb1fa45..0ff1bca7 100644 Binary files a/pg_data/global/pg_internal.init and b/pg_data/global/pg_internal.init differ diff --git a/pg_data/pg_wal/000000010000000000000001 b/pg_data/pg_wal/000000010000000000000001 index b505c9a2..f4dd6eac 100644 Binary files a/pg_data/pg_wal/000000010000000000000001 and b/pg_data/pg_wal/000000010000000000000001 differ diff --git a/pg_data/pg_xact/0000 b/pg_data/pg_xact/0000 index 3616a9bd..0d79a6bd 100644 Binary files a/pg_data/pg_xact/0000 and b/pg_data/pg_xact/0000 differ diff --git a/pg_data/postmaster.pid b/pg_data/postmaster.pid index e6ecfbd7..962beffd 100644 --- a/pg_data/postmaster.pid +++ b/pg_data/postmaster.pid @@ -1,6 +1,6 @@ 1 /var/lib/postgresql/data -1765872650 +1765875461 5432 /var/run/postgresql *