Add Mellat e-payment, videos/instructions CMS, and legacy domain lookup.

Store gateway credentials per business, initiate/verify Mellat at checkout, and resolve old CMS ids from domains in the migrate modal.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-13 09:51:17 +03:30
co-authored by Cursor
parent 513673a7da
commit d5e502cf77
44 changed files with 9303 additions and 996 deletions
+73
View File
@@ -0,0 +1,73 @@
-- Videos module (embedded YouTube / Aparat)
ALTER TYPE media_entity_type ADD VALUE IF NOT EXISTS 'video';
CREATE TYPE video_provider AS ENUM ('youtube', 'aparat');
CREATE TABLE videos (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
business_id BIGINT NOT NULL,
author_id BIGINT,
title VARCHAR(255) NOT NULL,
title_fa VARCHAR(255),
title_en VARCHAR(255),
slug VARCHAR(255) NOT NULL,
excerpt TEXT,
content JSONB NOT NULL DEFAULT '{}',
video_provider video_provider NOT NULL,
embed_code TEXT NOT NULL,
status content_status NOT NULL DEFAULT 'draft',
featured_media_id BIGINT,
published_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT videos_business_slug_unique UNIQUE (business_id, slug),
CONSTRAINT videos_business_id_fkey
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
CONSTRAINT videos_author_id_fkey
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE SET NULL,
CONSTRAINT videos_featured_media_id_fkey
FOREIGN KEY (featured_media_id) REFERENCES media (id) ON DELETE SET NULL
);
CREATE INDEX idx_videos_business_id ON videos (business_id);
CREATE INDEX idx_videos_business_status ON videos (business_id, status);
CREATE INDEX idx_videos_business_published ON videos (business_id, published_at DESC);
CREATE INDEX idx_videos_author_id ON videos (author_id);
CREATE TRIGGER videos_set_updated_at
BEFORE UPDATE ON videos
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
-- Permissions
INSERT INTO permissions (name, slug, group_name, description) VALUES
('View videos', 'videos.read', 'videos', 'View videos'),
('Create videos', 'videos.create', 'videos', 'Create videos'),
('Update videos', 'videos.update', 'videos', 'Edit videos'),
('Delete videos', 'videos.delete', 'videos', 'Delete videos'),
('Publish videos', 'videos.publish', 'videos', 'Publish and unpublish videos')
ON CONFLICT (slug) DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug LIKE 'videos.%'
WHERE r.slug IN ('business_owner', 'owner', 'admin')
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug IN ('videos.read', 'videos.create', 'videos.update', 'videos.publish')
WHERE r.slug = 'editor'
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug = 'videos.read'
WHERE r.slug = 'viewer'
ON CONFLICT DO NOTHING;
@@ -0,0 +1,7 @@
ALTER TABLE videos ADD COLUMN IF NOT EXISTS old_id BIGINT;
CREATE UNIQUE INDEX IF NOT EXISTS videos_business_old_id_unique
ON videos (business_id, old_id)
WHERE old_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_videos_old_id ON videos (old_id);
+72
View File
@@ -0,0 +1,72 @@
-- Instructions module (rich text + optional embedded video)
ALTER TYPE media_entity_type ADD VALUE IF NOT EXISTS 'instruction';
CREATE TABLE instructions (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
business_id BIGINT NOT NULL,
author_id BIGINT,
title VARCHAR(255) NOT NULL,
title_fa VARCHAR(255),
title_en VARCHAR(255),
slug VARCHAR(255) NOT NULL,
excerpt TEXT,
content JSONB NOT NULL DEFAULT '{}',
video_provider video_provider,
embed_code TEXT,
status content_status NOT NULL DEFAULT 'draft',
featured_media_id BIGINT,
published_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}',
old_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT instructions_business_slug_unique UNIQUE (business_id, slug),
CONSTRAINT instructions_business_old_id_unique UNIQUE (business_id, old_id),
CONSTRAINT instructions_business_id_fkey
FOREIGN KEY (business_id) REFERENCES businesses (id) ON DELETE CASCADE,
CONSTRAINT instructions_author_id_fkey
FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE SET NULL,
CONSTRAINT instructions_featured_media_id_fkey
FOREIGN KEY (featured_media_id) REFERENCES media (id) ON DELETE SET NULL
);
CREATE INDEX idx_instructions_business_id ON instructions (business_id);
CREATE INDEX idx_instructions_business_status ON instructions (business_id, status);
CREATE INDEX idx_instructions_business_published ON instructions (business_id, published_at DESC);
CREATE INDEX idx_instructions_author_id ON instructions (author_id);
CREATE TRIGGER instructions_set_updated_at
BEFORE UPDATE ON instructions
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
INSERT INTO permissions (name, slug, group_name, description) VALUES
('View instructions', 'instructions.read', 'instructions', 'View instructions'),
('Create instructions', 'instructions.create', 'instructions', 'Create instructions'),
('Update instructions', 'instructions.update', 'instructions', 'Edit instructions'),
('Delete instructions', 'instructions.delete', 'instructions', 'Delete instructions'),
('Publish instructions', 'instructions.publish', 'instructions', 'Publish and unpublish instructions')
ON CONFLICT (slug) DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug LIKE 'instructions.%'
WHERE r.slug IN ('business_owner', 'owner', 'admin')
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug IN ('instructions.read', 'instructions.create', 'instructions.update', 'instructions.publish')
WHERE r.slug = 'editor'
ON CONFLICT DO NOTHING;
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
JOIN permissions p ON p.slug = 'instructions.read'
WHERE r.slug = 'viewer'
ON CONFLICT DO NOTHING;
@@ -0,0 +1,14 @@
-- Meshkee CMS — e-payment gateway tracking on transactions
ALTER TABLE transactions
ADD COLUMN IF NOT EXISTS gateway_ref VARCHAR(255),
ADD COLUMN IF NOT EXISTS gateway_track_id VARCHAR(255),
ADD COLUMN IF NOT EXISTS gateway_meta JSONB;
CREATE INDEX IF NOT EXISTS idx_transactions_gateway_ref
ON transactions (business_id, gateway_ref)
WHERE gateway_ref IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_transactions_gateway_track
ON transactions (business_id, gateway_track_id)
WHERE gateway_track_id IS NOT NULL;