import fs from 'node:fs'; const parseUrl = process.env.PARSE_URL || 'http://127.0.0.1:3000/parse'; const appId = process.env.PARSE_APP_ID; const masterKey = process.env.PARSE_MASTER_KEY; const sellerId = process.env.AMAZON_SELLER_ID; const credentialFile = process.env.SP_API_CREDENTIAL_FILE; if (!appId || !masterKey || !sellerId || !credentialFile) { throw new Error('Missing bootstrap environment variables'); } const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId, 'X-Parse-Master-Key': masterKey, }; async function request(path, method = 'GET', body) { const response = await fetch(`${parseUrl}${path}`, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), }); const result = await response.json(); if (!response.ok || result.error) { throw new Error(`${method} ${path}: ${result.error || response.status}`); } return result; } const text = fs.readFileSync(credentialFile, 'utf8'); const clientId = text.match(/客户端编码:\s*(\S+)/)?.[1]; const clientSecret = text.match(/客户端秘钥:\s*(\S+)/)?.[1]; const refreshTokens = [...text.matchAll(/Atzr\|\S+/g)].map(match => match[0]); if (!clientId || !clientSecret || refreshTokens.length !== 6) { throw new Error('SP-API credential file is incomplete'); } const marketplaces = [ [ ['美国', 'ATVPDKIKX0DER', '1', 'NA', 'us-east-1'], ['加拿大', 'A2EUQ1WTGCTBG2', '6', 'NA', 'us-east-1'], ['墨西哥', 'A1AM78C64UM0Y8', '10', 'NA', 'us-east-1'], ['巴西', 'A2Q3Y263D00KWC', '13', 'NA', 'us-east-1'], ], [['新加坡', 'A19VAU5U5O7RUS', '', 'FE', 'us-west-2']], [ ['意大利', 'APJ6JRA9NG5V4', '9', 'EU', 'eu-west-1'], ['法国', 'A13V1IB3VIYZZH', '4', 'EU', 'eu-west-1'], ['爱尔兰', 'A28R8C7NBKEWEA', '', 'EU', 'eu-west-1'], ['西班牙', 'A1RKKUPIHCS9HS', '8', 'EU', 'eu-west-1'], ['波兰', 'A1C3SOZRARQ6R3', '', 'EU', 'eu-west-1'], ['荷兰', 'A1805IZSGTT6HS', '', 'EU', 'eu-west-1'], ['比利时', 'AMEN7PMS3EDWL', '', 'EU', 'eu-west-1'], ['瑞典', 'A2NODRKZP88ZB9', '', 'EU', 'eu-west-1'], ['德国', 'A1PA6795UKMFR9', '3', 'EU', 'eu-west-1'], ['英国', 'A1F83G8C2ARO7P', '2', 'EU', 'eu-west-1'], ], [['阿联酋', 'A2VIGQ35RCS4UG', '11', 'EU', 'eu-west-1']], [['沙特阿拉伯', 'A17E79C6D8DWNP', '14', 'EU', 'eu-west-1']], [['澳大利亚', 'A39IBJ37TRP1C6', '12', 'FE', 'us-west-2']], ]; let created = 0; let updated = 0; for (let groupIndex = 0; groupIndex < marketplaces.length; groupIndex += 1) { for (const [country, marketplaceId, domain, region, spRegion] of marketplaces[groupIndex]) { const where = encodeURIComponent(JSON.stringify({ marketplaceId })); const existing = await request(`/classes/Shop?where=${where}&limit=1`); const payload = { name: `${country} - zzhhxcl`, platform: 'amazon', region, marketplaceId, domain, nodeIds: [], config: { SpApiConfig: { clientId, sellerID: sellerId, clientSecret, refreshToken: refreshTokens[groupIndex], region: spRegion, sandbox: false, }, }, sync_status: 'idle', status: 'active', }; if (existing.results?.[0]?.objectId) { await request(`/classes/Shop/${existing.results[0].objectId}`, 'PUT', payload); updated += 1; } else { await request('/classes/Shop', 'POST', payload); created += 1; } console.log(`Shop ready: ${country} (${marketplaceId})`); } } const adminUsers = await request(`/users?where=${encodeURIComponent(JSON.stringify({ username: 'admin' }))}&limit=1`); const adminUser = adminUsers.results?.[0]; if (!adminUser) throw new Error('Initial admin user was not found'); const roles = await request(`/roles?where=${encodeURIComponent(JSON.stringify({ name: 'admin' }))}&limit=1`); let roleId = roles.results?.[0]?.objectId; if (!roleId) { const role = await request('/roles', 'POST', { name: 'admin', ACL: { '*': { read: false, write: false } }, }); roleId = role.objectId; } await request(`/roles/${roleId}`, 'PUT', { users: { __op: 'AddRelation', objects: [{ __type: 'Pointer', className: '_User', objectId: adminUser.objectId }], }, }); const adminOnly = { 'role:admin': true }; await request('/schemas/Shop', 'PUT', { classLevelPermissions: { find: adminOnly, count: adminOnly, get: adminOnly, create: adminOnly, update: adminOnly, delete: adminOnly, addField: {}, }, }); console.log(JSON.stringify({ created, updated, total: created + updated, adminRole: roleId }));