@profullstack/affiliates 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +248 -2
- package/dist/affiliates.d.ts +101 -0
- package/dist/affiliates.js +180 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +169 -0
- package/dist/codes.d.ts +19 -0
- package/dist/codes.js +48 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +8 -0
- package/dist/memory.d.ts +3 -0
- package/dist/memory.js +105 -0
- package/dist/postgres.d.ts +10 -0
- package/dist/postgres.js +8 -0
- package/dist/ref.d.ts +22 -0
- package/dist/ref.js +49 -0
- package/dist/sql.d.ts +12 -0
- package/dist/sql.js +172 -0
- package/dist/sqlite.d.ts +26 -0
- package/dist/sqlite.js +30 -0
- package/dist/store.d.ts +61 -0
- package/dist/store.js +1 -0
- package/dist/supabase.d.ts +31 -0
- package/dist/supabase.js +226 -0
- package/migrations/supabase.sql +319 -0
- package/package.json +65 -4
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
-- @profullstack/affiliates: Supabase migration (prefix aff_)
|
|
2
|
+
-- Generated by supabaseMigrationSql('aff_'). Same tables as postgresStore, so both stores can share them.
|
|
3
|
+
-- Use with supabaseStore(serviceRoleClient). Idempotent.
|
|
4
|
+
|
|
5
|
+
CREATE TABLE IF NOT EXISTS public.aff_invites (
|
|
6
|
+
id TEXT PRIMARY KEY,
|
|
7
|
+
code_hash TEXT NOT NULL UNIQUE,
|
|
8
|
+
inviter_id TEXT NOT NULL,
|
|
9
|
+
created_at BIGINT NOT NULL,
|
|
10
|
+
expires_at BIGINT,
|
|
11
|
+
used_by TEXT,
|
|
12
|
+
used_at BIGINT,
|
|
13
|
+
note TEXT NOT NULL DEFAULT '',
|
|
14
|
+
revoked_at BIGINT
|
|
15
|
+
);
|
|
16
|
+
CREATE INDEX IF NOT EXISTS aff_invites_inviter_idx ON public.aff_invites (inviter_id);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS aff_invites_used_by_idx ON public.aff_invites (used_by);
|
|
18
|
+
CREATE TABLE IF NOT EXISTS public.aff_quotas (
|
|
19
|
+
user_id TEXT PRIMARY KEY,
|
|
20
|
+
total INTEGER NOT NULL
|
|
21
|
+
);
|
|
22
|
+
CREATE TABLE IF NOT EXISTS public.aff_codes (
|
|
23
|
+
user_id TEXT PRIMARY KEY,
|
|
24
|
+
code TEXT NOT NULL UNIQUE,
|
|
25
|
+
created_at BIGINT NOT NULL
|
|
26
|
+
);
|
|
27
|
+
CREATE TABLE IF NOT EXISTS public.aff_attributions (
|
|
28
|
+
user_id TEXT PRIMARY KEY,
|
|
29
|
+
affiliate_id TEXT NOT NULL,
|
|
30
|
+
code TEXT NOT NULL,
|
|
31
|
+
at BIGINT NOT NULL
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS aff_attributions_affiliate_idx ON public.aff_attributions (affiliate_id);
|
|
34
|
+
|
|
35
|
+
-- RLS on with no policies: anon and authenticated keys see nothing. Access goes only through the
|
|
36
|
+
-- SECURITY DEFINER functions below, which only service_role may execute.
|
|
37
|
+
ALTER TABLE public.aff_invites ENABLE ROW LEVEL SECURITY;
|
|
38
|
+
ALTER TABLE public.aff_quotas ENABLE ROW LEVEL SECURITY;
|
|
39
|
+
ALTER TABLE public.aff_codes ENABLE ROW LEVEL SECURITY;
|
|
40
|
+
ALTER TABLE public.aff_attributions ENABLE ROW LEVEL SECURITY;
|
|
41
|
+
|
|
42
|
+
CREATE OR REPLACE FUNCTION public.aff_get_quota(p_user TEXT)
|
|
43
|
+
RETURNS INTEGER
|
|
44
|
+
LANGUAGE sql
|
|
45
|
+
SECURITY DEFINER
|
|
46
|
+
SET search_path = public, pg_temp
|
|
47
|
+
AS $fn$
|
|
48
|
+
SELECT total FROM public.aff_quotas WHERE user_id = p_user
|
|
49
|
+
$fn$;
|
|
50
|
+
|
|
51
|
+
CREATE OR REPLACE FUNCTION public.aff_set_quota(p_user TEXT, p_total INTEGER)
|
|
52
|
+
RETURNS VOID
|
|
53
|
+
LANGUAGE sql
|
|
54
|
+
SECURITY DEFINER
|
|
55
|
+
SET search_path = public, pg_temp
|
|
56
|
+
AS $fn$
|
|
57
|
+
INSERT INTO public.aff_quotas (user_id, total) VALUES (p_user, p_total)
|
|
58
|
+
ON CONFLICT (user_id) DO UPDATE SET total = excluded.total
|
|
59
|
+
$fn$;
|
|
60
|
+
|
|
61
|
+
CREATE OR REPLACE FUNCTION public.aff_count_invites(p_inviter TEXT)
|
|
62
|
+
RETURNS INTEGER
|
|
63
|
+
LANGUAGE sql
|
|
64
|
+
SECURITY DEFINER
|
|
65
|
+
SET search_path = public, pg_temp
|
|
66
|
+
AS $fn$
|
|
67
|
+
SELECT count(*)::INTEGER FROM public.aff_invites WHERE inviter_id = p_inviter AND (revoked_at IS NULL OR used_by IS NOT NULL)
|
|
68
|
+
$fn$;
|
|
69
|
+
|
|
70
|
+
CREATE OR REPLACE FUNCTION public.aff_insert_invite(p_id TEXT, p_hash TEXT, p_inviter TEXT, p_created BIGINT, p_expires BIGINT, p_note TEXT, p_limit INTEGER)
|
|
71
|
+
RETURNS BOOLEAN
|
|
72
|
+
LANGUAGE plpgsql
|
|
73
|
+
SECURITY DEFINER
|
|
74
|
+
SET search_path = public, pg_temp
|
|
75
|
+
AS $fn$
|
|
76
|
+
BEGIN
|
|
77
|
+
-- Per-inviter lock; plpgsql takes a fresh snapshot per statement, so the count sees every committed invite.
|
|
78
|
+
PERFORM pg_advisory_xact_lock(hashtext('aff_invites'), hashtext(p_inviter));
|
|
79
|
+
IF (SELECT count(*) FROM public.aff_invites WHERE inviter_id = p_inviter AND (revoked_at IS NULL OR used_by IS NOT NULL)) >= p_limit THEN
|
|
80
|
+
RETURN FALSE;
|
|
81
|
+
END IF;
|
|
82
|
+
INSERT INTO public.aff_invites (id, code_hash, inviter_id, created_at, expires_at, note)
|
|
83
|
+
VALUES (p_id, p_hash, p_inviter, p_created, p_expires, coalesce(p_note, ''));
|
|
84
|
+
RETURN TRUE;
|
|
85
|
+
END
|
|
86
|
+
$fn$;
|
|
87
|
+
|
|
88
|
+
CREATE OR REPLACE FUNCTION public.aff_get_invite_by_hash(p_hash TEXT)
|
|
89
|
+
RETURNS SETOF public.aff_invites
|
|
90
|
+
LANGUAGE sql
|
|
91
|
+
SECURITY DEFINER
|
|
92
|
+
SET search_path = public, pg_temp
|
|
93
|
+
AS $fn$
|
|
94
|
+
SELECT * FROM public.aff_invites WHERE code_hash = p_hash
|
|
95
|
+
$fn$;
|
|
96
|
+
|
|
97
|
+
CREATE OR REPLACE FUNCTION public.aff_get_invite(p_id TEXT)
|
|
98
|
+
RETURNS SETOF public.aff_invites
|
|
99
|
+
LANGUAGE sql
|
|
100
|
+
SECURITY DEFINER
|
|
101
|
+
SET search_path = public, pg_temp
|
|
102
|
+
AS $fn$
|
|
103
|
+
SELECT * FROM public.aff_invites WHERE id = p_id
|
|
104
|
+
$fn$;
|
|
105
|
+
|
|
106
|
+
CREATE OR REPLACE FUNCTION public.aff_claim_invite(p_id TEXT, p_user TEXT, p_at BIGINT)
|
|
107
|
+
RETURNS BOOLEAN
|
|
108
|
+
LANGUAGE sql
|
|
109
|
+
SECURITY DEFINER
|
|
110
|
+
SET search_path = public, pg_temp
|
|
111
|
+
AS $fn$
|
|
112
|
+
WITH won AS (
|
|
113
|
+
UPDATE public.aff_invites SET used_by = p_user, used_at = p_at
|
|
114
|
+
WHERE id = p_id AND used_by IS NULL AND revoked_at IS NULL
|
|
115
|
+
RETURNING 1
|
|
116
|
+
)
|
|
117
|
+
SELECT EXISTS (SELECT 1 FROM won)
|
|
118
|
+
$fn$;
|
|
119
|
+
|
|
120
|
+
CREATE OR REPLACE FUNCTION public.aff_revoke_invite(p_id TEXT, p_at BIGINT)
|
|
121
|
+
RETURNS VOID
|
|
122
|
+
LANGUAGE sql
|
|
123
|
+
SECURITY DEFINER
|
|
124
|
+
SET search_path = public, pg_temp
|
|
125
|
+
AS $fn$
|
|
126
|
+
UPDATE public.aff_invites SET revoked_at = p_at WHERE id = p_id AND revoked_at IS NULL
|
|
127
|
+
$fn$;
|
|
128
|
+
|
|
129
|
+
CREATE OR REPLACE FUNCTION public.aff_list_invites(p_inviter TEXT)
|
|
130
|
+
RETURNS SETOF public.aff_invites
|
|
131
|
+
LANGUAGE sql
|
|
132
|
+
SECURITY DEFINER
|
|
133
|
+
SET search_path = public, pg_temp
|
|
134
|
+
AS $fn$
|
|
135
|
+
SELECT * FROM public.aff_invites WHERE inviter_id = p_inviter ORDER BY created_at DESC, id
|
|
136
|
+
$fn$;
|
|
137
|
+
|
|
138
|
+
CREATE OR REPLACE FUNCTION public.aff_invited_by(p_user TEXT)
|
|
139
|
+
RETURNS TEXT
|
|
140
|
+
LANGUAGE sql
|
|
141
|
+
SECURITY DEFINER
|
|
142
|
+
SET search_path = public, pg_temp
|
|
143
|
+
AS $fn$
|
|
144
|
+
SELECT inviter_id FROM public.aff_invites WHERE used_by = p_user ORDER BY used_at ASC LIMIT 1
|
|
145
|
+
$fn$;
|
|
146
|
+
|
|
147
|
+
CREATE OR REPLACE FUNCTION public.aff_invitees(p_inviter TEXT)
|
|
148
|
+
RETURNS TABLE (user_id TEXT, at BIGINT)
|
|
149
|
+
LANGUAGE sql
|
|
150
|
+
SECURITY DEFINER
|
|
151
|
+
SET search_path = public, pg_temp
|
|
152
|
+
AS $fn$
|
|
153
|
+
SELECT used_by, used_at FROM public.aff_invites
|
|
154
|
+
WHERE inviter_id = p_inviter AND used_by IS NOT NULL ORDER BY used_at ASC
|
|
155
|
+
$fn$;
|
|
156
|
+
|
|
157
|
+
CREATE OR REPLACE FUNCTION public.aff_get_affiliate_code(p_user TEXT)
|
|
158
|
+
RETURNS TEXT
|
|
159
|
+
LANGUAGE sql
|
|
160
|
+
SECURITY DEFINER
|
|
161
|
+
SET search_path = public, pg_temp
|
|
162
|
+
AS $fn$
|
|
163
|
+
SELECT code FROM public.aff_codes WHERE user_id = p_user
|
|
164
|
+
$fn$;
|
|
165
|
+
|
|
166
|
+
CREATE OR REPLACE FUNCTION public.aff_put_affiliate_code(p_user TEXT, p_code TEXT, p_at BIGINT)
|
|
167
|
+
RETURNS TEXT
|
|
168
|
+
LANGUAGE plpgsql
|
|
169
|
+
SECURITY DEFINER
|
|
170
|
+
SET search_path = public, pg_temp
|
|
171
|
+
AS $fn$
|
|
172
|
+
BEGIN
|
|
173
|
+
INSERT INTO public.aff_codes (user_id, code, created_at) VALUES (p_user, p_code, p_at) ON CONFLICT DO NOTHING;
|
|
174
|
+
RETURN (SELECT code FROM public.aff_codes WHERE user_id = p_user);
|
|
175
|
+
END
|
|
176
|
+
$fn$;
|
|
177
|
+
|
|
178
|
+
CREATE OR REPLACE FUNCTION public.aff_affiliate_by_code(p_code TEXT)
|
|
179
|
+
RETURNS TEXT
|
|
180
|
+
LANGUAGE sql
|
|
181
|
+
SECURITY DEFINER
|
|
182
|
+
SET search_path = public, pg_temp
|
|
183
|
+
AS $fn$
|
|
184
|
+
SELECT user_id FROM public.aff_codes WHERE code = p_code
|
|
185
|
+
$fn$;
|
|
186
|
+
|
|
187
|
+
CREATE OR REPLACE FUNCTION public.aff_attribute(p_user TEXT, p_affiliate TEXT, p_code TEXT, p_at BIGINT)
|
|
188
|
+
RETURNS BOOLEAN
|
|
189
|
+
LANGUAGE sql
|
|
190
|
+
SECURITY DEFINER
|
|
191
|
+
SET search_path = public, pg_temp
|
|
192
|
+
AS $fn$
|
|
193
|
+
WITH ins AS (
|
|
194
|
+
INSERT INTO public.aff_attributions (user_id, affiliate_id, code, at) VALUES (p_user, p_affiliate, p_code, p_at)
|
|
195
|
+
ON CONFLICT (user_id) DO NOTHING
|
|
196
|
+
RETURNING 1
|
|
197
|
+
)
|
|
198
|
+
SELECT EXISTS (SELECT 1 FROM ins)
|
|
199
|
+
$fn$;
|
|
200
|
+
|
|
201
|
+
CREATE OR REPLACE FUNCTION public.aff_get_attribution(p_user TEXT)
|
|
202
|
+
RETURNS SETOF public.aff_attributions
|
|
203
|
+
LANGUAGE sql
|
|
204
|
+
SECURITY DEFINER
|
|
205
|
+
SET search_path = public, pg_temp
|
|
206
|
+
AS $fn$
|
|
207
|
+
SELECT * FROM public.aff_attributions WHERE user_id = p_user
|
|
208
|
+
$fn$;
|
|
209
|
+
|
|
210
|
+
CREATE OR REPLACE FUNCTION public.aff_is_external_code(p_code TEXT)
|
|
211
|
+
RETURNS BOOLEAN
|
|
212
|
+
LANGUAGE plpgsql
|
|
213
|
+
SECURITY DEFINER
|
|
214
|
+
SET search_path = public, pg_temp
|
|
215
|
+
AS $fn$
|
|
216
|
+
DECLARE found BOOLEAN := FALSE;
|
|
217
|
+
BEGIN
|
|
218
|
+
IF to_regclass('public.referral_codes') IS NULL THEN
|
|
219
|
+
RETURN FALSE;
|
|
220
|
+
END IF;
|
|
221
|
+
EXECUTE 'SELECT EXISTS (SELECT 1 FROM public.referral_codes WHERE code = $1)'
|
|
222
|
+
INTO found USING upper(btrim(p_code));
|
|
223
|
+
RETURN found;
|
|
224
|
+
END
|
|
225
|
+
$fn$;
|
|
226
|
+
|
|
227
|
+
REVOKE ALL ON FUNCTION public.aff_get_quota(TEXT) FROM PUBLIC;
|
|
228
|
+
REVOKE ALL ON FUNCTION public.aff_set_quota(TEXT, INTEGER) FROM PUBLIC;
|
|
229
|
+
REVOKE ALL ON FUNCTION public.aff_count_invites(TEXT) FROM PUBLIC;
|
|
230
|
+
REVOKE ALL ON FUNCTION public.aff_insert_invite(TEXT, TEXT, TEXT, BIGINT, BIGINT, TEXT, INTEGER) FROM PUBLIC;
|
|
231
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite_by_hash(TEXT) FROM PUBLIC;
|
|
232
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite(TEXT) FROM PUBLIC;
|
|
233
|
+
REVOKE ALL ON FUNCTION public.aff_claim_invite(TEXT, TEXT, BIGINT) FROM PUBLIC;
|
|
234
|
+
REVOKE ALL ON FUNCTION public.aff_revoke_invite(TEXT, BIGINT) FROM PUBLIC;
|
|
235
|
+
REVOKE ALL ON FUNCTION public.aff_list_invites(TEXT) FROM PUBLIC;
|
|
236
|
+
REVOKE ALL ON FUNCTION public.aff_invited_by(TEXT) FROM PUBLIC;
|
|
237
|
+
REVOKE ALL ON FUNCTION public.aff_invitees(TEXT) FROM PUBLIC;
|
|
238
|
+
REVOKE ALL ON FUNCTION public.aff_get_affiliate_code(TEXT) FROM PUBLIC;
|
|
239
|
+
REVOKE ALL ON FUNCTION public.aff_put_affiliate_code(TEXT, TEXT, BIGINT) FROM PUBLIC;
|
|
240
|
+
REVOKE ALL ON FUNCTION public.aff_affiliate_by_code(TEXT) FROM PUBLIC;
|
|
241
|
+
REVOKE ALL ON FUNCTION public.aff_attribute(TEXT, TEXT, TEXT, BIGINT) FROM PUBLIC;
|
|
242
|
+
REVOKE ALL ON FUNCTION public.aff_get_attribution(TEXT) FROM PUBLIC;
|
|
243
|
+
REVOKE ALL ON FUNCTION public.aff_is_external_code(TEXT) FROM PUBLIC;
|
|
244
|
+
|
|
245
|
+
DO $grants$
|
|
246
|
+
BEGIN
|
|
247
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN
|
|
248
|
+
REVOKE ALL ON FUNCTION public.aff_get_quota(TEXT) FROM anon;
|
|
249
|
+
REVOKE ALL ON FUNCTION public.aff_set_quota(TEXT, INTEGER) FROM anon;
|
|
250
|
+
REVOKE ALL ON FUNCTION public.aff_count_invites(TEXT) FROM anon;
|
|
251
|
+
REVOKE ALL ON FUNCTION public.aff_insert_invite(TEXT, TEXT, TEXT, BIGINT, BIGINT, TEXT, INTEGER) FROM anon;
|
|
252
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite_by_hash(TEXT) FROM anon;
|
|
253
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite(TEXT) FROM anon;
|
|
254
|
+
REVOKE ALL ON FUNCTION public.aff_claim_invite(TEXT, TEXT, BIGINT) FROM anon;
|
|
255
|
+
REVOKE ALL ON FUNCTION public.aff_revoke_invite(TEXT, BIGINT) FROM anon;
|
|
256
|
+
REVOKE ALL ON FUNCTION public.aff_list_invites(TEXT) FROM anon;
|
|
257
|
+
REVOKE ALL ON FUNCTION public.aff_invited_by(TEXT) FROM anon;
|
|
258
|
+
REVOKE ALL ON FUNCTION public.aff_invitees(TEXT) FROM anon;
|
|
259
|
+
REVOKE ALL ON FUNCTION public.aff_get_affiliate_code(TEXT) FROM anon;
|
|
260
|
+
REVOKE ALL ON FUNCTION public.aff_put_affiliate_code(TEXT, TEXT, BIGINT) FROM anon;
|
|
261
|
+
REVOKE ALL ON FUNCTION public.aff_affiliate_by_code(TEXT) FROM anon;
|
|
262
|
+
REVOKE ALL ON FUNCTION public.aff_attribute(TEXT, TEXT, TEXT, BIGINT) FROM anon;
|
|
263
|
+
REVOKE ALL ON FUNCTION public.aff_get_attribution(TEXT) FROM anon;
|
|
264
|
+
REVOKE ALL ON FUNCTION public.aff_is_external_code(TEXT) FROM anon;
|
|
265
|
+
END IF;
|
|
266
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN
|
|
267
|
+
REVOKE ALL ON FUNCTION public.aff_get_quota(TEXT) FROM authenticated;
|
|
268
|
+
REVOKE ALL ON FUNCTION public.aff_set_quota(TEXT, INTEGER) FROM authenticated;
|
|
269
|
+
REVOKE ALL ON FUNCTION public.aff_count_invites(TEXT) FROM authenticated;
|
|
270
|
+
REVOKE ALL ON FUNCTION public.aff_insert_invite(TEXT, TEXT, TEXT, BIGINT, BIGINT, TEXT, INTEGER) FROM authenticated;
|
|
271
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite_by_hash(TEXT) FROM authenticated;
|
|
272
|
+
REVOKE ALL ON FUNCTION public.aff_get_invite(TEXT) FROM authenticated;
|
|
273
|
+
REVOKE ALL ON FUNCTION public.aff_claim_invite(TEXT, TEXT, BIGINT) FROM authenticated;
|
|
274
|
+
REVOKE ALL ON FUNCTION public.aff_revoke_invite(TEXT, BIGINT) FROM authenticated;
|
|
275
|
+
REVOKE ALL ON FUNCTION public.aff_list_invites(TEXT) FROM authenticated;
|
|
276
|
+
REVOKE ALL ON FUNCTION public.aff_invited_by(TEXT) FROM authenticated;
|
|
277
|
+
REVOKE ALL ON FUNCTION public.aff_invitees(TEXT) FROM authenticated;
|
|
278
|
+
REVOKE ALL ON FUNCTION public.aff_get_affiliate_code(TEXT) FROM authenticated;
|
|
279
|
+
REVOKE ALL ON FUNCTION public.aff_put_affiliate_code(TEXT, TEXT, BIGINT) FROM authenticated;
|
|
280
|
+
REVOKE ALL ON FUNCTION public.aff_affiliate_by_code(TEXT) FROM authenticated;
|
|
281
|
+
REVOKE ALL ON FUNCTION public.aff_attribute(TEXT, TEXT, TEXT, BIGINT) FROM authenticated;
|
|
282
|
+
REVOKE ALL ON FUNCTION public.aff_get_attribution(TEXT) FROM authenticated;
|
|
283
|
+
REVOKE ALL ON FUNCTION public.aff_is_external_code(TEXT) FROM authenticated;
|
|
284
|
+
END IF;
|
|
285
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'service_role') THEN
|
|
286
|
+
GRANT EXECUTE ON FUNCTION public.aff_get_quota(TEXT) TO service_role;
|
|
287
|
+
GRANT EXECUTE ON FUNCTION public.aff_set_quota(TEXT, INTEGER) TO service_role;
|
|
288
|
+
GRANT EXECUTE ON FUNCTION public.aff_count_invites(TEXT) TO service_role;
|
|
289
|
+
GRANT EXECUTE ON FUNCTION public.aff_insert_invite(TEXT, TEXT, TEXT, BIGINT, BIGINT, TEXT, INTEGER) TO service_role;
|
|
290
|
+
GRANT EXECUTE ON FUNCTION public.aff_get_invite_by_hash(TEXT) TO service_role;
|
|
291
|
+
GRANT EXECUTE ON FUNCTION public.aff_get_invite(TEXT) TO service_role;
|
|
292
|
+
GRANT EXECUTE ON FUNCTION public.aff_claim_invite(TEXT, TEXT, BIGINT) TO service_role;
|
|
293
|
+
GRANT EXECUTE ON FUNCTION public.aff_revoke_invite(TEXT, BIGINT) TO service_role;
|
|
294
|
+
GRANT EXECUTE ON FUNCTION public.aff_list_invites(TEXT) TO service_role;
|
|
295
|
+
GRANT EXECUTE ON FUNCTION public.aff_invited_by(TEXT) TO service_role;
|
|
296
|
+
GRANT EXECUTE ON FUNCTION public.aff_invitees(TEXT) TO service_role;
|
|
297
|
+
GRANT EXECUTE ON FUNCTION public.aff_get_affiliate_code(TEXT) TO service_role;
|
|
298
|
+
GRANT EXECUTE ON FUNCTION public.aff_put_affiliate_code(TEXT, TEXT, BIGINT) TO service_role;
|
|
299
|
+
GRANT EXECUTE ON FUNCTION public.aff_affiliate_by_code(TEXT) TO service_role;
|
|
300
|
+
GRANT EXECUTE ON FUNCTION public.aff_attribute(TEXT, TEXT, TEXT, BIGINT) TO service_role;
|
|
301
|
+
GRANT EXECUTE ON FUNCTION public.aff_get_attribution(TEXT) TO service_role;
|
|
302
|
+
GRANT EXECUTE ON FUNCTION public.aff_is_external_code(TEXT) TO service_role;
|
|
303
|
+
END IF;
|
|
304
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN
|
|
305
|
+
REVOKE ALL ON TABLE public.aff_invites FROM anon;
|
|
306
|
+
REVOKE ALL ON TABLE public.aff_quotas FROM anon;
|
|
307
|
+
REVOKE ALL ON TABLE public.aff_codes FROM anon;
|
|
308
|
+
REVOKE ALL ON TABLE public.aff_attributions FROM anon;
|
|
309
|
+
END IF;
|
|
310
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN
|
|
311
|
+
REVOKE ALL ON TABLE public.aff_invites FROM authenticated;
|
|
312
|
+
REVOKE ALL ON TABLE public.aff_quotas FROM authenticated;
|
|
313
|
+
REVOKE ALL ON TABLE public.aff_codes FROM authenticated;
|
|
314
|
+
REVOKE ALL ON TABLE public.aff_attributions FROM authenticated;
|
|
315
|
+
END IF;
|
|
316
|
+
END
|
|
317
|
+
$grants$;
|
|
318
|
+
|
|
319
|
+
NOTIFY pgrst, 'reload schema';
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profullstack/affiliates",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scarce invite codes (per-user quota, single use, who-invited-whom) plus open affiliate links with first-touch attribution. Commission math and the referral_code cookie from @profullstack/referrals. Memory, SQLite (bun:sqlite, node:sqlite, better-sqlite3), Postgres and Supabase (supabase-js rpc) stores, plus a CLI.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"invite",
|
|
8
|
+
"invite-codes",
|
|
9
|
+
"invite-only",
|
|
10
|
+
"affiliate",
|
|
11
|
+
"referral",
|
|
12
|
+
"attribution",
|
|
13
|
+
"sqlite",
|
|
14
|
+
"postgres",
|
|
15
|
+
"supabase",
|
|
16
|
+
"postgrest",
|
|
17
|
+
"profullstack"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/profullstack/affiliates.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/profullstack/affiliates#readme",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
"./migrations/supabase.sql": "./migrations/supabase.sql"
|
|
35
|
+
},
|
|
36
|
+
"bin": {
|
|
37
|
+
"affiliates": "./dist/cli.js"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"migrations",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && sed -i -E \"s#(from '\\./[a-z-]+)\\.ts'#\\1.js'#\" dist/*.d.ts && chmod +x dist/cli.js",
|
|
47
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
48
|
+
"test": "node --experimental-strip-types --no-warnings --test test/*.test.ts",
|
|
49
|
+
"gen:migration": "node --experimental-strip-types --no-warnings scripts/gen-migration.ts",
|
|
50
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@profullstack/referrals": "^0.1.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^24.0.0",
|
|
57
|
+
"@types/pg": "^8.23.1",
|
|
58
|
+
"pg": "^8.23.1",
|
|
59
|
+
"typescript": "^5.9.3"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=22.13"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
}
|
|
67
|
+
}
|