Initial import into Gitea
This commit is contained in:
321
Gateway/Migrations/001_ChannelConfig.sql
Normal file
321
Gateway/Migrations/001_ChannelConfig.sql
Normal file
@@ -0,0 +1,321 @@
|
||||
-- ============================================================
|
||||
-- 001_ChannelConfig.sql
|
||||
-- Move channel provider configuration from appsettings.json
|
||||
-- into database-driven configuration.
|
||||
-- ============================================================
|
||||
|
||||
-- ── Table ──────────────────────────────────────────────────
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'tbChannelConfig')
|
||||
BEGIN
|
||||
CREATE TABLE dbo.tbChannelConfig (
|
||||
chcChannelType VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||
chcDisplayName NVARCHAR(100) NOT NULL,
|
||||
chcDescription NVARCHAR(500) NULL,
|
||||
chcIcon VARCHAR(50) NULL,
|
||||
chcColor VARCHAR(20) NULL,
|
||||
chcEnabled BIT NOT NULL DEFAULT 1,
|
||||
chcIsStub BIT NOT NULL DEFAULT 1,
|
||||
chcEndpoint VARCHAR(500) NULL,
|
||||
chcInternalKey VARCHAR(500) NULL,
|
||||
chcMinDailyBudget DECIMAL(10,2) NOT NULL DEFAULT 5.00,
|
||||
chcMinMonthlyBudget DECIMAL(10,2) NOT NULL DEFAULT 150.00,
|
||||
chcSupportedObjectives NVARCHAR(500) NULL, -- JSON array: ["sales","leads","traffic"]
|
||||
chcSupportedCreativeFormats NVARCHAR(500) NULL, -- JSON array: ["text","image","video"]
|
||||
chcApprovalEstimateHours INT NOT NULL DEFAULT 24,
|
||||
chcMetricsRefreshIntervalMinutes INT NOT NULL DEFAULT 60,
|
||||
chcAuthMethod VARCHAR(50) NULL,
|
||||
chcKeyVaultSecretName VARCHAR(200) NULL,
|
||||
chcStatusMappings NVARCHAR(MAX) NULL, -- JSON object: {"ENABLED":"active",...}
|
||||
chcSortOrder INT NOT NULL DEFAULT 0,
|
||||
chcCreatedAt DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
|
||||
chcUpdatedAt DATETIME2 NOT NULL DEFAULT GETUTCDATE()
|
||||
);
|
||||
PRINT 'Created table tbChannelConfig';
|
||||
END
|
||||
GO
|
||||
|
||||
-- ── Stored Procedure ───────────────────────────────────────
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.spChannelConfig
|
||||
@action VARCHAR(50),
|
||||
@rqst NVARCHAR(MAX) = '{}',
|
||||
@resp NVARCHAR(MAX) OUTPUT
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- ── list: return all enabled channels ──
|
||||
IF @action = 'list'
|
||||
BEGIN
|
||||
SET @resp = (
|
||||
SELECT
|
||||
chcChannelType AS channelType,
|
||||
chcDisplayName AS displayName,
|
||||
chcDescription AS [description],
|
||||
chcIcon AS icon,
|
||||
chcColor AS color,
|
||||
chcEnabled AS [enabled],
|
||||
chcIsStub AS isStub,
|
||||
chcEndpoint AS endpoint,
|
||||
chcInternalKey AS internalKey,
|
||||
chcMinDailyBudget AS minDailyBudget,
|
||||
chcMinMonthlyBudget AS minMonthlyBudget,
|
||||
JSON_QUERY(chcSupportedObjectives) AS supportedObjectives,
|
||||
JSON_QUERY(chcSupportedCreativeFormats) AS supportedCreativeFormats,
|
||||
chcApprovalEstimateHours AS approvalEstimateHours,
|
||||
chcMetricsRefreshIntervalMinutes AS metricsRefreshIntervalMinutes,
|
||||
chcAuthMethod AS authMethod,
|
||||
chcKeyVaultSecretName AS keyVaultSecretName,
|
||||
JSON_QUERY(chcStatusMappings) AS statusMappings,
|
||||
chcSortOrder AS sortOrder
|
||||
FROM dbo.tbChannelConfig
|
||||
ORDER BY chcSortOrder, chcChannelType
|
||||
FOR JSON PATH
|
||||
);
|
||||
|
||||
IF @resp IS NULL SET @resp = '[]';
|
||||
SET @resp = '{"ok":true,"channels":' + @resp + '}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── listAll: return all channels including disabled (for admin) ──
|
||||
IF @action = 'listAll'
|
||||
BEGIN
|
||||
SET @resp = (
|
||||
SELECT
|
||||
chcChannelType AS channelType,
|
||||
chcDisplayName AS displayName,
|
||||
chcDescription AS [description],
|
||||
chcIcon AS icon,
|
||||
chcColor AS color,
|
||||
chcEnabled AS [enabled],
|
||||
chcIsStub AS isStub,
|
||||
chcEndpoint AS endpoint,
|
||||
chcMinDailyBudget AS minDailyBudget,
|
||||
chcMinMonthlyBudget AS minMonthlyBudget,
|
||||
JSON_QUERY(chcSupportedObjectives) AS supportedObjectives,
|
||||
JSON_QUERY(chcSupportedCreativeFormats) AS supportedCreativeFormats,
|
||||
chcApprovalEstimateHours AS approvalEstimateHours,
|
||||
chcMetricsRefreshIntervalMinutes AS metricsRefreshIntervalMinutes,
|
||||
chcAuthMethod AS authMethod,
|
||||
chcKeyVaultSecretName AS keyVaultSecretName,
|
||||
JSON_QUERY(chcStatusMappings) AS statusMappings,
|
||||
chcSortOrder AS sortOrder,
|
||||
chcCreatedAt AS createdAt,
|
||||
chcUpdatedAt AS updatedAt
|
||||
FROM dbo.tbChannelConfig
|
||||
ORDER BY chcSortOrder, chcChannelType
|
||||
FOR JSON PATH
|
||||
);
|
||||
|
||||
IF @resp IS NULL SET @resp = '[]';
|
||||
SET @resp = '{"ok":true,"channels":' + @resp + '}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── get: return single channel by type ──
|
||||
IF @action = 'get'
|
||||
BEGIN
|
||||
DECLARE @channelType VARCHAR(50) = JSON_VALUE(@rqst, '$.channelType');
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.tbChannelConfig WHERE chcChannelType = @channelType)
|
||||
BEGIN
|
||||
SET @resp = '{"ok":false,"error":"Channel not found"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
SET @resp = (
|
||||
SELECT
|
||||
chcChannelType AS channelType,
|
||||
chcDisplayName AS displayName,
|
||||
chcDescription AS [description],
|
||||
chcIcon AS icon,
|
||||
chcColor AS color,
|
||||
chcEnabled AS [enabled],
|
||||
chcIsStub AS isStub,
|
||||
chcEndpoint AS endpoint,
|
||||
chcMinDailyBudget AS minDailyBudget,
|
||||
chcMinMonthlyBudget AS minMonthlyBudget,
|
||||
JSON_QUERY(chcSupportedObjectives) AS supportedObjectives,
|
||||
JSON_QUERY(chcSupportedCreativeFormats) AS supportedCreativeFormats,
|
||||
chcApprovalEstimateHours AS approvalEstimateHours,
|
||||
chcMetricsRefreshIntervalMinutes AS metricsRefreshIntervalMinutes,
|
||||
chcAuthMethod AS authMethod,
|
||||
chcKeyVaultSecretName AS keyVaultSecretName,
|
||||
JSON_QUERY(chcStatusMappings) AS statusMappings
|
||||
FROM dbo.tbChannelConfig
|
||||
WHERE chcChannelType = @channelType
|
||||
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
|
||||
);
|
||||
|
||||
SET @resp = '{"ok":true,"channel":' + @resp + '}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── upsert: create or update a channel (admin) ──
|
||||
IF @action = 'upsert'
|
||||
BEGIN
|
||||
DECLARE @uChannelType VARCHAR(50) = JSON_VALUE(@rqst, '$.channelType');
|
||||
|
||||
IF @uChannelType IS NULL
|
||||
BEGIN
|
||||
SET @resp = '{"ok":false,"error":"channelType is required"}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
MERGE dbo.tbChannelConfig AS tgt
|
||||
USING (SELECT @uChannelType AS chcChannelType) AS src
|
||||
ON tgt.chcChannelType = src.chcChannelType
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET
|
||||
chcDisplayName = ISNULL(JSON_VALUE(@rqst, '$.displayName'), tgt.chcDisplayName),
|
||||
chcDescription = ISNULL(JSON_VALUE(@rqst, '$.description'), tgt.chcDescription),
|
||||
chcIcon = ISNULL(JSON_VALUE(@rqst, '$.icon'), tgt.chcIcon),
|
||||
chcColor = ISNULL(JSON_VALUE(@rqst, '$.color'), tgt.chcColor),
|
||||
chcEnabled = ISNULL(CAST(JSON_VALUE(@rqst, '$.enabled') AS BIT), tgt.chcEnabled),
|
||||
chcIsStub = ISNULL(CAST(JSON_VALUE(@rqst, '$.isStub') AS BIT), tgt.chcIsStub),
|
||||
chcEndpoint = CASE WHEN JSON_VALUE(@rqst, '$.endpoint') IS NOT NULL
|
||||
THEN JSON_VALUE(@rqst, '$.endpoint')
|
||||
ELSE tgt.chcEndpoint END,
|
||||
chcInternalKey = CASE WHEN JSON_VALUE(@rqst, '$.internalKey') IS NOT NULL
|
||||
THEN JSON_VALUE(@rqst, '$.internalKey')
|
||||
ELSE tgt.chcInternalKey END,
|
||||
chcMinDailyBudget = ISNULL(CAST(JSON_VALUE(@rqst, '$.minDailyBudget') AS DECIMAL(10,2)), tgt.chcMinDailyBudget),
|
||||
chcMinMonthlyBudget = ISNULL(CAST(JSON_VALUE(@rqst, '$.minMonthlyBudget') AS DECIMAL(10,2)), tgt.chcMinMonthlyBudget),
|
||||
chcSupportedObjectives = CASE WHEN JSON_QUERY(@rqst, '$.supportedObjectives') IS NOT NULL
|
||||
THEN JSON_QUERY(@rqst, '$.supportedObjectives')
|
||||
ELSE tgt.chcSupportedObjectives END,
|
||||
chcSupportedCreativeFormats = CASE WHEN JSON_QUERY(@rqst, '$.supportedCreativeFormats') IS NOT NULL
|
||||
THEN JSON_QUERY(@rqst, '$.supportedCreativeFormats')
|
||||
ELSE tgt.chcSupportedCreativeFormats END,
|
||||
chcApprovalEstimateHours = ISNULL(CAST(JSON_VALUE(@rqst, '$.approvalEstimateHours') AS INT), tgt.chcApprovalEstimateHours),
|
||||
chcMetricsRefreshIntervalMinutes = ISNULL(CAST(JSON_VALUE(@rqst, '$.metricsRefreshIntervalMinutes') AS INT), tgt.chcMetricsRefreshIntervalMinutes),
|
||||
chcAuthMethod = ISNULL(JSON_VALUE(@rqst, '$.authMethod'), tgt.chcAuthMethod),
|
||||
chcKeyVaultSecretName = ISNULL(JSON_VALUE(@rqst, '$.keyVaultSecretName'), tgt.chcKeyVaultSecretName),
|
||||
chcStatusMappings = CASE WHEN JSON_QUERY(@rqst, '$.statusMappings') IS NOT NULL
|
||||
THEN JSON_QUERY(@rqst, '$.statusMappings')
|
||||
ELSE tgt.chcStatusMappings END,
|
||||
chcSortOrder = ISNULL(CAST(JSON_VALUE(@rqst, '$.sortOrder') AS INT), tgt.chcSortOrder),
|
||||
chcUpdatedAt = GETUTCDATE()
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (chcChannelType, chcDisplayName, chcDescription, chcIcon, chcColor,
|
||||
chcEnabled, chcIsStub, chcEndpoint, chcInternalKey,
|
||||
chcMinDailyBudget, chcMinMonthlyBudget,
|
||||
chcSupportedObjectives, chcSupportedCreativeFormats,
|
||||
chcApprovalEstimateHours, chcMetricsRefreshIntervalMinutes,
|
||||
chcAuthMethod, chcKeyVaultSecretName, chcStatusMappings, chcSortOrder)
|
||||
VALUES (
|
||||
@uChannelType,
|
||||
JSON_VALUE(@rqst, '$.displayName'),
|
||||
JSON_VALUE(@rqst, '$.description'),
|
||||
JSON_VALUE(@rqst, '$.icon'),
|
||||
JSON_VALUE(@rqst, '$.color'),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.enabled') AS BIT), 1),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.isStub') AS BIT), 1),
|
||||
JSON_VALUE(@rqst, '$.endpoint'),
|
||||
JSON_VALUE(@rqst, '$.internalKey'),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.minDailyBudget') AS DECIMAL(10,2)), 5.00),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.minMonthlyBudget') AS DECIMAL(10,2)), 150.00),
|
||||
JSON_QUERY(@rqst, '$.supportedObjectives'),
|
||||
JSON_QUERY(@rqst, '$.supportedCreativeFormats'),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.approvalEstimateHours') AS INT), 24),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.metricsRefreshIntervalMinutes') AS INT), 60),
|
||||
JSON_VALUE(@rqst, '$.authMethod'),
|
||||
JSON_VALUE(@rqst, '$.keyVaultSecretName'),
|
||||
JSON_QUERY(@rqst, '$.statusMappings'),
|
||||
ISNULL(CAST(JSON_VALUE(@rqst, '$.sortOrder') AS INT), 0)
|
||||
);
|
||||
|
||||
SET @resp = '{"ok":true}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- ── delete: remove a channel (admin) ──
|
||||
IF @action = 'delete'
|
||||
BEGIN
|
||||
DECLARE @dChannelType VARCHAR(50) = JSON_VALUE(@rqst, '$.channelType');
|
||||
|
||||
DELETE FROM dbo.tbChannelConfig WHERE chcChannelType = @dChannelType;
|
||||
|
||||
SET @resp = '{"ok":true,"deleted":' + CAST(@@ROWCOUNT AS VARCHAR) + '}';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
SET @resp = '{"ok":false,"error":"Unknown action: ' + @action + '"}';
|
||||
END
|
||||
GO
|
||||
|
||||
-- ── Seed Data ──────────────────────────────────────────────
|
||||
|
||||
-- Google Ads
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.tbChannelConfig WHERE chcChannelType = 'google_ads')
|
||||
INSERT INTO dbo.tbChannelConfig (
|
||||
chcChannelType, chcDisplayName, chcDescription, chcIcon, chcColor,
|
||||
chcEnabled, chcIsStub, chcEndpoint,
|
||||
chcMinDailyBudget, chcMinMonthlyBudget,
|
||||
chcSupportedObjectives, chcSupportedCreativeFormats,
|
||||
chcApprovalEstimateHours, chcAuthMethod, chcKeyVaultSecretName,
|
||||
chcStatusMappings, chcSortOrder
|
||||
) VALUES (
|
||||
'google_ads',
|
||||
'Google Ads',
|
||||
'Search, Display, Shopping & Performance Max across Google properties',
|
||||
'google', '#4285F4',
|
||||
1, 0, NULL,
|
||||
10.00, 300.00,
|
||||
'["awareness","traffic","conversions","leads","sales"]',
|
||||
'["text","image","responsive","video"]',
|
||||
24, 'mcc', 'google-ads-refresh-token',
|
||||
'{"ENABLED":"active","Enabled":"active","PAUSED":"paused","Paused":"paused","REMOVED":"cancelled","Removed":"cancelled","UNKNOWN":"error","UNSPECIFIED":"error"}',
|
||||
1
|
||||
);
|
||||
|
||||
-- Meta
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.tbChannelConfig WHERE chcChannelType = 'meta')
|
||||
INSERT INTO dbo.tbChannelConfig (
|
||||
chcChannelType, chcDisplayName, chcDescription, chcIcon, chcColor,
|
||||
chcEnabled, chcIsStub, chcEndpoint,
|
||||
chcMinDailyBudget, chcMinMonthlyBudget,
|
||||
chcSupportedObjectives, chcSupportedCreativeFormats,
|
||||
chcApprovalEstimateHours, chcAuthMethod, chcKeyVaultSecretName,
|
||||
chcStatusMappings, chcSortOrder
|
||||
) VALUES (
|
||||
'meta',
|
||||
'Meta Ads',
|
||||
'Facebook, Instagram, Messenger & Threads advertising',
|
||||
'meta', '#1877F2',
|
||||
1, 1, NULL,
|
||||
5.00, 250.00,
|
||||
'["awareness","traffic","conversions","leads","sales"]',
|
||||
'["image","video","carousel","stories"]',
|
||||
48, 'oauth2', 'meta-access-token',
|
||||
'{"ACTIVE":"active","PAUSED":"paused","DELETED":"cancelled","ARCHIVED":"completed","IN_PROCESS":"pending","WITH_ISSUES":"error","CAMPAIGN_PAUSED":"paused","ADSET_PAUSED":"paused","DISAPPROVED":"error","PREAPPROVED":"pending","PENDING_REVIEW":"pending","PENDING_BILLING_INFO":"error"}',
|
||||
2
|
||||
);
|
||||
|
||||
-- TikTok
|
||||
IF NOT EXISTS (SELECT 1 FROM dbo.tbChannelConfig WHERE chcChannelType = 'tiktok')
|
||||
INSERT INTO dbo.tbChannelConfig (
|
||||
chcChannelType, chcDisplayName, chcDescription, chcIcon, chcColor,
|
||||
chcEnabled, chcIsStub, chcEndpoint,
|
||||
chcMinDailyBudget, chcMinMonthlyBudget,
|
||||
chcSupportedObjectives, chcSupportedCreativeFormats,
|
||||
chcApprovalEstimateHours, chcAuthMethod, chcKeyVaultSecretName,
|
||||
chcStatusMappings, chcSortOrder
|
||||
) VALUES (
|
||||
'tiktok',
|
||||
'TikTok Ads',
|
||||
'In-feed video ads across TikTok and partner apps',
|
||||
'tiktok', '#000000',
|
||||
1, 1, NULL,
|
||||
20.00, 200.00,
|
||||
'["awareness","traffic","conversions","leads","sales"]',
|
||||
'["video","image","spark_ads"]',
|
||||
24, 'oauth2', 'tiktok-access-token',
|
||||
'{"ENABLE":"active","CAMPAIGN_STATUS_ENABLE":"active","DISABLE":"paused","CAMPAIGN_STATUS_DISABLE":"paused","DELETE":"cancelled","CAMPAIGN_STATUS_DELETE":"cancelled","BUDGET_EXCEED":"paused","CAMPAIGN_STATUS_BUDGET_EXCEED":"paused","ADVERTISER_AUDIT_DENY":"error","CAMPAIGN_STATUS_ADVERTISER_AUDIT_DENY":"error","NOT_DELETE":"active","ADVERTISER_AUDIT":"pending","CAMPAIGN_STATUS_ADVERTISER_AUDIT":"pending","REAUDIT":"pending","ALL":"active"}',
|
||||
3
|
||||
);
|
||||
|
||||
PRINT 'Channel config seeded successfully';
|
||||
GO
|
||||
Reference in New Issue
Block a user