Web app: new entities (Image, RenderedAsset, SharedImage, Token, DeviceImageHistory), enums, repositories, controllers, message handlers, migrations, tests, frontend upload/library/sticker UI, Vue components. Firmware: EPD background screen binaries + gen scripts, setup_bg header. Infra: ddev config, test bundle, gitignore coverage dir. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260505040613 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add image, rendered_asset, image_device_approval tables; add model column to device';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE image (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, original_filename VARCHAR(255) NOT NULL, storage_path VARCHAR(500) NOT NULL, uploaded_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, deleted_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, user_id INT NOT NULL, PRIMARY KEY (id))');
|
||||
$this->addSql('CREATE INDEX IDX_C53D045FA76ED395 ON image (user_id)');
|
||||
$this->addSql('CREATE TABLE image_device_approval (image_id INT NOT NULL, device_id INT NOT NULL, PRIMARY KEY (image_id, device_id))');
|
||||
$this->addSql('CREATE INDEX IDX_3524D29A3DA5256D ON image_device_approval (image_id)');
|
||||
$this->addSql('CREATE INDEX IDX_3524D29A94A4C7D4 ON image_device_approval (device_id)');
|
||||
$this->addSql('CREATE TABLE rendered_asset (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, device_model VARCHAR(255) NOT NULL, orientation VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, file_path VARCHAR(500) DEFAULT NULL, rendered_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, image_id INT NOT NULL, PRIMARY KEY (id))');
|
||||
$this->addSql('CREATE INDEX IDX_DF34C8E33DA5256D ON rendered_asset (image_id)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_DF34C8E33DA5256D111092BE3680C556 ON rendered_asset (image_id, device_model, orientation)');
|
||||
$this->addSql('ALTER TABLE image ADD CONSTRAINT FK_C53D045FA76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) NOT DEFERRABLE');
|
||||
$this->addSql('ALTER TABLE image_device_approval ADD CONSTRAINT FK_3524D29A3DA5256D FOREIGN KEY (image_id) REFERENCES image (id) ON DELETE CASCADE');
|
||||
$this->addSql('ALTER TABLE image_device_approval ADD CONSTRAINT FK_3524D29A94A4C7D4 FOREIGN KEY (device_id) REFERENCES device (id) ON DELETE CASCADE');
|
||||
$this->addSql('ALTER TABLE rendered_asset ADD CONSTRAINT FK_DF34C8E33DA5256D FOREIGN KEY (image_id) REFERENCES image (id) ON DELETE CASCADE NOT DEFERRABLE');
|
||||
$this->addSql("ALTER TABLE device ADD model VARCHAR(255) NOT NULL DEFAULT 'v1'");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE image DROP CONSTRAINT FK_C53D045FA76ED395');
|
||||
$this->addSql('ALTER TABLE image_device_approval DROP CONSTRAINT FK_3524D29A3DA5256D');
|
||||
$this->addSql('ALTER TABLE image_device_approval DROP CONSTRAINT FK_3524D29A94A4C7D4');
|
||||
$this->addSql('ALTER TABLE rendered_asset DROP CONSTRAINT FK_DF34C8E33DA5256D');
|
||||
$this->addSql('DROP TABLE image');
|
||||
$this->addSql('DROP TABLE image_device_approval');
|
||||
$this->addSql('DROP TABLE rendered_asset');
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN model');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260505120000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add wake_hour to device for time-based wake scheduling';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device ADD wake_hour INT DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN wake_hour');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260505130000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add timezone to user';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE "user" ADD timezone VARCHAR(60) DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE "user" DROP COLUMN timezone');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260505140000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add timezone to device (per-device scheduling context)';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("ALTER TABLE device ADD timezone VARCHAR(60) NOT NULL DEFAULT 'UTC'");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN timezone');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260505150000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add crop_params and sticker_state to image for re-edit support';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE image ADD crop_params TEXT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE image ADD sticker_state TEXT DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE image DROP COLUMN crop_params');
|
||||
$this->addSql('ALTER TABLE image DROP COLUMN sticker_state');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260506000000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add device_image_history table for rotation uniqueness tracking';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TABLE device_image_history (
|
||||
id SERIAL NOT NULL,
|
||||
device_id INT NOT NULL,
|
||||
image_id INT NOT NULL,
|
||||
served_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
)');
|
||||
$this->addSql('CREATE INDEX idx_history_device_served ON device_image_history (device_id, served_at)');
|
||||
$this->addSql('ALTER TABLE device_image_history ADD CONSTRAINT fk_history_device FOREIGN KEY (device_id) REFERENCES device (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE device_image_history ADD CONSTRAINT fk_history_image FOREIGN KEY (image_id) REFERENCES image (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('COMMENT ON COLUMN device_image_history.served_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device_image_history DROP CONSTRAINT fk_history_device');
|
||||
$this->addSql('ALTER TABLE device_image_history DROP CONSTRAINT fk_history_image');
|
||||
$this->addSql('DROP TABLE device_image_history');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260506010000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add last_seen_at to device';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device ADD last_seen_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN device.last_seen_at IS \'(DC2Type:datetime_immutable)\'');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN last_seen_at');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260506020000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add current_image_id FK to device';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device ADD current_image_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE device ADD CONSTRAINT fk_device_current_image FOREIGN KEY (current_image_id) REFERENCES image (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX idx_device_current_image ON device (current_image_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device DROP CONSTRAINT fk_device_current_image');
|
||||
$this->addSql('DROP INDEX idx_device_current_image');
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN current_image_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260506200000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Rename rotation_interval_hours to rotation_interval_minutes (1 hour = 60 minutes)';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device RENAME COLUMN rotation_interval_hours TO rotation_interval_minutes');
|
||||
$this->addSql('ALTER TABLE device ALTER COLUMN rotation_interval_minutes SET DEFAULT 1440');
|
||||
$this->addSql('UPDATE device SET rotation_interval_minutes = rotation_interval_minutes * 60');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('UPDATE device SET rotation_interval_minutes = rotation_interval_minutes / 60');
|
||||
$this->addSql('ALTER TABLE device ALTER COLUMN rotation_interval_minutes SET DEFAULT 24');
|
||||
$this->addSql('ALTER TABLE device RENAME COLUMN rotation_interval_minutes TO rotation_interval_hours');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260506210000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add locked_image_id to device for pinning an image and bypassing rotation';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device ADD COLUMN locked_image_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE device ADD CONSTRAINT fk_device_locked_image FOREIGN KEY (locked_image_id) REFERENCES image(id) ON DELETE SET NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE device DROP CONSTRAINT fk_device_locked_image');
|
||||
$this->addSql('ALTER TABLE device DROP COLUMN locked_image_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260507100000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add token table for share and hard-delete flows';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("CREATE TABLE token (
|
||||
uuid VARCHAR(36) NOT NULL,
|
||||
type VARCHAR(30) NOT NULL,
|
||||
image_id INT NOT NULL,
|
||||
recipient_user_id INT DEFAULT NULL,
|
||||
recipient_email VARCHAR(180) DEFAULT NULL,
|
||||
expires_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
||||
used_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
|
||||
PRIMARY KEY(uuid)
|
||||
)");
|
||||
$this->addSql("COMMENT ON COLUMN token.expires_at IS '(DC2Type:datetime_immutable)'");
|
||||
$this->addSql("COMMENT ON COLUMN token.used_at IS '(DC2Type:datetime_immutable)'");
|
||||
$this->addSql('ALTER TABLE token ADD CONSTRAINT fk_token_image FOREIGN KEY (image_id) REFERENCES image (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE token ADD CONSTRAINT fk_token_recipient FOREIGN KEY (recipient_user_id) REFERENCES "user" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX idx_token_recipient ON token (recipient_user_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE token DROP CONSTRAINT fk_token_image');
|
||||
$this->addSql('ALTER TABLE token DROP CONSTRAINT fk_token_recipient');
|
||||
$this->addSql('DROP TABLE token');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260507200000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add shared_image table for family sharing';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("CREATE TABLE shared_image (
|
||||
id SERIAL NOT NULL,
|
||||
source_image_id INT NOT NULL,
|
||||
recipient_user_id INT NOT NULL,
|
||||
shared_by_id INT NOT NULL,
|
||||
shared_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
PRIMARY KEY(id)
|
||||
)");
|
||||
$this->addSql("COMMENT ON COLUMN shared_image.shared_at IS '(DC2Type:datetime_immutable)'");
|
||||
$this->addSql('CREATE INDEX idx_shared_recipient_status ON shared_image (recipient_user_id, status)');
|
||||
$this->addSql('ALTER TABLE shared_image ADD CONSTRAINT fk_shared_source FOREIGN KEY (source_image_id) REFERENCES image (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE shared_image ADD CONSTRAINT fk_shared_recipient FOREIGN KEY (recipient_user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('ALTER TABLE shared_image ADD CONSTRAINT fk_shared_by FOREIGN KEY (shared_by_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE shared_image DROP CONSTRAINT fk_shared_source');
|
||||
$this->addSql('ALTER TABLE shared_image DROP CONSTRAINT fk_shared_recipient');
|
||||
$this->addSql('ALTER TABLE shared_image DROP CONSTRAINT fk_shared_by');
|
||||
$this->addSql('DROP TABLE shared_image');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user