<?php
namespace App\Entity;
use App\Services\TikTok\Dto\TikTokAuthDto;
use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Symfony\Component\Uid\Uuid;
#[Entity]
class TikTokAuth
{
#[Id]
#[Column(type: 'uuid', unique: true)]
private Uuid $id;
#[Column(type: 'uuid', unique: true, nullable: true)]
private ?Uuid $unionId = null;
#[Column(type: 'string')]
private string $accessToken;
#[Column(type: 'string')]
private string $refreshToken;
// A6: plain DEFAULT, no `ON UPDATE CURRENT_TIMESTAMP`, so refreshed_at advances
// only when we explicitly set it on a SUCCESSFUL token exchange — not on every
// row write (a failure-flag write used to make a dead token look freshly refreshed).
#[Column(
type: 'datetime_immutable',
nullable: false,
columnDefinition: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL',
)]
private DateTimeImmutable $refreshedAt;
#[Column(
type: 'datetime_immutable',
nullable: true,
columnDefinition: 'TIMESTAMP NULL',
)]
private ?DateTimeImmutable $refreshExpiresAt = null;
// A2/A3: a failed refresh flags the token here instead of poisoning
// refreshExpiresAt (which must stay the true 365-day expiry). needsReconnect
// is what the proactive refresher (findTikTokAuthToUpdate) excludes, and what
// the creator's reconnect clears.
#[Column(type: 'boolean', options: ['default' => false])]
private bool $needsReconnect = false;
#[Column(type: 'datetime_immutable', nullable: true)]
private ?DateTimeImmutable $refreshFailedAt = null;
// A5: when the 24h access token expires (= last issuance + expires_in). The
// lazy refresher keys off THIS, not a 1-hour timer on refreshedAt, so a
// still-valid access token isn't re-fetched on nearly every request.
#[Column(type: 'datetime_immutable', nullable: true)]
private ?DateTimeImmutable $accessTokenExpiresAt = null;
// One-off reconnect-email campaign (app:tiktok:reconnect-email): timestamp of when
// we last emailed this flagged account asking the owner to reconnect TikTok. Acts as
// a suppression marker so the batch never double-emails the same flagged account, and
// it is independent of needsReconnect (which the creator's reconnect clears).
#[Column(type: 'datetime_immutable', nullable: true)]
private ?DateTimeImmutable $reconnectEmailSentAt = null;
public function getId(): Uuid
{
return $this->id;
}
public function setId(Uuid $id): static
{
$this->id = $id;
return $this;
}
public function setUnionId(Uuid $unionId): static
{
$this->unionId = $unionId;
return $this;
}
public function getUnionId(): ?Uuid
{
return $this->unionId;
}
public function getAccessToken(): string
{
return $this->accessToken;
}
public function setAccessToken(string $accessToken): static
{
$this->accessToken = $accessToken;
return $this;
}
public function getRefreshToken(): string
{
return $this->refreshToken;
}
public function setRefreshToken(string $refreshToken): static
{
$this->refreshToken = $refreshToken;
return $this;
}
public function getRefreshedAt(): DateTimeImmutable
{
return $this->refreshedAt;
}
public function setRefreshedAt(DateTimeImmutable $refreshedAt): static
{
$this->refreshedAt = $refreshedAt;
return $this;
}
public function setRefreshedAtNow(): static
{
$this->refreshedAt = new DateTimeImmutable;
return $this;
}
public function getRefreshExpiresAt(): ?DateTimeImmutable
{
return $this->refreshExpiresAt;
}
public function setRefreshExpiresAt(?DateTimeImmutable $refreshExpiresAt): static
{
$this->refreshExpiresAt = $refreshExpiresAt;
return $this;
}
public function needsReconnect(): bool
{
return $this->needsReconnect;
}
public function setNeedsReconnect(bool $needsReconnect): static
{
$this->needsReconnect = $needsReconnect;
return $this;
}
public function getRefreshFailedAt(): ?DateTimeImmutable
{
return $this->refreshFailedAt;
}
public function setRefreshFailedAt(?DateTimeImmutable $refreshFailedAt): static
{
$this->refreshFailedAt = $refreshFailedAt;
return $this;
}
public function getAccessTokenExpiresAt(): ?DateTimeImmutable
{
return $this->accessTokenExpiresAt;
}
public function setAccessTokenExpiresAt(?DateTimeImmutable $accessTokenExpiresAt): static
{
$this->accessTokenExpiresAt = $accessTokenExpiresAt;
return $this;
}
public function getReconnectEmailSentAt(): ?DateTimeImmutable
{
return $this->reconnectEmailSentAt;
}
public function setReconnectEmailSentAt(?DateTimeImmutable $reconnectEmailSentAt): static
{
$this->reconnectEmailSentAt = $reconnectEmailSentAt;
return $this;
}
public function toDto(): TikTokAuthDto
{
return new TikTokAuthDto(
$this->getId(),
$this->getAccessToken(),
$this->getRefreshToken(),
$this->getRefreshedAt(),
$this->getRefreshExpiresAt(),
$this->getAccessTokenExpiresAt(),
$this->getUnionId(),
);
}
}