add dcloud-renew
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -78,6 +78,7 @@ _desktop.ini
|
|||||||
.thumbnails
|
.thumbnails
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
|||||||
1
dcloud-renew/.gitignore
vendored
Normal file
1
dcloud-renew/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
73
dcloud-renew/package-lock.json
generated
Normal file
73
dcloud-renew/package-lock.json
generated
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"name": "dcloud-renew",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "dcloud-renew",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"playwright": "^1.62.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "17.4.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.4.2.tgz",
|
||||||
|
"integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright": {
|
||||||
|
"version": "1.62.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.62.0.tgz",
|
||||||
|
"integrity": "sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"playwright-core": "1.62.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"playwright": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/playwright-core": {
|
||||||
|
"version": "1.62.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.62.0.tgz",
|
||||||
|
"integrity": "sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"playwright-core": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
dcloud-renew/package.json
Normal file
17
dcloud-renew/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "dcloud-renew",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"description": "",
|
||||||
|
"main": "renew.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"playwright": "^1.62.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
110
dcloud-renew/renew.mjs
Normal file
110
dcloud-renew/renew.mjs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import 'dotenv/config'
|
||||||
|
import { chromium } from 'playwright'
|
||||||
|
|
||||||
|
const email = process.env.UNICLOUD_EMAIL
|
||||||
|
const password = process.env.UNICLOUD_PASSWORD
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
throw new Error('Missing UNICLOUD_EMAIL or UNICLOUD_PASSWORD')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForLoginFrame (page, timeoutMs = 30000) {
|
||||||
|
const start = Date.now()
|
||||||
|
while (Date.now() - start < timeoutMs) {
|
||||||
|
const f = page.frames().find(fr => fr.url().includes('account.dcloud.net.cn'))
|
||||||
|
if (f) return f
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
}
|
||||||
|
throw new Error('Timed out waiting for account.dcloud.net.cn login iframe')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForUrl (page, predicate, timeoutMs = 30000) {
|
||||||
|
const start = Date.now()
|
||||||
|
while (Date.now() - start < timeoutMs) {
|
||||||
|
if (predicate(page.url())) return page.url()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
}
|
||||||
|
throw new Error(`Timed out waiting for URL condition. Current: ${page.url()}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
const browser = await chromium.launch({
|
||||||
|
channel: 'msedge',
|
||||||
|
headless: false,
|
||||||
|
slowMo: 200
|
||||||
|
})
|
||||||
|
|
||||||
|
const context = await browser.newContext()
|
||||||
|
const page = await context.newPage()
|
||||||
|
page.setDefaultTimeout(30000)
|
||||||
|
|
||||||
|
await page.goto('https://unicloud.dcloud.net.cn/', {
|
||||||
|
waitUntil: 'domcontentloaded',
|
||||||
|
timeout: 60000
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Page loaded, waiting for login iframe...')
|
||||||
|
|
||||||
|
// 1. Login via the account.dcloud.net.cn iframe.
|
||||||
|
const loginFrame = await waitForLoginFrame(page)
|
||||||
|
await loginFrame.waitForSelector('input.uni-input-input', { timeout: 20000 })
|
||||||
|
await page.waitForTimeout(1500)
|
||||||
|
|
||||||
|
const inputs = loginFrame.locator('input.uni-input-input')
|
||||||
|
if ((await inputs.count()) < 2) {
|
||||||
|
throw new Error('Expected 2 inputs in login iframe')
|
||||||
|
}
|
||||||
|
await inputs.nth(0).click({ clickCount: 3 })
|
||||||
|
await inputs.nth(0).fill(email)
|
||||||
|
await inputs.nth(1).click({ clickCount: 3 })
|
||||||
|
await inputs.nth(1).fill(password)
|
||||||
|
|
||||||
|
await loginFrame.getByText('登录', { exact: true }).click()
|
||||||
|
|
||||||
|
await waitForUrl(page, u => u.includes('unicloud.dcloud.net.cn') && !u.includes('/login/login'))
|
||||||
|
await page.waitForTimeout(5000)
|
||||||
|
console.log('Login successful. URL:', page.url())
|
||||||
|
|
||||||
|
// 2. Click "续费" — opens a NEW TAB that first goes through SSO
|
||||||
|
// (uni-trade.dcloud.net.cn/pages/login/login?oauthToken=...) then lands
|
||||||
|
// on the create-order page.
|
||||||
|
const renewBtn = page.getByText('续费', { exact: true }).first()
|
||||||
|
await renewBtn.waitFor({ timeout: 30000 })
|
||||||
|
const newPagePromise = new Promise(resolve => context.once('page', resolve))
|
||||||
|
await renewBtn.click()
|
||||||
|
const orderPage = await newPagePromise
|
||||||
|
|
||||||
|
// Wait for the OAuth redirect to settle on create-order.
|
||||||
|
await waitForUrl(
|
||||||
|
orderPage,
|
||||||
|
u => u.includes('uni-trade.dcloud.net.cn') && u.includes('create-order'),
|
||||||
|
45000
|
||||||
|
)
|
||||||
|
await orderPage.waitForTimeout(3000)
|
||||||
|
console.log('Order page ready:', orderPage.url())
|
||||||
|
|
||||||
|
// 3. Click "立即购买" — same tab navigates to /order-payment.
|
||||||
|
const buyBtn = orderPage.getByText('立即购买', { exact: true }).first()
|
||||||
|
await buyBtn.waitFor({ timeout: 30000 })
|
||||||
|
await buyBtn.click()
|
||||||
|
|
||||||
|
await waitForUrl(
|
||||||
|
orderPage,
|
||||||
|
u => u.includes('order-payment'),
|
||||||
|
30000
|
||||||
|
)
|
||||||
|
await orderPage.waitForTimeout(3000)
|
||||||
|
console.log('Payment page ready:', orderPage.url())
|
||||||
|
|
||||||
|
// 4. Click "确认开通" to finalize.
|
||||||
|
const confirmBtn = orderPage.getByText('确认开通', { exact: true }).first()
|
||||||
|
await confirmBtn.waitFor({ timeout: 30000 })
|
||||||
|
await confirmBtn.click()
|
||||||
|
console.log('Clicked 确认开通. Renewal submitted.')
|
||||||
|
|
||||||
|
// Wait briefly for the confirmation redirect to complete, then exit.
|
||||||
|
await orderPage.waitForTimeout(5000)
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
process.exit(0)
|
||||||
|
})()
|
||||||
@@ -78,6 +78,7 @@ _desktop.ini
|
|||||||
.thumbnails
|
.thumbnails
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user