agrs-sequelize-sdk 1.4.59 → 1.4.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/migrations/2026-05-17-PIXELS-FOLLOWUP.md +85 -85
- package/migrations/2026-05-17-add-platform-to-ad.js +19 -19
- package/migrations/2026-05-17-add-platform-to-adaccount.js +19 -19
- package/migrations/2026-05-17-add-platform-to-adperformance.js +19 -19
- package/migrations/2026-05-17-add-platform-to-adset.js +19 -19
- package/migrations/2026-05-17-add-platform-to-adsetperformance.js +19 -19
- package/migrations/2026-05-17-add-platform-to-aiarticleretryqueue.js +19 -19
- package/migrations/2026-05-17-add-platform-to-aicampaignqueue.js +19 -19
- package/migrations/2026-05-17-add-platform-to-article.js +19 -19
- package/migrations/2026-05-17-add-platform-to-campaign.js +19 -19
- package/migrations/2026-05-17-add-platform-to-codefuelcampaign.js +19 -19
- package/migrations/2026-05-17-add-platform-to-dynamicfeed.js +19 -19
- package/migrations/2026-05-17-add-platform-to-facebookretryqueue.js +19 -19
- package/migrations/2026-05-17-add-platform-to-pages.js +19 -19
- package/migrations/2026-05-17-add-platform-to-presets.js +19 -19
- package/migrations/2026-05-18-add-group-id-to-ai-campaign-queue.js +18 -18
- package/migrations/2026-05-18-add-platform-code-to-creation-logs.js +65 -65
- package/migrations/2026-05-18-add-platform-code-to-pixels.js +55 -55
- package/migrations/2026-05-18-add-platform-code-to-rsoc-feed-campaigns.js +55 -55
- package/migrations/2026-05-18-add-platform-date-index-to-adperformance.js +16 -16
- package/migrations/2026-05-18-add-platform-date-index-to-adsetperformance.js +16 -16
- package/migrations/2026-05-18-add-review-status-to-ad.js +36 -36
- package/migrations/2026-05-18-create-canonical-insights.js +84 -84
- package/migrations/2026-05-18-create-snapchat-public-profiles.js +68 -68
- package/migrations/2026-05-18-create-tiktok-identities.js +71 -71
- package/migrations/2026-05-18-create-tiktok-snapchat-campaigns.js +143 -143
- package/migrations/2026-05-18-create-tt-snp-adset-ad-tables.js +309 -309
- package/migrations/RUN_ME_MANUALLY_2026-05-17.sql +99 -99
- package/migrations/add-requested-from-dashboard-to-articles.js +17 -17
- package/migrations/change-adset-name-to-text.js +79 -79
- package/models/AICampaignQueue.js +151 -151
- package/models/AIGenerationLog.js +85 -85
- package/models/AIGenerationRequest.js +212 -212
- package/models/AdAccountValues.js +25 -25
- package/models/AdHistory.js +30 -30
- package/models/AdPerformance.js +104 -104
- package/models/AdSet.js +295 -295
- package/models/AdSetHistory.js +30 -30
- package/models/AdsetPerformance.js +137 -137
- package/models/AiArticleRetryQueue.js +157 -157
- package/models/AiCreationRetryQueue.js +134 -134
- package/models/ApiAuditLog.js +127 -127
- package/models/ApiToken.js +67 -67
- package/models/Article.js +222 -222
- package/models/AutomationRule.js +179 -179
- package/models/BannerTemplate.js +129 -129
- package/models/Buyers.js +25 -25
- package/models/Campaign.js +168 -168
- package/models/CampaignActionHistory.js +101 -101
- package/models/CampaignCreationLog.js +340 -340
- package/models/CampaignCreationLogV2.js +392 -392
- package/models/CampaignHistory.js +33 -33
- package/models/CampaignTag.js +46 -46
- package/models/CampaignTagAssignment.js +48 -48
- package/models/CanonicalInsights.js +68 -68
- package/models/Channel.js +55 -55
- package/models/Domain.js +49 -44
- package/models/DynamicFeed.js +218 -218
- package/models/ExplorAdsChannel.js +61 -61
- package/models/FacebookRetryQueue.js +163 -163
- package/models/Feed.js +33 -33
- package/models/FeedArticleConfiguration.js +80 -80
- package/models/FrontStoryChannel.js +59 -59
- package/models/FrontStoryChannelV2.js +60 -60
- package/models/GenericFlowRequest.js +114 -114
- package/models/MidoWebChannel.js +47 -47
- package/models/MineBrandBlocklist.js +56 -56
- package/models/MineBrandConfig.js +26 -26
- package/models/MineChannel.js +47 -47
- package/models/MineRedirectDomain.js +42 -42
- package/models/MineWebhookEvent.js +53 -53
- package/models/Pages.js +117 -117
- package/models/PipelineExecution.js +59 -59
- package/models/PolicyDogsCreativeCache.js +50 -50
- package/models/PolicyDogsImageCache.js +30 -30
- package/models/Presets.js +40 -40
- package/models/PublicApiBatch.js +155 -155
- package/models/PublicApiBatchItem.js +166 -166
- package/models/PublicApiIdempotencyCache.js +135 -135
- package/models/RSOCFeedCampaign.js +387 -387
- package/models/RSOCFeedConfig.js +46 -46
- package/models/RSOCRedirectWsCode.js +22 -22
- package/models/RsocKeywordPerformance.js +110 -110
- package/models/RuleAction.js +90 -90
- package/models/RuleCondition.js +137 -137
- package/models/RuleExecution.js +107 -107
- package/models/RulesValues.js +56 -56
- package/models/SingularChannel.js +31 -31
- package/models/SnapchatAd.js +69 -69
- package/models/SnapchatAdSquad.js +92 -92
- package/models/SnapchatCampaign.js +69 -69
- package/models/SnapchatChannel.js +31 -31
- package/models/SnapchatPublicProfiles.js +67 -67
- package/models/SupportedLocale.js +23 -23
- package/models/SyncHistory.js +249 -249
- package/models/TTQChannel.js +42 -42
- package/models/Team.js +33 -43
- package/models/TemplateMetadata.js +260 -260
- package/models/Tier2_AdAccounts.js +110 -110
- package/models/Tier2_Assets.js +70 -70
- package/models/Tier2_BusinessManagers.js +105 -105
- package/models/Tier2_CreditLines.js +99 -99
- package/models/Tier2_Pages.js +91 -91
- package/models/Tier2_Pixels.js +82 -82
- package/models/Tier2_Tokens.js +64 -64
- package/models/Tier2_UserAdAccounts.js +83 -83
- package/models/TikTokAd.js +71 -71
- package/models/TikTokAdGroup.js +82 -82
- package/models/TikTokCampaign.js +71 -71
- package/models/TiktokChannel.js +31 -31
- package/models/TiktokIdentities.js +65 -65
- package/models/TokenRotationState.js +121 -121
- package/models/TonicRSOCKeywordPerformance.js +122 -122
- package/models/Users.js +204 -204
- package/models/Vertical.js +25 -25
- package/models/newFiles.js +137 -137
- package/package.json +21 -19
- package/run.sh +214 -214
- package/services/sequelizeService.js +110 -110
- package/tarzo-skalio-integration-docs.md +691 -691
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const PolicyDogsCreativeCache = sequelize.define(
|
|
3
|
-
"PolicyDogsCreativeCache",
|
|
4
|
-
{
|
|
5
|
-
id: {
|
|
6
|
-
type: DataTypes.INTEGER,
|
|
7
|
-
primaryKey: true,
|
|
8
|
-
autoIncrement: true,
|
|
9
|
-
},
|
|
10
|
-
ad_account_id: {
|
|
11
|
-
type: DataTypes.STRING,
|
|
12
|
-
allowNull: false,
|
|
13
|
-
comment: "Facebook ad account ID (e.g., act_123456)",
|
|
14
|
-
},
|
|
15
|
-
page_id: {
|
|
16
|
-
type: DataTypes.STRING,
|
|
17
|
-
allowNull: false,
|
|
18
|
-
comment: "Facebook page ID used in the creative",
|
|
19
|
-
},
|
|
20
|
-
dog_url: {
|
|
21
|
-
type: DataTypes.TEXT,
|
|
22
|
-
allowNull: false,
|
|
23
|
-
comment: "The dog article URL used in the creative",
|
|
24
|
-
},
|
|
25
|
-
creative_id: {
|
|
26
|
-
type: DataTypes.STRING,
|
|
27
|
-
allowNull: false,
|
|
28
|
-
comment: "Facebook creative ID to reuse",
|
|
29
|
-
},
|
|
30
|
-
created_at: {
|
|
31
|
-
type: DataTypes.DATE,
|
|
32
|
-
allowNull: false,
|
|
33
|
-
defaultValue: DataTypes.NOW,
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
tableName: "policy_dogs_creative_cache",
|
|
38
|
-
timestamps: false,
|
|
39
|
-
indexes: [
|
|
40
|
-
{
|
|
41
|
-
unique: true,
|
|
42
|
-
fields: ["ad_account_id", "page_id", "dog_url"],
|
|
43
|
-
name: "idx_dogs_creative_cache_unique",
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
}
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return PolicyDogsCreativeCache;
|
|
50
|
-
};
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const PolicyDogsCreativeCache = sequelize.define(
|
|
3
|
+
"PolicyDogsCreativeCache",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.INTEGER,
|
|
7
|
+
primaryKey: true,
|
|
8
|
+
autoIncrement: true,
|
|
9
|
+
},
|
|
10
|
+
ad_account_id: {
|
|
11
|
+
type: DataTypes.STRING,
|
|
12
|
+
allowNull: false,
|
|
13
|
+
comment: "Facebook ad account ID (e.g., act_123456)",
|
|
14
|
+
},
|
|
15
|
+
page_id: {
|
|
16
|
+
type: DataTypes.STRING,
|
|
17
|
+
allowNull: false,
|
|
18
|
+
comment: "Facebook page ID used in the creative",
|
|
19
|
+
},
|
|
20
|
+
dog_url: {
|
|
21
|
+
type: DataTypes.TEXT,
|
|
22
|
+
allowNull: false,
|
|
23
|
+
comment: "The dog article URL used in the creative",
|
|
24
|
+
},
|
|
25
|
+
creative_id: {
|
|
26
|
+
type: DataTypes.STRING,
|
|
27
|
+
allowNull: false,
|
|
28
|
+
comment: "Facebook creative ID to reuse",
|
|
29
|
+
},
|
|
30
|
+
created_at: {
|
|
31
|
+
type: DataTypes.DATE,
|
|
32
|
+
allowNull: false,
|
|
33
|
+
defaultValue: DataTypes.NOW,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
tableName: "policy_dogs_creative_cache",
|
|
38
|
+
timestamps: false,
|
|
39
|
+
indexes: [
|
|
40
|
+
{
|
|
41
|
+
unique: true,
|
|
42
|
+
fields: ["ad_account_id", "page_id", "dog_url"],
|
|
43
|
+
name: "idx_dogs_creative_cache_unique",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return PolicyDogsCreativeCache;
|
|
50
|
+
};
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const PolicyDogsImageCache = sequelize.define(
|
|
3
|
-
"PolicyDogsImageCache",
|
|
4
|
-
{
|
|
5
|
-
ad_account_id: {
|
|
6
|
-
type: DataTypes.STRING,
|
|
7
|
-
primaryKey: true,
|
|
8
|
-
allowNull: false,
|
|
9
|
-
comment: "Facebook ad account ID (e.g., act_123456)",
|
|
10
|
-
},
|
|
11
|
-
image_hash: {
|
|
12
|
-
type: DataTypes.STRING,
|
|
13
|
-
allowNull: false,
|
|
14
|
-
comment: "Facebook image hash for the dog image uploaded to this account",
|
|
15
|
-
},
|
|
16
|
-
uploaded_at: {
|
|
17
|
-
type: DataTypes.DATE,
|
|
18
|
-
allowNull: false,
|
|
19
|
-
defaultValue: DataTypes.NOW,
|
|
20
|
-
comment: "When the image was uploaded to this ad account",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
tableName: "policy_dogs_image_cache",
|
|
25
|
-
timestamps: false,
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
return PolicyDogsImageCache;
|
|
30
|
-
};
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const PolicyDogsImageCache = sequelize.define(
|
|
3
|
+
"PolicyDogsImageCache",
|
|
4
|
+
{
|
|
5
|
+
ad_account_id: {
|
|
6
|
+
type: DataTypes.STRING,
|
|
7
|
+
primaryKey: true,
|
|
8
|
+
allowNull: false,
|
|
9
|
+
comment: "Facebook ad account ID (e.g., act_123456)",
|
|
10
|
+
},
|
|
11
|
+
image_hash: {
|
|
12
|
+
type: DataTypes.STRING,
|
|
13
|
+
allowNull: false,
|
|
14
|
+
comment: "Facebook image hash for the dog image uploaded to this account",
|
|
15
|
+
},
|
|
16
|
+
uploaded_at: {
|
|
17
|
+
type: DataTypes.DATE,
|
|
18
|
+
allowNull: false,
|
|
19
|
+
defaultValue: DataTypes.NOW,
|
|
20
|
+
comment: "When the image was uploaded to this ad account",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
tableName: "policy_dogs_image_cache",
|
|
25
|
+
timestamps: false,
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return PolicyDogsImageCache;
|
|
30
|
+
};
|
package/models/Presets.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
// models/Presets.js
|
|
2
|
-
module.exports = (sequelize, DataTypes) => {
|
|
3
|
-
const Presets = sequelize.define("Presets", {
|
|
4
|
-
id: {
|
|
5
|
-
type: DataTypes.UUID,
|
|
6
|
-
defaultValue: DataTypes.UUIDV4,
|
|
7
|
-
primaryKey: true,
|
|
8
|
-
},
|
|
9
|
-
name: {
|
|
10
|
-
type: DataTypes.STRING,
|
|
11
|
-
allowNull: false,
|
|
12
|
-
},
|
|
13
|
-
feeds: {
|
|
14
|
-
type: DataTypes.JSONB,
|
|
15
|
-
allowNull: true,
|
|
16
|
-
},
|
|
17
|
-
userId: {
|
|
18
|
-
type: DataTypes.UUID,
|
|
19
|
-
allowNull: false,
|
|
20
|
-
references: {
|
|
21
|
-
model: 'Users',
|
|
22
|
-
key: 'id',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
platform: {
|
|
26
|
-
type: DataTypes.ENUM("fb", "tt", "snp"),
|
|
27
|
-
allowNull: false,
|
|
28
|
-
defaultValue: "fb",
|
|
29
|
-
comment: "Ad platform identifier: fb=Facebook, tt=TikTok, snp=Snapchat",
|
|
30
|
-
},
|
|
31
|
-
}, {
|
|
32
|
-
tableName: "Presets",
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
Presets.associate = (models) => {
|
|
36
|
-
Presets.belongsTo(models.Users, { foreignKey: 'userId', as: 'user' });
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return Presets;
|
|
40
|
-
};
|
|
1
|
+
// models/Presets.js
|
|
2
|
+
module.exports = (sequelize, DataTypes) => {
|
|
3
|
+
const Presets = sequelize.define("Presets", {
|
|
4
|
+
id: {
|
|
5
|
+
type: DataTypes.UUID,
|
|
6
|
+
defaultValue: DataTypes.UUIDV4,
|
|
7
|
+
primaryKey: true,
|
|
8
|
+
},
|
|
9
|
+
name: {
|
|
10
|
+
type: DataTypes.STRING,
|
|
11
|
+
allowNull: false,
|
|
12
|
+
},
|
|
13
|
+
feeds: {
|
|
14
|
+
type: DataTypes.JSONB,
|
|
15
|
+
allowNull: true,
|
|
16
|
+
},
|
|
17
|
+
userId: {
|
|
18
|
+
type: DataTypes.UUID,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
references: {
|
|
21
|
+
model: 'Users',
|
|
22
|
+
key: 'id',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
platform: {
|
|
26
|
+
type: DataTypes.ENUM("fb", "tt", "snp"),
|
|
27
|
+
allowNull: false,
|
|
28
|
+
defaultValue: "fb",
|
|
29
|
+
comment: "Ad platform identifier: fb=Facebook, tt=TikTok, snp=Snapchat",
|
|
30
|
+
},
|
|
31
|
+
}, {
|
|
32
|
+
tableName: "Presets",
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
Presets.associate = (models) => {
|
|
36
|
+
Presets.belongsTo(models.Users, { foreignKey: 'userId', as: 'user' });
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return Presets;
|
|
40
|
+
};
|
package/models/PublicApiBatch.js
CHANGED
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
// public_api_batches — the envelope for one POST /public/v1/campaigns/bulk
|
|
3
|
-
// request. One batch owns N public_api_batch_items (one per campaign). The
|
|
4
|
-
// batch id is the `batchId` returned to the client at 202 and looked up via
|
|
5
|
-
// GET /public/v1/processes/{batchId}. Reconciler rolls item terminal states
|
|
6
|
-
// up into the *_count columns and flips `status` when pending_count hits 0.
|
|
7
|
-
//
|
|
8
|
-
// Idempotency note: HTTP-level replay dedup is OWNED by PublicApiIdempotencyCache
|
|
9
|
-
// (scoped by (user_id, key), caches the 202 response). This table does NOT
|
|
10
|
-
// re-implement that — `idempotency_key` here is descriptive/correlation only,
|
|
11
|
-
// deliberately NOT unique (a global-unique key would collide across buyers).
|
|
12
|
-
const PublicApiBatch = sequelize.define(
|
|
13
|
-
"PublicApiBatch",
|
|
14
|
-
{
|
|
15
|
-
id: {
|
|
16
|
-
// UUID (not autoincrement) so the batchId is unguessable / non-enumerable
|
|
17
|
-
// across buyers and stable as an external reference.
|
|
18
|
-
type: DataTypes.UUID,
|
|
19
|
-
primaryKey: true,
|
|
20
|
-
defaultValue: DataTypes.UUIDV4,
|
|
21
|
-
},
|
|
22
|
-
user_id: {
|
|
23
|
-
// Owning buyer (a buyer = a Users row, may hold several ApiTokens).
|
|
24
|
-
// GET /processes is scoped by this, matching PublicApiIdempotencyCache's
|
|
25
|
-
// (user_id, key) grain. Nullable to leave room for a future internal /
|
|
26
|
-
// dashboard-initiated bulk with no buyer user.
|
|
27
|
-
type: DataTypes.UUID,
|
|
28
|
-
allowNull: true,
|
|
29
|
-
field: "user_id",
|
|
30
|
-
},
|
|
31
|
-
api_token_id: {
|
|
32
|
-
// Which ApiToken issued the batch (ApiToken.id is UUID). Debug/support
|
|
33
|
-
// only — not part of scoping (a buyer rotating tokens still owns it via
|
|
34
|
-
// user_id).
|
|
35
|
-
type: DataTypes.UUID,
|
|
36
|
-
allowNull: true,
|
|
37
|
-
field: "api_token_id",
|
|
38
|
-
},
|
|
39
|
-
idempotency_key: {
|
|
40
|
-
// Descriptive: the Idempotency-Key the buyer sent for this batch, stored
|
|
41
|
-
// for correlation/debug. NOT a dedup key here (see header note).
|
|
42
|
-
type: DataTypes.STRING(255),
|
|
43
|
-
allowNull: true,
|
|
44
|
-
field: "idempotency_key",
|
|
45
|
-
},
|
|
46
|
-
buyer: {
|
|
47
|
-
// Buyer marker carried from the request root `buyer` key (mirrors the
|
|
48
|
-
// campaign-logs Source classification). Purely descriptive.
|
|
49
|
-
type: DataTypes.STRING(255),
|
|
50
|
-
allowNull: true,
|
|
51
|
-
field: "buyer",
|
|
52
|
-
},
|
|
53
|
-
source: {
|
|
54
|
-
// Origin front-door. 'public_api' today; leaves room for a future
|
|
55
|
-
// dashboard-initiated bulk without another table.
|
|
56
|
-
type: DataTypes.STRING(50),
|
|
57
|
-
allowNull: false,
|
|
58
|
-
defaultValue: "public_api",
|
|
59
|
-
field: "source",
|
|
60
|
-
},
|
|
61
|
-
status: {
|
|
62
|
-
type: DataTypes.STRING(50),
|
|
63
|
-
allowNull: false,
|
|
64
|
-
defaultValue: "PENDING",
|
|
65
|
-
validate: {
|
|
66
|
-
isIn: [
|
|
67
|
-
[
|
|
68
|
-
"PENDING",
|
|
69
|
-
"PROCESSING",
|
|
70
|
-
"COMPLETED",
|
|
71
|
-
"PARTIALLY_COMPLETED",
|
|
72
|
-
"FAILED",
|
|
73
|
-
],
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
total_items: {
|
|
78
|
-
type: DataTypes.INTEGER,
|
|
79
|
-
allowNull: false,
|
|
80
|
-
defaultValue: 0,
|
|
81
|
-
field: "total_items",
|
|
82
|
-
},
|
|
83
|
-
pending_count: {
|
|
84
|
-
// Reconciler decrements as items reach a terminal state; batch is
|
|
85
|
-
// terminal when this hits 0.
|
|
86
|
-
type: DataTypes.INTEGER,
|
|
87
|
-
allowNull: false,
|
|
88
|
-
defaultValue: 0,
|
|
89
|
-
field: "pending_count",
|
|
90
|
-
},
|
|
91
|
-
processing_count: {
|
|
92
|
-
type: DataTypes.INTEGER,
|
|
93
|
-
allowNull: false,
|
|
94
|
-
defaultValue: 0,
|
|
95
|
-
field: "processing_count",
|
|
96
|
-
},
|
|
97
|
-
completed_count: {
|
|
98
|
-
type: DataTypes.INTEGER,
|
|
99
|
-
allowNull: false,
|
|
100
|
-
defaultValue: 0,
|
|
101
|
-
field: "completed_count",
|
|
102
|
-
},
|
|
103
|
-
failed_count: {
|
|
104
|
-
type: DataTypes.INTEGER,
|
|
105
|
-
allowNull: false,
|
|
106
|
-
defaultValue: 0,
|
|
107
|
-
field: "failed_count",
|
|
108
|
-
},
|
|
109
|
-
request_meta: {
|
|
110
|
-
// Optional provenance (source IP, token label, client-declared count).
|
|
111
|
-
type: DataTypes.JSONB,
|
|
112
|
-
allowNull: true,
|
|
113
|
-
field: "request_meta",
|
|
114
|
-
},
|
|
115
|
-
created_at: {
|
|
116
|
-
type: DataTypes.DATE,
|
|
117
|
-
allowNull: false,
|
|
118
|
-
defaultValue: DataTypes.NOW,
|
|
119
|
-
field: "created_at",
|
|
120
|
-
},
|
|
121
|
-
updated_at: {
|
|
122
|
-
type: DataTypes.DATE,
|
|
123
|
-
allowNull: true,
|
|
124
|
-
field: "updated_at",
|
|
125
|
-
},
|
|
126
|
-
completed_at: {
|
|
127
|
-
type: DataTypes.DATE,
|
|
128
|
-
allowNull: true,
|
|
129
|
-
field: "completed_at",
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
tableName: "public_api_batches",
|
|
134
|
-
timestamps: false, // created_at/updated_at managed manually (matches ai_campaign_queue)
|
|
135
|
-
// Indexes are named explicitly: prod sync runs alter:false, so an index
|
|
136
|
-
// added later to this (by-then existing) table must be backfilled by a
|
|
137
|
-
// migration that targets the same name.
|
|
138
|
-
indexes: [
|
|
139
|
-
{ name: "public_api_batches_user_id_idx", fields: ["user_id"] },
|
|
140
|
-
{ name: "public_api_batches_status_idx", fields: ["status"] },
|
|
141
|
-
{ name: "public_api_batches_created_at_idx", fields: ["created_at"] },
|
|
142
|
-
],
|
|
143
|
-
}
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
PublicApiBatch.associate = (models) => {
|
|
147
|
-
PublicApiBatch.hasMany(models.PublicApiBatchItem, {
|
|
148
|
-
foreignKey: "batch_id",
|
|
149
|
-
sourceKey: "id",
|
|
150
|
-
as: "items",
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
return PublicApiBatch;
|
|
155
|
-
};
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
// public_api_batches — the envelope for one POST /public/v1/campaigns/bulk
|
|
3
|
+
// request. One batch owns N public_api_batch_items (one per campaign). The
|
|
4
|
+
// batch id is the `batchId` returned to the client at 202 and looked up via
|
|
5
|
+
// GET /public/v1/processes/{batchId}. Reconciler rolls item terminal states
|
|
6
|
+
// up into the *_count columns and flips `status` when pending_count hits 0.
|
|
7
|
+
//
|
|
8
|
+
// Idempotency note: HTTP-level replay dedup is OWNED by PublicApiIdempotencyCache
|
|
9
|
+
// (scoped by (user_id, key), caches the 202 response). This table does NOT
|
|
10
|
+
// re-implement that — `idempotency_key` here is descriptive/correlation only,
|
|
11
|
+
// deliberately NOT unique (a global-unique key would collide across buyers).
|
|
12
|
+
const PublicApiBatch = sequelize.define(
|
|
13
|
+
"PublicApiBatch",
|
|
14
|
+
{
|
|
15
|
+
id: {
|
|
16
|
+
// UUID (not autoincrement) so the batchId is unguessable / non-enumerable
|
|
17
|
+
// across buyers and stable as an external reference.
|
|
18
|
+
type: DataTypes.UUID,
|
|
19
|
+
primaryKey: true,
|
|
20
|
+
defaultValue: DataTypes.UUIDV4,
|
|
21
|
+
},
|
|
22
|
+
user_id: {
|
|
23
|
+
// Owning buyer (a buyer = a Users row, may hold several ApiTokens).
|
|
24
|
+
// GET /processes is scoped by this, matching PublicApiIdempotencyCache's
|
|
25
|
+
// (user_id, key) grain. Nullable to leave room for a future internal /
|
|
26
|
+
// dashboard-initiated bulk with no buyer user.
|
|
27
|
+
type: DataTypes.UUID,
|
|
28
|
+
allowNull: true,
|
|
29
|
+
field: "user_id",
|
|
30
|
+
},
|
|
31
|
+
api_token_id: {
|
|
32
|
+
// Which ApiToken issued the batch (ApiToken.id is UUID). Debug/support
|
|
33
|
+
// only — not part of scoping (a buyer rotating tokens still owns it via
|
|
34
|
+
// user_id).
|
|
35
|
+
type: DataTypes.UUID,
|
|
36
|
+
allowNull: true,
|
|
37
|
+
field: "api_token_id",
|
|
38
|
+
},
|
|
39
|
+
idempotency_key: {
|
|
40
|
+
// Descriptive: the Idempotency-Key the buyer sent for this batch, stored
|
|
41
|
+
// for correlation/debug. NOT a dedup key here (see header note).
|
|
42
|
+
type: DataTypes.STRING(255),
|
|
43
|
+
allowNull: true,
|
|
44
|
+
field: "idempotency_key",
|
|
45
|
+
},
|
|
46
|
+
buyer: {
|
|
47
|
+
// Buyer marker carried from the request root `buyer` key (mirrors the
|
|
48
|
+
// campaign-logs Source classification). Purely descriptive.
|
|
49
|
+
type: DataTypes.STRING(255),
|
|
50
|
+
allowNull: true,
|
|
51
|
+
field: "buyer",
|
|
52
|
+
},
|
|
53
|
+
source: {
|
|
54
|
+
// Origin front-door. 'public_api' today; leaves room for a future
|
|
55
|
+
// dashboard-initiated bulk without another table.
|
|
56
|
+
type: DataTypes.STRING(50),
|
|
57
|
+
allowNull: false,
|
|
58
|
+
defaultValue: "public_api",
|
|
59
|
+
field: "source",
|
|
60
|
+
},
|
|
61
|
+
status: {
|
|
62
|
+
type: DataTypes.STRING(50),
|
|
63
|
+
allowNull: false,
|
|
64
|
+
defaultValue: "PENDING",
|
|
65
|
+
validate: {
|
|
66
|
+
isIn: [
|
|
67
|
+
[
|
|
68
|
+
"PENDING",
|
|
69
|
+
"PROCESSING",
|
|
70
|
+
"COMPLETED",
|
|
71
|
+
"PARTIALLY_COMPLETED",
|
|
72
|
+
"FAILED",
|
|
73
|
+
],
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
total_items: {
|
|
78
|
+
type: DataTypes.INTEGER,
|
|
79
|
+
allowNull: false,
|
|
80
|
+
defaultValue: 0,
|
|
81
|
+
field: "total_items",
|
|
82
|
+
},
|
|
83
|
+
pending_count: {
|
|
84
|
+
// Reconciler decrements as items reach a terminal state; batch is
|
|
85
|
+
// terminal when this hits 0.
|
|
86
|
+
type: DataTypes.INTEGER,
|
|
87
|
+
allowNull: false,
|
|
88
|
+
defaultValue: 0,
|
|
89
|
+
field: "pending_count",
|
|
90
|
+
},
|
|
91
|
+
processing_count: {
|
|
92
|
+
type: DataTypes.INTEGER,
|
|
93
|
+
allowNull: false,
|
|
94
|
+
defaultValue: 0,
|
|
95
|
+
field: "processing_count",
|
|
96
|
+
},
|
|
97
|
+
completed_count: {
|
|
98
|
+
type: DataTypes.INTEGER,
|
|
99
|
+
allowNull: false,
|
|
100
|
+
defaultValue: 0,
|
|
101
|
+
field: "completed_count",
|
|
102
|
+
},
|
|
103
|
+
failed_count: {
|
|
104
|
+
type: DataTypes.INTEGER,
|
|
105
|
+
allowNull: false,
|
|
106
|
+
defaultValue: 0,
|
|
107
|
+
field: "failed_count",
|
|
108
|
+
},
|
|
109
|
+
request_meta: {
|
|
110
|
+
// Optional provenance (source IP, token label, client-declared count).
|
|
111
|
+
type: DataTypes.JSONB,
|
|
112
|
+
allowNull: true,
|
|
113
|
+
field: "request_meta",
|
|
114
|
+
},
|
|
115
|
+
created_at: {
|
|
116
|
+
type: DataTypes.DATE,
|
|
117
|
+
allowNull: false,
|
|
118
|
+
defaultValue: DataTypes.NOW,
|
|
119
|
+
field: "created_at",
|
|
120
|
+
},
|
|
121
|
+
updated_at: {
|
|
122
|
+
type: DataTypes.DATE,
|
|
123
|
+
allowNull: true,
|
|
124
|
+
field: "updated_at",
|
|
125
|
+
},
|
|
126
|
+
completed_at: {
|
|
127
|
+
type: DataTypes.DATE,
|
|
128
|
+
allowNull: true,
|
|
129
|
+
field: "completed_at",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
tableName: "public_api_batches",
|
|
134
|
+
timestamps: false, // created_at/updated_at managed manually (matches ai_campaign_queue)
|
|
135
|
+
// Indexes are named explicitly: prod sync runs alter:false, so an index
|
|
136
|
+
// added later to this (by-then existing) table must be backfilled by a
|
|
137
|
+
// migration that targets the same name.
|
|
138
|
+
indexes: [
|
|
139
|
+
{ name: "public_api_batches_user_id_idx", fields: ["user_id"] },
|
|
140
|
+
{ name: "public_api_batches_status_idx", fields: ["status"] },
|
|
141
|
+
{ name: "public_api_batches_created_at_idx", fields: ["created_at"] },
|
|
142
|
+
],
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
PublicApiBatch.associate = (models) => {
|
|
147
|
+
PublicApiBatch.hasMany(models.PublicApiBatchItem, {
|
|
148
|
+
foreignKey: "batch_id",
|
|
149
|
+
sourceKey: "id",
|
|
150
|
+
as: "items",
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return PublicApiBatch;
|
|
155
|
+
};
|