From c17c1b469e94f9e3fe3722917428a5785386e87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Kr=C3=BChlmann?= Date: Tue, 28 Apr 2026 14:46:43 +0200 Subject: [PATCH] feat: add copyright_reports table and channel banned flag (V15, V16) --- .../imgfloat/model/db/imgfloat/Channel.java | 11 ++ .../model/db/imgfloat/CopyrightReport.java | 117 ++++++++++++++++++ .../db/imgfloat/CopyrightReportStatus.java | 9 ++ .../db/migration/V15__copyright_reports.sql | 19 +++ .../db/migration/V16__channel_banned.sql | 1 + 5 files changed, 157 insertions(+) create mode 100644 src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReport.java create mode 100644 src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReportStatus.java create mode 100644 src/main/resources/db/migration/V15__copyright_reports.sql create mode 100644 src/main/resources/db/migration/V16__channel_banned.sql diff --git a/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/Channel.java b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/Channel.java index 6e482c2..05df837 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/Channel.java +++ b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/Channel.java @@ -44,6 +44,9 @@ public class Channel { @Column(name = "allow_script_chat_access", nullable = false) private boolean allowScriptChatAccess = true; + @Column(name = "banned", nullable = false) + private boolean banned = false; + @Column(name = "created_at", nullable = false, updatable = false) private Instant createdAt; @@ -120,6 +123,14 @@ public class Channel { this.allowScriptChatAccess = allowScriptChatAccess; } + public boolean isBanned() { + return banned; + } + + public void setBanned(boolean banned) { + this.banned = banned; + } + public Instant getCreatedAt() { return createdAt; } diff --git a/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReport.java b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReport.java new file mode 100644 index 0000000..47f8c5e --- /dev/null +++ b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReport.java @@ -0,0 +1,117 @@ +package dev.kruhlmann.imgfloat.model.db.imgfloat; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Table; +import java.time.Instant; +import java.util.Locale; +import java.util.UUID; + +@Entity +@Table(name = "copyright_reports") +public class CopyrightReport { + + @Id + private String id; + + @Column(name = "asset_id", nullable = false) + private String assetId; + + @Column(nullable = false) + private String broadcaster; + + @Column(name = "claimant_name", nullable = false) + private String claimantName; + + @Column(name = "claimant_email", nullable = false) + private String claimantEmail; + + @Column(name = "original_work_description", nullable = false) + private String originalWorkDescription; + + @Column(name = "infringing_description", nullable = false) + private String infringingDescription; + + @Column(name = "good_faith_declaration", nullable = false) + private boolean goodFaithDeclaration; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private CopyrightReportStatus status = CopyrightReportStatus.PENDING; + + @Column(name = "resolution_notes") + private String resolutionNotes; + + @Column(name = "resolved_by") + private String resolvedBy; + + @Column(name = "created_at", nullable = false, updatable = false) + private Instant createdAt; + + @Column(name = "updated_at", nullable = false) + private Instant updatedAt; + + public CopyrightReport() {} + + @PrePersist + public void prepare() { + if (id == null) { + id = UUID.randomUUID().toString(); + } + Instant now = Instant.now(); + if (createdAt == null) { + createdAt = now; + } + if (updatedAt == null) { + updatedAt = now; + } + if (status == null) { + status = CopyrightReportStatus.PENDING; + } + if (broadcaster != null) { + broadcaster = broadcaster.toLowerCase(Locale.ROOT); + } + } + + @PreUpdate + public void touch() { + updatedAt = Instant.now(); + } + + public String getId() { return id; } + public String getAssetId() { return assetId; } + public void setAssetId(String assetId) { this.assetId = assetId; } + public String getBroadcaster() { return broadcaster; } + public void setBroadcaster(String broadcaster) { + this.broadcaster = broadcaster == null ? null : broadcaster.toLowerCase(Locale.ROOT); + } + public String getClaimantName() { return claimantName; } + public void setClaimantName(String claimantName) { this.claimantName = claimantName; } + public String getClaimantEmail() { return claimantEmail; } + public void setClaimantEmail(String claimantEmail) { this.claimantEmail = claimantEmail; } + public String getOriginalWorkDescription() { return originalWorkDescription; } + public void setOriginalWorkDescription(String originalWorkDescription) { + this.originalWorkDescription = originalWorkDescription; + } + public String getInfringingDescription() { return infringingDescription; } + public void setInfringingDescription(String infringingDescription) { + this.infringingDescription = infringingDescription; + } + public boolean isGoodFaithDeclaration() { return goodFaithDeclaration; } + public void setGoodFaithDeclaration(boolean goodFaithDeclaration) { + this.goodFaithDeclaration = goodFaithDeclaration; + } + public CopyrightReportStatus getStatus() { return status; } + public void setStatus(CopyrightReportStatus status) { this.status = status; } + public String getResolutionNotes() { return resolutionNotes; } + public void setResolutionNotes(String resolutionNotes) { this.resolutionNotes = resolutionNotes; } + public String getResolvedBy() { return resolvedBy; } + public void setResolvedBy(String resolvedBy) { this.resolvedBy = resolvedBy; } + public Instant getCreatedAt() { return createdAt; } + public Instant getUpdatedAt() { return updatedAt; } +} diff --git a/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReportStatus.java b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReportStatus.java new file mode 100644 index 0000000..c235a37 --- /dev/null +++ b/src/main/java/dev/kruhlmann/imgfloat/model/db/imgfloat/CopyrightReportStatus.java @@ -0,0 +1,9 @@ +package dev.kruhlmann.imgfloat.model.db.imgfloat; + +public enum CopyrightReportStatus { + PENDING, + DISMISSED, + RESOLVED, + /** Sysadmin sent a notice to the broadcaster; awaiting their acknowledgement. */ + NOTIFIED +} diff --git a/src/main/resources/db/migration/V15__copyright_reports.sql b/src/main/resources/db/migration/V15__copyright_reports.sql new file mode 100644 index 0000000..d4f9e88 --- /dev/null +++ b/src/main/resources/db/migration/V15__copyright_reports.sql @@ -0,0 +1,19 @@ +CREATE TABLE copyright_reports ( + id TEXT PRIMARY KEY, + asset_id TEXT NOT NULL REFERENCES assets(id) ON DELETE CASCADE, + broadcaster TEXT NOT NULL, + claimant_name TEXT NOT NULL, + claimant_email TEXT NOT NULL, + original_work_description TEXT NOT NULL, + infringing_description TEXT NOT NULL, + good_faith_declaration BOOLEAN NOT NULL DEFAULT FALSE, + status TEXT NOT NULL DEFAULT 'PENDING', + resolution_notes TEXT, + resolved_by TEXT, + created_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL +); + +CREATE INDEX idx_copyright_reports_asset_id ON copyright_reports(asset_id); +CREATE INDEX idx_copyright_reports_broadcaster ON copyright_reports(broadcaster); +CREATE INDEX idx_copyright_reports_status ON copyright_reports(status); diff --git a/src/main/resources/db/migration/V16__channel_banned.sql b/src/main/resources/db/migration/V16__channel_banned.sql new file mode 100644 index 0000000..bde3f25 --- /dev/null +++ b/src/main/resources/db/migration/V16__channel_banned.sql @@ -0,0 +1 @@ +ALTER TABLE channels ADD COLUMN banned BOOLEAN NOT NULL DEFAULT FALSE;