Skip to content

Commit 8004c1f

Browse files
committed
Merge branch 'master' of github.com:profullstack/generate-pdf-api
2 parents 1dbe020 + 91d8def commit 8004c1f

29 files changed

+1997
-178
lines changed

.github/workflows/deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ jobs:
8888
# Explicitly copy the .env file to the remote server
8989
echo "Copying .env file to remote server..."
9090
scp -P 2048 .env [email protected]:$DEPLOY_REMOTE_DIR/.env
91-
91+
9292
# Run deploy script with migrations
9393
./bin/deploy-with-migrations.sh
94-
94+
9595
# Create a timestamp file to verify deployment
9696
echo "Creating timestamp file to verify deployment..."
9797
TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,5 +401,3 @@ node scripts/test-pdf-generation.js
401401
```
402402

403403
This script will generate a test PDF and output the detected Chrome path.
404-
405-
test2

TODO.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,6 @@ This document outlines planned features and enhancements to provide additional v
316316
- [ ] Data export and import
317317
- [ ] Data audit logging
318318
- [ ] Data access monitoring
319-
319+
320+
321+
test\

bin/apply-migration.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env node
2+
3+
// Apply migration to add subscription fields to users table
4+
import 'dotenv/config';
5+
import { createClient } from '@supabase/supabase-js';
6+
7+
// Access environment variables
8+
const supabaseUrl = process.env.SUPABASE_URL;
9+
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
10+
11+
if (!supabaseUrl || !supabaseServiceRoleKey) {
12+
console.error('Missing Supabase credentials');
13+
process.exit(1);
14+
}
15+
16+
// Create Supabase client with service role key for admin privileges
17+
const supabase = createClient(supabaseUrl, supabaseServiceRoleKey, {
18+
auth: {
19+
autoRefreshToken: false,
20+
persistSession: false
21+
}
22+
});
23+
24+
async function applyMigration() {
25+
console.log('Applying migration: Adding subscription fields to users table...');
26+
27+
try {
28+
// Execute raw SQL to add the new columns
29+
const { data, error } = await supabase.rpc('exec_sql', {
30+
query: `
31+
ALTER TABLE public.users
32+
ADD COLUMN IF NOT EXISTS subscription_id UUID,
33+
ADD COLUMN IF NOT EXISTS subscription_status TEXT,
34+
ADD COLUMN IF NOT EXISTS subscription_plan TEXT;
35+
`
36+
});
37+
38+
if (error) {
39+
throw error;
40+
}
41+
42+
console.log('✅ Migration applied successfully!');
43+
console.log('Added columns: subscription_id, subscription_status, subscription_plan');
44+
45+
// Run the link subscription script again
46+
console.log('\nNow run the link-subscription.js script to link the subscription to the user profile:');
47+
console.log('node bin/link-subscription.js --email [email protected]');
48+
49+
} catch (error) {
50+
console.error('❌ Error applying migration:', error.message);
51+
console.log('\nAlternative approach:');
52+
console.log('1. Log into the Supabase dashboard');
53+
console.log('2. Go to the SQL Editor');
54+
console.log('3. Run the following SQL:');
55+
console.log(`
56+
ALTER TABLE public.users
57+
ADD COLUMN IF NOT EXISTS subscription_id UUID,
58+
ADD COLUMN IF NOT EXISTS subscription_status TEXT,
59+
ADD COLUMN IF NOT EXISTS subscription_plan TEXT;
60+
`);
61+
}
62+
}
63+
64+
applyMigration();

bin/create-test-payment.js

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Script to create test subscriptions directly
5+
*
6+
* Example:
7+
* node bin/create-test-payment.js --email [email protected]
8+
*/
9+
10+
import { supabase } from '../src/utils/supabase.js';
11+
import crypto from 'crypto';
12+
13+
console.log('Supabase configuration:');
14+
console.log('SUPABASE_URL:', process.env.SUPABASE_URL);
15+
console.log('SUPABASE_KEY exists:', !!process.env.SUPABASE_KEY);
16+
console.log('SUPABASE_KEY from process.env:', !!process.env.SUPABASE_KEY);
17+
console.log('SUPABASE_SERVICE_ROLE_KEY from process.env:', !!process.env.SUPABASE_SERVICE_ROLE_KEY);
18+
if (process.env.SUPABASE_KEY) {
19+
console.log('SUPABASE_KEY length:', process.env.SUPABASE_KEY.length);
20+
console.log('Using service role key:', process.env.SUPABASE_KEY === process.env.SUPABASE_SERVICE_ROLE_KEY);
21+
}
22+
console.log('NODE_ENV:', process.env.NODE_ENV);
23+
console.log('All environment variables:');
24+
Object.keys(process.env).forEach(key => {
25+
const value = key.includes('KEY') || key.includes('SECRET') || key.includes('PASSWORD')
26+
? '[REDACTED]'
27+
: process.env[key];
28+
console.log(`${key}: ${value}`);
29+
});
30+
31+
console.log('Creating Supabase client...');
32+
try {
33+
if (supabase) {
34+
console.log('Supabase client created successfully' +
35+
(process.env.SUPABASE_KEY === process.env.SUPABASE_SERVICE_ROLE_KEY ? ' with service role key' : ''));
36+
}
37+
} catch (e) {
38+
console.error('Error creating Supabase client:', e);
39+
process.exit(1);
40+
}
41+
42+
// Simple argument parsing
43+
function parseCommandLineArgs() {
44+
const args = process.argv.slice(2);
45+
const options = {
46+
email: null,
47+
plan: 'monthly',
48+
help: false
49+
};
50+
51+
for (let i = 0; i < args.length; i++) {
52+
const arg = args[i];
53+
54+
if (arg === '--help' || arg === '-h') {
55+
options.help = true;
56+
}
57+
else if (arg.startsWith('--email=')) {
58+
options.email = arg.split('=')[1];
59+
}
60+
else if (arg === '--email' || arg === '-e') {
61+
options.email = args[++i];
62+
}
63+
else if (arg.startsWith('--plan=')) {
64+
options.plan = arg.split('=')[1];
65+
}
66+
else if (arg === '--plan' || arg === '-p') {
67+
options.plan = args[++i];
68+
}
69+
}
70+
71+
return options;
72+
}
73+
74+
// Parse command line arguments
75+
const { email, plan, help } = parseCommandLineArgs();
76+
77+
// Show help if requested or no email provided
78+
if (help || !email) {
79+
console.log(`
80+
Create Test Subscription
81+
=======================
82+
This script creates an active subscription for testing purposes,
83+
bypassing the actual payment process.
84+
85+
Usage:
86+
node bin/create-test-payment.js --email <email> [--plan <plan_type>]
87+
88+
Options:
89+
--email, -e User's email address (required)
90+
--plan, -p Subscription plan: 'monthly' or 'yearly' (default: monthly)
91+
--help, -h Show this help message
92+
93+
Examples:
94+
node bin/create-test-payment.js --email [email protected]
95+
node bin/create-test-payment.js --email [email protected] --plan yearly
96+
`);
97+
process.exit(0);
98+
}
99+
100+
// Validate inputs
101+
if (!['monthly', 'yearly'].includes(plan)) {
102+
console.error(`Error: Invalid plan '${plan}'. Must be 'monthly' or 'yearly'.`);
103+
process.exit(1);
104+
}
105+
106+
async function activateSubscription() {
107+
try {
108+
console.log(`Creating active subscription for ${email} with ${plan} plan...`);
109+
110+
// Calculate expiration date
111+
const now = new Date();
112+
const expirationDate = new Date(now);
113+
if (plan === 'yearly') {
114+
expirationDate.setFullYear(expirationDate.getFullYear() + 1);
115+
} else {
116+
expirationDate.setMonth(expirationDate.getMonth() + 1);
117+
}
118+
119+
// Check if user exists
120+
const { data: userData, error: userError } = await supabase
121+
.from('users')
122+
.select('id')
123+
.eq('email', email)
124+
.maybeSingle();
125+
126+
if (userError) {
127+
console.error('Error checking for user:', userError.message);
128+
process.exit(1);
129+
}
130+
131+
const userId = userData?.id;
132+
if (userId) {
133+
console.log(`Found user with ID: ${userId}`);
134+
} else {
135+
console.log('User not found in database. Will create subscription without user ID.');
136+
}
137+
138+
// First inspect the subscriptions table to see what columns we can use
139+
console.log('Inspecting subscriptions table structure...');
140+
const { data: subSample, error: subSampleError } = await supabase
141+
.from('subscriptions')
142+
.select('*')
143+
.limit(1);
144+
145+
if (subSampleError) {
146+
console.error('Error inspecting subscriptions table:', subSampleError.message);
147+
process.exit(1);
148+
}
149+
150+
// Get column names from database
151+
const subColumns = subSample && subSample.length > 0
152+
? Object.keys(subSample[0])
153+
: ['id', 'email', 'status', 'expiration_date'];
154+
155+
console.log('Available subscription columns:', subColumns.join(', '));
156+
157+
// Set subscription data (only include fields that exist in the schema)
158+
const subscriptionData = {
159+
email,
160+
status: 'active',
161+
};
162+
163+
// Add other fields only if they exist in the schema
164+
if (subColumns.includes('expiration_date')) {
165+
subscriptionData.expiration_date = expirationDate.toISOString();
166+
}
167+
168+
if (subColumns.includes('start_date')) {
169+
subscriptionData.start_date = now.toISOString();
170+
}
171+
172+
if (subColumns.includes('plan')) {
173+
subscriptionData.plan = plan;
174+
}
175+
176+
if (subColumns.includes('interval')) {
177+
subscriptionData.interval = plan === 'monthly' ? 'month' : 'year';
178+
}
179+
180+
if (subColumns.includes('amount')) {
181+
subscriptionData.amount = plan === 'monthly' ? 5.00 : 30.00;
182+
}
183+
184+
if (subColumns.includes('payment_method')) {
185+
subscriptionData.payment_method = 'test';
186+
}
187+
188+
// Add user_id if available and column exists
189+
if (userId && subColumns.includes('user_id')) {
190+
subscriptionData.user_id = userId;
191+
}
192+
193+
// Check if subscription already exists - get the most recent one
194+
const { data: existingSubs, error: subCheckError } = await supabase
195+
.from('subscriptions')
196+
.select('id')
197+
.eq('email', email)
198+
.order('created_at', { ascending: false })
199+
.limit(1);
200+
201+
if (subCheckError) {
202+
console.error('Error checking for existing subscription:', subCheckError.message);
203+
process.exit(1);
204+
}
205+
206+
// Get the first item if it exists
207+
const existingSub = existingSubs && existingSubs.length > 0 ? existingSubs[0] : null;
208+
209+
if (existingSub) {
210+
// Update existing subscription
211+
const { error: updateError } = await supabase
212+
.from('subscriptions')
213+
.update(subscriptionData)
214+
.eq('id', existingSub.id);
215+
216+
if (updateError) {
217+
console.error('Error updating subscription:', updateError.message);
218+
process.exit(1);
219+
}
220+
221+
console.log(`✅ Updated existing subscription (ID: ${existingSub.id})`);
222+
console.log(`✅ Subscription is now active until: ${expirationDate.toISOString()}`);
223+
} else {
224+
// Create new subscription
225+
const { data: newSub, error: createSubError } = await supabase
226+
.from('subscriptions')
227+
.insert(subscriptionData)
228+
.select()
229+
.single();
230+
231+
if (createSubError) {
232+
console.error('Error creating subscription:', createSubError.message);
233+
process.exit(1);
234+
}
235+
236+
console.log(`✅ Created new subscription with ID: ${newSub.id}`);
237+
console.log(`✅ Subscription is active until: ${expirationDate.toISOString()}`);
238+
}
239+
console.log('Test subscription creation successful!');
240+
} catch (error) {
241+
console.error('Error creating test subscription:', error.message);
242+
process.exit(1);
243+
}
244+
}
245+
246+
activateSubscription();

0 commit comments

Comments
 (0)