efront 4.5.0 → 4.5.4

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.
@@ -1,3 +1,33 @@
1
+ - zh-CN: 端口异常
2
+ en: Port abnormality
3
+
4
+ - zh-CN: 分配地址失败
5
+ en: Address allocation failed
6
+
7
+ - zh-CN: 电子邮箱地址应包含符号“@”
8
+ en: "The email address should contain the symbol \"@\""
9
+
10
+ - zh-CN: "@符号前应有内容"
11
+ en: "There should be content before the symbol \"@\""
12
+
13
+ - zh-CN: "@符号后应有内容"
14
+ en: "There should be content after the symbol\"@\""
15
+
16
+ - zh-CN: 私钥
17
+ en: Private key
18
+
19
+ - zh-CN: 证书
20
+ en: certificate
21
+
22
+ - zh-CN: 账户
23
+ en: account
24
+
25
+ - zh-CN: 当前服务器的域名
26
+ en: The domain name of the current server
27
+
28
+ - zh-CN: 订单失效时间
29
+ en: Order expiration time
30
+
1
31
  - zh-CN: 请求无效!
2
32
  en: Request is invalid!
3
33
 
@@ -21,6 +21,7 @@
21
21
  setauth|: options ::setauth-/.well-known/acme-challenge/:token?:token.:thumb
22
22
  unique: options ::unique
23
23
  unique-save: options ::unique-:data
24
+ recert: options ::recert
24
25
  upload: put :path
25
26
  https://www.ip.cn/:
26
27
  iplocation: get https://www.ip.cn/api/index?ip=:ip&type=1
@@ -0,0 +1 @@
1
+ pedit.bind(null, "证书", "cert");
@@ -0,0 +1,9 @@
1
+ plist.bind(null, '接口管理', "cert", refilm`
2
+ *域名/hostname input/253
3
+ 公钥/private gen/600 ${async function (data) {
4
+ var [private_key, public_key] = await acme2.createKeyPair();
5
+ data.private = private_key;
6
+ data.public = public_key;
7
+ }}
8
+ -私钥/public text
9
+ $证书/cert text`, '/cert/edit');
@@ -46,10 +46,8 @@
46
46
  border: none;
47
47
  }
48
48
  </style>
49
- <div -if="!enabled">
50
- 请在支持window.crypto.subtle的浏览器上使用此页面的功能
51
- </div>
52
- <form on-submit="nosubmit" #box>
49
+ <div -if="!enabled"></div>
50
+ <form on-submit="nosubmit" renderid=box>
53
51
  <xmenu head #menubar -src="(m,i) in menus" @active="activeMenu(m,i)"></xmenu>
54
52
  <div body #body>
55
53
  <div info>${i18n`本页使用的证书服务来自`}<a target="_blank" href="${i18n`https://letsencrypt.org/zh-cn/`}">Let's
@@ -66,9 +64,8 @@
66
64
  </div>
67
65
  </form>
68
66
  <script>
69
- var subtle = window.crypto?.subtle;
70
67
  var enabled = acme2.enabled;
71
- if (!enabled) return;
68
+ if (!enabled) return ["请在支持window.crypto.subtle的浏览器上使用此页面的功能"];
72
69
  var form = view;
73
70
  var activeIndex = 0;
74
71
  var nosubmit = e => e.preventDefault();
@@ -79,14 +76,20 @@
79
76
  var loadUnique = async function () {
80
77
  unique = await data.from("unique");
81
78
  await acme2.makeUnique(unique);
82
- if (acme2.orders?.length) activeMenu(menus[2], 2);
83
- else if (acme2.kid) activeMenu(menus[1], 1);
79
+ if (menuIndex === 0) {
80
+ if (acme2.orders?.length) activeMenu(menus[2], 2);
81
+ else if (acme2.kid) activeMenu(menus[1], 1);
82
+ }
84
83
  extend(formdata, acme2);
85
84
  };
85
+
86
86
  var publicKey, privateKey;
87
- loadUnique().then(render.digest).then(lazy(function () {
87
+ loadUnique().then(render.digest).then(lazy(async function () {
88
88
  box.reshape();
89
89
  bar.reshape();
90
+ var certlist = await plist.load('cert');
91
+ formdata.domain = certlist.map(c => c.hostname).join("\r\n");
92
+ render.digest();
90
93
  }));
91
94
  var a = button;
92
95
  var xmenu = menu;
@@ -114,7 +117,7 @@
114
117
  await saveUnique();
115
118
  }
116
119
  })
117
- }}
120
+ }}
118
121
  *邮箱/email email
119
122
  /termsOfServiceAgreed/同意 checker ${{
120
123
  async"《服务条款》"(data) {
@@ -148,7 +151,6 @@
148
151
  async 创建订单(actived) {
149
152
  var params = submit(actived.fields, formdata);
150
153
  var order = await acme2.newOrder(params);
151
- formdata.orders = acme2.orders;
152
154
  await saveUnique();
153
155
  activeMenu(menus[2], 2);
154
156
  }
@@ -158,8 +160,16 @@
158
160
  name: "订单信息",
159
161
  src: '/cert/orders',
160
162
  get params() {
161
- return formdata.orders;
163
+ return { orders: acme2.orders, saveUnique };
162
164
  }
165
+ },
166
+ {
167
+ name: "证书列表",
168
+ src: "/cert/list",
169
+ },
170
+ {
171
+ name: "更新服务",
172
+ src: "/cert/update",
163
173
  }
164
174
  ];
165
175
 
@@ -170,12 +180,9 @@
170
180
  m.actived = true;
171
181
  actived = m;
172
182
  activeIndex = i;
183
+ state({ index: i })
173
184
  };
174
- activeMenu(menus[0], 0);
175
- var normalList = plist.bind(null, '接口管理', "cert", refilm`
176
- * 域名 / hostname input / 190
177
- 更新连接 / url text / 290
178
- 密码 / password input / 290
179
- `, '/cert/edit');
185
+ var menuIndex = state().index || 0;
186
+ activeMenu(menus[menuIndex], menuIndex);
180
187
 
181
188
  </script>
@@ -10,22 +10,11 @@
10
10
 
11
11
  &>xmenu[horizonal] {
12
12
  width: 100%;
13
- margin-bottom: -2px;
13
+ margin-bottom: -1px;
14
14
  z-index: 1;
15
15
  border-bottom: none;
16
16
  box-shadow: none;
17
-
18
- &:after {
19
- content: "";
20
- display: block;
21
- position: absolute;
22
- left: 0;
23
- bottom: 0;
24
- right: 0;
25
- height: 0;
26
- border-bottom: --border;
27
- z-index: -1;
28
- }
17
+ background: none;
29
18
 
30
19
  .actived {
31
20
  background: #fff;
@@ -67,26 +56,36 @@
67
56
  @status(invalid, #c24);
68
57
  </style>
69
58
  <xmenu -src="o of orders" @active="openOrder(o)"></xmenu>
70
- <form -if="order?.oid" onsubmit="event.preventDefault()">
59
+ <div -if="!order">
60
+ 暂无订单
61
+ </div>
62
+ <form -elseif="order.oid" onsubmit="event.preventDefault()">
71
63
  <div body>
72
64
  <field -repeat="f in fields" _src="[f,order]"></field>
73
65
  </div>
74
66
  <div foot>
75
- <button -if="order.authorizations?.length" @click="auditOrder(order)">提审</button>
76
- <button -if="order.finalize">定案</button>
67
+ <button -if="order?.removable" class="danger" confirm @click="dropOrder(order)">删除订单</button>
68
+ <button -if="order.status==='processing'" @click="openOrder(order)">刷新</button>
69
+ <button -if="order.status==='pending'" @click="auditOrder(order)">提交审核</button>
70
+ <button -if="order.status==='ready'" @click="finalize(order)">请求证书</button>
71
+ <button -if="order.status==='valid'" @click="see(order)">查看证书</button>
72
+ <button -if="order.shouldupload" @click="upload(order)">上传到服务器</button>
77
73
  </div>
78
74
  </form>
75
+ <div -else>
76
+ 订单异常
77
+ </div>
79
78
  <script>
80
79
  var fields = refilm`
81
- $状态/status radio [待定/pending,完毕/ready,审核中/processing,生效/valid,无效/invalid]
82
- $证书/certificate anchor
80
+ $状态/status radio [未审核/pending,审核通过/ready,生成中/processing,生效/valid,无效/invalid]
81
+ $证书/certificate/下载 anchor
83
82
  $域名/identifiers ${function (elem) {
84
83
  var { field, data } = elem;
85
84
  var value = data[field.key];
86
85
  if (value instanceof Array) elem.innerHTML = value.map(v => `${v.value} (${v.type})`).join("<br/>")
87
86
  else elem.innerHTML = '';
88
87
  }}
89
- $失效时间/expires ${function (elem) {
88
+ $订单失效时间/expires ${function (elem) {
90
89
  var { field, data } = elem;
91
90
  elem.innerHTML = filterTime(data[field.key]);
92
91
  }}
@@ -100,9 +99,9 @@
100
99
  };
101
100
 
102
101
  function main(params) {
103
- var orders = params || [];
102
+ var { orders = [], saveUnique } = params;
104
103
  orders = orders.map(acme2.parseOrder);
105
- orders[0].actived = true;
104
+ if (orders.length) orders[0].actived = true;
106
105
  var page = document.createElement('orders');
107
106
  page.innerHTML = template;
108
107
  var scope = {
@@ -112,28 +111,106 @@
112
111
  orders,
113
112
  order: null,
114
113
  async openOrder(o) {
115
- this.order = o;
116
- if (!o.oid) return;
117
- var order = await acme2.getOrder(orders[0]);
114
+ o = this.order = shallowClone(o);
115
+ if (!o || !o.oid) return;
116
+ var order = await acme2.getOrder(o);
118
117
  extend(o, order);
119
118
  if (o !== this.order) return;
119
+ if (o.certificate && !o.shouldupload) {
120
+ var identifiers = o.identifiers;
121
+ var d = await pedit.query("cert", identifiers[identifiers.length - 1].value);
122
+ if (d && d.oid === o.oid && !d.cert) {
123
+ o.shouldupload = true;
124
+ }
125
+ else {
126
+ o.removable = true;
127
+ }
128
+ }
129
+ if (o.status === 'invalid') {
130
+ o.removable = true;
131
+ }
132
+ else {
133
+ if (o.certificate && +o.expires < Date.now()) {
134
+ o.removable = true;
135
+ }
136
+ }
120
137
  this.order = shallowClone(o);
121
138
  },
122
139
  async auditOrder(o) {
123
140
  a: for (var a of o.authorizations) {
124
- var o = await acme2.audit(a);
125
- if (o.challenges) {
126
- for (var c of o.challenges) {
141
+ var b = await acme2.audit(a);
142
+ if (b.challenges) {
143
+ for (var c of b.challenges) {
127
144
  if (c.type === 'http-01') {
145
+ if (c.status !== 'pending') continue a;
128
146
  await data.from("setauth", { token: c.token, thumb: await acme2.thumbprint() });
129
- await data.fromURL(c.url);
147
+ await acme2.requestURL(c.url, {});
130
148
  continue a;
131
149
  }
132
150
  }
133
151
  }
134
- alert("无法找到可用的提审方案" + o.identifier?.value, 'warn');
152
+ alert("无法找到可用的提审方案" + b.identifier?.value, 'warn');
153
+ }
154
+ await wait(600);
155
+ if (this.order === o) await this.openOrder(this.order);
156
+ },
157
+ async finalize(o) {
158
+ var domains = o.identifiers.map(d => d.value);
159
+ var kp = await acme2.createKeyPair();
160
+ var csr = await acme2.createCSR(domains, kp[0]);
161
+ var order = await acme2.requestURL(o.finalize, { csr });
162
+ order = extend({}, o, order);
163
+ if (o === this.order) o = this.order = order;
164
+ render.digest();
165
+ await wait(600);
166
+ var order = await acme2.getOrder(o);
167
+ var cert = order.certificate;
168
+ if (!cert) {
169
+ order.shouldupload = true;
170
+ }
171
+ else {
172
+ cert = await data.fromURL(cert);
173
+ }
174
+ for (var d of domains) {
175
+ pedit.update("cert", d, { hostname: d, private: kp[0], public: kp[1], cert, oid: o.oid })
176
+ }
177
+ if (this.order === o) this.order = extend({}, o, order);
178
+ },
179
+ async dropOrder(o) {
180
+ for (var cx = 0, dx = orders.length; cx < dx; cx++) {
181
+ if (orders[cx].oid === o.oid) break;
182
+ };
183
+ if (cx < 0) return;
184
+ orders.splice(cx, 1);
185
+ acme2.orders.splice(cx, 1);
186
+ if (cx >= orders.length) cx = orders.length - 1;
187
+ await saveUnique();
188
+ this.openOrder(orders[cx]);
189
+ },
190
+ async see(o) {
191
+ var content = `<div head>${o.identifiers.map(d => d.value).join(', ')}<close @click=remove()></close></div><loading -if='cert.loading'></loading><div body style='font-size:10px;font-family:Consolas, "Courier New", monospace;white-space:pre;' -bind='cert.data'></div><scrollbar y></scrollbar>`;
192
+ var e = view();
193
+ css(e, 'position:absolute;width:366px;');
194
+ e.innerHTML = content;
195
+ drag.on(e.firstElementChild, e);
196
+ resize.on(e);
197
+ renderWithDefaults(e, {
198
+ cert: data.fromURL(o.certificate),
199
+ scrollbar,
200
+ remove() {
201
+ remove(e);
202
+ }
203
+ })
204
+ popup(e);
205
+ move.setPosition(e, [.5, .5]);
206
+ },
207
+ async upload(o) {
208
+ var cert = await data.fromURL(o.certificate);
209
+ for (var d of o.identifiers) {
210
+ await pedit.merge("cert", d.value, { cert });
135
211
  }
136
- await this.openOrder(this.order);
212
+ o.shouldupload = false;
213
+ o.removable = true;
137
214
  }
138
215
  }
139
216
  renderWithDefaults(page, scope);
@@ -0,0 +1,7 @@
1
+ <div><button @click="updateCert()">更新证书</button></div>
2
+ <script>
3
+ var updateCert = async function () {
4
+ await data.from("recert");
5
+ alert("更新完成!", 'success');
6
+ };
7
+ </script>
@@ -154,8 +154,9 @@ function cross_(jsonp, digest = noop, method, url, headers) {
154
154
  if (e.type === 'error') {
155
155
  e = createResponse("无法访问服务器!");
156
156
  }
157
+ headers = extend({}, _headers);
157
158
  for (var r of reforms) {
158
- var r = await reform.call(xhr, r, { method, url, status: xhr.status, headers: _headers }, fire, onerror1, e);
159
+ var r = await reform.call(xhr, r, { method, url, status: xhr.status, headers }, fire, onerror1, e);
159
160
  if (r === false) {
160
161
  return;
161
162
  }
@@ -45,8 +45,10 @@ function valid(field, data) {
45
45
  if (e) return e;
46
46
  }
47
47
  if (field.options instanceof Function) {
48
- var e = field.options(data[field.key]);
49
- if (e) return e;
48
+ if (/^(input|text|html|password|raw)/.test(field.type)) {
49
+ var e = field.options(data[field.key]);
50
+ if (e) return e;
51
+ }
50
52
  }
51
53
  return error;
52
54
  }
@@ -514,6 +514,11 @@ Javascript.prototype.detour = function (o, ie) {
514
514
  context = null;
515
515
  return envs;
516
516
  }
517
+ var getfunc = function (o, k) {
518
+ var q = o.queue;
519
+ while (q && (!q.scoped || !q.scoped.used[k])) q = q.queue;
520
+ return q;
521
+ };
517
522
  var context = null, rootenvs = null;
518
523
  function detour(o, ie) {
519
524
  while (o) {
@@ -528,21 +533,30 @@ function detour(o, ie) {
528
533
  var m = /^[^\.\[\]]+/.exec(o.text);
529
534
  if (m) { context.avoidMap[m[0]] = true; }
530
535
  }
531
- if (/\?\./.test(text)) {
532
- if (/\?\.$/.test(text)) {
533
- o = snapExpressHead(o);
534
- var f = snapExpressFoot(o);
535
- var rest = [o];
536
- remove(o, f.prev);
537
- while (o !== f) {
538
- o = o.next;
539
- rest.push(o);
540
- }
541
- text = createString(rest);
536
+ if (/\?\.|\?\?/.test(text)) {
537
+ o = snapExpressHead(o);
538
+ var f = snapExpressFoot(o);
539
+ var rest = [o];
540
+ remove(o, f.prev);
541
+ while (o !== f) {
542
+ o = o.next;
543
+ rest.push(o);
542
544
  }
545
+ text = createString(rest);
543
546
  text = renderExpress(text, false);
544
547
  if (hasdot) text = "..." + text;
545
- o = replace(o, ...scan(text));
548
+ var o1 = scan(text);
549
+ detour(o1.first, ie);
550
+ var s1 = createScoped(o1);
551
+ if (s1.used.this) {
552
+ var s = getfunc(o, 'this').scoped;
553
+ s.used.this.push(...s1.used.this);
554
+ }
555
+ if (s1.used.arguments) {
556
+ var s = getfunc(o, 'arguments').scoped;
557
+ s.used.arguments.push(...s1.used.arguments);
558
+ };
559
+ o = replace(o, ...o1);
546
560
  continue;
547
561
  }
548
562
  text = text.replace(/\.([^\.\[\!\=\:]+)/g, (_, a) => ie === undefined || context.strap_reg.test(a) || /#/.test(a) ? `[${strings.recode(a)}]` : _);
@@ -4,6 +4,7 @@
4
4
  <div body>
5
5
  <field v-if="!f.hidden" -repeat="f in fields" ng-src="[f,data]"></field>
6
6
  </div>
7
+ <scrollbar></scrollbar>
7
8
  <div foot>
8
9
  <btn @click="remove()" class="white">取消</btn>
9
10
  <button type="submit">保存</button>
@@ -11,6 +11,7 @@ function main(title, { submit }, { data: origin, fields, }) {
11
11
  fields,
12
12
  title,
13
13
  origin,
14
+ scrollbar,
14
15
  data: item,
15
16
  remove() {
16
17
  remove(page);
@@ -19,7 +20,7 @@ function main(title, { submit }, { data: origin, fields, }) {
19
20
  on('submit')(page, async function (e) {
20
21
  e.preventDefault();
21
22
  var res = await submit(item, fields, origin);
22
- if (typeof res === 'string') {
23
+ if (typeof res === 'string' && res) {
23
24
  return alert(res, 'error');
24
25
  }
25
26
  if (res === false) return;
@@ -31,7 +32,7 @@ function main(title, { submit }, { data: origin, fields, }) {
31
32
  await submit(item, fields, origin);
32
33
  dispatch(this, 'submited');
33
34
  });
34
- on("append")(page, lazy(function () {
35
+ on("mounted")(page, lazy(function () {
35
36
  page.querySelector("input").focus();
36
37
  }));
37
38
  return page;