@chargecarepro/host-auth-sdk 1.1.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/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @chargecarepro/host-auth-sdk
2
+
3
+ 充换电智能运维平台 — 托管应用认证工具包。
4
+
5
+ 托管应用通过此 SDK 与运维平台通信,按需获取 Token,无需直接编写 postMessage 代码。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install @chargecarepro/host-auth-sdk
11
+ ```
12
+
13
+ ## 快速开始
14
+
15
+ ```javascript
16
+ import { HostAuthSDK } from '@chargecarepro/host-auth-sdk'
17
+
18
+ const authSDK = new HostAuthSDK()
19
+
20
+ // 1. 初始化(必须在 IFrame 中运行)
21
+ await authSDK.init({ origin: 'https://nwyw.wodeev.com/' })
22
+
23
+ // 2. 获取 Token
24
+ const { accessToken } = await authSDK.ensureValidToken()
25
+
26
+ // 3. 调用业务 API
27
+ await fetch('/api/v1/work-orders', {
28
+ headers: { Authorization: `Bearer ${accessToken}` }
29
+ })
30
+ ```
31
+
32
+ ## API
33
+
34
+ | 方法 | 返回值 | 说明 |
35
+ |------|--------|------|
36
+ | `init({ origin })` | `Promise<void>` | 初始化 SDK |
37
+ | `getToken()` | `Promise<{accessToken, expiresIn, userId}>` | 获取 Token |
38
+ | `ensureValidToken()` | `Promise<{accessToken, expiresIn, userId}>` | 获取有效 Token(自动续期) |
39
+ | `getUserProfile()` | `Promise<object>` | 获取用户资料 |
40
+ | `call({ url, method, data, params, headers })` | `Promise<any>` | 调用业务 API,自动注入 Token,401 自动续期重试 |
41
+ | `destroy()` | `void` | 销毁实例 |
42
+
43
+ ### call() 示例
44
+
45
+ ```javascript
46
+ // GET 请求
47
+ const list = await sdk.call({
48
+ url: '/api/v1/work-orders',
49
+ method: 'GET',
50
+ params: { page: 1, size: 20 }
51
+ })
52
+
53
+ // POST 请求
54
+ const result = await sdk.call({
55
+ url: '/api/v1/orders',
56
+ method: 'POST',
57
+ data: { name: '测试工单', type: 1 }
58
+ })
59
+ ```
60
+
61
+ ## License
62
+
63
+ UNLICENSED
package/index.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ declare class HostAuthSDK {
2
+ /**
3
+ * 初始化 SDK,校验 IFrame 上下文并注册消息监听
4
+ * @param options.origin - 运维平台地址,如 'https://nwyw.wodeev.com/'
5
+ */
6
+ init(options: { origin: string }): Promise<void>
7
+
8
+ /**
9
+ * 获取当前用户的 AccessToken
10
+ * @returns Promise<{ accessToken: string; expiresIn: number; userId: string }>
11
+ */
12
+ getToken(): Promise<{
13
+ accessToken: string
14
+ expiresIn: number
15
+ userId: string
16
+ }>
17
+
18
+ /**
19
+ * 确保返回有效的 AccessToken(过期时自动续期)
20
+ * @returns Promise<{ accessToken: string; expiresIn: number; userId: string }>
21
+ */
22
+ ensureValidToken(): Promise<{
23
+ accessToken: string
24
+ expiresIn: number
25
+ userId: string
26
+ }>
27
+
28
+ /**
29
+ * 获取当前登录用户完整资料
30
+ * @returns Promise<Record<string, any>>
31
+ */
32
+ getUserProfile(): Promise<Record<string, any>>
33
+
34
+ /**
35
+ * 调用业务 API,自动注入 Token 并处理 401 续期
36
+ * @example
37
+ * const list = await sdk.call({ url: '/api/v1/work-orders', method: 'GET', params: { page: 1 } })
38
+ * const result = await sdk.call({ url: '/api/v1/orders', method: 'POST', data: { name: 'test' } })
39
+ */
40
+ call(options: {
41
+ url: string
42
+ method?: string
43
+ data?: Record<string, any>
44
+ params?: Record<string, any>
45
+ headers?: Record<string, string>
46
+ }): Promise<any>
47
+
48
+ /**
49
+ * 销毁 SDK 实例,移除事件监听
50
+ */
51
+ destroy(): void
52
+ }
53
+
54
+ export { HostAuthSDK }
package/index.js ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * CCP Host Auth SDK v1.0.0
3
+ * 托管应用认证工具包 — 通过 postMessage 与智能运维平台通信获取 Token
4
+ *
5
+ * CJS: const { HostAuthSDK } = require('@chargecarepro/host-auth-sdk')
6
+ * ESM: import { HostAuthSDK } from '@chargecarepro/host-auth-sdk'
7
+ * CDN: <script src="https://nwyw.wodeev.com/sdk/host-auth.js"></script>
8
+ */
9
+ (function (root, factory) {
10
+ if (typeof module !== 'undefined' && module.exports) {
11
+ module.exports = factory()
12
+ } else if (typeof define === 'function' && define.amd) {
13
+ define([], factory)
14
+ } else {
15
+ root.HostAuthSDK = factory().HostAuthSDK
16
+ }
17
+ }(typeof self !== 'undefined' ? self : this, function () {
18
+ 'use strict'
19
+
20
+ var REQUEST_TYPE = 'host-auth.request'
21
+ var RESPONSE_TYPE = 'host-auth.response'
22
+ var TIMEOUT = 10000
23
+
24
+ function generateId() {
25
+ return 'ha_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 9)
26
+ }
27
+
28
+ function HostAuthSDK() {
29
+ var _origin = ''
30
+ var _initialized = false
31
+ var _pending = {}
32
+
33
+ function _handleMessage(event) {
34
+ var data = event.data
35
+ if (!data || data.type !== RESPONSE_TYPE) return
36
+
37
+ var pending = _pending[data.id]
38
+ if (!pending) return
39
+
40
+ clearTimeout(pending.timer)
41
+ delete _pending[data.id]
42
+
43
+ if (data.success) {
44
+ pending.resolve(data.data)
45
+ } else {
46
+ pending.reject(new Error(data.error || '[HostAuthSDK] 请求失败'))
47
+ }
48
+ }
49
+
50
+ function _request(method) {
51
+ if (!_initialized) {
52
+ return Promise.reject(new Error('[HostAuthSDK] 请先调用 init()'))
53
+ }
54
+
55
+ return new Promise(function (resolve, reject) {
56
+ var id = generateId()
57
+
58
+ var timer = setTimeout(function () {
59
+ delete _pending[id]
60
+ reject(new Error('[HostAuthSDK] 请求超时 (' + method + ')'))
61
+ }, TIMEOUT)
62
+
63
+ _pending[id] = { resolve: resolve, reject: reject, timer: timer }
64
+
65
+ window.parent.postMessage(
66
+ { type: REQUEST_TYPE, id: id, method: method },
67
+ '*'
68
+ )
69
+ })
70
+ }
71
+
72
+ this.init = function (options) {
73
+ if (!options || !options.origin) {
74
+ return Promise.reject(new Error('[HostAuthSDK] init 需要传入 origin 参数'))
75
+ }
76
+
77
+ if (window === window.parent) {
78
+ return Promise.reject(
79
+ new Error('[HostAuthSDK] 托管应用须在智能运维平台 IFrame 中运行')
80
+ )
81
+ }
82
+
83
+ _origin = options.origin.replace(/\/+$/, '')
84
+ _initialized = true
85
+
86
+ window.addEventListener('message', _handleMessage)
87
+
88
+ return Promise.resolve()
89
+ }
90
+
91
+ this.getToken = function () {
92
+ return _request('getToken')
93
+ }
94
+
95
+ this.ensureValidToken = function () {
96
+ return _request('ensureValidToken')
97
+ }
98
+
99
+ this.getUserProfile = function () {
100
+ return _request('getUserProfile')
101
+ }
102
+
103
+ /**
104
+ * 调用业务 API,自动注入 Token 并处理 401 续期
105
+ * @param {Object} options
106
+ * @param {string} options.url - API 路径,如 '/api/v1/work-orders'
107
+ * @param {string} [options.method='GET'] - HTTP 方法
108
+ * @param {Object} [options.data] - 请求体(JSON)
109
+ * @param {Object} [options.params] - URL 查询参数
110
+ * @param {Object} [options.headers] - 额外请求头
111
+ * @returns {Promise<any>}
112
+ *
113
+ * @example
114
+ * const list = await sdk.call({ url: '/api/v1/work-orders', method: 'GET', params: { page: 1 } })
115
+ * const result = await sdk.call({ url: '/api/v1/orders', method: 'POST', data: { name: 'test' } })
116
+ */
117
+ this.call = function (options) {
118
+ if (!options || !options.url) {
119
+ return Promise.reject(new Error('[HostAuthSDK] call 需要传入 url 参数'))
120
+ }
121
+
122
+ function doFetch(overrideToken) {
123
+ var token = overrideToken
124
+ function buildHeaders() {
125
+ var h = { 'Content-Type': 'application/json' }
126
+ if (options.headers) {
127
+ for (var k in options.headers) { h[k] = options.headers[k] }
128
+ }
129
+ h['Authorization'] = 'Bearer ' + token
130
+ return h
131
+ }
132
+ // 拼接查询参数
133
+ var url = options.url
134
+ if (options.params) {
135
+ var qs = []
136
+ for (var k in options.params) {
137
+ if (options.params[k] !== undefined && options.params[k] !== null) {
138
+ qs.push(encodeURIComponent(k) + '=' + encodeURIComponent(options.params[k]))
139
+ }
140
+ }
141
+ if (qs.length) url += (url.indexOf('?') === -1 ? '?' : '&') + qs.join('&')
142
+ }
143
+ var init = {
144
+ method: options.method || 'GET',
145
+ headers: buildHeaders()
146
+ }
147
+ if (options.data && init.method !== 'GET') {
148
+ init.body = JSON.stringify(options.data)
149
+ }
150
+ return fetch(url, init).then(function (resp) {
151
+ if (resp.status === 401 && !overrideToken) {
152
+ // Token 失效 → 续期后重试一次
153
+ return _request('ensureValidToken').then(function (tokenData) {
154
+ return doFetch(tokenData.accessToken)
155
+ })
156
+ }
157
+ if (!resp.ok) {
158
+ return resp.json().then(function (body) {
159
+ var err = new Error(body.msg || body.message || '请求失败 (' + resp.status + ')')
160
+ err.status = resp.status
161
+ err.data = body
162
+ throw err
163
+ }).catch(function (e) {
164
+ if (e.status) throw e
165
+ var err2 = new Error('请求失败 (' + resp.status + ')')
166
+ err2.status = resp.status
167
+ throw err2
168
+ })
169
+ }
170
+ return resp.json()
171
+ })
172
+ }
173
+
174
+ return _request('ensureValidToken').then(function (tokenData) {
175
+ return doFetch(tokenData.accessToken)
176
+ })
177
+ }
178
+
179
+ this.destroy = function () {
180
+ window.removeEventListener('message', _handleMessage)
181
+
182
+ var keys = Object.keys(_pending)
183
+ for (var i = 0; i < keys.length; i++) {
184
+ var pending = _pending[keys[i]]
185
+ clearTimeout(pending.timer)
186
+ pending.reject(new Error('[HostAuthSDK] SDK 已销毁'))
187
+ }
188
+ _pending = {}
189
+ _initialized = false
190
+ }
191
+ }
192
+
193
+ return { HostAuthSDK: HostAuthSDK }
194
+ }))
package/index.mjs ADDED
@@ -0,0 +1,156 @@
1
+ var REQUEST_TYPE = 'host-auth.request'
2
+ var RESPONSE_TYPE = 'host-auth.response'
3
+ var TIMEOUT = 10000
4
+
5
+ function generateId() {
6
+ return 'ha_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 9)
7
+ }
8
+
9
+ function HostAuthSDK() {
10
+ var _origin = ''
11
+ var _initialized = false
12
+ var _pending = {}
13
+
14
+ function _handleMessage(event) {
15
+ var data = event.data
16
+ if (!data || data.type !== RESPONSE_TYPE) return
17
+
18
+ var pending = _pending[data.id]
19
+ if (!pending) return
20
+
21
+ clearTimeout(pending.timer)
22
+ delete _pending[data.id]
23
+
24
+ if (data.success) {
25
+ pending.resolve(data.data)
26
+ } else {
27
+ pending.reject(new Error(data.error || '[HostAuthSDK] 请求失败'))
28
+ }
29
+ }
30
+
31
+ function _request(method) {
32
+ if (!_initialized) {
33
+ return Promise.reject(new Error('[HostAuthSDK] 请先调用 init()'))
34
+ }
35
+
36
+ return new Promise(function (resolve, reject) {
37
+ var id = generateId()
38
+
39
+ var timer = setTimeout(function () {
40
+ delete _pending[id]
41
+ reject(new Error('[HostAuthSDK] 请求超时 (' + method + ')'))
42
+ }, TIMEOUT)
43
+
44
+ _pending[id] = { resolve: resolve, reject: reject, timer: timer }
45
+
46
+ window.parent.postMessage(
47
+ { type: REQUEST_TYPE, id: id, method: method },
48
+ '*'
49
+ )
50
+ })
51
+ }
52
+
53
+ this.init = function (options) {
54
+ if (!options || !options.origin) {
55
+ return Promise.reject(new Error('[HostAuthSDK] init 需要传入 origin 参数'))
56
+ }
57
+
58
+ if (window === window.parent) {
59
+ return Promise.reject(
60
+ new Error('[HostAuthSDK] 托管应用须在智能运维平台 IFrame 中运行')
61
+ )
62
+ }
63
+
64
+ _origin = options.origin.replace(/\/+$/, '')
65
+ _initialized = true
66
+
67
+ window.addEventListener('message', _handleMessage)
68
+
69
+ return Promise.resolve()
70
+ }
71
+
72
+ this.getToken = function () {
73
+ return _request('getToken')
74
+ }
75
+
76
+ this.ensureValidToken = function () {
77
+ return _request('ensureValidToken')
78
+ }
79
+
80
+ this.getUserProfile = function () {
81
+ return _request('getUserProfile')
82
+ }
83
+
84
+ this.call = function (options) {
85
+ if (!options || !options.url) {
86
+ return Promise.reject(new Error('[HostAuthSDK] call 需要传入 url 参数'))
87
+ }
88
+ function doFetch(overrideToken) {
89
+ var token = overrideToken
90
+ function buildHeaders() {
91
+ var h = { 'Content-Type': 'application/json' }
92
+ if (options.headers) {
93
+ for (var k in options.headers) { h[k] = options.headers[k] }
94
+ }
95
+ h['Authorization'] = 'Bearer ' + token
96
+ return h
97
+ }
98
+ var url = options.url
99
+ if (options.params) {
100
+ var qs = []
101
+ for (var k in options.params) {
102
+ if (options.params[k] !== undefined && options.params[k] !== null) {
103
+ qs.push(encodeURIComponent(k) + '=' + encodeURIComponent(options.params[k]))
104
+ }
105
+ }
106
+ if (qs.length) url += (url.indexOf('?') === -1 ? '?' : '&') + qs.join('&')
107
+ }
108
+ var init = {
109
+ method: options.method || 'GET',
110
+ headers: buildHeaders()
111
+ }
112
+ if (options.data && init.method !== 'GET') {
113
+ init.body = JSON.stringify(options.data)
114
+ }
115
+ return fetch(url, init).then(function (resp) {
116
+ if (resp.status === 401 && !overrideToken) {
117
+ return _request('ensureValidToken').then(function (tokenData) {
118
+ return doFetch(tokenData.accessToken)
119
+ })
120
+ }
121
+ if (!resp.ok) {
122
+ return resp.json().then(function (body) {
123
+ var err = new Error(body.msg || body.message || '请求失败 (' + resp.status + ')')
124
+ err.status = resp.status
125
+ err.data = body
126
+ throw err
127
+ }).catch(function (e) {
128
+ if (e.status) throw e
129
+ var err2 = new Error('请求失败 (' + resp.status + ')')
130
+ err2.status = resp.status
131
+ throw err2
132
+ })
133
+ }
134
+ return resp.json()
135
+ })
136
+ }
137
+ return _request('ensureValidToken').then(function (tokenData) {
138
+ return doFetch(tokenData.accessToken)
139
+ })
140
+ }
141
+
142
+ this.destroy = function () {
143
+ window.removeEventListener('message', _handleMessage)
144
+
145
+ var keys = Object.keys(_pending)
146
+ for (var i = 0; i < keys.length; i++) {
147
+ var pending = _pending[keys[i]]
148
+ clearTimeout(pending.timer)
149
+ pending.reject(new Error('[HostAuthSDK] SDK 已销毁'))
150
+ }
151
+ _pending = {}
152
+ _initialized = false
153
+ }
154
+ }
155
+
156
+ export { HostAuthSDK }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@chargecarepro/host-auth-sdk",
3
+ "version": "1.1.0",
4
+ "description": "充换电智能运维平台 — 托管应用认证工具包。通过 postMessage 与运维平台通信获取 Token。",
5
+ "main": "index.js",
6
+ "module": "index.mjs",
7
+ "types": "index.d.ts",
8
+ "files": [
9
+ "index.js",
10
+ "index.mjs",
11
+ "index.d.ts",
12
+ "README.md"
13
+ ],
14
+ "keywords": [
15
+ "chargecarepro",
16
+ "host-auth",
17
+ "iframe",
18
+ "postmessage",
19
+ "auth",
20
+ "sso"
21
+ ],
22
+ "license": "UNLICENSED",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/chargecarepro/host-auth-sdk"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ }
30
+ }