@fayz-ai/plugin-marketing 0.2.1 → 0.8.0
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/README.md +2 -0
- package/dist/data/contentSupabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +6 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/index.cjs +121 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -84
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +8 -3
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/schema/index.d.ts +44 -44
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema.cjs +9 -9
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +9 -9
- package/dist/schema.js.map +1 -1
- package/package.json +8 -5
- package/src/data/contentSupabase.ts +18 -17
- package/src/data/tables.ts +7 -0
- package/src/index.ts +12 -3
- package/src/migrations/000_plg_rename.sql +15 -0
- package/src/migrations/001_content_planner.sql +48 -48
- package/src/migrations/002_multi_platform.sql +5 -5
- package/src/migrations/003_recording_ops.sql +10 -10
- package/src/migrations/index.ts +99 -74
- package/src/schema/index.ts +9 -9
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fayz-ai/plugin-marketing",
|
|
3
|
-
"
|
|
3
|
+
"fayz": {
|
|
4
|
+
"status": "beta"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.8.0",
|
|
4
7
|
"description": "Fayz SDK — marketing campaigns & broadcasts plugin",
|
|
5
8
|
"type": "module",
|
|
6
9
|
"main": "./dist/index.cjs",
|
|
@@ -32,10 +35,10 @@
|
|
|
32
35
|
"@tanstack/react-table": "^8.20.0",
|
|
33
36
|
"lucide-react": "^0.400.0",
|
|
34
37
|
"zustand": "^4.5.0",
|
|
35
|
-
"@fayz-ai/
|
|
36
|
-
"@fayz-ai/
|
|
37
|
-
"@fayz-ai/
|
|
38
|
-
"@fayz-ai/
|
|
38
|
+
"@fayz-ai/ui": "^0.8.0",
|
|
39
|
+
"@fayz-ai/core": "^0.8.0",
|
|
40
|
+
"@fayz-ai/saas": "^0.8.0",
|
|
41
|
+
"@fayz-ai/db": "^0.8.0"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@types/react": "^18.3.0",
|
|
@@ -5,10 +5,11 @@ import type {
|
|
|
5
5
|
ContentPost,
|
|
6
6
|
SocialAccount,
|
|
7
7
|
} from './contentTypes'
|
|
8
|
+
import { T } from './tables'
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
|
-
// Supabase content-planner provider. Tables:
|
|
11
|
-
//
|
|
11
|
+
// Supabase content-planner provider. Tables: plg_marketing_social_accounts,
|
|
12
|
+
// plg_marketing_content_plans, plg_marketing_content_posts (see src/migrations/001_content_planner.sql).
|
|
12
13
|
// RLS scopes rows to the user's tenants; we still filter tenant_id explicitly
|
|
13
14
|
// on reads so multi-tenant sessions see only the active tenant.
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
@@ -61,7 +62,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
61
62
|
const tenantId = getActiveTenantId()
|
|
62
63
|
if (!tenantId) return []
|
|
63
64
|
const { data } = await getClient()
|
|
64
|
-
.from(
|
|
65
|
+
.from(T.socialAccounts)
|
|
65
66
|
.select('*')
|
|
66
67
|
.eq('tenant_id', tenantId)
|
|
67
68
|
.eq('is_active', true)
|
|
@@ -77,7 +78,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
77
78
|
if (input.platforms !== undefined) payload.platforms = input.platforms
|
|
78
79
|
if (input.isActive !== undefined) payload.is_active = input.isActive
|
|
79
80
|
const { data, error } = await getClient()
|
|
80
|
-
.from(
|
|
81
|
+
.from(T.socialAccounts)
|
|
81
82
|
.update(payload)
|
|
82
83
|
.eq('id', input.id)
|
|
83
84
|
.select('*')
|
|
@@ -86,7 +87,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
86
87
|
return toAccount(data)
|
|
87
88
|
}
|
|
88
89
|
const { data, error } = await getClient()
|
|
89
|
-
.from(
|
|
90
|
+
.from(T.socialAccounts)
|
|
90
91
|
.insert({
|
|
91
92
|
tenant_id: requireTenantId(),
|
|
92
93
|
name: input.name,
|
|
@@ -101,7 +102,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
101
102
|
},
|
|
102
103
|
|
|
103
104
|
async deleteAccount(id) {
|
|
104
|
-
const { error } = await getClient().from(
|
|
105
|
+
const { error } = await getClient().from(T.socialAccounts).delete().eq('id', id)
|
|
105
106
|
if (error) throw error
|
|
106
107
|
},
|
|
107
108
|
|
|
@@ -109,7 +110,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
109
110
|
const tenantId = getActiveTenantId()
|
|
110
111
|
if (!tenantId) return []
|
|
111
112
|
const { data } = await getClient()
|
|
112
|
-
.from(
|
|
113
|
+
.from(T.contentPlans)
|
|
113
114
|
.select('*')
|
|
114
115
|
.eq('tenant_id', tenantId)
|
|
115
116
|
.eq('account_id', accountId)
|
|
@@ -119,7 +120,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
119
120
|
|
|
120
121
|
async getPlan(id) {
|
|
121
122
|
const { data } = await getClient()
|
|
122
|
-
.from(
|
|
123
|
+
.from(T.contentPlans)
|
|
123
124
|
.select('*')
|
|
124
125
|
.eq('id', id)
|
|
125
126
|
.maybeSingle()
|
|
@@ -140,7 +141,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
140
141
|
if (input.weeklyFrequency !== undefined) payload.weekly_frequency = input.weeklyFrequency
|
|
141
142
|
if (input.briefMd !== undefined) payload.brief_md = input.briefMd
|
|
142
143
|
const { data, error } = await getClient()
|
|
143
|
-
.from(
|
|
144
|
+
.from(T.contentPlans)
|
|
144
145
|
.update(payload)
|
|
145
146
|
.eq('id', input.id)
|
|
146
147
|
.select('*')
|
|
@@ -150,7 +151,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
150
151
|
}
|
|
151
152
|
if (!input.accountId) throw new Error('accountId is required')
|
|
152
153
|
const { data, error } = await getClient()
|
|
153
|
-
.from(
|
|
154
|
+
.from(T.contentPlans)
|
|
154
155
|
.insert({
|
|
155
156
|
tenant_id: requireTenantId(),
|
|
156
157
|
account_id: input.accountId,
|
|
@@ -172,12 +173,12 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
172
173
|
},
|
|
173
174
|
|
|
174
175
|
async deletePlan(id) {
|
|
175
|
-
await getClient().from(
|
|
176
|
+
await getClient().from(T.contentPlans).delete().eq('id', id)
|
|
176
177
|
},
|
|
177
178
|
|
|
178
179
|
async listPosts(planId) {
|
|
179
180
|
const { data } = await getClient()
|
|
180
|
-
.from(
|
|
181
|
+
.from(T.contentPosts)
|
|
181
182
|
.select('*')
|
|
182
183
|
.eq('plan_id', planId)
|
|
183
184
|
.order('week_number', { ascending: true })
|
|
@@ -187,7 +188,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
187
188
|
|
|
188
189
|
async getPost(id) {
|
|
189
190
|
const { data } = await getClient()
|
|
190
|
-
.from(
|
|
191
|
+
.from(T.contentPosts)
|
|
191
192
|
.select('*')
|
|
192
193
|
.eq('id', id)
|
|
193
194
|
.maybeSingle()
|
|
@@ -210,7 +211,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
210
211
|
if (input.cta !== undefined) payload.cta = input.cta
|
|
211
212
|
if (input.contentMd !== undefined) payload.content_md = input.contentMd
|
|
212
213
|
const { data, error } = await getClient()
|
|
213
|
-
.from(
|
|
214
|
+
.from(T.contentPosts)
|
|
214
215
|
.update(payload)
|
|
215
216
|
.eq('id', input.id)
|
|
216
217
|
.select('*')
|
|
@@ -223,7 +224,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
223
224
|
|
|
224
225
|
// Next position within the (plan, week) group.
|
|
225
226
|
const { data: maxData } = await getClient()
|
|
226
|
-
.from(
|
|
227
|
+
.from(T.contentPosts)
|
|
227
228
|
.select('position')
|
|
228
229
|
.eq('plan_id', input.planId)
|
|
229
230
|
.eq('week_number', week)
|
|
@@ -232,7 +233,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
232
233
|
const maxPos = maxData?.[0]?.position ?? -1
|
|
233
234
|
|
|
234
235
|
const { data, error } = await getClient()
|
|
235
|
-
.from(
|
|
236
|
+
.from(T.contentPosts)
|
|
236
237
|
.insert({
|
|
237
238
|
tenant_id: requireTenantId(),
|
|
238
239
|
plan_id: input.planId,
|
|
@@ -256,7 +257,7 @@ export function createSupabaseContentPlannerProvider(): ContentPlannerProvider {
|
|
|
256
257
|
},
|
|
257
258
|
|
|
258
259
|
async deletePost(id) {
|
|
259
|
-
await getClient().from(
|
|
260
|
+
await getClient().from(T.contentPosts).delete().eq('id', id)
|
|
260
261
|
},
|
|
261
262
|
}
|
|
262
263
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Physical table names for the marketing plugin (plg_marketing_* prefix). Import T
|
|
2
|
+
// and reference T.<key> in the data providers so a rename lands in one place.
|
|
3
|
+
export const T = {
|
|
4
|
+
socialAccounts: 'plg_marketing_social_accounts',
|
|
5
|
+
contentPlans: 'plg_marketing_content_plans',
|
|
6
|
+
contentPosts: 'plg_marketing_content_posts',
|
|
7
|
+
} as const
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { createMockContentPlannerProvider } from './data/contentMock'
|
|
|
22
22
|
import { createSupabaseContentPlannerProvider } from './data/contentSupabase'
|
|
23
23
|
import { createMarketingStore } from './store'
|
|
24
24
|
import { createContentPlannerStore } from './views/content/contentStore'
|
|
25
|
-
import { MIGRATION_001_CONTENT_PLANNER, MIGRATION_002_MULTI_PLATFORM, MIGRATION_003_RECORDING_OPS } from './migrations'
|
|
25
|
+
import { MIGRATION_000_PLG_RENAME, MIGRATION_001_CONTENT_PLANNER, MIGRATION_002_MULTI_PLATFORM, MIGRATION_003_RECORDING_OPS } from './migrations'
|
|
26
26
|
import { DEFAULT_CURRENCY, type MarketingCurrency } from './format'
|
|
27
27
|
import { MARKETING_PRESETS, type MarketingDomain, type MarketingDomainModules } from './presets'
|
|
28
28
|
import type { AcquisitionChannel, ConversionModel } from './types'
|
|
@@ -141,6 +141,9 @@ export function createMarketingPlugin(options?: MarketingPluginOptions): PluginM
|
|
|
141
141
|
version: '1.0.0',
|
|
142
142
|
scope: options?.scope ?? 'universal',
|
|
143
143
|
verticalId: options?.verticalId,
|
|
144
|
+
// Admin content-planning surface (campaigns, content calendar) — targets the
|
|
145
|
+
// saas/admin Panel world, not the ecommerce storefront/operator console.
|
|
146
|
+
scaffolds: ['saas'],
|
|
144
147
|
defaultEnabled: true,
|
|
145
148
|
dependencies: [],
|
|
146
149
|
declaredFeatures: [
|
|
@@ -177,11 +180,17 @@ export function createMarketingPlugin(options?: MarketingPluginOptions): PluginM
|
|
|
177
180
|
{ name: 'marketing.content.post.updated', description: 'A content post was updated' },
|
|
178
181
|
],
|
|
179
182
|
migrations: [
|
|
183
|
+
{
|
|
184
|
+
id: 'marketing-000-plg-rename',
|
|
185
|
+
version: '1.0.0',
|
|
186
|
+
sql: MIGRATION_000_PLG_RENAME,
|
|
187
|
+
description: 'Rename legacy mkt_* tables to plg_marketing_* in-place for pools provisioned before the industry-pool rename (guarded no-op on fresh pools)',
|
|
188
|
+
},
|
|
180
189
|
{
|
|
181
190
|
id: 'marketing-001-content-planner',
|
|
182
191
|
version: '1.0.0',
|
|
183
192
|
sql: MIGRATION_001_CONTENT_PLANNER,
|
|
184
|
-
description: 'Create
|
|
193
|
+
description: 'Create plg_marketing_social_accounts, plg_marketing_content_plans and plg_marketing_content_posts',
|
|
185
194
|
},
|
|
186
195
|
{
|
|
187
196
|
id: 'marketing-002-multi-platform',
|
|
@@ -193,7 +202,7 @@ export function createMarketingPlugin(options?: MarketingPluginOptions): PluginM
|
|
|
193
202
|
id: 'marketing-003-recording-ops',
|
|
194
203
|
version: '1.2.0',
|
|
195
204
|
sql: MIGRATION_003_RECORDING_OPS,
|
|
196
|
-
description: 'Recording-day checklist + media asset URL on posts;
|
|
205
|
+
description: 'Recording-day checklist + media asset URL on posts; content-media storage bucket',
|
|
197
206
|
},
|
|
198
207
|
],
|
|
199
208
|
aiTools: [
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- 000_plg_rename.sql — rename legacy marketing tables to plg_marketing_* for pools
|
|
2
|
+
-- provisioned before the industry-pool rename. Guarded: fires only when legacy
|
|
3
|
+
-- name exists and target does not, so fresh pools skip every branch.
|
|
4
|
+
DO $$
|
|
5
|
+
BEGIN
|
|
6
|
+
IF to_regclass('public.mkt_social_accounts') IS NOT NULL AND to_regclass('public.plg_marketing_social_accounts') IS NULL THEN
|
|
7
|
+
ALTER TABLE public.mkt_social_accounts RENAME TO plg_marketing_social_accounts;
|
|
8
|
+
END IF;
|
|
9
|
+
IF to_regclass('public.mkt_content_plans') IS NOT NULL AND to_regclass('public.plg_marketing_content_plans') IS NULL THEN
|
|
10
|
+
ALTER TABLE public.mkt_content_plans RENAME TO plg_marketing_content_plans;
|
|
11
|
+
END IF;
|
|
12
|
+
IF to_regclass('public.mkt_content_posts') IS NOT NULL AND to_regclass('public.plg_marketing_content_posts') IS NULL THEN
|
|
13
|
+
ALTER TABLE public.mkt_content_posts RENAME TO plg_marketing_content_posts;
|
|
14
|
+
END IF;
|
|
15
|
+
END $$;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
-- Marketing Plugin: Content Planner
|
|
2
|
-
-- Prefix:
|
|
2
|
+
-- Prefix: plg_marketing_
|
|
3
3
|
-- Social accounts + content plans + posts (Notion-style markdown pages).
|
|
4
4
|
|
|
5
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
5
|
+
CREATE TABLE IF NOT EXISTS public.plg_marketing_social_accounts (
|
|
6
6
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
7
|
-
tenant_id uuid NOT NULL REFERENCES
|
|
7
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
8
8
|
name text NOT NULL,
|
|
9
9
|
handle text,
|
|
10
10
|
platform text NOT NULL DEFAULT 'instagram'
|
|
@@ -12,13 +12,13 @@ CREATE TABLE IF NOT EXISTS public.mkt_social_accounts (
|
|
|
12
12
|
is_active boolean NOT NULL DEFAULT true,
|
|
13
13
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
14
14
|
);
|
|
15
|
-
ALTER TABLE public.
|
|
16
|
-
CREATE INDEX IF NOT EXISTS
|
|
15
|
+
ALTER TABLE public.plg_marketing_social_accounts ENABLE ROW LEVEL SECURITY;
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_social_accounts_tenant ON public.plg_marketing_social_accounts(tenant_id);
|
|
17
17
|
|
|
18
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
18
|
+
CREATE TABLE IF NOT EXISTS public.plg_marketing_content_plans (
|
|
19
19
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
20
|
-
tenant_id uuid NOT NULL REFERENCES
|
|
21
|
-
account_id uuid NOT NULL REFERENCES public.
|
|
20
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
21
|
+
account_id uuid NOT NULL REFERENCES public.plg_marketing_social_accounts(id) ON DELETE CASCADE,
|
|
22
22
|
name text NOT NULL,
|
|
23
23
|
status text NOT NULL DEFAULT 'active'
|
|
24
24
|
CHECK (status IN ('draft', 'active', 'archived')),
|
|
@@ -33,14 +33,14 @@ CREATE TABLE IF NOT EXISTS public.mkt_content_plans (
|
|
|
33
33
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
34
34
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
35
35
|
);
|
|
36
|
-
ALTER TABLE public.
|
|
37
|
-
CREATE INDEX IF NOT EXISTS
|
|
38
|
-
CREATE INDEX IF NOT EXISTS
|
|
36
|
+
ALTER TABLE public.plg_marketing_content_plans ENABLE ROW LEVEL SECURITY;
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_content_plans_tenant ON public.plg_marketing_content_plans(tenant_id);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_content_plans_account ON public.plg_marketing_content_plans(account_id);
|
|
39
39
|
|
|
40
|
-
CREATE TABLE IF NOT EXISTS public.
|
|
40
|
+
CREATE TABLE IF NOT EXISTS public.plg_marketing_content_posts (
|
|
41
41
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
42
|
-
tenant_id uuid NOT NULL REFERENCES
|
|
43
|
-
plan_id uuid NOT NULL REFERENCES public.
|
|
42
|
+
tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
|
|
43
|
+
plan_id uuid NOT NULL REFERENCES public.plg_marketing_content_plans(id) ON DELETE CASCADE,
|
|
44
44
|
week_number integer NOT NULL DEFAULT 1,
|
|
45
45
|
position integer NOT NULL DEFAULT 0,
|
|
46
46
|
title text NOT NULL,
|
|
@@ -55,40 +55,40 @@ CREATE TABLE IF NOT EXISTS public.mkt_content_posts (
|
|
|
55
55
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
56
56
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
57
57
|
);
|
|
58
|
-
ALTER TABLE public.
|
|
59
|
-
CREATE INDEX IF NOT EXISTS
|
|
60
|
-
CREATE INDEX IF NOT EXISTS
|
|
61
|
-
CREATE INDEX IF NOT EXISTS
|
|
58
|
+
ALTER TABLE public.plg_marketing_content_posts ENABLE ROW LEVEL SECURITY;
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_content_posts_tenant ON public.plg_marketing_content_posts(tenant_id);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_content_posts_plan ON public.plg_marketing_content_posts(plan_id, week_number, position);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_plg_marketing_content_posts_status ON public.plg_marketing_content_posts(tenant_id, status);
|
|
62
62
|
|
|
63
|
-
-- RLS policies for
|
|
64
|
-
DROP POLICY IF EXISTS
|
|
65
|
-
DROP POLICY IF EXISTS
|
|
66
|
-
DROP POLICY IF EXISTS
|
|
67
|
-
DROP POLICY IF EXISTS
|
|
68
|
-
CREATE POLICY
|
|
69
|
-
CREATE POLICY
|
|
70
|
-
CREATE POLICY
|
|
71
|
-
CREATE POLICY
|
|
72
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON public.
|
|
63
|
+
-- RLS policies for plg_marketing_social_accounts
|
|
64
|
+
DROP POLICY IF EXISTS plg_marketing_social_accounts_select ON public.plg_marketing_social_accounts;
|
|
65
|
+
DROP POLICY IF EXISTS plg_marketing_social_accounts_insert ON public.plg_marketing_social_accounts;
|
|
66
|
+
DROP POLICY IF EXISTS plg_marketing_social_accounts_update ON public.plg_marketing_social_accounts;
|
|
67
|
+
DROP POLICY IF EXISTS plg_marketing_social_accounts_delete ON public.plg_marketing_social_accounts;
|
|
68
|
+
CREATE POLICY plg_marketing_social_accounts_select ON public.plg_marketing_social_accounts FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
69
|
+
CREATE POLICY plg_marketing_social_accounts_insert ON public.plg_marketing_social_accounts FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
70
|
+
CREATE POLICY plg_marketing_social_accounts_update ON public.plg_marketing_social_accounts FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
71
|
+
CREATE POLICY plg_marketing_social_accounts_delete ON public.plg_marketing_social_accounts FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
72
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_marketing_social_accounts TO authenticated;
|
|
73
73
|
|
|
74
|
-
-- RLS policies for
|
|
75
|
-
DROP POLICY IF EXISTS
|
|
76
|
-
DROP POLICY IF EXISTS
|
|
77
|
-
DROP POLICY IF EXISTS
|
|
78
|
-
DROP POLICY IF EXISTS
|
|
79
|
-
CREATE POLICY
|
|
80
|
-
CREATE POLICY
|
|
81
|
-
CREATE POLICY
|
|
82
|
-
CREATE POLICY
|
|
83
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON public.
|
|
74
|
+
-- RLS policies for plg_marketing_content_plans
|
|
75
|
+
DROP POLICY IF EXISTS plg_marketing_content_plans_select ON public.plg_marketing_content_plans;
|
|
76
|
+
DROP POLICY IF EXISTS plg_marketing_content_plans_insert ON public.plg_marketing_content_plans;
|
|
77
|
+
DROP POLICY IF EXISTS plg_marketing_content_plans_update ON public.plg_marketing_content_plans;
|
|
78
|
+
DROP POLICY IF EXISTS plg_marketing_content_plans_delete ON public.plg_marketing_content_plans;
|
|
79
|
+
CREATE POLICY plg_marketing_content_plans_select ON public.plg_marketing_content_plans FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
80
|
+
CREATE POLICY plg_marketing_content_plans_insert ON public.plg_marketing_content_plans FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
81
|
+
CREATE POLICY plg_marketing_content_plans_update ON public.plg_marketing_content_plans FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
82
|
+
CREATE POLICY plg_marketing_content_plans_delete ON public.plg_marketing_content_plans FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
83
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_marketing_content_plans TO authenticated;
|
|
84
84
|
|
|
85
|
-
-- RLS policies for
|
|
86
|
-
DROP POLICY IF EXISTS
|
|
87
|
-
DROP POLICY IF EXISTS
|
|
88
|
-
DROP POLICY IF EXISTS
|
|
89
|
-
DROP POLICY IF EXISTS
|
|
90
|
-
CREATE POLICY
|
|
91
|
-
CREATE POLICY
|
|
92
|
-
CREATE POLICY
|
|
93
|
-
CREATE POLICY
|
|
94
|
-
GRANT SELECT, INSERT, UPDATE, DELETE ON public.
|
|
85
|
+
-- RLS policies for plg_marketing_content_posts
|
|
86
|
+
DROP POLICY IF EXISTS plg_marketing_content_posts_select ON public.plg_marketing_content_posts;
|
|
87
|
+
DROP POLICY IF EXISTS plg_marketing_content_posts_insert ON public.plg_marketing_content_posts;
|
|
88
|
+
DROP POLICY IF EXISTS plg_marketing_content_posts_update ON public.plg_marketing_content_posts;
|
|
89
|
+
DROP POLICY IF EXISTS plg_marketing_content_posts_delete ON public.plg_marketing_content_posts;
|
|
90
|
+
CREATE POLICY plg_marketing_content_posts_select ON public.plg_marketing_content_posts FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
91
|
+
CREATE POLICY plg_marketing_content_posts_insert ON public.plg_marketing_content_posts FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
92
|
+
CREATE POLICY plg_marketing_content_posts_update ON public.plg_marketing_content_posts FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
93
|
+
CREATE POLICY plg_marketing_content_posts_delete ON public.plg_marketing_content_posts FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));
|
|
94
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_marketing_content_posts TO authenticated;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
-- YouTube, TikTok, ...). Posts optionally narrow the target platforms; an
|
|
4
4
|
-- empty array means "inherit the account's platforms".
|
|
5
5
|
|
|
6
|
-
ALTER TABLE public.
|
|
6
|
+
ALTER TABLE public.plg_marketing_social_accounts
|
|
7
7
|
ADD COLUMN IF NOT EXISTS platforms text[] NOT NULL DEFAULT '{instagram}';
|
|
8
8
|
|
|
9
9
|
-- Backfill from the legacy single-platform column, then drop it (also drops
|
|
@@ -12,14 +12,14 @@ DO $$
|
|
|
12
12
|
BEGIN
|
|
13
13
|
IF EXISTS (
|
|
14
14
|
SELECT 1 FROM information_schema.columns
|
|
15
|
-
WHERE table_schema = 'public' AND table_name = '
|
|
15
|
+
WHERE table_schema = 'public' AND table_name = 'plg_marketing_social_accounts' AND column_name = 'platform'
|
|
16
16
|
) THEN
|
|
17
|
-
UPDATE public.
|
|
17
|
+
UPDATE public.plg_marketing_social_accounts
|
|
18
18
|
SET platforms = ARRAY[platform]
|
|
19
19
|
WHERE platform IS NOT NULL AND platforms = '{instagram}';
|
|
20
|
-
ALTER TABLE public.
|
|
20
|
+
ALTER TABLE public.plg_marketing_social_accounts DROP COLUMN platform;
|
|
21
21
|
END IF;
|
|
22
22
|
END $$;
|
|
23
23
|
|
|
24
|
-
ALTER TABLE public.
|
|
24
|
+
ALTER TABLE public.plg_marketing_content_posts
|
|
25
25
|
ADD COLUMN IF NOT EXISTS platforms text[] NOT NULL DEFAULT '{}';
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
-- companion for recording day. media_url: uploaded asset (static posts get a
|
|
4
4
|
-- caption + final art instead of a script).
|
|
5
5
|
|
|
6
|
-
ALTER TABLE public.
|
|
6
|
+
ALTER TABLE public.plg_marketing_content_posts
|
|
7
7
|
ADD COLUMN IF NOT EXISTS checklist jsonb NOT NULL DEFAULT '[]';
|
|
8
8
|
|
|
9
|
-
ALTER TABLE public.
|
|
9
|
+
ALTER TABLE public.plg_marketing_content_posts
|
|
10
10
|
ADD COLUMN IF NOT EXISTS media_url text;
|
|
11
11
|
|
|
12
12
|
-- Public media bucket for content assets (mirrors the avatars-bucket pattern:
|
|
@@ -15,11 +15,11 @@ INSERT INTO storage.buckets (id, name, public)
|
|
|
15
15
|
VALUES ('mkt-media', 'mkt-media', true)
|
|
16
16
|
ON CONFLICT (id) DO NOTHING;
|
|
17
17
|
|
|
18
|
-
DROP POLICY IF EXISTS
|
|
19
|
-
DROP POLICY IF EXISTS
|
|
20
|
-
DROP POLICY IF EXISTS
|
|
21
|
-
DROP POLICY IF EXISTS
|
|
22
|
-
CREATE POLICY
|
|
23
|
-
CREATE POLICY
|
|
24
|
-
CREATE POLICY
|
|
25
|
-
CREATE POLICY
|
|
18
|
+
DROP POLICY IF EXISTS plg_marketing_media_read ON storage.objects;
|
|
19
|
+
DROP POLICY IF EXISTS plg_marketing_media_insert ON storage.objects;
|
|
20
|
+
DROP POLICY IF EXISTS plg_marketing_media_update ON storage.objects;
|
|
21
|
+
DROP POLICY IF EXISTS plg_marketing_media_delete ON storage.objects;
|
|
22
|
+
CREATE POLICY plg_marketing_media_read ON storage.objects FOR SELECT TO public USING (bucket_id = 'mkt-media');
|
|
23
|
+
CREATE POLICY plg_marketing_media_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (bucket_id = 'mkt-media');
|
|
24
|
+
CREATE POLICY plg_marketing_media_update ON storage.objects FOR UPDATE TO authenticated USING (bucket_id = 'mkt-media');
|
|
25
|
+
CREATE POLICY plg_marketing_media_delete ON storage.objects FOR DELETE TO authenticated USING (bucket_id = 'mkt-media');
|