EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
pax8_sync_standalone.js
Go to the documentation of this file.
1#!/usr/bin/env node
2// Standalone Pax8 product sync - direct database and API access
3const { Pool } = require('pg');
4const axios = require('axios');
5
6const PAX8_API_BASE = 'https://api.pax8.com/v1';
7const PAX8_AUTH_URL = 'https://api.pax8.com/v1/oauth/token';
8const PAX8_CLIENT_ID = 'Kc57c2kDnwisVNia0m3yKOQGoiglaq3d';
9const PAX8_CLIENT_SECRET = 'kFQarde_yooSLBnF-y6ZXohOr_6lZT87GX_SrL7taji-LTaaCmSopvo6nSLodF_M';
10const TENANT_ID = '00000000-0000-0000-0000-000000000001';
11const VENDOR = 'Microsoft';
12
13// Database connection
14const pool = new Pool({
15 host: 'rmm-psa-db-do-user-28531160-0.i.db.ondigitalocean.com',
16 port: 25060,
17 user: 'doadmin',
18 password: 'AVNS_J8RJAmsEwsHFG52_-F2',
19 database: 'defaultdb',
20 ssl: { rejectUnauthorized: false }
21});
22
23console.log('šŸš€ Pax8 Product Sync - Standalone');
24console.log(`Tenant: ${TENANT_ID}`);
25console.log(`Vendor: ${VENDOR}\n`);
26
27/**
28 *
29 */
30async function getAccessToken() {
31 console.log('šŸ” Getting Pax8 access token...');
32
33 try {
34 const response = await axios.post(PAX8_AUTH_URL, {
35 grant_type: 'client_credentials',
36 client_id: PAX8_CLIENT_ID,
37 client_secret: PAX8_CLIENT_SECRET
38 }, {
39 headers: { 'Content-Type': 'application/json' }
40 });
41
42 console.log('āœ“ Access token acquired\n');
43 return response.data.access_token;
44 } catch (error) {
45 console.error('āœ— OAuth2 authentication failed:', error.response?.data || error.message);
46 throw error;
47 }
48}
49
50/**
51 *
52 * @param accessToken
53 * @param vendor
54 */
55async function listProducts(accessToken, vendor) {
56 console.log(`šŸ“¦ Fetching ${vendor} products from Pax8...`);
57
58 try {
59 const response = await axios.get(`${PAX8_API_BASE}/products`, {
60 headers: {
61 'Authorization': `Bearer ${accessToken}`,
62 'Content-Type': 'application/json'
63 },
64 params: { vendor }
65 });
66
67 const products = response.data.content || response.data.products || response.data;
68 console.log(`āœ“ Found ${products.length} products\n`);
69 return products;
70 } catch (error) {
71 console.error('āœ— Failed to fetch products:', error.response?.data || error.message);
72 throw error;
73 }
74}
75
76/**
77 *
78 */
79async function syncProducts() {
80 try {
81 const accessToken = await getAccessToken();
82 const products = await listProducts(accessToken, VENDOR);
83
84 if (!products || products.length === 0) {
85 console.log('No products to sync');
86 return;
87 }
88
89 console.log('šŸ’¾ Syncing products to database...\n');
90
91 let synced = 0;
92 let updated = 0;
93 let errors = 0;
94
95 for (const product of products) {
96 try {
97 const pax8ProductId = product.id || product.productId;
98 const name = product.name || product.productName || 'Unknown Product';
99 const description = product.description || product.productDescription || '';
100 const priceRetail = product.pricing?.retail || product.price || 0;
101 const priceExTax = product.pricing?.cost || priceRetail;
102 const pax8Vendor = product.vendor || VENDOR;
103 const pax8Category = product.category || null;
104 const pax8Subcategory = product.subcategory || product.subCategory || null;
105 const pax8Sku = product.sku || product.productSku || null;
106 const billingTerm = product.billingTerm || product.billing?.term || 'monthly';
107 const isAutoRenew = product.autoRenew !== false;
108 const pax8Metadata = JSON.stringify(product);
109
110 // Check if exists
111 const existingResult = await pool.query(
112 'SELECT product_id FROM products WHERE pax8_product_id = $1',
113 [pax8ProductId]
114 );
115
116 if (existingResult.rows.length > 0) {
117 // Update
118 await pool.query(
119 `UPDATE products SET
120 name = $1, description = $2, price_retail = $3, price_ex_tax = $4,
121 pax8_vendor = $5, pax8_category = $6, pax8_subcategory = $7, pax8_sku = $8,
122 billing_term = $9, is_auto_renew = $10, pax8_metadata = $11,
123 last_synced_at = NOW(), updated_at = NOW()
124 WHERE pax8_product_id = $12`,
125 [name, description, priceRetail, priceExTax, pax8Vendor, pax8Category,
126 pax8Subcategory, pax8Sku, billingTerm, isAutoRenew, pax8Metadata, pax8ProductId]
127 );
128 updated++;
129 console.log(` ↻ Updated: ${name}`);
130 } else {
131 // Insert
132 await pool.query(
133 `INSERT INTO products (
134 tenant_id, name, description, supplier, price_retail, price_ex_tax,
135 is_service, divisible, is_stock,
136 pax8_product_id, pax8_vendor, pax8_category, pax8_subcategory,
137 pax8_sku, billing_term, is_auto_renew, pax8_metadata,
138 is_active, last_synced_at, created_at, updated_at
139 ) VALUES (
140 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,
141 NOW(), NOW(), NOW()
142 )`,
143 [TENANT_ID, name, description, 'Pax8', priceRetail, priceExTax,
144 true, true, false, pax8ProductId, pax8Vendor, pax8Category, pax8Subcategory,
145 pax8Sku, billingTerm, isAutoRenew, pax8Metadata, true]
146 );
147 synced++;
148 console.log(` + Added: ${name}`);
149 }
150
151 } catch (productErr) {
152 errors++;
153 console.error(` āœ— Error: ${product.name}:`, productErr.message);
154 }
155 }
156
157 console.log('\nāœ… Sync Complete!');
158 console.log(` Total: ${products.length}`);
159 console.log(` Added: ${synced}`);
160 console.log(` Updated: ${updated}`);
161 console.log(` Errors: ${errors}`);
162
163 } catch (error) {
164 console.error('\nāŒ Sync failed:', error.message);
165 throw error;
166 } finally {
167 await pool.end();
168 }
169}
170
171// Run
172syncProducts()
173 .then(() => process.exit(0))
174 .catch(err => {
175 console.error(err);
176 process.exit(1);
177 });