1
0
mirror of https://shylinux.com/x/icebergs synced 2025-05-12 07:28:15 +08:00

Compare commits

...

1925 Commits

Author SHA1 Message Date
shy
1f9e8d756f add some 2025-05-04 08:48:56 +08:00
shy
705f3d074e add some 2025-05-02 01:36:47 +08:00
shy
b0c7feb56e add some 2025-04-25 18:23:55 +08:00
shy
7e5cff6b81 add some 2025-03-22 12:54:06 +08:00
shy
6c0e3a0a58 add some 2025-03-15 20:27:05 +08:00
shy
08f2f1626d add some 2025-03-10 18:38:39 +08:00
shy
5260027c9f add some 2025-03-08 08:38:13 +08:00
shy
26e62a1b22 add some 2025-03-05 23:33:47 +08:00
shy
e008f4d417 add some 2025-03-05 08:55:30 +08:00
shy
5fc5ccd0ea add some 2025-03-04 14:52:57 +08:00
shy
35c1407d29 add some 2025-03-03 22:13:05 +08:00
shy
55a0c3d120 add some 2025-03-03 18:39:51 +08:00
shy
968d9389c9 opt some 2025-03-02 22:10:17 +08:00
shy
7fea8e88a4 add some 2025-03-01 11:36:50 +08:00
shy
8fa2f6e8b3 add some 2025-02-28 14:17:28 +08:00
shy
fe1a6d30aa opt some 2025-02-27 17:56:24 +08:00
shy
02962f9d20 add some 2025-02-27 16:39:54 +08:00
shy
612ecdf4a6 add some 2025-02-27 16:29:06 +08:00
shy
c3f37f626b add some 2025-02-27 16:27:42 +08:00
shy
b4627b2975 add some 2025-02-26 21:51:32 +08:00
shy
f3bc5eb1e1 opt some 2025-02-25 21:57:33 +08:00
shy
03990b839e add some 2025-02-25 21:54:41 +08:00
shy
473826664a add some 2025-02-25 12:14:41 +08:00
root
be66627287 add some 2025-02-25 10:52:06 +08:00
shy
030c018877 add some 2025-02-25 09:59:30 +08:00
root
0071ba89d9 add some 2025-02-25 07:32:49 +08:00
shy
c067576dd6 add some 2025-02-23 09:58:37 +08:00
shy
96d812612b add some 2025-02-23 08:03:37 +08:00
root
a33bc88dcc add some 2025-02-19 21:00:50 +08:00
shy
2f715c5233 opt some 2025-02-18 11:26:19 +08:00
root
8a27ce7d66 add some 2025-02-18 11:18:22 +08:00
root
da81284b37 add some 2025-02-17 19:54:36 +08:00
root
bbb0ae9de9 add some 2025-02-16 08:44:53 +08:00
shy
7bb197ed94 add some 2025-02-16 08:27:23 +08:00
shy
bc94d8e1e4 add some 2025-02-15 18:35:15 +08:00
shy
942606d750 add some 2025-02-15 12:32:06 +08:00
shy
c418c8c070 add some 2025-02-13 22:35:33 +08:00
shy
87c96c880d opt some 2025-02-10 23:03:10 +08:00
shy
cdaa2d7984 opt some 2025-02-10 10:11:34 +08:00
root
38b7942c1b add some 2025-02-10 08:30:04 +08:00
root
240a0c88bf add some 2025-02-08 15:00:09 +08:00
root
297c299694 add some 2025-02-08 12:02:50 +08:00
root
324cd9dc57 add some 2025-02-06 16:21:11 +08:00
shy
1829822e43 add some 2025-02-05 17:37:25 +08:00
shy
a8b365dc31 add some 2025-02-05 09:49:32 +08:00
root
b64a3b5725 add some 2025-02-05 09:44:27 +08:00
root
0ef29edbad add some 2025-02-04 14:10:16 +08:00
shy
6fc5cbde02 add publish 2025-02-04 12:01:02 +08:00
shy
85e1f1dcb6 add some 2025-02-04 10:26:51 +08:00
root
cd5882b2ee add some 2025-02-04 08:52:06 +08:00
shy
259aa7639a add some 2025-02-03 20:42:02 +08:00
shy
91f4578d63 add some 2025-02-02 23:54:43 +08:00
root
54211e220d add some 2025-02-02 23:53:03 +08:00
shy
00d0a3f449 add some 2025-01-31 10:26:51 +08:00
root
8782b192e3 add some 2025-01-30 10:49:34 +08:00
root
92a2fcac3c add some 2025-01-29 12:00:59 +08:00
shy
c045033e55 add some 2025-01-25 15:15:48 +08:00
shy
c022852de3 add some 2025-01-23 21:36:46 +08:00
shy
fa73c5c940 add some 2025-01-23 18:59:31 +08:00
shy
0be5c0f6f3 add some 2025-01-22 16:23:02 +08:00
shy
524bbf6430 add some 2025-01-22 16:12:23 +08:00
root
50ba5c3a3f add some 2025-01-22 15:32:30 +08:00
shy
d9fcacd679 opt some 2025-01-17 21:18:36 +08:00
root
c661ed4f12 add some 2025-01-14 08:42:01 +08:00
root
193a2ab917 add some 2025-01-13 18:02:50 +08:00
shy
aa756c2bc3 add some 2025-01-12 21:53:42 +08:00
shy
7551a8dfab add some 2025-01-10 15:04:47 +08:00
shy
be8c60d97c add some 2025-01-06 21:38:46 +08:00
root
61b81cf089 add some 2025-01-05 20:36:15 +08:00
shy
813ec2b17a add some 2025-01-05 12:45:21 +08:00
shy
d4c73ce195 add some 2025-01-05 12:01:27 +08:00
shy
472f72889a add some 2025-01-03 12:36:17 +08:00
shy
a3e4861989 add some 2025-01-02 20:48:29 +08:00
root
18d65c81e5 add some 2025-01-02 20:42:58 +08:00
root
f59b7cc461 add some 2025-01-02 18:29:31 +08:00
shy
ec961b40fb add some 2025-01-01 09:49:06 +08:00
shy
a075aa7975 add some 2025-01-01 09:23:30 +08:00
root
287083f9ca add some 2024-12-31 19:22:00 +08:00
shy
2e0131a331 add some 2024-12-31 12:33:48 +08:00
root
7dcf68b0d8 add some 2024-12-16 16:02:46 +08:00
shy
66033654c5 add some 2024-12-08 22:27:31 +08:00
shy
6540f3f3f1 add some 2024-12-03 22:45:07 +08:00
shy
37fe3b44b0 add some 2024-11-28 12:32:02 +08:00
shy
bcdce97856 add some 2024-11-26 23:12:53 +08:00
shy
ff0590852d add some 2024-11-25 10:02:13 +08:00
shy
a352374bb4 add some 2024-11-24 07:46:53 +08:00
shy
d30b4e2034 add some 2024-11-23 16:51:24 +08:00
shy
131858f4b1 opt some 2024-11-23 10:11:56 +08:00
shy
e042dc832c add some 2024-11-20 22:49:56 +08:00
shy
8f10a46fb5 add some 2024-11-19 09:32:17 +08:00
root
313b7d4c95 add some 2024-11-18 09:11:37 +08:00
shy
f8af90a71e add some 2024-11-15 20:09:08 +08:00
root
52b47dee2a add some 2024-11-15 09:56:30 +08:00
root
5776c42f7b add some 2024-11-10 13:07:07 +08:00
root
73c32ccc9b add some 2024-11-09 11:35:33 +08:00
root
f9932b5dba add some 2024-11-04 10:27:19 +08:00
root
5afc3781a6 add some 2024-10-25 17:49:24 +08:00
root
dc242dfa54 add some 2024-10-22 07:35:48 +08:00
root
e02517a57e add some 2024-10-20 22:55:19 +08:00
root
f6a009c7fa add some 2024-10-20 08:16:21 +08:00
root
be0a295b52 add some 2024-10-18 14:45:24 +08:00
root
39a5ce360e add some 2024-10-18 14:08:56 +08:00
root
928666d2c9 add some 2024-10-17 23:45:20 +08:00
root
51ae44aeb3 add some 2024-10-17 12:13:39 +08:00
root
d3ba62cc61 add some 2024-10-17 09:38:54 +08:00
root
07111495dd add some 2024-10-16 11:18:41 +08:00
shy
e34752141a opt some 2024-10-16 10:27:26 +08:00
root
95b45c2f15 add some 2024-10-15 18:25:37 +08:00
root
b2b4616ec1 add some 2024-10-13 18:25:36 +08:00
root
1060a60a5e add some 2024-10-12 11:06:29 +08:00
shy
4d892e03d3 add some 2024-10-10 22:23:51 +08:00
shy
ca734d3baf add some 2024-10-09 14:41:19 +08:00
shy
2deff32468 add some 2024-10-06 22:19:03 +08:00
shy
ed39ff23e7 add some 2024-10-04 12:23:39 +08:00
shy
d4f4754be8 add some 2024-10-02 22:24:09 +08:00
shy
80807ed1d2 add some 2024-10-01 00:12:59 +08:00
shy
c4b9641a5e add some 2024-09-27 23:58:19 +08:00
shy
0bff96f485 add some 2024-09-25 17:16:22 +08:00
shy
7ab38ec81f add some 2024-09-24 13:10:56 +08:00
shy
56b87e9e78 add some 2024-09-20 20:53:41 +08:00
shy
f174577bde opt some 2024-09-19 21:09:34 +08:00
shy
ed65d194e6 add some 2024-09-18 20:40:55 +08:00
shy
15a79273ac add some 2024-09-18 13:32:50 +08:00
shy
0eac777f6f add some 2024-09-14 00:36:42 +08:00
shy
0f8a77af25 add some 2024-09-10 08:06:53 +08:00
shy
9a192b012f add some 2024-09-07 17:11:52 +08:00
shy
4b27054210 add some 2024-09-05 20:02:56 +08:00
shy
8d1374149c add some 2024-09-04 07:54:59 +08:00
shy
768bca93b1 add some 2024-09-03 22:42:25 +08:00
shy
f45d784af7 add some 2024-09-03 18:29:15 +08:00
shy
329f963a38 add some 2024-09-03 07:35:53 +08:00
shy
53c5a80da6 add some 2024-09-02 17:34:07 +08:00
shy
b1d5d09a80 add some 2024-08-30 21:56:33 +08:00
shy
4a5256c19f add some 2024-08-29 11:09:33 +08:00
shy
e3fb897137 add some 2024-08-26 22:15:14 +08:00
shy
b1bd23cc26 add some 2024-08-24 20:22:51 +08:00
shy
67b1e41db9 add some 2024-08-18 02:26:42 +08:00
shy
ce0735856e add some 2024-08-16 07:53:33 +08:00
jingganjiaoyu
a0ef75490c opt some 2024-08-15 11:48:38 +08:00
jingganjiaoyu
5891f4f621 opt some 2024-08-15 11:38:51 +08:00
shy
d4a5e4df44 add some 2024-08-12 21:06:51 +08:00
shy
25df349028 add some 2024-08-09 17:58:17 +08:00
shy
618218247b add some 2024-08-09 00:04:14 +08:00
shy
15a4603a18 add some 2024-08-06 19:10:22 +08:00
shy
2f33ff38de add some 2024-08-06 01:35:21 +08:00
shy
972c9fc97f opt some 2024-08-05 23:47:16 +08:00
shy
41cea7f8b0 add some 2024-08-05 23:20:52 +08:00
shy
5a4b79b4eb add some 2024-08-05 02:10:09 +08:00
shy
a7014de914 add some 2024-08-04 14:55:09 +08:00
shy
eb73841a72 add some 2024-08-04 01:49:24 +08:00
shy
704519e336 add some 2024-08-02 18:41:44 +08:00
shy
2f4e941aa0 add some 2024-07-31 22:20:28 +08:00
shy
e29a4fa6b8 add some 2024-07-29 22:49:54 +08:00
shy
af1a9be68e opt portal 2024-07-28 23:21:54 +08:00
shy
a5cd77e87d add some 2024-07-26 21:38:07 +08:00
shy
ccfffd917c opt some 2024-07-24 20:04:32 +08:00
shy
fb99d27694 add some 2024-07-23 23:42:35 +08:00
shy
805cfbcc25 add some 2024-07-23 16:04:42 +08:00
shy
170365ddab add some 2024-07-22 22:45:41 +08:00
shy
271e743c3c add some 2024-07-21 14:50:43 +08:00
shy
d401e8ec2c add some 2024-07-20 20:50:25 +08:00
shy
daa740ffbe add some 2024-07-20 11:05:14 +08:00
shy
9ba006a1da add some 2024-07-19 18:11:03 +08:00
shy
97bdf84965 add some 2024-07-19 00:07:21 +08:00
shy
555be526e3 add some 2024-07-16 22:24:39 +08:00
shy
06a89de015 add some 2024-07-16 19:50:08 +08:00
shy
2fe571f9d6 add some 2024-07-16 18:44:31 +08:00
shy
36f1fe2a7b add some 2024-07-16 10:54:32 +08:00
shy
7214a38d52 add some 2024-07-15 22:26:35 +08:00
shy
0f87c5ac5e add some 2024-07-15 17:55:57 +08:00
shy
8328b4cb69 add some 2024-07-15 09:36:47 +08:00
shy
0742533ce6 add some 2024-07-14 21:51:14 +08:00
shy
889f221e4b add some 2024-07-14 19:23:14 +08:00
shy
77a2cb1a43 add some 2024-07-12 21:05:19 +08:00
shy
6ff235f13a add some 2024-07-12 16:30:09 +08:00
shy
906183a522 opt some 2024-07-11 20:08:39 +08:00
shy
38d74bc01f add web.team.corporation 2024-07-11 01:54:19 +08:00
shy
7b21b28b98 add some 2024-07-09 19:49:29 +08:00
shy
d3001524f2 add some 2024-07-09 12:23:37 +08:00
shy
b9a8678410 opt some 2024-07-09 10:09:53 +08:00
shy
7b8e933bb9 add some 2024-07-09 09:56:18 +08:00
shy
3e63a8c243 opt some 2024-07-08 19:46:17 +08:00
shy
af0d446abc opt some 2024-07-08 19:45:05 +08:00
shy
01bacc73a2 add some 2024-07-07 18:24:15 +08:00
shy
9bf29af91f add some 2024-07-06 02:04:30 +08:00
shy
8b23ad92e0 add some 2024-07-05 20:51:24 +08:00
shy
cc0a5de7a0 add some 2024-07-04 19:34:59 +08:00
shy
eb69a26017 add some 2024-07-04 03:02:13 +08:00
shy
4dcb03c9dc add some 2024-07-03 19:24:34 +08:00
shy
e6c64b7e1f opt some 2024-07-03 14:31:53 +08:00
shy
27064b6aa9 opt some 2024-07-01 20:46:21 +08:00
shy
178a73ae4f add some 2024-07-01 20:42:27 +08:00
shy
c18f363085 opt some 2024-07-01 10:59:26 +08:00
shy
67b4924b9a add some 2024-07-01 10:40:13 +08:00
shy
cfd5db37ca add some 2024-06-30 19:52:35 +08:00
shy
dd974b0980 add some 2024-06-29 23:57:15 +08:00
shy
6f4d324808 add some 2024-06-28 19:35:26 +08:00
shy
96dd7e96de add some 2024-06-28 18:39:11 +08:00
shy
82c0c14dea add some 2024-06-20 20:49:29 +08:00
shy
1aa04477c4 add some 2024-06-20 17:18:16 +08:00
shy
7862121a3c opt some 2024-06-20 15:41:36 +08:00
shy
755aaf72b0 add some 2024-06-19 09:11:43 +08:00
shy
a77f9207c0 add some 2024-06-18 10:11:45 +08:00
shy
adea456e67 add aaa.asign 2024-06-17 18:27:14 +08:00
shy
d69b895983 opt some 2024-06-15 18:48:26 +08:00
shy
35c8a1ca44 add some 2024-06-15 18:45:56 +08:00
shy
0959289870 add some 2024-06-14 19:33:49 +08:00
shy
d264cd3261 add some 2024-06-13 07:33:58 +08:00
shy
558bf677da add some 2024-06-11 22:02:49 +08:00
shy
57035a9fa5 add some 2024-06-11 11:04:23 +08:00
shy
ee32bd8346 add some 2024-06-09 23:32:10 +08:00
shy
bc6e12a6f0 add some 2024-06-09 22:58:23 +08:00
shy
95ba9fc6cd add some 2024-06-09 20:04:31 +08:00
shy
96713c1e75 add some 2024-06-08 23:06:35 +08:00
shy
74075002f1 add some 2024-06-08 10:19:54 +08:00
shy
fda6e6f969 add some 2024-06-08 08:49:02 +08:00
shy
f30bdb9696 add some 2024-06-08 08:01:15 +08:00
shy
4d8a21fd54 add some 2024-06-08 07:56:24 +08:00
shy
c129026c52 opt some 2024-06-06 00:04:07 +08:00
shy
f8ca54a85e add some 2024-06-06 00:02:01 +08:00
shy
5c63a4b467 add some 2024-06-05 15:32:41 +08:00
shy
9d72dd7890 add some 2024-06-04 23:15:28 +08:00
shy
b5d6f7a94c opt some 2024-06-04 11:42:54 +08:00
shy
24e9a8682f add some 2024-06-03 18:36:07 +08:00
shy
0587d4dd75 add some 2024-06-01 23:04:39 +08:00
shy
019f536248 add some 2024-05-31 19:57:53 +08:00
shy
194fd6f5bc add some 2024-05-30 19:56:43 +08:00
shy
22ccf77a66 add some 2024-05-28 22:42:53 +08:00
shy
745ea23b48 add some 2024-05-28 18:48:36 +08:00
shy
4e03450ce2 add some 2024-05-28 07:31:20 +08:00
shy
13afc46682 add some 2024-05-27 19:32:12 +08:00
shy
d4f9ea0f81 add some 2024-05-27 07:29:32 +08:00
shy
3f945f963a add some 2024-05-26 11:27:48 +08:00
shy
120e65bb92 add some 2024-05-26 08:28:03 +08:00
shy
bfaf7380e1 add some 2024-05-26 08:07:13 +08:00
shy
bd7d558208 add some 2024-05-25 20:47:58 +08:00
shy
ac151623c1 add some 2024-05-25 19:56:50 +08:00
shy
9e1ff8d564 add some 2024-05-24 19:27:27 +08:00
shy
598942a836 add some 2024-05-23 23:41:23 +08:00
shy
7830bf0bf7 add some 2024-05-23 19:16:33 +08:00
shy
8021138a80 add some 2024-05-22 23:07:14 +08:00
shy
41c157c6ed add some 2024-05-22 18:44:30 +08:00
shy
c27d82e83e add some 2024-05-21 23:46:04 +08:00
shy
af91084d59 add some 2024-05-21 19:22:43 +08:00
shy
a27fd93686 add some 2024-05-21 07:39:24 +08:00
shy
f7b4edde23 add some 2024-05-20 20:52:40 +08:00
shy
0bfb8673a5 add some 2024-05-20 01:32:07 +08:00
shy
e6607bcd17 add some 2024-05-19 23:49:45 +08:00
shy
f64b141930 add some 2024-05-19 17:02:57 +08:00
shy
ac3241b168 add some 2024-05-18 21:02:28 +08:00
shy
1bc8a05ca6 add some 2024-05-17 19:39:37 +08:00
shy
272c91f469 add some 2024-05-16 08:19:24 +08:00
shy
f359c438fc add some 2024-05-15 18:40:26 +08:00
shy
82415d742a add some 2024-05-15 15:58:50 +08:00
shy
bfa6ecefb2 add some 2024-05-14 23:27:38 +08:00
shy
6b6e20458b add some 2024-05-14 19:13:36 +08:00
shy
b499d09f37 add some 2024-05-14 00:02:33 +08:00
shy
fd324e5f14 add tutor 2024-05-13 20:33:19 +08:00
shy
95ef705c49 add some 2024-05-13 08:08:28 +08:00
shy
12c66f89bb add some 2024-05-12 23:40:58 +08:00
shy
b31a5f8286 add some 2024-05-12 20:37:10 +08:00
shy
73ecfc0cb6 add some 2024-05-12 15:03:57 +08:00
shy
8b0e3ebe55 add some 2024-05-11 18:21:13 +08:00
shy
bcbfb9aaed add some 2024-05-10 08:04:23 +08:00
shy
b5d3ecf103 add some 2024-05-09 23:14:34 +08:00
shy
209d911e25 add some 2024-05-09 13:25:27 +08:00
shy
960748e223 opt some 2024-05-09 07:46:15 +08:00
shy
eac98f23f5 add some 2024-05-09 07:25:24 +08:00
shy
baca546e49 add some 2024-05-08 00:14:16 +08:00
shy
63cbd6d4c0 add some 2024-05-07 15:21:25 +08:00
shy
d8c381a933 add studio 2024-05-06 23:05:57 +08:00
shy
b8c8d1603e add some 2024-05-05 23:24:50 +08:00
shy
555b91fa8f add some 2024-05-05 17:32:25 +08:00
shy
d71d72aa19 add some 2024-05-04 19:20:32 +08:00
shy
bb99205fdb add some 2024-05-04 00:31:52 +08:00
shy
f7e3dcd9ff add some 2024-05-01 18:25:56 +08:00
shy
582519d290 add some 2024-04-30 22:11:58 +08:00
shy
e2babe49a6 add some 2024-04-30 02:35:03 +08:00
shy
172e734f76 add some 2024-04-29 14:49:00 +08:00
shy
d0de69d086 add some 2024-04-28 23:26:46 +08:00
shy
9ecfc80f03 add some 2024-04-27 21:40:39 +08:00
shy
c39b138277 add some 2024-04-27 20:43:12 +08:00
shy
ed06078ce4 opt some 2024-04-27 18:58:24 +08:00
shy
4dfe6269c5 add some 2024-04-27 18:35:13 +08:00
shy
183faff9ce add some 2024-04-27 09:20:51 +08:00
shy
9bdc7af5e9 add some 2024-04-25 21:11:19 +08:00
shy
90469e941d add some 2024-04-25 12:38:15 +08:00
shy
b98b3052d4 add some 2024-04-24 18:10:13 +08:00
shy
e220f82568 add some 2024-04-24 16:26:42 +08:00
shy
fe89d6346c add some 2024-04-24 10:23:18 +08:00
shy
f0ad76fe71 add some 2024-04-22 18:53:28 +08:00
shy
aec9fcf977 add some 2024-04-22 17:15:39 +08:00
eea57c28a4 opt some 2024-04-22 15:37:39 +08:00
shy
30c457bb07 add some 2024-04-22 15:27:41 +08:00
shy
94c2979c93 add some 2024-04-22 08:27:21 +08:00
f79d9b23d7 add windows 2024-04-22 00:36:57 +08:00
b3959aa68d opt some 2024-04-21 16:27:38 +08:00
shy
b980d48390 add some 2024-04-20 22:42:58 +08:00
shy
cfec29c68d add some 2024-04-20 21:57:46 +08:00
shy
a404a7cb08 add some 2024-04-20 19:26:26 +08:00
shy
cc820576f0 add some 2024-04-20 15:56:24 +08:00
shy
5d95320d1e add some 2024-04-20 07:25:48 +08:00
shy
56d4775fed add some 2024-04-19 19:35:46 +08:00
shy
60602bb7a9 add some 2024-04-19 17:58:11 +08:00
shy
43646c5e3e add some 2024-04-19 12:34:12 +08:00
shy
c04c3f0d1d add some 2024-04-18 23:52:02 +08:00
shy
03e18c9325 add some 2024-04-18 23:35:46 +08:00
shy
0c62da9a08 add some 2024-04-18 22:47:39 +08:00
shy
00362e1197 opt border 2024-04-18 22:15:24 +08:00
shy
eea21aaad8 opt some 2024-04-18 13:54:34 +08:00
shy
08b89ece9f add some 2024-04-18 13:40:45 +08:00
shy
41064a2809 add some 2024-04-18 10:51:38 +08:00
shy
86251fb5b3 add some 2024-04-18 10:48:38 +08:00
shy
b56063a183 add some 2024-04-18 00:10:56 +08:00
shy
65697070f4 opt some 2024-04-17 04:15:19 +08:00
shy
4b4179bd04 opt some 2024-04-16 08:21:24 +08:00
shy
158c738970 add some 2024-04-15 12:25:16 +08:00
shy
1564bb6899 add some 2024-04-15 09:08:39 +08:00
shy
3a2fd6d3bb add some 2024-04-14 23:26:13 +08:00
shy
aff83532dd add some 2024-04-14 19:26:34 +08:00
root
ec780aa64c add some 2024-04-14 16:57:47 +08:00
shy
8d3dbc1dea add some 2024-04-14 12:47:31 +08:00
root
77f10bbe69 add some 2024-04-13 23:18:15 +08:00
shy
0d0718e9e8 add some 2024-04-12 22:21:27 +08:00
shy
0338ef7b46 add some 2024-04-11 23:59:37 +08:00
root
74e72d76b9 add some 2024-04-11 21:58:09 +08:00
root
7de837851e add some 2024-04-11 17:08:19 +08:00
shy
25ac2e3a1b opt some 2024-04-11 12:38:38 +08:00
root
29d7a98ec6 add some 2024-04-10 22:58:38 +08:00
root
f802bda23a add some 2024-04-10 22:38:38 +08:00
shy
2075f6e227 add some 2024-04-10 13:33:14 +08:00
shy
144bacc365 add some 2024-04-10 00:32:48 +08:00
shy
3fee88f2bf add some 2024-04-09 18:25:08 +08:00
shy
74358ff30d add some 2024-04-09 13:19:07 +08:00
shy
7bf5ba9d8e opt some 2024-04-08 23:15:56 +08:00
shy
0d6db1c35e add some 2024-04-08 23:00:33 +08:00
shy
21df7f027d add some 2024-04-08 21:05:36 +08:00
shy
5f3e62cf71 add some 2024-04-08 13:31:10 +08:00
shy
4b09f92531 add some 2024-04-08 11:20:17 +08:00
shy
708ed30f74 add some 2024-04-08 00:04:10 +08:00
shy
d0710dc9e2 add some 2024-04-07 23:14:08 +08:00
shy
6154aa93aa add some 2024-04-07 15:33:13 +08:00
shy
bae35d2ce3 add some 2024-04-07 13:28:38 +08:00
shy
603c5ffb0c opt some 2024-04-07 12:20:00 +08:00
shy
1757e0e524 add some 2024-04-07 12:16:47 +08:00
shy
f310a782c4 opt some 2024-04-07 02:06:43 +08:00
shy
03f3d4d8c3 opt some 2024-04-07 01:39:03 +08:00
shy
feb337ba5b add some 2024-04-07 01:22:50 +08:00
shy
5bd4038d15 add some 2024-04-06 22:32:14 +08:00
shy
81f69d34f8 add some 2024-04-06 15:36:29 +08:00
shy
9a9e1b91fd add some 2024-04-06 15:05:27 +08:00
shy
ef7144c908 add some 2024-04-06 00:23:41 +08:00
shy
b00e5d8fb3 add some 2024-04-05 21:15:30 +08:00
shy
23abaae36b add some 2024-04-05 00:24:17 +08:00
shy
2c9cd04a41 add some 2024-04-04 23:11:40 +08:00
shy
da31659105 add some 2024-04-04 15:34:08 +08:00
shy
a840ff12ea add some 2024-04-04 13:49:03 +08:00
shy
e4d0d98702 add some 2024-04-03 14:15:30 +08:00
shy
fd319e30e4 add some 2024-04-03 02:55:42 +08:00
shy
045f57af43 add some 2024-04-03 01:19:04 +08:00
shy
4ea7d66862 opt some 2024-04-03 00:14:33 +08:00
shy
5735299596 add some 2024-04-02 21:49:19 +08:00
shy
b63cdf740c add some 2024-04-02 20:58:17 +08:00
shy
8caf4ece67 add some 2024-04-02 20:14:46 +08:00
shy
f31e56cb10 add some 2024-04-01 22:10:42 +08:00
shy
a42eb5ac4a add some 2024-04-01 20:08:39 +08:00
shy
0028f79073 add some 2024-03-31 04:43:58 +08:00
shy
9ed01c06a9 add some 2024-03-30 22:26:11 +08:00
shy
d477733e23 add some 2024-03-30 22:09:25 +08:00
shy
ae5db88909 add some 2024-03-30 18:08:35 +08:00
shy
34210fb807 add some 2024-03-29 22:32:01 +08:00
shy
38824867b5 add package 2024-03-28 23:45:02 +08:00
shy
36f92bd9a5 add some 2024-03-28 08:56:12 +08:00
shy
8ed6e6df65 add some 2024-03-27 22:19:09 +08:00
shy
1dc5dbf547 add some 2024-03-27 00:03:30 +08:00
shy
37b26a8bfc add some 2024-03-26 15:32:32 +08:00
shy
4f181981df add some 2024-03-25 22:08:33 +08:00
shy
b1ac4e9b38 add some 2024-03-25 12:39:40 +08:00
shy
a281411239 add some 2024-03-23 13:23:26 +08:00
shy
d3e0e06831 add some 2024-03-23 00:22:17 +08:00
shy
b71cf092a2 add some 2024-03-21 14:44:15 +08:00
shy
bacfa95eed add aliyun 2024-03-21 01:55:58 +08:00
shy
6fb1d394b2 add some 2024-03-20 14:19:54 +08:00
shy
96566bf29d add some 2024-03-20 13:18:55 +08:00
shy
46f57dbbb5 add some 2024-03-20 11:55:42 +08:00
shy
632cc3c276 add some 2024-03-19 23:43:15 +08:00
shy
3d9c99ae31 opt some 2024-03-19 22:22:42 +08:00
shy
e0ca977ba1 add some 2024-03-19 22:21:00 +08:00
shy
d2613f1c16 add some 2024-03-19 11:31:27 +08:00
shy
a874d3b0cc add some 2024-03-18 21:24:22 +08:00
shy
1abaf50c40 add some 2024-03-18 13:28:05 +08:00
shy
06eb4a2582 add some 2024-03-18 00:31:30 +08:00
shy
06f8f8ebe1 add some 2024-03-18 00:12:36 +08:00
shy
06314eb331 add some 2024-03-17 23:55:33 +08:00
shy
b3a5b1052b add some 2024-03-17 22:13:09 +08:00
shy
6cac11467e add some 2024-03-17 17:15:00 +08:00
shy
80aba21fcd add some 2024-03-17 15:40:18 +08:00
shy
ae3c7b2656 add some 2024-03-17 13:27:42 +08:00
shy
45f7f5bc8e add some 2024-03-17 12:28:02 +08:00
shy
3b2299b51f add some 2024-03-17 11:54:32 +08:00
shy
ee93cfe5f4 add some 2024-03-16 20:23:57 +08:00
shy
59159a897a opt some 2024-03-16 19:56:46 +08:00
shy
74b1019a1d add some 2024-03-16 16:20:17 +08:00
shy
832b58197a opt some 2024-03-16 15:25:43 +08:00
shy
f5cbe0739c add some 2024-03-16 14:26:26 +08:00
shy
5aff286771 add some 2024-03-16 12:38:41 +08:00
shy
0a98abcc5f add some 2024-03-15 22:37:20 +08:00
shy
5cfe19e5b2 add some 2024-03-15 21:58:15 +08:00
shy
e4102f6c60 add some 2024-03-15 14:41:08 +08:00
shy
ae10996a7b add some 2024-03-15 13:36:08 +08:00
shy
30ac9411bf add some 2024-03-15 12:31:53 +08:00
shy
640b26b2cc add some 2024-03-15 12:14:06 +08:00
shy
dabdc6e11e add some 2024-03-15 10:33:48 +08:00
shy
2498693ee7 opt some 2024-03-15 00:33:36 +08:00
shy
faa4dfb3cc add some 2024-03-15 00:07:19 +08:00
shy
bf91715f8f add some 2024-03-14 20:58:47 +08:00
shy
da8cc86e3a add some 2024-03-14 15:33:21 +08:00
shy
8d3cc5239e add some 2024-03-14 14:54:58 +08:00
shy
66a529cec0 add some 2024-03-14 14:06:42 +08:00
shy
420c3ed8aa add some 2024-03-14 12:48:14 +08:00
shy
ff4fc43c36 add some 2024-03-14 12:05:58 +08:00
shy
781db5caf2 add some 2024-03-14 00:51:15 +08:00
shy
e3bb0b774d add some 2024-03-13 20:35:39 +08:00
shy
ce5e499ce1 add some 2024-03-13 19:40:30 +08:00
shy
76a8c812b2 add some 2024-03-13 16:15:48 +08:00
shy
8f44941f25 opt some 2024-03-13 13:42:57 +08:00
shy
6ecf44962d add some 2024-03-13 11:56:11 +08:00
shy
4bdc6c8347 add some 2024-03-12 22:25:36 +08:00
shy
f40dfeda4d add some 2024-03-12 21:45:32 +08:00
shy
c2a1e36bda add some 2024-03-12 21:02:33 +08:00
shy
1542a364eb add some 2024-03-12 18:15:54 +08:00
shy
b420249a1e add some 2024-03-12 16:23:20 +08:00
shy
e821a8a104 add some 2024-03-12 13:39:48 +08:00
shy
bd3660f02e add some 2024-03-12 13:32:07 +08:00
shy
340c1f15e3 add some 2024-03-12 10:20:22 +08:00
shy
3ef850c581 add some 2024-03-12 09:52:02 +08:00
shy
0d84e0c9ad opt some 2024-03-11 23:32:05 +08:00
shy
082cf02461 add some 2024-03-11 18:41:37 +08:00
shy
290936eaff add some 2024-03-11 15:52:30 +08:00
shy
d8ad68c2ba add some 2024-03-11 15:12:36 +08:00
shy
a859e8f253 opt some 2024-03-11 13:10:01 +08:00
shy
c8e1644cd2 opt some 2024-03-11 13:00:30 +08:00
shy
aa531b17e2 add some 2024-03-10 19:48:51 +08:00
shy
5fce84de25 add some 2024-03-10 13:51:51 +08:00
shy
564d320c7e add some 2024-03-10 10:52:21 +08:00
shy
cae65f34e8 add some 2024-03-09 21:26:43 +08:00
shy
a446216c75 add some 2024-03-09 19:30:11 +08:00
shy
ca4e7aeb53 add some 2024-03-09 16:27:28 +08:00
shy
b91f83adb5 add some 2024-03-09 15:07:46 +08:00
shy
ec3a976337 add some 2024-03-09 12:23:58 +08:00
shy
0fa3965d6e add some 2024-03-09 09:43:33 +08:00
shy
abcddce974 add some 2024-03-08 23:48:50 +08:00
shy
939de032c1 add some 2024-03-08 21:39:48 +08:00
shy
84fe540a9a add some 2024-03-08 19:01:42 +08:00
shy
956f823558 add some 2024-03-08 11:37:42 +08:00
shy
253811a653 add some 2024-03-08 02:16:55 +08:00
shy
ee88456228 add ssh.cloud.tencent 2024-03-07 20:32:23 +08:00
shy
99c99955d5 add some 2024-03-07 10:39:34 +08:00
shy
4160041d01 add some 2024-03-07 08:19:36 +08:00
shy
f450f44088 add some 2024-03-07 00:03:49 +08:00
shy
a72045154d add some 2024-03-06 23:40:49 +08:00
shy
e6450ddc6c add some 2024-03-06 22:30:52 +08:00
shy
5676810436 add some 2024-03-06 19:48:07 +08:00
shy
0eaafa85ef add some 2024-03-06 19:46:44 +08:00
shy
661f77afc1 add some 2024-03-06 14:45:10 +08:00
shy
a8af5a6797 add some 2024-03-06 12:55:57 +08:00
shy
092a8533be add some 2024-03-06 12:43:07 +08:00
shy
53686b9c4b add some 2024-03-06 01:36:38 +08:00
shy
af80b9e2d4 add some 2024-03-05 22:47:43 +08:00
shy
3627f5f900 add some 2024-03-05 21:24:35 +08:00
shy
01f5f90c2a add some 2024-03-05 19:13:49 +08:00
shy
39518a878c opt some 2024-03-05 17:44:21 +08:00
shy
476880c344 opt some 2024-03-05 17:06:45 +08:00
shy
65dae15efb opt some 2024-03-05 17:00:34 +08:00
shy
e8c68bac4b add some 2024-03-05 16:49:47 +08:00
shy
6d2992cf0b add some 2024-03-05 14:14:31 +08:00
shy
4a777bc166 add some 2024-03-05 14:10:07 +08:00
shy
32ca5c81df opt portal 2024-03-05 13:59:02 +08:00
shy
45493499e9 opt portal 2024-03-05 13:55:23 +08:00
shy
21fdda3335 add some 2024-03-04 23:27:48 +08:00
shy
1f0f934ec0 add some 2024-03-04 19:16:39 +08:00
shy
437ec06692 add some 2024-03-04 16:51:39 +08:00
shy
2c791bef93 add some 2024-03-04 12:45:38 +08:00
shy
89f39ae9a4 add some 2024-03-04 10:48:23 +08:00
shy
1460608658 add some 2024-03-04 02:36:16 +08:00
shy
e8aa547c22 add some 2024-03-03 21:14:38 +08:00
shy
3d845cc0b7 add some 2024-03-03 17:17:41 +08:00
shy
d41585e91c add some 2024-03-03 14:26:42 +08:00
shy
338a0a08c0 add some 2024-03-02 23:42:00 +08:00
shy
57b5fdfed1 add some 2024-03-02 19:03:40 +08:00
shy
a12978d028 add some 2024-03-02 17:26:08 +08:00
shy
72bbad483a add some 2024-03-02 14:57:03 +08:00
shy
fbb1fe4413 add some 2024-03-01 21:21:58 +08:00
shy
7fbeb2c15c add some 2024-03-01 20:35:49 +08:00
shy
04a842a1c8 add some 2024-03-01 17:28:23 +08:00
shy
d3221f5453 add some 2024-03-01 16:30:35 +08:00
shy
b6bcf08176 add some 2024-03-01 15:18:32 +08:00
shy
fb516872e9 add some 2024-03-01 13:35:56 +08:00
shy
3a7b04f1d3 add some 2024-03-01 12:01:51 +08:00
shy
53c925d0cc add some 2024-02-29 23:49:03 +08:00
shy
d89e143cb8 add some 2024-02-29 20:14:28 +08:00
shy
d7116def5b add web.stream 2024-02-29 14:04:11 +08:00
shy
9bc165866f add some 2024-02-29 02:36:51 +08:00
shy
8a33ab3b09 add some 2024-02-29 00:29:17 +08:00
shy
432fecca49 add some 2024-02-28 23:29:38 +08:00
shy
76ea69c590 add some 2024-02-28 15:18:25 +08:00
shy
3e1d00f391 add some 2024-02-28 02:14:55 +08:00
shy
7f7a042951 add some 2024-02-27 22:02:19 +08:00
shy
0f5d9e6c8a add some 2024-02-27 17:02:32 +08:00
shy
ac54db626d add some 2024-02-27 13:43:54 +08:00
shy
b360e8a70a add some 2024-02-27 11:37:08 +08:00
shy
0fcf147873 add some 2024-02-27 01:41:29 +08:00
shy
b31c63989f add web.group 2024-02-27 00:58:58 +08:00
shy
6d13a280cd add some 2024-02-26 14:00:01 +08:00
shy
28792874d4 add some 2024-02-26 02:20:07 +08:00
shy
8abffce38c add some 2024-02-25 23:37:04 +08:00
shy
1cc9ad4108 add some 2024-02-25 22:45:51 +08:00
shy
8099e5893c add some 2024-02-25 20:35:00 +08:00
shy
644e80d040 add some 2024-02-25 17:56:11 +08:00
shy
437a01a4c9 add some 2024-02-25 13:22:28 +08:00
shy
ef50bef040 add some 2024-02-25 12:53:53 +08:00
shy
361f4ee8e9 add some 2024-02-25 12:14:26 +08:00
shy
f8880cdf4c add some 2024-02-25 01:28:35 +08:00
shy
2e7c086158 add some 2024-02-25 00:12:19 +08:00
shy
dec30e9ad7 add some 2024-02-24 23:28:03 +08:00
shy
47d01ecea9 add some 2024-02-24 20:39:38 +08:00
shy
a1c7c5307b add some 2024-02-24 18:37:33 +08:00
shy
c959706ba4 add some 2024-02-24 18:18:50 +08:00
shy
62fb71f333 opt some 2024-02-24 15:25:35 +08:00
shy
5fd5cbf774 add some 2024-02-24 14:39:10 +08:00
shy
8f916dca23 add some 2024-02-24 14:35:39 +08:00
shy
a1e8ce22c8 add some 2024-02-24 14:30:05 +08:00
shy
15e1ada00d add some 2024-02-24 13:39:29 +08:00
shy
70dfec6da6 add some 2024-02-24 12:32:41 +08:00
shy
a7ca869a16 add some 2024-02-24 00:32:05 +08:00
shy
ae57fc0271 add some 2024-02-23 23:46:59 +08:00
shy
f052dcf296 add some 2024-02-23 23:32:17 +08:00
shy
b5a2a234f2 add some 2024-02-23 23:05:25 +08:00
shy
8c8238ceb9 opt some 2024-02-23 22:43:20 +08:00
shy
880631418c add some 2024-02-23 19:02:57 +08:00
shy
fc61c4d320 add some 2024-02-23 10:22:09 +08:00
shy
deddfc9457 add some 2024-02-22 14:37:12 +08:00
shy
9568a8c215 add some 2024-02-22 13:37:08 +08:00
shy
8fe47b6ca7 add some 2024-02-22 13:27:49 +08:00
shy
5d386d63e2 add some 2024-02-22 11:27:13 +08:00
shy
94b2266982 add some 2024-02-22 11:16:55 +08:00
shy
377e165765 add some 2024-02-22 11:06:31 +08:00
shy
bfb9f99734 add some 2024-02-22 09:20:01 +08:00
shy
eb1e42295d add some 2024-02-22 08:16:23 +08:00
shy
48f2322d0c add some 2024-02-21 20:40:40 +08:00
shy
ef91a31480 add some 2024-02-21 14:16:22 +08:00
shy
88e3fef444 add some 2024-02-21 01:31:16 +08:00
shy
7831d5343e add some 2024-02-21 00:41:46 +08:00
shy
6b8e6e11f9 add some 2024-02-18 18:48:08 +08:00
shy
a63f5600b7 add some 2024-02-18 12:30:56 +08:00
shy
d33f38d7e7 opt process 2024-02-17 18:15:46 +08:00
shy
83dc4bc12b add some 2024-02-17 08:19:33 +08:00
shy
bb783b794a add some 2024-02-17 08:19:14 +08:00
shy
d1c31ad7a7 add some 2024-02-16 16:11:37 +08:00
shy
d62af7bc90 opt toast 2024-02-16 16:05:33 +08:00
shy
e98b9848fe add some 2024-02-16 00:28:53 +08:00
shy
a2ca5fbecb add some 2024-02-16 00:06:36 +08:00
shy
bb673d80d7 add some 2024-02-15 19:12:01 +08:00
shy
e00dd72ae4 add some 2024-02-15 17:12:42 +08:00
shy
42b45ba0b7 add some 2024-02-15 16:37:17 +08:00
shy
712fe28952 add toast hash 2024-02-15 15:57:53 +08:00
shy
586610ce64 add some 2024-02-15 00:59:21 +08:00
shy
4cc9da13c5 add some 2024-02-14 23:28:59 +08:00
shy
0fe6237649 opt some 2024-02-14 18:03:30 +08:00
shy
8b55171ae5 opt matrix 2024-02-14 17:59:27 +08:00
shy
1277c42256 add some 2024-02-13 21:21:56 +08:00
shy
22be04e823 add some 2024-02-13 19:32:44 +08:00
shy
be63e92893 opt matrix 2024-02-13 18:40:19 +08:00
shy
516f91c8c2 add matrix 2024-02-13 15:48:59 +08:00
shy
e5b1f1cc97 add matrix 2024-02-13 15:48:27 +08:00
shy
acb9cc0eb0 add island.matrix 2024-02-13 03:55:36 +08:00
shy
d98a322d05 add some 2024-02-12 13:16:04 +08:00
shy
30a7ce39b4 add some 2024-02-12 11:12:28 +08:00
shy
248258d71d add some 2024-02-11 22:56:07 +08:00
shy
ca6dc0f3c0 add some 2024-02-09 14:19:22 +08:00
shy
e2e73ba7b3 add some 2024-02-09 14:19:12 +08:00
shy
04ad921dd8 opt some 2024-02-09 11:30:20 +08:00
shy
29c1a023e3 opt traceid 2024-02-08 10:33:05 +08:00
shy
dc9dd86bba add some 2024-02-07 17:45:11 +08:00
shy
df88fbf2b8 add some 2024-02-06 23:33:32 +08:00
shy
96671f03c8 opt gdb 2024-02-06 23:25:14 +08:00
shy
a2a14be186 opt aaa 2024-02-06 17:56:17 +08:00
shy
d4c197c737 opt aaa.apply 2024-02-06 15:08:02 +08:00
shy
69c2d29df7 opt some 2024-02-05 23:03:50 +08:00
shy
ce833a87e3 opt some 2024-02-05 22:45:51 +08:00
shy
78afcfeb60 add some 2024-02-05 13:47:38 +08:00
shy
1b0d0db770 add some 2024-02-04 11:04:22 +08:00
shy
27d8cbad48 opt theme 2024-02-03 17:56:18 +08:00
shy
e1a891fdc0 add some 2024-02-02 22:26:57 +08:00
shy
df4575cf2e add some 2024-02-02 17:49:46 +08:00
shy
83fdeae371 add island 2024-02-02 17:29:56 +08:00
shy
f11eebe099 add some 2024-02-02 00:23:31 +08:00
shy
bd3deec2f9 add island.group 2024-02-02 00:05:53 +08:00
shy
931ff8c428 add some 2024-01-31 23:37:12 +08:00
shy
299f3ea184 add some 2024-01-31 15:16:32 +08:00
shy
6ea1bd5a9d opt process 2024-01-31 14:15:45 +08:00
shy
d7dc4693a2 add some 2024-01-31 09:54:08 +08:00
shy
3655662ad8 add some 2024-01-30 23:52:18 +08:00
shy
6a68dea00c opt some 2024-01-30 21:58:44 +08:00
shy
ae7afafe13 add some 2024-01-30 21:54:02 +08:00
shy
4b6531a015 add some 2024-01-30 19:23:15 +08:00
shy
4eea30cc6e add some 2024-01-30 18:32:47 +08:00
shy
33d6bcc013 add some 2024-01-30 15:07:38 +08:00
shy
4e12b37e33 add some 2024-01-30 13:51:12 +08:00
shy
1a75e9a77e opt count 2024-01-30 12:54:08 +08:00
shy
39908a29b1 add some 2024-01-29 22:33:10 +08:00
shy
ddf48abff8 add some 2024-01-29 22:06:52 +08:00
shy
c145f0cbc7 add some 2024-01-29 21:23:59 +08:00
shy
dc082e7dc5 add some 2024-01-29 20:48:24 +08:00
shy
1ca141e3dc add some 2024-01-29 15:54:26 +08:00
shy
727de55818 add some 2024-01-29 15:19:05 +08:00
shy
2f84ef4c0e opt some 2024-01-29 15:07:55 +08:00
shy
70e91d3822 add some 2024-01-29 10:56:54 +08:00
shy
729a502161 add some 2024-01-28 20:02:06 +08:00
shy
d8115cbf3b add some 2024-01-28 19:11:29 +08:00
shy
2ebabe38dd add some 2024-01-28 14:28:23 +08:00
shy
4b1a228078 opt some 2024-01-28 14:20:24 +08:00
shy
9dc442755d opt some 2024-01-28 13:57:50 +08:00
shy
4057e30de7 add some 2024-01-28 12:27:35 +08:00
shy
05a29fc32e add some 2024-01-28 12:27:29 +08:00
shy
c1b9b86330 add some 2024-01-27 22:38:10 +08:00
shy
56bfde1aa5 add some 2024-01-27 18:14:48 +08:00
shy
0eeb02aa8c add some 2024-01-27 17:35:20 +08:00
shy
20d31f9a14 add some 2024-01-27 17:35:16 +08:00
shy
c15bd851ec add some 2024-01-27 12:08:37 +08:00
shy
67733cbfd2 add some 2024-01-26 22:07:27 +08:00
shy
fa79259c14 add some 2024-01-26 21:41:51 +08:00
shy
9563612448 add some 2024-01-26 20:55:28 +08:00
shy
6249d2584f add some 2024-01-26 18:28:50 +08:00
shy
cf10a5cf36 add some 2024-01-26 17:16:39 +08:00
shy
0ce48b54f4 add some 2024-01-26 16:07:31 +08:00
shy
b54ba9a300 opt const 2024-01-26 10:19:05 +08:00
shy
59461dbba0 add some 2024-01-25 23:58:57 +08:00
shy
c6684e26f1 add some 2024-01-25 15:03:07 +08:00
shy
a4224e5178 add some 2024-01-25 15:03:02 +08:00
shy
5666ec8f1f add chat.message 2024-01-25 13:03:15 +08:00
shy
ac04d6abd3 add some 2024-01-25 00:01:19 +08:00
shy
93be363ce2 add some 2024-01-24 21:43:19 +08:00
shy
e668707738 add chat.message 2024-01-24 19:15:31 +08:00
shy
61b601c3c4 add some 2024-01-24 10:20:43 +08:00
shy
f462d5eca5 add some 2024-01-24 09:29:37 +08:00
shy
000804dd06 add some 2024-01-23 21:14:09 +08:00
shy
45e2bcfeb6 add some 2024-01-23 19:02:58 +08:00
shy
0fccf31d00 add some 2024-01-23 15:16:38 +08:00
shy
e1f72758df add web.store 2024-01-23 14:54:49 +08:00
shy
c56ca8d4e2 add web.store 2024-01-23 14:54:25 +08:00
shy
af481aec3b add script 2024-01-22 22:24:08 +08:00
shy
6023c1506d add some 2024-01-22 17:28:10 +08:00
shy
89518b3333 add some 2024-01-22 14:33:16 +08:00
shy
c869e1fed7 add some 2024-01-22 11:29:14 +08:00
shy
ec2c270301 add some 2024-01-22 11:03:51 +08:00
shy
2c5be00d12 add some 2024-01-21 23:08:11 +08:00
shy
f6d3c2821d add chat.script 2024-01-21 21:18:35 +08:00
shy
c6791d78dc add chat.script 2024-01-21 21:18:17 +08:00
shy
73731c898a add some 2024-01-21 17:02:00 +08:00
shy
2db2f39649 add chat.script 2024-01-21 16:00:59 +08:00
shy
5d8cfa9138 add chat.script 2024-01-21 10:08:12 +08:00
shy
aa9624d62e add some 2024-01-20 22:27:42 +08:00
shy
71f8117415 add ice.port 2024-01-20 22:01:21 +08:00
shy
df1fbade2f add some 2024-01-20 21:14:30 +08:00
shy
6e05c8243e add some 2024-01-20 19:21:10 +08:00
shy
ceb2758c3e add some 2024-01-20 15:03:04 +08:00
shy
e7d136268d add some 2024-01-20 13:50:40 +08:00
shy
a598a58a01 add some 2024-01-20 12:10:47 +08:00
shy
9628216d0f add some 2024-01-19 22:56:31 +08:00
shy
e09f0eb9d9 add some 2024-01-19 18:34:41 +08:00
shy
4f817911cc add some 2024-01-19 14:11:31 +08:00
shy
aea7a148bc add some 2024-01-18 22:10:40 +08:00
shy
82423b9112 add some 2024-01-18 10:21:33 +08:00
shy
01329fe476 add some 2024-01-18 00:11:52 +08:00
shy
53b6706863 add some 2024-01-17 22:46:59 +08:00
shy
ac90f10b41 add some 2024-01-17 19:30:28 +08:00
shy
5bc1e480fa add some 2024-01-17 14:57:18 +08:00
shy
3f8462301f add some 2024-01-17 14:35:19 +08:00
shy
826cd54b79 add some 2024-01-17 11:25:44 +08:00
shy
aeea2a4bad opt some 2024-01-16 22:09:32 +08:00
shy
e076a59eec add some 2024-01-16 18:27:34 +08:00
shy
6e1f498139 add some 2024-01-16 15:28:52 +08:00
shy
1b47f20dae add some 2024-01-16 15:28:44 +08:00
shy
8a36cbfe51 add some 2024-01-16 13:14:59 +08:00
shy
25016141bb add some 2024-01-15 19:11:57 +08:00
shy
a3328f8e83 opt token 2024-01-15 16:14:12 +08:00
shy
ef822f0c5b add some 2024-01-15 14:37:55 +08:00
shy
f22a74210e opt document 2024-01-15 12:19:48 +08:00
shy
70024b0baf opt binpack 2024-01-15 09:43:21 +08:00
shy
dbac8e1380 opt dream 2024-01-15 09:06:56 +08:00
shy
e89e120523 add some 2024-01-14 23:24:11 +08:00
shy
4d117d1bbf add some 2024-01-14 23:15:28 +08:00
shy
62d86ee286 add some 2024-01-14 22:59:20 +08:00
shy
b559e6c92b opt some 2024-01-14 20:21:17 +08:00
shy
be1bbfb2a4 add some 2024-01-14 20:18:38 +08:00
shy
c235a501b1 add spide token 2024-01-14 19:51:16 +08:00
shy
cbaebedd32 opt some 2024-01-14 16:42:21 +08:00
shy
06e16b0e5c add some 2024-01-14 16:15:49 +08:00
shy
e3f99c3042 add some 2024-01-14 16:09:42 +08:00
shy
8fb70fa41d add some 2024-01-14 13:11:19 +08:00
shy
e6e89563ef add some 2024-01-14 01:55:43 +08:00
shy
e67d59d310 opt island 2024-01-13 22:39:30 +08:00
shy
1f0a904767 opt island 2024-01-13 15:32:06 +08:00
shy
bbb4994422 add some 2024-01-13 08:47:16 +08:00
shy
f3612ef710 opt some 2024-01-12 23:53:07 +08:00
shy
4ba8bca6c9 opt island 2024-01-12 23:51:05 +08:00
shy
ef97bf61ca opt island 2024-01-12 23:37:26 +08:00
shy
a12a4836b9 opt some 2024-01-12 17:02:43 +08:00
shy
dc9f44f90e add mdb.DevDataAction 2024-01-12 00:40:54 +08:00
shy
7eb7d7b708 opt layout 2024-01-11 19:03:52 +08:00
shy
d5280d34c9 opt layout 2024-01-11 12:33:19 +08:00
shy
053d2e3bf9 opt layout 2024-01-11 00:07:21 +08:00
shy
9baed62ec9 add some 2024-01-10 21:06:41 +08:00
shy
cc503e37b9 add some 2024-01-10 14:41:14 +08:00
shy
fb3b822496 add some 2024-01-10 14:41:09 +08:00
shy
4d891fd380 add some 2024-01-10 13:31:33 +08:00
shy
3c9db97ffa add some 2024-01-10 13:31:25 +08:00
shy
e2acf4baf2 add some 2024-01-09 23:06:09 +08:00
shy
41ad9afa67 add some 2024-01-09 23:03:27 +08:00
shy
370420adff add some 2024-01-09 20:02:13 +08:00
shy
6e9b941c0b add some 2024-01-09 10:40:29 +08:00
shylinux
165b57f23a add some 2024-01-09 09:03:04 +08:00
shy
e0fec66989 add some 2024-01-08 21:47:46 +08:00
shy
216941a389 opt aaa.offer 2024-01-08 20:57:48 +08:00
shy
3d20218b47 add aaa.SendEmail 2024-01-08 13:18:21 +08:00
shy
830da6b46c add some 2024-01-08 11:13:36 +08:00
shy
fa158fa9ac opt some 2024-01-07 17:46:10 +08:00
shy
b0ac34c86b opt relay 2024-01-06 09:09:51 +08:00
shy
307c1edb5d opt some 2024-01-05 14:26:23 +08:00
shy
1f86d5716d fix ice.MSG_DAEMON 2024-01-05 13:02:15 +08:00
shy
97bcf318c6 opt ice.MSG_DAEMON 2024-01-05 12:25:56 +08:00
shy
0223a40087 add MergeLink 2024-01-04 17:33:30 +08:00
shy
8264f902b8 opt some 2024-01-04 12:51:33 +08:00
shy
a32a22b239 add some 2024-01-04 11:09:18 +08:00
shy
b7d3b9c976 opt dream 2024-01-04 10:50:10 +08:00
shy
480fe1d05d add some 2024-01-04 10:16:46 +08:00
shy
28d8c3a8e2 add some 2024-01-03 22:03:04 +08:00
shy
d400f80172 opt some 2024-01-03 20:15:07 +08:00
shy
9ba57a0b6d add some 2024-01-03 19:42:15 +08:00
shy
ec60322330 opt some 2024-01-03 16:59:57 +08:00
shy
d2e3cada57 opt role 2024-01-03 11:54:12 +08:00
shy
da2b486b7c add some 2024-01-02 21:32:56 +08:00
shylinux
d83dbb501e opt tcp.host 2024-01-02 21:03:25 +08:00
shy
89129568e2 add some 2024-01-02 20:33:32 +08:00
shy
4e2d515c9e fix yac.stack 2024-01-02 18:36:22 +08:00
shy
2727a198ae opt aaa.sesss 2024-01-02 17:37:00 +08:00
shy
85c44706ae opt aaa.sess 2024-01-02 16:15:32 +08:00
shy
8e49c83780 opt aaa.apply 2024-01-02 12:59:48 +08:00
shylinux@163.com
92e7416d82 opt aaa.apply 2024-01-02 12:20:58 +08:00
shy
08aa8cd8f6 add aaa.apply 2024-01-01 20:35:01 +08:00
shy
8b184981b6 opt git 2023-12-31 10:32:56 +08:00
shy
6bb29fed18 fix token 2023-12-30 21:11:51 +08:00
shy
80aa4ce207 opt git 2023-12-30 20:18:48 +08:00
shy
932002cf32 opt git 2023-12-30 20:17:56 +08:00
shy
693ee73a82 opt dream 2023-12-30 11:23:38 +08:00
shy
67a1ffd4b3 opt auto 2023-12-29 22:58:35 +08:00
shy
35e635b3a9 opt url 2023-12-29 17:58:55 +08:00
shylinux
f7bfc31de1 add some 2023-12-29 09:43:45 +08:00
shy
b3aaae590f add status.source 2023-12-28 23:15:59 +08:00
shy
c95da31e37 opt portal 2023-12-28 10:00:40 +08:00
shy
feb4d49d63 opt icon 2023-12-27 08:51:44 +08:00
shy
f315c7d0e7 opt icon 2023-12-26 18:34:47 +08:00
shy
e16c803d87 opt icon 2023-12-26 15:18:35 +08:00
shy
4fb198f2d5 opt desktop 2023-12-26 11:43:46 +08:00
shy
1810041116 opt some 2023-12-25 23:19:57 +08:00
shy
7e958b03fb opt some 2023-12-25 13:43:33 +08:00
shy
b122d1021c opt relay 2023-12-25 11:21:32 +08:00
shy
d60b1fc1a5 opt some 2023-12-25 10:28:18 +08:00
shy
2d1c99af56 add lock 2023-12-24 21:43:21 +08:00
shy
7bdc1bc380 add can.dir 2023-12-24 12:23:12 +08:00
shy
bdb7177091 opt status 2023-12-24 00:18:32 +08:00
shy
0ef623ae25 add git 2023-12-23 17:02:42 +08:00
shy
7c6cfde7ba add some 2023-12-23 16:59:18 +08:00
shy
ef18b21598 opt debug 2023-12-22 22:59:54 +08:00
shy
302a75485e opt debug 2023-12-22 17:49:00 +08:00
shy
7796bf4ce1 opt some 2023-12-21 19:21:24 +08:00
shy
33a62d7f2d add some 2023-12-21 18:54:17 +08:00
shy
563cbec926 add some 2023-12-21 18:37:47 +08:00
shy
e152063510 add some 2023-12-21 16:54:48 +08:00
shy
3d2e964267 add some 2023-12-21 15:54:31 +08:00
shy
3b5659f124 add some 2023-12-21 13:22:21 +08:00
shy
9d24461de8 opt space 2023-12-21 12:29:21 +08:00
shy
9a4e1a266a add some 2023-12-21 00:32:52 +08:00
shy
68dc8bc671 opt repos 2023-12-20 23:59:46 +08:00
shy
eed06ba25c add some 2023-12-20 15:04:32 +08:00
shy
9829b5c3ef add some 2023-12-20 13:53:56 +08:00
shy
038f419c8a add some 2023-12-20 13:48:53 +08:00
shy
ca501f7a12 add some 2023-12-19 19:06:59 +08:00
shy
c7e797e614 add some 2023-12-14 08:53:54 +08:00
shy
d3a7c38d5c add some 2023-12-14 08:53:47 +08:00
shy
c28a382295 opt icons 2023-12-13 19:10:28 +08:00
shy
7d28a1b93d add some 2023-12-13 17:57:44 +08:00
shy
6437fc6c05 add some 2023-12-13 13:27:59 +08:00
shy
e545b34306 add some 2023-12-13 12:40:01 +08:00
shy
f579ad868f add some 2023-12-13 12:23:07 +08:00
shy
4df3d2a5b4 add some 2023-12-13 09:49:12 +08:00
shy
c100727a95 add some 2023-12-13 00:00:27 +08:00
shy
37d2d84381 add some 2023-12-12 21:42:39 +08:00
shy
b54e6b0a01 fix dream.start 2023-12-12 15:59:22 +08:00
shy
0b042abfbe fix git.status 2023-12-12 15:47:14 +08:00
root
a4573dd54a fix sessauth 2023-12-12 12:39:06 +08:00
shy
3ce839ecc9 opt some 2023-12-12 11:32:37 +08:00
shy
4fd30e611d add web 2023-12-12 08:42:16 +08:00
shy
d94284c968 add some 2023-12-11 22:42:53 +08:00
shy
faefc3878b add some 2023-12-11 22:23:19 +08:00
shy
32e5ea4067 add web.count 2023-12-11 22:15:23 +08:00
shy
afdebbdc67 add some 2023-12-11 19:08:33 +08:00
shy
8684e804d3 add some 2023-12-11 16:40:44 +08:00
shy
149412b1a5 add admin 2023-12-11 15:59:30 +08:00
shy
e18395626a add checkbox 2023-12-11 15:38:23 +08:00
shy
93a9591cf7 opt some 2023-12-11 11:47:30 +08:00
shy
beea49d5dd add some 2023-12-10 23:56:55 +08:00
shy
251d28ccc6 fix language 2023-12-10 20:40:18 +08:00
shy
5051a7d2c1 opt ice.MSG_LANGUAGE 2023-12-10 14:59:44 +08:00
shy
aba012a20e opt wx 2023-12-10 09:51:02 +08:00
shy
8ff113403e add wx 2023-12-10 00:16:50 +08:00
shy
072996863d add wx.ide autogen 2023-12-09 18:03:57 +08:00
shy
e5e6f37e3e add wx.ide autogen 2023-12-09 18:03:47 +08:00
shy
ee1ef94a72 add some 2023-12-09 11:27:15 +08:00
shy
8d9b4c7840 add some 2023-12-09 10:33:16 +08:00
shy
b2e8e5b28f opt m.FormatMeta 2023-12-09 08:31:58 +08:00
shy
c434e334a9 opt web.stats 2023-12-08 19:37:25 +08:00
shy
2832fe1819 opt toast icon 2023-12-08 12:42:49 +08:00
shy
a69a06dc5e opt some 2023-12-08 11:59:58 +08:00
shy
d9574b3225 add usr/program/ 2023-12-07 22:47:42 +08:00
shy
f5a4bbfe54 opt some 2023-12-07 11:06:54 +08:00
shy
00c6e8e3e6 fix share 2023-12-07 08:57:23 +08:00
shy
9d96365c66 add some 2023-12-06 20:01:35 +08:00
shy
9e129db29d add relay.go 2023-12-06 13:55:56 +08:00
shy
5bf1c17cf0 add relay.go 2023-12-06 12:51:25 +08:00
shy
a16b7e9c3a add sleep 2023-12-05 23:17:43 +08:00
shy
8dc2aaa35b add some 2023-12-05 21:56:10 +08:00
shy
aad9c09e96 opt ssh/connect.go 2023-12-05 04:03:44 +08:00
shy
e1bb4568d9 opt header 2023-12-04 13:15:19 +08:00
shy
7d76a316b4 add some 2023-12-03 10:51:48 +08:00
shy
6166a5b0af add some 2023-12-02 18:14:56 +08:00
shy
d745552827 add some 2023-12-02 17:04:45 +08:00
shy
ec7f5eba8e add relay 2023-12-02 15:38:36 +08:00
shy
35007a3e6e add boot 2023-12-02 11:13:29 +08:00
shy
2939426a73 add some 2023-12-02 09:59:35 +08:00
shy
67cfeb5e04 add rc_local.sh 2023-12-01 23:32:57 +08:00
shy
37186f3cf4 add some 2023-12-01 14:06:58 +08:00
shy
591b405724 fix some 2023-12-01 12:37:11 +08:00
shy
37cab25dc0 opt mirrors.go 2023-12-01 12:05:26 +08:00
shy
3fef266c10 add some 2023-11-30 14:21:06 +08:00
shy
23c601d309 add some 2023-11-30 14:09:07 +08:00
shy
67fdb0479a add wx 2023-11-30 13:49:08 +08:00
shy
3a87341d26 add some 2023-11-30 13:14:36 +08:00
shy
9d3e1fa3fb opt wx.login 2023-11-30 00:39:05 +08:00
shy
3c63abbbe2 opt wx.ide 2023-11-29 16:10:44 +08:00
shy
a7cc74c80b add some 2023-11-27 13:24:39 +08:00
shy
a530f62cc0 add some 2023-11-27 13:18:52 +08:00
shy
4ba34ff106 add some 2023-11-27 10:57:14 +08:00
shy
5b102c5d63 add some 2023-11-27 00:06:51 +08:00
shy
df2958c76e add main.css 2023-11-26 21:08:51 +08:00
shy
ecbcde55b2 add some 2023-11-26 14:31:55 +08:00
shy
fca822c569 opt desktop 2023-11-26 13:43:07 +08:00
shy
592dff8a9e opt favor 2023-11-26 00:03:40 +08:00
shy
e63626b873 add some 2023-11-24 20:22:04 +08:00
shy
d2bb213a42 add some 2023-11-24 17:46:40 +08:00
shy
181b6b4650 add some 2023-11-23 23:25:09 +08:00
shy
7544359c5c opt theme 2023-11-23 23:02:14 +08:00
shy
74d74886f1 opt css size 2023-11-22 10:17:11 +08:00
shy
abf9afe3e8 add some 2023-11-22 00:03:33 +08:00
shy
e847189720 add some 2023-11-21 21:10:26 +08:00
shy
4679d5475a add mp 2023-11-21 15:53:05 +08:00
shy
593c5a3313 add wifi 2023-11-21 12:25:32 +08:00
shy
1d88096d83 add wx.ide 2023-11-20 22:33:27 +08:00
shy
3909630331 add some 2023-11-20 22:30:14 +08:00
shy
b1d36d832c add login.go 2023-11-19 23:21:44 +08:00
shy
002995b483 add wifi.go 2023-11-19 00:06:55 +08:00
shy
982250df4f add some 2023-11-18 18:58:59 +08:00
shy
832f5f0a43 add some 2023-11-18 16:25:25 +08:00
shy
f28d046358 add mp 2023-11-18 13:06:41 +08:00
shy
7b31a67335 add mp 2023-11-18 13:05:50 +08:00
shy
dcad30341b add mp 2023-11-18 00:04:56 +08:00
shy
61828d28f5 add some 2023-11-16 21:35:40 +08:00
shy
4d34d67ddd add some 2023-11-16 17:39:36 +08:00
shy
104c0752e3 add stats 2023-11-16 16:48:55 +08:00
shy
08befa2f96 add some 2023-11-16 13:53:29 +08:00
shy
15066a6706 add stats 2023-11-16 13:11:03 +08:00
shy
96553ee70c add asset 2023-11-16 00:27:12 +08:00
shy
ea3d45e77b add some 2023-11-15 14:35:54 +08:00
shy
493702da14 add some 2023-11-15 11:11:26 +08:00
shy
859ff766b4 add some 2023-11-14 08:46:23 +08:00
shy
0cf8512bb4 opt header 2023-11-12 10:54:01 +08:00
shy
3c680d6f88 add header 2023-11-12 08:30:11 +08:00
shy
f2c804167a add some 2023-11-11 19:54:03 +08:00
shy
57764d19ba add some 2023-11-11 18:22:39 +08:00
shy
6c2c10d307 add some 2023-11-11 14:12:56 +08:00
shy
c5190266c9 add some 2023-11-11 14:06:20 +08:00
shy
a77ddbe902 add some 2023-11-11 00:07:04 +08:00
shy
7d138f1030 add wx.shy 2023-11-10 17:34:04 +08:00
shy
ab8de9ba49 add wx.shy 2023-11-10 02:12:48 +08:00
shy
798dfd62aa add disk 2023-11-09 09:10:00 +08:00
shy
338b56fcf8 add some 2023-11-08 18:22:50 +08:00
shy
d9f1ec7bfc add some 2023-11-08 17:57:44 +08:00
shy
409dd40848 add some 2023-11-08 17:36:26 +08:00
shy
db53a31bcf opt location 2023-11-08 16:35:13 +08:00
shy
37b5d0cf13 add location 2023-11-08 00:59:05 +08:00
shy
6a6939fa6c add loan.go 2023-11-07 09:30:08 +08:00
shy
96d30d7beb add some 2023-11-06 16:02:25 +08:00
shy
e8487da572 add some 2023-11-06 13:48:40 +08:00
shy
e5dad62c87 add some 2023-11-05 13:27:56 +08:00
shy
fc71adb56d add geoarea 2023-11-05 08:32:54 +08:00
shy
1a02b3c0b9 add some 2023-11-02 23:25:31 +08:00
shy
c16a622aed add some 2023-11-02 17:07:52 +08:00
shy
e4fc6f7a8f add some 2023-11-02 15:16:01 +08:00
shy
cabecf6985 add some 2023-11-02 11:50:58 +08:00
shy
144f23cd8b add some 2023-11-02 09:05:21 +08:00
shy
532a2f8457 add some 2023-11-01 19:35:49 +08:00
shy
47f2092705 add mall/region.go 2023-10-31 10:00:03 +08:00
shy
31a5be0e6f add some 2023-10-31 07:35:33 +08:00
shy
57297e28df add some 2023-10-29 10:09:38 +08:00
shy
52697a198f add some 2023-10-29 00:00:24 +08:00
shy
a1732e6292 add some 2023-10-28 23:59:37 +08:00
shy
eb5b405e8e add some 2023-10-27 13:39:01 +08:00
shy
99a42fe3a7 add some 2023-10-27 00:38:27 +08:00
shy
adbf04964e add some 2023-10-27 00:19:06 +08:00
shy
d9a44bedc5 add mall/admin.go 2023-10-26 17:59:12 +08:00
shy
3991619ad0 add some 2023-10-23 22:30:19 +08:00
shy
66d0aaf688 opt some 2023-10-23 18:43:16 +08:00
shy
5d1f998cdd opt some 2023-10-20 00:27:05 +08:00
shy
33194101bf add some 2023-10-19 21:07:52 +08:00
shy
b5f1c75ddc add some 2023-10-17 16:19:12 +08:00
shy
5f6cb250a1 add some 2023-10-17 10:42:26 +08:00
shy
fa44a6d370 add some 2023-10-16 19:55:32 +08:00
shy
001cbfd713 add some 2023-10-16 18:27:49 +08:00
shy
19382e1f28 add some 2023-10-14 13:20:33 +08:00
shy
bf95b4d87c add some 2023-10-14 12:53:56 +08:00
shy
cb3af35de1 add some 2023-10-13 22:27:19 +08:00
shy
2f8c081a38 add some 2023-10-13 21:04:52 +08:00
shy
d517ee6ebe opt some 2023-10-13 10:41:58 +08:00
shy
2f6795a70e add some 2023-10-13 10:01:20 +08:00
shy
7deacdb671 add some 2023-10-13 00:42:48 +08:00
shy
8360cae3c9 add some 2023-10-12 22:10:31 +08:00
shy
1428dcd427 opt log 2023-10-12 20:14:15 +08:00
shy
84109ec520 add some 2023-10-12 17:24:56 +08:00
shy
57b2aed386 opt ssh/service.go 2023-10-12 11:51:04 +08:00
shy
1d9fd6a7f2 opt m.Status 2023-10-12 08:53:43 +08:00
shy
50e99b9fed opt ssh 2023-10-11 20:30:53 +08:00
shy
15a3f015c7 opt git 2023-10-11 11:52:47 +08:00
shy
5759eb31db add traceid 2023-10-10 16:38:32 +08:00
shy
b3024262ca add some 2023-10-10 01:13:40 +08:00
shy
2b4b3e16b2 opt git 2023-10-09 20:21:37 +08:00
shy
a908aaaa72 opt wiki 2023-10-09 13:07:34 +08:00
shy
a4eb75e5f5 opt wiki 2023-10-09 13:07:02 +08:00
shy
8a8e06cd10 opt draw 2023-10-08 16:58:57 +08:00
shy
2794463664 add some 2023-10-08 09:15:16 +08:00
shy
914292f072 opt draw 2023-10-07 07:48:45 +08:00
shy
60c7b3d1ca opt feel 2023-10-05 21:14:34 +08:00
shy
1a4add15bb add some 2023-10-05 13:03:40 +08:00
shy
8ea2beab9d opt code 2023-10-05 11:55:31 +08:00
shy
70f50b91c1 opt code 2023-10-04 23:04:44 +08:00
shy
16889fefc9 opt code 2023-10-04 15:00:27 +08:00
shy
154ec9cb70 add some 2023-10-03 20:03:22 +08:00
shy
e88ed2791d add some 2023-10-03 17:49:39 +08:00
shy
b68b76e9ec add some 2023-10-02 09:53:45 +08:00
shy
43af99a59b add some 2023-09-30 08:58:41 +08:00
shy
ce563e4fea add some 2023-09-29 18:18:07 +08:00
shy
776dc32431 add some 2023-09-29 12:26:46 +08:00
shy
8df263fceb add some 2023-09-27 19:18:46 +08:00
shy
26fe9b47f9 opt sso 2023-09-26 10:06:41 +08:00
shy
719decf59a add some 2023-09-25 21:40:16 +08:00
shy
8e2ccbaa12 fix important 2023-09-25 19:43:16 +08:00
shy
c6e9d87477 opt chat 2023-09-25 18:45:17 +08:00
shy
f94cfdcc4c add some 2023-09-22 07:49:46 +08:00
shy
adcbdb8365 opt location.go 2023-09-22 07:17:02 +08:00
shy
18d9f9f3e4 opt ctx.CmdAction 2023-09-21 01:33:02 +08:00
shy
e84ecb049c opt chat 2023-09-20 00:29:12 +08:00
shy
d9dcd6480c opt base 2023-09-19 21:35:37 +08:00
shy
b3520df582 add icon 2023-09-18 22:07:42 +08:00
shy
c356669ff5 add some 2023-09-18 15:19:24 +08:00
shy
6e6041aa88 add some 2023-09-17 15:40:28 +08:00
shy
ac9335f46e add some 2023-09-17 09:06:47 +08:00
shy
299077b6f1 add some 2023-09-16 20:10:45 +08:00
shy
101002e269 add some 2023-09-16 17:39:52 +08:00
shy
dea7b707f6 add some 2023-09-16 13:35:23 +08:00
shy
df554384ef add some 2023-09-16 12:04:17 +08:00
shy
695284c469 add some 2023-09-14 15:22:23 +08:00
shy
ec4cc2b660 add some 2023-09-13 09:40:48 +08:00
shy
eab3c6c447 add some 2023-09-12 08:55:06 +08:00
shy
cc801911ec add some 2023-09-12 08:08:38 +08:00
shy
27f23e5dbf add some 2023-09-11 09:26:12 +08:00
shy
c6916cd152 add some 2023-09-10 18:00:28 +08:00
shy
fd1a1613a5 add some 2023-09-10 17:14:34 +08:00
shy
f9f4324bce opt oauth 2023-09-10 11:27:06 +08:00
shy
c81980bd72 opt oauth 2023-09-10 11:23:04 +08:00
shy
0a1a28f13e add some 2023-09-09 18:38:07 +08:00
shy
7f8e0c4d90 add some 2023-09-08 21:02:33 +08:00
shy
890c661782 add clock 2023-09-08 18:20:55 +08:00
shy
56a4be9eec fix some 2023-09-06 19:25:25 +08:00
shy
1c490a3e45 fix some 2023-09-06 19:24:30 +08:00
shy
e70a4743ba fix some 2023-09-06 19:22:13 +08:00
shy
a1d67a3cd9 fix some 2023-09-06 19:20:08 +08:00
shy
501098a858 fix some 2023-09-06 19:19:11 +08:00
shy
a6b48d55d8 fix some 2023-09-06 19:17:22 +08:00
shy
da37a136e1 fix some 2023-09-06 19:08:35 +08:00
shy
c9c5b87302 opt some 2023-09-06 18:59:50 +08:00
shy
ec60e9dce1 opt some 2023-09-06 16:08:21 +08:00
shy
a6bb5e7f4d add some 2023-09-06 16:06:04 +08:00
shy
83b7841e01 add some 2023-09-05 12:27:04 +08:00
shy
a220752657 add some 2023-09-04 11:39:30 +08:00
shy
6e8235346f add git.search 2023-09-03 23:33:59 +08:00
shy
ac660c4f04 add some 2023-09-03 09:40:18 +08:00
shy
8ad2fd8e32 add some 2023-09-01 15:57:06 +08:00
shy
c26e2030e8 add monaco 2023-08-30 22:09:10 +08:00
shy
94aab0a928 add some 2023-08-28 23:23:13 +08:00
shy
1956cc811f opt some 2023-08-28 22:56:08 +08:00
shy
d0fe3fa473 opt index.css 2023-08-28 19:42:31 +08:00
shy
ebaa3a0491 opt theme 2023-08-28 13:47:12 +08:00
shy
869318e7b2 add some 2023-08-27 21:36:46 +08:00
shy
99865b5459 opt studio 2023-08-27 21:06:27 +08:00
shy
58d94c80bb opt icon 2023-08-26 22:45:49 +08:00
shy
b33c98c634 add some 2023-08-26 21:09:20 +08:00
shy
c0a8cdc167 add some 2023-08-25 17:49:43 +08:00
shy
02b707e79f add some 2023-08-23 11:14:26 +08:00
shy
3534665b88 add some 2023-08-21 19:05:44 +08:00
shy
fc59317d85 add mail 2023-08-21 01:33:55 +08:00
shy
c9a8bbbcf4 add email 2023-08-19 22:07:01 +08:00
shy
a3b915b34a add some 2023-08-18 17:46:31 +08:00
shy
1e485f9a46 add some 2023-08-18 17:05:38 +08:00
shy
f0aeaa6784 add some 2023-08-18 13:04:17 +08:00
shy
71624742e8 fix token 2023-08-17 00:44:29 +08:00
shy
b76d5b7269 add some 2023-08-17 00:15:34 +08:00
shylinux
d9477ee60b add some 2023-08-16 23:38:20 +08:00
shylinux
b7840792b2 add some 2023-08-16 08:47:20 +08:00
shy
2602e421af opt some 2023-08-16 01:07:18 +08:00
shy
5bdef43436 add some 2023-08-16 00:13:06 +08:00
shy
931adfcdc5 add some 2023-08-14 12:45:32 +08:00
shy
0441fad5e4 opt some 2023-08-14 11:05:18 +08:00
shy
4e8b1cf68d add some 2023-08-13 22:13:54 +08:00
shy
1f0f42db7d add some 2023-08-13 09:59:32 +08:00
9f7a0fe1e6 add some 2023-08-11 18:24:02 +08:00
03c5e3108a add some 2023-08-10 21:28:11 +08:00
8e58a5bcb8 add some 2023-08-09 16:22:52 +08:00
0291b619d2 add some 2023-08-08 10:56:29 +08:00
1197a56ced add some 2023-08-08 03:45:06 +08:00
e6d0f561b6 opt some 2023-08-04 12:25:26 +08:00
0b7f4873f0 add some 2023-08-03 12:14:49 +08:00
dae59b423c opt some 2023-08-01 09:58:57 +08:00
b3bc3b6a69 add some 2023-07-31 16:48:51 +08:00
2b136906f7 add some 2023-07-31 00:17:30 +08:00
72943d78a9 add some 2023-07-30 23:43:18 +08:00
cb40e98104 add web.flows 2023-07-30 23:35:18 +08:00
a107674fb7 add web.flows 2023-07-30 09:48:09 +08:00
935621186b opt web.route 2023-07-28 22:49:18 +08:00
eba92ecf12 add web.route 2023-07-28 00:48:59 +08:00
cd84800ac9 add some 2023-07-26 22:47:58 +08:00
8a1c4ab943 add some 2023-07-26 22:04:44 +08:00
03b074c909 add some 2023-07-26 21:53:15 +08:00
8811c44c6a add some 2023-07-26 17:57:05 +08:00
a82c81813b add web.count 2023-07-26 14:23:27 +08:00
dad4c73808 add some 2023-07-25 17:46:40 +08:00
ed8a417851 opt some 2023-07-22 16:55:08 +08:00
a23d14b11a opt some 2023-07-22 10:57:06 +08:00
6c2899868b opt some 2023-07-22 09:04:29 +08:00
4ef21b479e opt some 2023-07-21 15:48:55 +08:00
495083a249 opt some 2023-07-17 10:42:49 +08:00
65edaa6c17 opt some 2023-07-16 22:18:57 +08:00
4df6e70d73 fix require 2023-07-16 19:21:47 +08:00
0b610ba05b opt some 2023-07-16 16:05:00 +08:00
d173c0b5af opt some 2023-07-16 15:04:01 +08:00
6886c29355 opt some 2023-07-16 14:03:57 +08:00
53b2efc9fd opt some 2023-07-15 15:21:08 +08:00
7379a4e87a opt git.repos 2023-07-15 11:57:10 +08:00
f36fa0d148 opt git.service 2023-07-15 10:08:37 +08:00
b9464d2b43 opt some 2023-07-14 23:39:55 +08:00
857baa7a27 add tcp.host publish 2023-07-14 21:23:56 +08:00
8dbb121912 fix git-service 2023-07-14 18:46:28 +08:00
d00650bbb0 opt some 2023-07-14 16:50:44 +08:00
cf79f573e7 opt some 2023-07-14 16:37:40 +08:00
80edcfd1be opt some 2023-07-14 12:58:42 +08:00
7438f45e56 opt some 2023-07-13 19:03:51 +08:00
b131e6c61a opt some 2023-07-13 15:01:40 +08:00
63fb21f3ca opt some 2023-07-12 23:04:45 +08:00
fadc244701 opt some 2023-07-12 22:47:19 +08:00
3283cbc479 opt some 2023-07-12 12:50:13 +08:00
0a1f231ca3 opt some 2023-07-11 17:58:54 +08:00
19d6bb332e opt some 2023-07-11 11:04:53 +08:00
f2766ce969 opt some 2023-07-10 12:48:30 +08:00
44a6325559 opt some 2023-07-08 16:04:18 +08:00
e73f29d130 opt some 2023-07-08 13:51:21 +08:00
dffc592aa1 opt some 2023-07-08 13:19:22 +08:00
bd4f17865d opt some 2023-07-08 12:42:55 +08:00
shy
ab94f662b8 opt some 2023-07-08 08:40:44 +08:00
a361139f3c opt some 2023-07-08 08:34:20 +08:00
c8130ce062 opt some 2023-07-07 18:40:02 +08:00
d616744c2b opt some 2023-07-07 16:35:02 +08:00
2dbf032e02 opt feel 2023-07-06 18:47:41 +08:00
d92dfd27bd opt some 2023-07-05 15:57:55 +08:00
5af0bee1c6 opt portal 2023-07-05 00:45:21 +08:00
54e5ba6e79 add style.go 2023-07-04 11:05:14 +08:00
6d7cf18dce opt some 2023-07-04 11:05:02 +08:00
b7726ae301 opt some 2023-07-01 22:36:02 +08:00
5bc8a0b470 opt portal 2023-07-01 13:44:18 +08:00
477c368c78 opt portal 2023-06-29 21:23:45 +08:00
1590112fba add portal 2023-06-28 21:55:01 +08:00
9db39a4abb opt some 2023-06-23 18:31:15 +08:00
87d36b4437 opt some 2023-06-21 17:15:06 +08:00
2969cf43f3 opt some 2023-06-15 16:18:56 +08:00
7d6b3a8763 opt some 2023-06-12 10:13:39 +08:00
2e0f074e20 opt some 2023-06-12 10:01:05 +08:00
0c1a0a2f06 opt some 2023-06-08 22:56:17 +08:00
3b2a741fdb opt some 2023-06-07 11:21:59 +08:00
b3f1fefedf opt vue 2023-06-04 21:27:34 +08:00
f0e799c970 add some 2023-06-03 17:07:19 +08:00
bd4b8e29c8 add node 2023-06-03 10:16:57 +08:00
746fb79e7c add node 2023-06-03 09:06:37 +08:00
5a1f38e7d9 add some 2023-05-29 14:45:45 +08:00
0cc064db96 add some 2023-05-28 20:06:48 +08:00
b7582ee7d4 add some 2023-05-27 12:19:47 +08:00
ff498bbd8d add some 2023-05-27 10:33:58 +08:00
30db24ab8b add some 2023-05-26 15:51:32 +08:00
a86ca7b2d0 add some 2023-05-25 16:00:52 +08:00
d61d524b89 opt some 2023-05-24 17:19:27 +08:00
c71b01e4bb opt procstat.go 2023-05-24 17:01:27 +08:00
ec40e9d9eb opt procstat 2023-05-24 14:22:39 +08:00
b1cfdccc28 add procstat 2023-05-24 01:42:19 +08:00
921ceae2fb opt some 2023-05-23 19:16:20 +08:00
e6b0c6f7a1 opt some 2023-05-23 18:42:18 +08:00
8fd40a8fdb opt some 2023-05-23 12:05:52 +08:00
91b13b4bfd opt some 2023-05-23 01:27:09 +08:00
b4ed74a874 opt some 2023-05-23 01:19:37 +08:00
faf74492ef opt some 2023-05-22 23:24:41 +08:00
4548e3a62f add some 2023-05-22 22:19:57 +08:00
ee6e5d4248 add some 2023-05-22 15:48:16 +08:00
2c84b1f8fa add some 2023-05-21 17:44:19 +08:00
1a74b3e936 add some 2023-05-20 14:37:37 +08:00
2ee0367f96 opt some 2023-05-16 09:22:26 +08:00
faed551a35 add readelf.go 2023-05-14 16:01:25 +08:00
2f031d6a2b opt iterm 2023-05-13 20:45:20 +08:00
bd15da5f40 add iterm.go 2023-05-13 00:46:38 +08:00
b3b37b37d5 opt some 2023-05-12 00:02:47 +08:00
4ccf56a3d2 opt some 2023-05-11 16:17:05 +08:00
cd621f13c1 opt some 2023-05-11 13:10:22 +08:00
0abc171bf8 opt some 2023-05-10 10:38:31 +08:00
aa7fcaf472 opt some 2023-05-07 22:23:51 +08:00
3f745faa10 opt some 2023-05-05 00:20:53 +08:00
980fa1b4da opt some 2023-05-04 14:46:27 +08:00
6da848f005 opt some 2023-05-04 14:18:12 +08:00
442b740cbb add macos 2023-05-03 23:23:49 +08:00
b293cf553e add searchs 2023-05-03 16:47:07 +08:00
d93687ee50 opt some 2023-05-02 17:30:24 +08:00
3a994cf287 opt some 2023-05-02 07:42:22 +08:00
119661a12e opt macos 2023-04-30 10:50:15 +08:00
61a4032ac6 opt some 2023-04-29 23:17:03 +08:00
4ae646ba30 add macosx 2023-04-28 22:35:16 +08:00
2d3adedc57 opt some 2023-04-27 14:25:32 +08:00
6ccffa64bc opt service 2023-04-26 23:36:53 +08:00
72afbd81ab opt some 2023-04-26 00:43:21 +08:00
6352d73905 opt some 2023-04-26 00:31:23 +08:00
281840b799 opt some 2023-04-26 00:30:27 +08:00
7a551aaa4b opt some 2023-04-26 00:27:55 +08:00
1a404786d6 add space 2023-04-25 22:41:42 +08:00
6df3a8144c opt some 2023-04-24 21:45:17 +08:00
73d66b7c37 opt some 2023-04-24 16:02:01 +08:00
c2d4cea08a opt some 2023-04-23 21:24:26 +08:00
8d30b1c0bb opt some 2023-04-23 12:19:35 +08:00
7c2269d02d opt const 2023-04-23 12:06:58 +08:00
4f28389ff6 opt some 2023-04-22 21:24:07 +08:00
1095aee85c opt some 2023-04-21 08:00:00 +08:00
73a0cd7bbc opt vimer.js 2023-04-20 21:00:56 +08:00
678af1039c opt some 2023-04-19 07:08:35 +08:00
96e501d949 add some 2023-04-18 07:32:32 +08:00
bb5fb5a908 add some 2023-04-17 21:21:17 +08:00
4e4a9d80ba add some 2023-04-17 21:16:16 +08:00
ddfac6f635 opt some 2023-04-16 23:34:52 +08:00
38b0e906a4 opt some 2023-04-16 22:59:26 +08:00
753c180947 opt some 2023-04-16 22:49:23 +08:00
fe97877a95 opt some 2023-04-16 22:37:30 +08:00
97e003f465 add some 2023-04-16 22:36:23 +08:00
fa23808cba add some 2023-04-16 22:36:19 +08:00
83c959a14e opt git 2023-04-16 16:01:33 +08:00
8fa1b390e5 add some 2023-04-16 16:01:10 +08:00
79f31f49e2 add repos 2023-04-16 12:56:58 +08:00
6a7433f0e5 opt some 2023-04-16 08:51:10 +08:00
2864019132 add git 2023-04-15 10:43:56 +08:00
4b37df12e3 add git 2023-04-15 00:26:30 +08:00
ee2bde3732 opt some 2023-04-14 16:08:22 +08:00
df6c21da07 opt some 2023-04-14 11:58:34 +08:00
422e209dc3 opt some 2023-04-14 10:20:51 +08:00
d784aec625 opt story 2023-04-13 23:43:52 +08:00
2f77add671 opt wiki 2023-04-13 02:52:58 +08:00
c33ddf4795 opt some 2023-04-12 11:51:05 +08:00
40d48142b6 opt wiki 2023-04-12 10:35:06 +08:00
d582280b0a opt kit 2023-04-10 00:25:39 +08:00
6e08e29b54 opt stack 2023-04-07 21:41:39 +08:00
1545b142fd opt type 2023-04-07 00:25:24 +08:00
c8ca9c7c63 add yac.type 2023-04-06 15:35:09 +08:00
6f5e22631c opt some 2023-04-03 01:05:16 +08:00
883ec3c0f2 opt some 2023-04-02 21:37:23 +08:00
9e2bc71dff opt some 2023-04-02 15:39:58 +08:00
ec75142f96 add expr 2023-04-02 08:22:03 +08:00
3edc006f07 opt yac 2023-03-31 13:01:27 +08:00
ca064bcc17 opt some 2023-03-30 08:34:49 +08:00
e1d3530226 opt yac 2023-03-30 07:38:42 +08:00
80e3d06108 opt yac 2023-03-29 15:15:12 +08:00
87c3467930 opt some 2023-03-29 14:05:08 +08:00
45ceab38a6 opt yac 2023-03-29 12:54:55 +08:00
b50bfeccf8 opt yac 2023-03-29 10:23:33 +08:00
1bc212f7f8 opt yac 2023-03-28 22:38:07 +08:00
086c51d70c opt base 2023-03-28 14:30:10 +08:00
b1550871e7 opt base 2023-03-28 14:21:27 +08:00
617b6df05a opt base 2023-03-28 00:37:27 +08:00
efe0a13e06 opt base 2023-03-27 21:39:18 +08:00
a564821a6a opt base 2023-03-27 21:22:53 +08:00
356d3b1670 opt serve 2023-03-26 22:17:58 +08:00
18ec7a9329 opt broad 2023-03-26 20:53:58 +08:00
9860966b10 opt broad 2023-03-26 17:55:40 +08:00
5ecd91626a opt dream 2023-03-26 02:52:39 +08:00
92febcf848 opt space 2023-03-26 01:57:30 +08:00
0915eeb700 opt serve 2023-03-25 11:44:04 +08:00
ae77de1a80 opt some 2023-03-24 20:47:50 +08:00
9eb655b0a1 opt some 2023-03-24 20:12:48 +08:00
0f6111bbea opt some 2023-03-24 16:08:09 +08:00
0adc958bc2 opt icebergs 2023-03-24 15:14:29 +08:00
af6cc2fcf3 opt ice 2023-03-24 08:04:27 +08:00
f0fd9ed204 opt some 2023-03-23 21:31:12 +08:00
fda4e3b889 opt aaa 2023-03-23 07:52:18 +08:00
1da4185842 opt aaa.user 2023-03-23 02:26:24 +08:00
cf184b384e opt mdb 2023-03-23 01:48:27 +08:00
860f79496a opt mdb 2023-03-23 00:13:29 +08:00
82f59f190b opt some 2023-03-22 18:12:52 +08:00
2473f3cf3e opt lock 2023-03-22 17:31:16 +08:00
735ba5c7c2 opt some 2023-03-20 18:39:22 +08:00
f000c676d9 opt git/status.go 2023-03-20 12:32:04 +08:00
774cee0d0c opt some 2023-03-20 11:50:00 +08:00
7130dfa0b5 opt some 2023-03-20 10:38:28 +08:00
793d158479 opt some 2023-03-20 10:25:59 +08:00
be89a05922 opt some 2023-03-20 01:26:34 +08:00
096553967d opt some 2023-03-20 00:49:13 +08:00
e5eb0509fa opt some 2023-03-20 00:33:01 +08:00
5859d830b0 opt some 2023-03-19 22:49:18 +08:00
9ff9b4ddd7 opt some 2023-03-19 22:34:37 +08:00
7f7ab6d62e opt some 2023-03-19 21:03:33 +08:00
e92db537fa opt some 2023-03-19 20:44:30 +08:00
3fda66455c opt code 2023-03-19 15:18:40 +08:00
0d62c5cccc opt code 2023-03-17 08:08:57 +08:00
12758d813d opt code 2023-03-16 23:05:30 +08:00
2edcda85e8 opt vimer.js 2023-03-15 20:37:25 +08:00
6283d157ce opt vimer.go 2023-03-14 13:40:53 +08:00
62e4fa032d opt xterm.go 2023-03-14 12:09:00 +08:00
df16437a34 opt some 2023-03-12 10:11:03 +08:00
48f9ff3255 opt some 2023-03-12 09:11:59 +08:00
8144a4dc2a opt some 2023-03-12 09:07:47 +08:00
e7380dd981 opt some 2023-03-12 08:45:29 +08:00
405bd641f3 opt some 2023-03-11 17:20:44 +08:00
2bb2b168f8 add git.token 2023-03-11 14:03:19 +08:00
68cc665d54 opt some 2023-03-10 23:27:15 +08:00
f4507d9a1a opt some 2023-03-10 22:14:39 +08:00
73ebb10b1e opt some 2023-03-10 13:37:08 +08:00
8215d26059 opt some 2023-03-10 13:35:20 +08:00
c843dd8ac9 opt some 2023-03-10 10:26:03 +08:00
94b7c4b4a1 opt some 2023-03-10 10:03:28 +08:00
4d8bf2ccd1 opt xterm 2023-03-10 07:24:31 +08:00
45c782207f opt code 2023-03-09 11:39:32 +08:00
f3e0b8bf58 opt code 2023-03-07 23:41:45 +08:00
96df24975d opt code 2023-03-07 03:44:27 +08:00
be829ce488 opt module 2023-03-06 18:59:16 +08:00
7c14a9735a opt word.js 2023-03-05 10:47:43 +08:00
9b8b7831fe opt web.chat.favor 2023-03-04 21:00:52 +08:00
e460316ede opt sh.go 2023-03-04 16:46:44 +08:00
b15547d281 opt some 2023-03-03 08:50:49 +08:00
d86c6db791 opt some 2023-03-02 20:38:20 +08:00
93e32aa8a0 opt some 2023-03-02 13:29:06 +08:00
5b0a86f019 opt some 2023-03-02 10:01:36 +08:00
4d1f2256fc opt some 2023-03-02 08:27:44 +08:00
1ab91f08ba opt some 2023-03-02 08:20:05 +08:00
51f24bbc48 opt some 2023-03-02 00:40:22 +08:00
bd25fad239 opt some 2023-03-01 20:23:27 +08:00
529f957b87 opt some 2023-03-01 19:51:29 +08:00
481ac90d2c opt some 2023-03-01 19:16:42 +08:00
94e00335f7 opt some 2023-03-01 19:11:24 +08:00
d1c8339de2 opt some 2023-03-01 18:48:17 +08:00
1
77d53af10f opt some 2023-03-01 13:36:06 +08:00
f75b554fe3 opt some 2023-03-01 12:43:40 +08:00
f5573b92f1 opt some 2023-02-28 21:53:10 +08:00
f95632b30a opt some 2023-02-28 21:45:56 +08:00
52d581c4b5 opt some 2023-02-28 17:17:16 +08:00
4cfa425690 opt some 2023-02-28 14:08:09 +08:00
9172b1d43c opt some 2023-02-28 11:02:23 +08:00
f508ad19b3 opt some 2023-02-28 10:50:33 +08:00
9a1c8c3eb7 opt some 2023-02-26 13:44:25 +08:00
cbd8d485de opt some 2023-02-26 11:01:36 +08:00
948cffed88 opt some 2023-02-26 10:47:50 +08:00
shaoying
fba8c1251b opt some 2023-02-26 09:48:25 +08:00
2eccc6c00a opt some 2023-02-26 08:55:01 +08:00
7a76758128 opt some 2023-02-26 08:53:00 +08:00
shaoying
9e3aacbb8d opt some 2023-02-26 08:50:37 +08:00
shaoying
c9b96e1312 opt some 2023-02-26 08:41:14 +08:00
shaoying
09d6ad4bce opt some 2023-02-26 08:26:10 +08:00
shaoying
09fc9c50ee opt some 2023-02-26 00:56:46 +08:00
2f9b12de83 opt some 2023-02-26 00:43:48 +08:00
85f3ab8503 opt some 2023-02-26 00:27:49 +08:00
00aea9d1c9 opt some 2023-02-26 00:02:40 +08:00
harveyshao
01f43866c7 opt some 2023-02-24 00:01:20 +08:00
harveyshao
351809f085 opt some 2023-02-21 21:12:27 +08:00
shylinux
ecf3d4d888 opt some 2023-02-21 15:53:32 +08:00
shylinux
87fd091e09 opt some 2023-02-21 15:26:24 +08:00
2fb38a05f9 opt some 2023-02-21 14:57:12 +08:00
1ce9dae8b8 opt some 2023-02-21 14:37:24 +08:00
harveyshao
b825c83e3a opt intshell 2023-02-21 13:26:41 +08:00
shylinux
cf36511cc5 opt some 2023-02-20 19:42:40 +08:00
harveyshao
d48225bfe6 opt code 2023-02-20 19:17:08 +08:00
shylinux
cb16cee60e opt some 2023-02-20 01:12:20 +08:00
harveyshao
8d084c3c2d opt module 2023-02-20 01:07:25 +08:00
harveyshao
80df68adf5 opt some 2023-02-19 20:26:17 +08:00
harveyshao
4156fac09a opt some 2023-02-19 19:52:49 +08:00
shylinux
7def4f0f3f opt some 2023-02-19 19:38:18 +08:00
harveyshao
05ce53dcc8 opt code 2023-02-19 19:19:36 +08:00
harveyshao
6127d47aee opt code 2023-02-19 19:14:25 +08:00
shylinux
9207059fce opt some 2023-02-19 09:58:44 +08:00
harveyshao
c936ec4401 opt some 2023-02-18 13:05:53 +08:00
shaoying
6cf4b75d5f opt some 2023-02-18 09:07:22 +08:00
a17a4ebfec opt some 2023-02-18 07:50:50 +08:00
shylinux
19524faa67 opt some 2023-02-18 00:09:41 +08:00
shylinux
baf9763256 opt some 2023-02-17 22:14:24 +08:00
harveyshao
4121f60275 opt some 2023-02-17 17:00:20 +08:00
shylinux
42ddaeab2f opt some 2023-02-17 15:23:30 +08:00
shylinux
b3a0cb60e4 opt some 2023-02-17 15:08:52 +08:00
harveyshao
aa2e020629 opt some 2023-02-15 10:30:09 +08:00
harveyshao
eceea605ad opt windows 2023-02-14 19:48:00 +08:00
harveyshao
0ef5509ccf opt some 2023-02-13 22:30:39 +08:00
shaoying
bc93ec6fa0 opt some 2023-02-13 09:06:06 +08:00
harveyshao
64e1de9a3e opt key.js 2023-02-11 13:58:43 +08:00
harveyshao
3b6e9b4ea0 opt search.js 2023-01-31 00:00:19 +08:00
harveyshao
04c9cb0062 opt search.js 2023-01-29 23:18:56 +08:00
harveyshao
d823900ef4 opt panel 2023-01-28 23:18:11 +08:00
harveyshao
1d4cd0087e opt action.js 2023-01-27 20:54:10 +08:00
harveyshao
3625742900 opt inner.js 2023-01-26 13:26:43 +08:00
harveyshao
b31a217baf opt inner.js 2023-01-25 23:13:35 +08:00
harveyshao
06bffb9218 opt vimer.js 2023-01-25 00:35:49 +08:00
harveyshao
40f562ae7a opt log.debug 2023-01-23 14:20:17 +08:00
harveyshao
15da224680 opt log 2023-01-20 14:51:05 +08:00
harveyshao
c07994d42b opt onappend 2023-01-18 17:02:05 +08:00
harveyshao
0cd92ae347 opt onappend 2023-01-17 23:28:31 +08:00
harveyshao
805fe37ec8 opt frame.js 2023-01-16 22:21:00 +08:00
harveyshao
7bf0bd877c opt index.css 2023-01-15 18:00:13 +08:00
harveyshao
a350a17b69 opt some 2023-01-14 08:44:42 +08:00
harveyshao
5ddc851aad opt index.css 2023-01-13 16:11:34 +08:00
harveyshao
0f3eea8b43 opt some 2023-01-12 22:26:10 +08:00
harveyshao
48c9e77cea opt inner.css 2023-01-12 20:21:29 +08:00
harveyshao
511204504c add dark 2023-01-10 21:42:28 +08:00
harveyshao
d131489b19 opt some 2023-01-08 09:55:06 +08:00
harveyshao
98c3461152 opt some 2023-01-04 21:34:50 +08:00
73e782e684 opt some 2023-01-04 18:36:25 +08:00
6509bb8290 opt some 2023-01-03 21:36:30 +08:00
98683cc779 opt some 2023-01-03 15:11:46 +08:00
harveyshao
ae91a34c5a opt some 2023-01-02 20:28:59 +08:00
harveyshao
0d9931c1fb opt some 2023-01-02 18:30:57 +08:00
harveyshao
3a42c0aff2 opt some 2023-01-02 14:20:51 +08:00
harveyshao
39d8d94a7a opt some 2023-01-01 23:17:23 +08:00
harveyshao
66f7ec1b72 opt some 2023-01-01 22:45:10 +08:00
harveyshao
3ad6a45ec6 opt some 2022-12-31 21:00:45 +08:00
harveyshao
132dc7c758 opt some 2022-12-31 18:38:44 +08:00
harveyshao
90037da808 opt some 2022-12-30 21:08:10 +08:00
shaoying
18b14bf393 opt some 2022-12-27 11:52:08 +08:00
harveyshao
9855e122db opt page/index.css 2022-12-26 21:31:11 +08:00
harveyshao
bb3b898dc7 opt some 2022-12-26 15:25:26 +08:00
shaoying
5f3c3e4b36 opt some 2022-12-26 09:09:19 +08:00
shaoying
24bc1861ab opt some 2022-12-26 09:07:11 +08:00
shaoying
80758f7259 opt some 2022-12-26 08:56:09 +08:00
harveyshao
4d2ae25ee0 opt some 2022-12-24 23:44:14 +08:00
harveyshao
8c571fdd2f opt some 2022-12-24 15:13:38 +08:00
56b8c16d4c opt some 2022-12-24 10:14:31 +08:00
harveyshao
9a36e8fcc1 opt some 2022-12-23 20:18:37 +08:00
harveyshao
314655bc5e opt some 2022-12-23 11:13:52 +08:00
harveyshao
3541f8010a opt some 2022-12-22 20:43:46 +08:00
harveyshao
3389be9f74 opt vimer 2022-12-22 12:04:29 +08:00
harveyshao
c7699c0049 opt some 2022-12-21 16:23:03 +08:00
6d6408bc1f opt some 2022-12-19 19:47:22 +08:00
harveyshao
bdc6a96a0d opt vimer.css 2022-12-19 19:03:22 +08:00
shaoying
48eab473bd opt html 2022-12-17 09:11:41 +08:00
16618a388f opt lark 2022-12-15 22:04:21 +08:00
harveyshao
0274061161 opt some 2022-12-15 15:20:22 +08:00
3ef13accf7 opt some 2022-12-15 14:51:47 +08:00
harveyshao
48de925bb3 opt some 2022-12-15 11:15:22 +08:00
harveyshao
43fe4a588c opt some 2022-12-15 10:48:09 +08:00
harveyshao
038de957dc opt some 2022-12-15 10:46:42 +08:00
shaoying
8030b26216 opt some 2022-12-15 09:14:50 +08:00
harveyshao
d1dba9db37 opt chrome 2022-12-14 23:00:21 +08:00
harveyshao
7fcbb658a3 opt chrome 2022-12-14 13:41:49 +08:00
harveyshao
cfdced2a57 opt some 2022-12-13 21:50:46 +08:00
harveyshao
60a275897f opt vim 2022-12-13 21:26:30 +08:00
harveyshao
d98d392a61 opt vim 2022-12-12 19:42:06 +08:00
harveyshao
3de5a53832 opt bash 2022-12-11 20:18:08 +08:00
shaoying
b791c5aa77 opt some 2022-12-09 11:39:18 +08:00
harveyshao
d8ee0e91c3 opt some 2022-12-08 22:42:39 +08:00
harveyshao
1fe85e82f7 opt some 2022-12-08 21:58:40 +08:00
harveyshao
888060e32c opt some 2022-12-08 21:48:38 +08:00
harveyshao
ba581c4ea7 opt git 2022-12-08 20:57:08 +08:00
harveyshao
72c79bc279 opt git 2022-12-07 21:46:28 +08:00
harveyshao
8dcde629d8 opt git 2022-12-07 13:52:16 +08:00
harveyshao
35c5a799f1 opt git 2022-12-07 03:12:29 +08:00
bff7fab4d6 opt some 2022-12-05 22:44:15 +08:00
harveyshao
9f3e99dcf7 opt code 2022-12-05 22:39:37 +08:00
harveyshao
9e8563071a opt git 2022-12-05 16:47:18 +08:00
1a787a9a14 opt some 2022-12-05 09:46:36 +08:00
8ecadb8443 opt some 2022-12-04 20:36:01 +08:00
harveyshao
961a5903d9 opt some 2022-12-04 20:29:02 +08:00
shaoying
8a6e6a7cde opt some 2022-12-04 19:12:34 +08:00
harveyshao
abf843548f opt code 2022-12-04 15:38:41 +08:00
harveyshao
20e53d3885 opt code 2022-12-04 13:04:59 +08:00
shaoying
30fdb259c6 opt code 2022-12-04 09:17:10 +08:00
harveyshao
d2027d19ea opt git 2022-12-03 23:14:16 +08:00
harveyshao
bfddbb18b6 opt some 2022-12-03 22:46:29 +08:00
harveyshao
9463b2a845 opt wiki 2022-12-03 22:39:50 +08:00
harveyshao
8f273b15c0 opt chat 2022-12-03 22:15:27 +08:00
shaoying
4e9b49a970 opt chat 2022-12-03 18:05:38 +08:00
b8a3304d8b opt some 2022-12-03 14:39:38 +08:00
harveyshao
9b46681751 opt some 2022-12-03 14:26:29 +08:00
ccca1f74b0 opt base 2022-12-03 14:07:02 +08:00
harveyshao
583b820e65 opt base 2022-12-03 13:46:12 +08:00
harveyshao
261ef042b9 opt base 2022-12-03 13:42:26 +08:00
harveyshao
8772a64efa opt base 2022-12-03 13:40:45 +08:00
harveyshao
fadcac3ddb opt base 2022-12-03 13:33:05 +08:00
shaoying
73580d0df5 opt web 2022-12-03 09:38:31 +08:00
harveyshao
b9b56ace5b opt some 2022-12-03 00:10:00 +08:00
harveyshao
79792837a5 opt some 2022-12-02 13:53:54 +08:00
shaoying
c9e6fb5a50 opt some 2022-12-02 11:40:53 +08:00
harveyshao
c92eda968f opt weg 2022-12-01 23:36:23 +08:00
harveyshao
e39c6cdf8f opt some 2022-12-01 09:59:45 +08:00
harveyshao
46193f5f2f opt some 2022-12-01 01:25:23 +08:00
fbdca73905 opt some 2022-11-30 17:43:46 +08:00
shaoying
0c50d80214 opt some 2022-11-30 17:36:03 +08:00
harveyshao
1355ba9432 opt some 2022-11-30 12:43:19 +08:00
shaoying
15afccc923 opt some 2022-11-30 12:08:21 +08:00
harveyshao
050e796486 opt some 2022-11-29 23:56:28 +08:00
harveyshao
0f65bdb891 opt space 2022-11-29 15:59:46 +08:00
shaoying
577dd9cafc opt some 2022-11-29 13:08:06 +08:00
harveyshao
ee976ee357 opt space.go 2022-11-29 11:30:23 +08:00
shaoying
a0ec7370b1 opt space 2022-11-29 08:55:52 +08:00
harveyshao
c0e59eec3c opt some 2022-11-29 00:51:09 +08:00
harveyshao
9c4e24cc5c opt web.serve 2022-11-28 20:32:41 +08:00
harveyshao
5ed8e75b40 opt web 2022-11-28 10:22:19 +08:00
shaoying
b4159f31ad opt web 2022-11-28 09:11:01 +08:00
harveyshao
ec83a5b39c opt web 2022-11-28 00:36:04 +08:00
shaoying
6d7e26b887 opt some 2022-11-27 09:32:00 +08:00
harveyshao
20a3aa3d84 opt nfs 2022-11-27 01:34:03 +08:00
harveyshao
12e35976ee opt tcp 2022-11-26 21:01:52 +08:00
harveyshao
b35ef05a43 opt ssh 2022-11-26 19:35:27 +08:00
shaoying
a14a231e8a opt ssh 2022-11-26 18:17:35 +08:00
harveyshao
bf1edda0e7 opt nfs 2022-11-26 15:02:19 +08:00
harveyshao
40064c2c9c opt nfs 2022-11-26 14:54:49 +08:00
shaoying
3ac6548226 opt nfs 2022-11-26 12:29:41 +08:00
harveyshao
244dab2e4b opt mdb 2022-11-25 21:05:10 +08:00
harveyshao
aacb485deb opt mdb 2022-11-25 14:38:46 +08:00
shaoying
97cc535496 opt mdb 2022-11-25 12:59:22 +08:00
harveyshao
90eeb3e258 opt mdb 2022-11-24 23:37:02 +08:00
shaoying
1301439db2 opt mdb 2022-11-24 13:24:09 +08:00
harveyshao
33170bd6a9 opt gdb 2022-11-24 10:56:45 +08:00
shaoying
550d08092f opt some 2022-11-24 02:04:42 +08:00
harveyshao
8a9bb181a5 opt some 2022-11-24 00:28:33 +08:00
shaoying
ad0a0e95df opt aaa 2022-11-23 18:23:17 +08:00
harveyshao
ffe18771a1 opt aaa 2022-11-23 13:37:27 +08:00
shaoying
3eb075be04 opt some 2022-11-23 00:50:38 +08:00
harveyshao
bd503579ad opt some 2022-11-23 00:13:00 +08:00
harveyshao
f8a408131d add offer 2022-11-22 15:13:33 +08:00
shaoying
969b4feabe opt some 2022-11-22 10:04:14 +08:00
harveyshao
b187f2c65b opt cli 2022-11-22 00:07:27 +08:00
harveyshao
f57e198c7e opt some 2022-11-20 23:39:59 +08:00
shaoying
028a6ba82b opt aaa 2022-11-20 12:07:54 +08:00
be05bc9a5f opt some 2022-11-20 01:47:21 +08:00
shaoying
b2b20bcc1b opt chat 2022-11-20 01:38:31 +08:00
harveyshao
41021ff55b opt some 2022-11-19 23:13:39 +08:00
harveyshao
15c6d3f064 opt chat 2022-11-19 22:54:37 +08:00
deca0a8e8d opt some 2022-11-18 22:39:04 +08:00
harveyshao
db4ecebd45 opt dream 2022-11-18 21:59:26 +08:00
harveyshao
6c10855648 opt chat 2022-11-18 14:40:42 +08:00
harveyshao
e2f4769e95 opt keyboard 2022-11-17 23:52:54 +08:00
harveyshao
8e8470d910 add chat/favor.go 2022-11-17 15:43:25 +08:00
shaoying
5db51d4724 opt chat 2022-11-17 09:29:28 +08:00
harveyshao
a85e701e5b opt some 2022-11-16 21:29:46 +08:00
harveyshao
3d8fd432dc opt chat 2022-11-16 13:13:16 +08:00
496c042c7c opt some 2022-11-15 23:14:08 +08:00
harveyshao
fc4a5e66bb opt chat 2022-11-15 23:01:47 +08:00
harveyshao
a6f0a18bf4 opt chat 2022-11-15 10:42:31 +08:00
harveyshao
5b3b3df314 opt some 2022-11-14 23:19:17 +08:00
harveyshao
2f43d52c2d opt chat 2022-11-14 15:26:16 +08:00
harveyshao
ec9ce046da opt role 2022-11-14 01:27:01 +08:00
harveyshao
1c85d50c56 opt chat 2022-11-13 23:32:15 +08:00
harveyshao
7addfa5581 opt some 2022-11-13 14:52:46 +08:00
harveyshao
398be22600 opt chat 2022-11-12 22:14:56 +08:00
harveyshao
20b60b11bf opt chat 2022-11-12 00:33:19 +08:00
harveyshao
975791294c opt chat 2022-11-11 14:13:38 +08:00
shaoying
32670368ee opt chat 2022-11-11 08:42:28 +08:00
shaoying
73f304d185 opt some 2022-11-11 00:25:50 +08:00
harveyshao
4728ea3244 opt feel.go 2022-11-10 23:09:00 +08:00
harveyshao
8036ff4420 opt some 2022-11-09 21:48:12 +08:00
harveyshao
e5c6fee92b opt inner.go 2022-11-08 23:44:00 +08:00
harveyshao
c27f669787 opt some 2022-11-06 21:41:38 +08:00
harveyshao
a63831976b opt some 2022-11-05 23:31:24 +08:00
harveyshao
87bb0d8dbc opt wiki 2022-11-05 14:46:07 +08:00
shaoying
c02663d87d opt some 2022-11-05 09:13:05 +08:00
harveyshao
1dea5983e1 opt some 2022-11-04 23:15:07 +08:00
shaoying
7ce4da96ef opt chart.go 2022-11-04 18:31:45 +08:00
harveyshao
7b9ada65ac opt chart.go 2022-11-04 16:25:39 +08:00
shaoying
b0346ea915 opt wiki 2022-11-04 09:36:27 +08:00
harveyshao
12a7ac761a opt wiki 2022-11-04 02:40:42 +08:00
harveyshao
e2549d0f82 opt team 2022-11-03 14:21:30 +08:00
harveyshao
e7721f1c6c opt team 2022-11-02 15:17:32 +08:00
harveyshao
56576f958f opt some 2022-11-01 20:00:39 +08:00
shaoying
ccea229435 opt some 2022-11-01 08:57:07 +08:00
harveyshao
d09107dccb opt some 2022-10-29 14:09:48 +08:00
harveyshao
1274909618 opt some 2022-10-29 12:56:52 +08:00
shaoying
13856edd6b opt some 2022-10-29 09:30:00 +08:00
harveyshao
c4108d5875 opt input 2022-10-28 22:42:54 +08:00
harveyshao
816ea9f3b4 opt some 2022-10-28 14:16:44 +08:00
shaoying
cfa411d9db opt proto.js 2022-10-27 09:21:02 +08:00
harveyshao
19e4f6df22 opt some 2022-10-25 21:33:03 +08:00
harveyshao
4a63b654bd opt some 2022-10-24 17:38:08 +08:00
harveyshao
f1994a0837 opt status.go 2022-10-24 16:18:25 +08:00
shaoying
d76ecc9cbc opt some 2022-10-23 14:26:57 +08:00
harveyshao
f0ce38fdc1 opt some 2022-10-23 12:49:40 +08:00
harveyshao
f93de948d6 opt some 2022-10-23 00:18:01 +08:00
harveyshao
1348b8a1f5 opt some 2022-10-22 16:36:20 +08:00
harveyshao
97442c1104 opt some 2022-10-22 11:24:49 +08:00
harveyshao
8dbd66f22b opt some 2022-10-21 22:18:56 +08:00
harveyshao
6959aae1fe opt some 2022-10-21 11:38:58 +08:00
harveyshao
4f44433478 opt some 2022-10-20 20:27:39 +08:00
shaoying
1991db8302 opt some 2022-10-20 09:40:40 +08:00
harveyshao
4554de495d opt some 2022-10-19 21:56:37 +08:00
harveyshao
3eadc753ab opt some 2022-10-19 12:28:08 +08:00
harveyshao
f003a6b8da opt some 2022-10-18 23:38:45 +08:00
harveyshao
30348175f4 opt some 2022-10-18 12:23:52 +08:00
shaoying
623cb165e2 opt some 2022-10-18 09:28:58 +08:00
harveyshao
a54df18a51 add goods 2022-10-17 23:04:07 +08:00
harveyshao
81b1bb8fc2 opt some 2022-10-17 14:06:32 +08:00
shaoying
fbc725d641 opt some 2022-10-17 08:15:39 +08:00
harveyshao
9b18b12f77 opt some 2022-10-16 22:39:46 +08:00
harveyshao
f32cff5473 opt some 2022-10-16 13:55:43 +08:00
shaoying
1aec5bd434 opt some 2022-10-16 08:20:02 +08:00
harveyshao
47a1dc4cdd opt plan 2022-10-15 23:18:01 +08:00
harveyshao
56ce985dad opt some 2022-10-15 11:45:52 +08:00
harveyshao
5669803c4f opt code 2022-10-15 01:50:02 +08:00
harveyshao
218b48f3c5 opt some 2022-10-08 09:22:51 +08:00
shaoying
7dcf971df4 opt some 2022-10-08 07:10:05 +08:00
harveyshao
3c7655b804 opt some 2022-10-07 22:39:47 +08:00
harveyshao
12e30f99c3 opt some 2022-10-07 13:48:33 +08:00
shaoying
c6002519fd opt some 2022-10-06 08:59:46 +08:00
shaoying
484a2acf77 opt some 2022-10-04 22:59:23 +08:00
harveyshao
ff410adcc5 opt some 2022-10-04 15:23:22 +08:00
harveyshao
801acac1da opt some 2022-10-04 12:22:54 +08:00
shaoying
dd0a98a73e opt some 2022-10-04 08:29:04 +08:00
shylinux@163.com
ecddb6cdae opt some 2022-10-03 07:12:03 +08:00
shylinux@163.com
1382ed1ff3 opt some 2022-10-02 18:55:26 +08:00
shylinux@163.com
c161d739f4 opt some 2022-09-30 19:53:19 +08:00
3d367e7c5a opt some 2022-09-29 21:22:49 +08:00
193e55602d opt some 2022-09-29 18:41:15 +08:00
3a7338a425 opt chat 2022-09-29 17:18:14 +08:00
3208f8bf8a opt chat 2022-09-29 10:34:35 +08:00
shylinux@163.com
3efdc8b7dc opt some 2022-09-25 22:50:10 +08:00
dd83d6040d opt some 2022-09-24 11:14:32 +08:00
shylinux@163.com
25ae63ec50 opt some 2022-09-24 10:16:36 +08:00
shylinux@163.com
0b9ec7cab0 add iframe 2022-09-23 23:38:39 +08:00
shylinux@163.com
a91766548a opt code 2022-09-22 16:08:43 +08:00
shaoying
58c1c86504 opt some 2022-09-21 20:37:36 +08:00
shylinux@163.com
57189da537 opt some 2022-09-21 17:45:17 +08:00
shaoying
2ec04c32be opt some 2022-09-20 12:42:35 +08:00
shylinux@163.com
987e84f428 opt vimer 2022-09-19 23:07:20 +08:00
shylinux@163.com
1b21938136 opt xterm 2022-09-17 22:42:24 +08:00
shylinux@163.com
276c97a050 opt some 2022-09-16 18:27:43 +08:00
shylinux@163.com
be9e03a440 opt some 2022-09-16 12:44:11 +08:00
shylinux@163.com
a5a6aa53a8 opt some 2022-09-15 21:04:41 +08:00
shylinux@163.com
7505c4f55f opt some 2022-09-14 22:21:43 +08:00
shylinux@163.com
604213d2f0 opt some 2022-09-14 13:37:44 +08:00
shylinux@163.com
c3cc658bad opt some 2022-09-13 15:35:03 +08:00
shaoying
db41b748ff opt some 2022-09-13 13:08:25 +08:00
shylinux@163.com
634054425d opt some 2022-09-13 11:42:33 +08:00
shylinux@163.com
8d3bb899b1 opt some 2022-09-12 14:14:16 +08:00
ef8367a7af opt some 2022-09-12 08:31:09 +08:00
fcdc05ca0c opt some 2022-09-11 22:11:13 +08:00
shylinux@163.com
44527ed4ef opt some 2022-09-11 19:18:12 +08:00
shylinux@163.com
e20bcc8c85 opt some 2022-09-09 07:41:32 +08:00
e2b2212724 opt some 2022-09-08 13:35:19 +08:00
64b7398f9b opt some 2022-09-04 23:01:56 +08:00
d93e5b56ad opt some 2022-09-03 16:43:13 +08:00
83136bc19f opt some 2022-08-30 07:22:00 +08:00
shylinux@163.com
0ead20ce3b opt some 2022-08-30 07:20:34 +08:00
shylinux@163.com
cab4df37b9 opt some 2022-08-29 09:14:48 +08:00
harveyshao
4c981b2236 opt some 2022-08-28 20:35:26 +08:00
d8310d576b opt some 2022-08-28 18:14:41 +08:00
31fdb8af5a opt some 2022-08-28 11:51:10 +08:00
3bb6a19156 opt some 2022-08-28 09:11:07 +08:00
d83c1f67ed opt some 2022-08-28 09:09:35 +08:00
shylinux@163.com
49b2580fbc opt some 2022-08-27 21:54:47 +08:00
shylinux@163.com
71d5799d8c opt some 2022-08-27 18:50:10 +08:00
shylinux@163.com
db64c6a7e1 opt some 2022-08-26 22:45:04 +08:00
shylinux@163.com
8f12abd2f5 opt some 2022-08-26 10:38:37 +08:00
7537ef343b opt some 2022-08-24 20:54:48 +08:00
28724b07d9 opt some 2022-08-24 16:35:27 +08:00
b196af799c opt some 2022-08-24 07:59:55 +08:00
55757db1d8 opt some 2022-08-21 19:03:16 +08:00
harveyshao
0ff9e79ac4 opt some 2022-08-21 17:35:00 +08:00
0fa74bd96e opt some 2022-08-21 15:56:48 +08:00
f06013c0c2 opt some 2022-08-19 19:18:54 +08:00
a89e008c79 opt some 2022-08-19 11:37:48 +08:00
14aa9691c8 opt git 2022-08-19 11:11:58 +08:00
108001e500 opt git 2022-08-19 11:10:08 +08:00
6852ba21be opt some 2022-08-17 20:58:44 +08:00
d3fb476785 opt some 2022-08-17 20:28:43 +08:00
760de54c11 opt some 2022-08-17 20:23:46 +08:00
harveyshao
87e3cbcdde opt some 2022-08-17 09:10:22 +08:00
harveyshao
37b8b8434d opt some 2022-08-17 08:41:24 +08:00
9d388114be opt bash 2022-08-17 05:18:30 +08:00
harveyshao
5d485bb0dd opt ssh 2022-08-16 19:39:08 +08:00
harveyshao
e5dade2619 opt some 2022-08-16 18:00:32 +08:00
harveyshao
325d28f090 opt ssh 2022-08-16 17:39:12 +08:00
harveyshao
bf34e9ceee opt ssh 2022-08-16 14:38:36 +08:00
4bbdd8d9e4 opt misc 2022-08-16 10:41:05 +08:00
056684387a opt some 2022-08-15 12:26:06 +08:00
shy
87e8036cfa opt some 2022-08-14 22:17:19 +08:00
shy
70c4544bf7 opt some 2022-08-14 22:13:50 +08:00
shy
6e1cc3119f opt some 2022-08-11 15:33:07 +08:00
harveyshao
c329a577ea opt some 2022-08-09 15:48:26 +08:00
harveyshao
268d363a57 opt some 2022-08-09 15:14:53 +08:00
harveyshao
c676c9d024 opt some 2022-08-09 14:34:32 +08:00
harveyshao
e2e04b2b3f opt some 2022-08-09 11:00:48 +08:00
harveyshao
e232ffc59e opt some 2022-08-08 14:49:06 +08:00
harveyshao
5b764758dd opt some 2022-08-08 10:22:56 +08:00
harveyshao
99d94aece8 opt some 2022-08-08 08:23:55 +08:00
shy
2af002441a add media 2022-08-08 08:22:25 +08:00
shy
b6aa318322 opt some 2022-08-07 11:09:33 +08:00
harveyshao
2ba30d881d opt some 2022-08-06 02:02:48 +08:00
harveyshao
8e4751719e opt some 2022-08-06 02:01:34 +08:00
harveyshao
a6e422a0bb opt some 2022-08-06 00:29:26 +08:00
harveyshao
ffccc0c321 opt some 2022-08-06 00:08:48 +08:00
harveyshao
3b5b2b717b opt chat 2022-08-05 23:44:09 +08:00
shy
441013508b opt chat 2022-08-05 18:57:00 +08:00
harveyshao
11b8bfd6be opt chat 2022-08-05 17:28:14 +08:00
harveyshao
55c606516a opt chat 2022-08-05 17:27:55 +08:00
harveyshao
e516bf6ec8 opt base 2022-08-03 08:14:22 +08:00
harveyshao
2e9f91192f opt some 2022-08-01 20:07:56 +08:00
harveyshao
f226c5f08e opt some 2022-08-01 20:07:11 +08:00
harveyshao
8e7a403b35 opt base 2022-08-01 18:09:36 +08:00
harveyshao
1ec7aeff79 opt some 2022-07-25 22:00:00 +08:00
harveyshao
6329b66df2 opt some 2022-07-25 20:13:27 +08:00
harveyshao
ff6d1469b8 opt some 2022-07-25 18:12:32 +08:00
harveyshao
b568aa8c8b opt some 2022-07-25 15:08:42 +08:00
shy
9fb2e967a9 opt some 2022-07-25 15:07:05 +08:00
harveyshao
cbdabc9a74 opt xterm.go 2022-07-25 08:11:17 +08:00
harveyshao
ab05ddc76d add hash.lock 2022-07-24 19:57:11 +08:00
shy
e829036e17 opt xterm.js 2022-07-24 08:04:30 +08:00
harveyshao
44c610c203 opt xterm 2022-07-23 05:23:07 +08:00
shy
fa7d3d6b4e add xterm.go 2022-07-23 00:03:51 +08:00
shy
3fe511f14a add location.go 2022-07-23 00:03:04 +08:00
harveyshao
58fb2e29f4 opt some 2022-07-18 15:05:02 +08:00
harveyshao
fcd0630eca opt some 2022-07-17 23:52:59 +08:00
shy
82c8f3a18b opt some 2022-07-17 22:28:21 +08:00
shy
d77e8161d5 opt some 2022-07-17 17:38:35 +08:00
harveyshao
35b06a3526 opt some 2022-07-17 17:33:03 +08:00
harveyshao
dfa2834e61 opt div.go 2022-07-17 12:51:55 +08:00
harveyshao
ba52eb6435 opt vimer.js 2022-07-14 23:19:45 +08:00
harveyshao
fddbde8c44 opt vimer.js 2022-07-14 18:38:53 +08:00
harveyshao
475250ec71 opt vimer.js 2022-07-13 17:47:07 +08:00
harveyshao
05c4c401e6 opt vimer.js 2022-07-13 08:20:07 +08:00
harveyshao
530aff21c6 opt inner.css 2022-07-11 22:39:50 +08:00
harveyshao
a2cdf26148 opt some 2022-07-11 08:41:09 +08:00
harveyshao
c0d06e18c2 opt inner.js 2022-07-10 22:32:46 +08:00
harveyshao
2bb502bdeb opt some 2022-07-10 05:53:51 +08:00
harveyshao
994f64a274 opt some 2022-07-09 21:05:10 +08:00
harveyshao
5e951c4c36 add topic 2022-07-09 20:13:52 +08:00
harveyshao
34ffc9bf73 opt css 2022-07-09 20:13:21 +08:00
shy
0f848feb53 opt some 2022-07-08 01:02:03 +08:00
shy
cb8f5261f5 opt mobile 2022-07-07 15:37:11 +08:00
harveyshao
f306d7a4cb opt data.js 2022-07-07 13:06:46 +08:00
harveyshao
6da7784838 opt draw.js 2022-07-05 23:49:52 +08:00
harveyshao
7d388597eb opt draw.js 2022-07-05 18:00:53 +08:00
harveyshao
6ed14de57c opt can 2022-07-05 08:49:05 +08:00
harveyshao
51d87c3f54 opt search.js 2022-07-04 18:02:12 +08:00
harveyshao
cb80b795ae opt header.go 2022-07-04 10:36:22 +08:00
shaoying
eed11c051a opt footer.js 2022-07-03 21:03:20 +08:00
shaoying
1a79b2edcd opt some 2022-07-02 22:57:15 +08:00
harveyshao
49e1558133 opt some 2022-07-02 12:34:12 +08:00
shy
5587905708 opt some 2022-07-01 21:32:23 +08:00
harveyshao
1ee314e4f5 opt some 2022-06-27 18:06:53 +08:00
harveyshao
8108f8c13b opt some 2022-06-26 14:29:28 +08:00
shy
46f74c05b0 opt some 2022-06-26 14:03:17 +08:00
shy
47c1696b73 opt forever 2022-06-25 17:37:41 +08:00
harveyshao
0d532edfb8 opt some 2022-06-19 22:31:09 +08:00
harveyshao
ace01e577c opt some 2022-06-19 20:02:26 +08:00
harveyshao
13df83be17 opt some 2022-06-19 16:00:44 +08:00
harveyshao
c3c91d5eb9 opt some 2022-06-19 12:12:58 +08:00
harveyshao
98f4835f9d add oauth 2022-06-19 08:18:50 +08:00
harveyshao
70ee348ae9 opt some 2022-06-14 17:16:33 +08:00
harveyshao
7d62e43420 opt some 2022-06-12 17:27:14 +08:00
harveyshao
ea23392f7d opt some 2022-06-12 11:38:01 +08:00
harveyshao
809b18abb7 opt some 2022-06-07 11:44:50 +08:00
harveyshao
698182f5f6 opt some 2022-06-05 17:23:46 +08:00
shy
8782f01ec8 opt some 2022-06-05 17:18:23 +08:00
harveyshao
db15dfed1b opt some 2022-06-04 16:21:52 +08:00
harveyshao
7bead22151 add java 2022-06-04 10:45:18 +08:00
harveyshao
3d0dd2c8cd add coder 2022-06-03 01:11:09 +08:00
harveyshao
82c33e211b opt some 2022-05-30 19:25:00 +08:00
harveyshao
6f15537dde opt some 2022-05-26 14:29:05 +08:00
harveyshao
02050077f3 opt some 2022-05-23 22:40:16 +08:00
harveyshao
7c0892cba7 opt some 2022-05-23 08:31:26 +08:00
harveyshao
f98cf8c113 opt some 2022-05-21 14:38:27 +08:00
shaoying
3cf004299e opt some 2022-05-17 22:00:10 +08:00
harveyshao
9357c73cd7 opt some 2022-05-17 10:41:20 +08:00
harveyshao
2ccabb3d45 opt some 2022-05-17 10:15:59 +08:00
harveyshao
7747e183b9 opt some 2022-05-16 12:03:18 +08:00
harveyshao
7ca0a478fd opt index 2022-05-16 11:26:39 +08:00
harveyshao
e7fd0d1555 opt vimer 2022-05-16 01:37:09 +08:00
harveyshao
30b0d12f6f opt vimer 2022-05-16 00:56:32 +08:00
harveyshao
13a9b81ea4 opt vimer 2022-05-15 23:28:56 +08:00
harveyshao
ef81d0b5a2 opt some 2022-05-14 15:23:44 +08:00
harveyshao
6698cf3e0d opt some 2022-05-10 11:08:51 +08:00
harveyshao
629744f18c opt some 2022-05-09 21:31:44 +08:00
harveyshao
edf04423eb opt some 2022-05-09 10:22:37 +08:00
harveyshao
626b765de5 opt zml 2022-05-09 10:07:03 +08:00
harveyshao
5630f42efc add complete 2022-05-08 18:27:28 +08:00
harveyshao
e4d49f4c1f opt some 2022-05-07 17:33:02 +08:00
harveyshao
47ea2e37a5 add parse 2022-05-04 23:24:10 +08:00
harveyshao
9e1fd3acee opt some 2022-04-30 18:31:25 +08:00
harveyshao
bdc21c1207 add oauth 2022-04-30 15:15:11 +08:00
harveyshao
c9ad3cdcc9 opt some 2022-04-30 02:04:06 +08:00
shaoying
7ba2c139dc opt some 2022-04-29 08:17:32 +08:00
harveyshao
8df81a51a8 opt some 2022-04-25 15:11:39 +08:00
harveyshao
d3e32837c4 opt some 2022-04-25 15:07:41 +08:00
harveyshao
52567c7f3e opt some 2022-04-23 20:45:42 +08:00
shy
1db8b1ee52 opt some 2022-04-23 20:28:10 +08:00
harveyshao
debd74960b opt some 2022-04-23 11:23:31 +08:00
harveyshao
66d48572a0 opt some 2022-04-23 09:36:38 +08:00
harveyshao
fc85438a01 opt some 2022-04-23 09:16:10 +08:00
harveyshao
28046db51a opt some 2022-04-23 05:11:38 +08:00
harveyshao
5f5d58725f opt some 2022-04-23 04:59:01 +08:00
harveyshao
3c40de3c1c opt some 2022-04-22 19:11:28 +08:00
shy
fc55214a38 opt some 2022-04-18 22:23:47 +08:00
harveyshao
0efa99e9c6 add some 2022-04-18 13:18:25 +08:00
harveyshao
8f98a1db7a opt some 2022-04-15 10:20:46 +08:00
harveyshao
95d42317a9 opt some 2022-04-15 10:15:32 +08:00
harveyshao
afd68e0c0b opt oauth 2022-04-14 09:43:50 +08:00
shy
46d61e4f56 add oauth 2022-04-13 17:04:06 +08:00
harveyshao
d5023bccad opt some 2022-04-12 07:43:57 +08:00
harveyshao
bca3c8bb0d opt some 2022-04-11 10:22:34 +08:00
harveyshao
d99cd8c0c6 opt some 2022-04-11 00:56:51 +08:00
harveyshao
d0f656f725 opt some 2022-04-10 23:18:17 +08:00
harveyshao
bc8d965683 opt icebergs 2022-04-10 23:03:58 +08:00
harveyshao
a37674abec opt some 2022-04-10 13:02:06 +08:00
shy
4bc53b7764 opt some 2022-04-10 12:56:58 +08:00
harveyshao
0ab3b73e38 opt some 2022-04-10 11:43:17 +08:00
shy
40628e005a opt url 2022-04-10 11:22:34 +08:00
harveyshao
5b9fce5dc6 opt some 2022-04-09 23:28:44 +08:00
harveyshao
762bb6056f opt some 2022-04-08 18:24:52 +08:00
harveyshao
20ebcc4754 opt search 2022-04-08 12:29:16 +08:00
shy
855aaac279 opt some 2022-04-06 12:27:03 +08:00
shy
755c29eea3 add udp 2022-04-05 00:46:50 +08:00
harveyshao
e979499033 opt some 2022-04-04 10:18:55 +08:00
harveyshao
f8c971cef9 opt inner 2022-04-04 09:59:59 +08:00
harveyshao
25e0f9b050 opt some 2022-04-02 15:21:16 +08:00
harveyshao
0c6bb3d2c2 opt some 2022-04-01 10:09:16 +08:00
harveyshao
221c999d30 opt some 2022-04-01 09:34:02 +08:00
harveyshao
f1efc38f47 opt some 2022-04-01 09:29:16 +08:00
harveyshao
97e4564f6e opt some 2022-03-31 12:22:22 +08:00
harveyshao
25edf639ae opt some 2022-03-30 19:21:31 +08:00
harveyshao
11e5f1ba2c opt some 2022-03-29 14:08:39 +08:00
shy
63ab0a6a86 opt some 2022-03-27 21:34:31 +08:00
shy
a901d62c89 opt some 2022-03-26 23:08:09 +08:00
harveyshao
30208298cb opt some 2022-03-26 07:59:48 +08:00
harveyshao
2a6e445d80 opt some 2022-03-23 22:37:49 +08:00
harveyshao
4278590cd2 opt some 2022-03-23 10:18:13 +08:00
harveyshao
178008f78a opt css 2022-03-21 12:29:53 +08:00
harveyshao
1ba3a0fa62 add mirror 2022-03-20 12:21:28 +08:00
harveyshao
1976d83a49 opt some 2022-03-19 23:58:36 +08:00
harveyshao
c4fe0b8f4b opt some 2022-03-19 19:25:10 +08:00
harveyshao
26b9667279 opt some 2022-03-19 19:06:52 +08:00
shy
5227398cec opt some 2022-03-18 10:32:28 +08:00
shy
dd920ee22c opt some 2022-03-17 21:10:52 +08:00
harveyshao
10819db8ef opt some 2022-03-17 13:30:55 +08:00
harveyshao
a36d017203 opt some 2022-03-16 23:25:52 +08:00
harveyshao
77b4354d27 opt some 2022-03-16 18:16:09 +08:00
harveyshao
81445f889e opt some 2022-03-15 23:06:53 +08:00
shy
de1c4af963 opt some 2022-03-15 20:49:56 +08:00
shy
91236d82ed add staff 2022-03-15 18:07:58 +08:00
shy
37b16d89d1 opt some 2022-03-15 14:17:39 +08:00
shy
b2232d458c opt some 2022-03-15 03:41:14 +08:00
harveyshao
e9253f9ac1 opt iml 2022-03-15 03:30:10 +08:00
harveyshao
0f82c6ec19 add iml 2022-03-15 02:30:43 +08:00
harveyshao
e69f39a8af opt iml 2022-03-15 02:29:14 +08:00
harveyshao
debdfaae1c opt some 2022-03-14 21:23:38 +08:00
harveyshao
4d816d2d5c opt some 2022-03-12 21:52:26 +08:00
harveyshao
b0de72c204 opt some 2022-03-12 19:21:05 +08:00
harveyshao
a8f7cccf33 opt some 2022-03-12 19:16:48 +08:00
harveyshao
84460c4d41 opt some 2022-03-12 19:10:55 +08:00
harveyshao
2b1f7bb9b5 opt some 2022-03-10 16:58:26 +08:00
harveyshao
6c91073e3d opt some 2022-03-10 10:28:04 +08:00
harveyshao
d2bf427703 opt some 2022-03-09 00:41:43 +08:00
harveyshao
f208c8114d opt some 2022-03-08 23:53:33 +08:00
harveyshao
ef79b4d524 opt some 2022-03-07 14:30:16 +08:00
harveyshao
0926f5d58b opt some 2022-03-07 11:10:51 +08:00
harveyshao
a3a764d0d6 opt some 2022-03-03 14:40:49 +08:00
harveyshao
16b8cb1ded opt website 2022-03-03 10:38:25 +08:00
shy
03aaa69c3d opt some 2022-03-01 17:18:34 +08:00
harveyshao
b4ab171037 opt some 2022-03-01 16:36:28 +08:00
7b99b1cc29 opt some 2022-02-25 19:38:47 +08:00
e154b0fa18 opt some 2022-02-24 23:33:28 +08:00
harveyshao
5cdb8fc10a opt some 2022-02-24 23:25:16 +08:00
harveyshao
5a13b97f01 opt some 2022-02-24 22:43:35 +08:00
harveyshao
1e654f3f94 opt render 2022-02-24 17:30:28 +08:00
harveyshao
d2c1f62102 opt login 2022-02-23 12:24:48 +08:00
shy
b278396504 opt login 2022-02-23 12:16:03 +08:00
harveyshao
47b61c540a opt some 2022-02-22 13:38:04 +08:00
shy
b1d656fa90 opt some 2022-02-21 11:21:57 +08:00
harveyshao
81bc8309a9 opt code 2022-02-20 22:59:45 +08:00
harveyshao
7a65f0b4ef opt code 2022-02-19 19:50:42 +08:00
harveyshao
c2d67e7081 opt webpack 2022-02-18 11:20:14 +08:00
harveyshao
278b016efd opt webpack 2022-02-18 11:16:00 +08:00
harveyshao
a3e3d9a82e opt code 2022-02-17 11:26:13 +08:00
harveyshao
396c088e4d opt cli 2022-02-16 10:01:25 +08:00
harveyshao
ec3d847010 opt webpack 2022-02-14 16:49:46 +08:00
harveyshao
6004693ca4 opt some 2022-02-14 16:04:15 +08:00
shy
8d82f56bf9 opt some 2022-02-14 13:04:38 +08:00
shy
c42444ccf5 opt some 2022-02-14 12:17:31 +08:00
harveyshao
cd2c102369 opt some 2022-02-14 02:22:29 +08:00
harveyshao
f6eab408b7 add todo 2022-02-14 01:49:35 +08:00
shy
fcdc32b253 opt some 2022-02-13 17:01:47 +08:00
harveyshao
c83cac512b opt tutor 2022-02-13 14:24:26 +08:00
harveyshao
5b80b021a9 opt some 2022-02-12 15:28:46 +08:00
harveyshao
a3acc9deb3 add forever 2022-02-12 15:20:38 +08:00
harveyshao
10c8d7b799 opt some 2022-02-11 16:02:00 +08:00
shy
e4a7ad0f2d opt some 2022-02-11 09:29:56 +08:00
harveyshao
84ae0c28fa opt website 2022-02-10 17:52:01 +08:00
shy
1563fd7ee6 opt website 2022-02-10 16:05:59 +08:00
harveyshao
2d03bdbbef opt website 2022-02-09 17:15:50 +08:00
82e7ec5a13 opt website 2022-02-06 20:19:18 +08:00
20ad3d9cf1 opt website 2022-02-06 20:15:58 +08:00
55529c00f0 opt some 2022-02-01 14:02:02 +08:00
982483dc39 opt some 2022-02-01 13:59:55 +08:00
c625ff88a3 opt some 2022-01-31 22:50:28 +08:00
harveyshao
a17a4fce6e opt some 2022-01-26 01:40:34 +08:00
harveyshao
ab7ab6eb19 opt vimer 2022-01-24 17:29:25 +08:00
shy
b4e95f8dab opt some 2022-01-23 17:07:39 +08:00
harveyshao
5562c45a26 opt website 2022-01-23 16:52:59 +08:00
bergyu
25628004a4 opt some 2022-01-23 14:16:05 +08:00
harveyshao
f993fa4890 opt some 2022-01-23 10:51:57 +08:00
harveyshao
d5c117738f opt init 2022-01-22 23:37:19 +08:00
harveyshao
439d60c124 opt dream 2022-01-22 17:40:20 +08:00
harveyshao
9ebe7add4a opt some 2022-01-22 13:51:02 +08:00
9919e3bcdf add template.go 2022-01-22 10:12:22 +08:00
harveyshao
c2ff6a5b08 opt vimer.js 2022-01-21 17:48:08 +08:00
5676ceaf3e opt find.go 2022-01-20 09:32:21 +08:00
b23fe02af5 opt inner.go 2022-01-20 09:31:58 +08:00
harveyshao
73e92ec42a opt vimer.js 2022-01-19 17:33:41 +08:00
harveyshao
4acbe03c1f opt vimer.go 2022-01-18 17:58:10 +08:00
e8623eaba4 opt inner.js 2022-01-18 00:31:17 +08:00
harveyshao
ea1b19f7b0 add grep 2022-01-17 17:59:36 +08:00
harveyshao
79ebe307e7 opt inner.go 2022-01-17 17:59:25 +08:00
harveyshao
d28a1e674a opt inner.js 2022-01-17 08:06:22 +08:00
fdc1b5420a opt inner.js 2022-01-17 00:17:00 +08:00
harveyshao
298d1ac6f2 opt inner.js 2022-01-16 16:41:08 +08:00
harveyshao
27e0f84c07 opt inner.js 2022-01-16 09:35:09 +08:00
harveyshao
43c35e0d1f opt inner.go 2022-01-14 17:59:46 +08:00
fc6fa9dd40 opt inner.go 2022-01-14 09:25:31 +08:00
harveyshao
22d1dfe714 opt action.js 2022-01-13 18:04:08 +08:00
harveyshao
bff34f7c23 opt action.js 2022-01-12 18:12:04 +08:00
harveyshao
c169cf9ad6 opt some 2022-01-11 16:14:48 +08:00
2899e2bf82 opt some 2022-01-11 15:38:59 +08:00
shy
e29653a649 opt some 2022-01-11 15:22:31 +08:00
harveyshao
231812d2bd opt some 2022-01-11 12:30:32 +08:00
harveyshao
7868ea57f2 opt some 2022-01-10 00:26:21 +08:00
22606ffaac opt relay 2022-01-09 22:18:44 +08:00
harveyshao
a8bfff72ce opt chat 2022-01-09 17:50:52 +08:00
harveyshao
09ed497f97 opt some 2022-01-09 17:30:12 +08:00
harveyshao
ef11fb2bba opt code 2022-01-09 17:30:04 +08:00
f962f43d99 opt some 2022-01-09 14:56:52 +08:00
1fff54b9bb opt some 2022-01-09 13:41:40 +08:00
harveyshao
a2219676ff opt some 2022-01-09 03:29:19 +08:00
harveyshao
351c1981ca opt alpha 2022-01-09 03:27:13 +08:00
harveyshao
bf3847e436 opt some 2022-01-08 11:51:45 +08:00
bergyu
59d84f9033 opt ssh 2022-01-04 16:37:13 +08:00
bergyu
5c704512e1 opt app 2022-01-04 16:13:58 +08:00
bergyu
0d93101653 opt app 2022-01-04 16:03:09 +08:00
bergyu
f83447c088 opt app 2022-01-04 11:30:06 +08:00
8e23a29c0a add app 2022-01-03 22:58:53 +08:00
harveyshao
10bf2d3e37 opt sso 2022-01-03 17:23:30 +08:00
harveyshao
9b92b7b849 opt sso 2022-01-03 14:17:46 +08:00
harveyshao
079342ba8c opt sso 2022-01-03 14:05:36 +08:00
shy
cee53d8626 opt sso 2022-01-03 14:04:52 +08:00
shy
efd2eb3c08 add sso 2022-01-03 13:31:44 +08:00
shy
c7d8d33e2c opt grant 2022-01-03 11:32:37 +08:00
shy
a15365fbb0 opt grant 2022-01-03 11:32:24 +08:00
3fc85d2178 opt some 2022-01-02 22:05:47 +08:00
069808d74b opt chrome 2022-01-02 22:02:44 +08:00
c43212865a opt chrome 2022-01-02 22:02:27 +08:00
ea23534857 opt nfs 2022-01-01 09:08:32 +08:00
114a75b45f opt web 2021-12-31 18:10:49 +08:00
shy
e3c74fa4ac opt ctx 2021-12-31 03:36:19 +08:00
shy
28d70c5c4a opt ctx 2021-12-31 03:26:17 +08:00
harveyshao
61aa881b7d opt cli 2021-12-30 17:37:18 +08:00
harveyshao
fe937b8d9f opt aaa 2021-12-30 16:14:32 +08:00
bergyu
8feb26089f opt gdb 2021-12-30 10:04:47 +08:00
bergyu
7355064173 opt chrome 2021-12-29 17:23:10 +08:00
harveyshao
160e782def opt some 2021-12-28 09:51:59 +08:00
harveyshao
c83549f7e5 opt some 2021-12-27 23:02:51 +08:00
harveyshao
569e2791d7 opt some 2021-12-22 11:14:24 +08:00
harveyshao
05340dee07 opt some 2021-12-22 10:30:25 +08:00
shy
5a60718d64 opt some 2021-12-15 11:14:25 +08:00
harveyshao
1ef53c5610 opt some 2021-12-11 17:30:23 +08:00
harveyshao
87b675d294 opt chart 2021-12-11 17:30:02 +08:00
harveyshao
28151f01ea opt chart 2021-12-09 00:46:25 +08:00
shy
305f5d4fdd opt some 2021-12-07 14:27:17 +08:00
shy
e99a3ee1c9 opt some 2021-12-07 09:45:04 +08:00
harveyshao
a862a03c31 opt website 2021-12-07 07:53:08 +08:00
harveyshao
6d807bd5d9 opt some 2021-12-06 19:34:37 +08:00
harveyshao
71576fe085 opt website.go 2021-12-06 19:28:06 +08:00
harveyshao
0e04e31f58 add website.go 2021-12-06 17:27:20 +08:00
harveyshao
30918cb70d opt some 2021-12-04 11:53:51 +08:00
harveyshao
9d9920d36a opt some 2021-12-04 11:01:09 +08:00
harveyshao
8a85e8b7a8 opt some 2021-12-04 10:45:42 +08:00
harveyshao
10c34585c2 opt lib 2021-12-01 17:37:59 +08:00
harveyshao
3ea5de2480 opt some 2021-11-30 20:16:48 +08:00
harveyshao
3e92ecd3a8 opt some 2021-11-30 14:29:47 +08:00
harveyshao
15b92fb167 opt route 2021-11-30 05:13:23 +08:00
harveyshao
be8e8e67e6 opt some 2021-11-29 17:36:47 +08:00
harveyshao
2eb4113693 opt some 2021-11-29 17:36:37 +08:00
harveyshao
7588d84b62 opt some 2021-11-29 10:07:32 +08:00
harveyshao
5e781b2528 add msg.MergeURL2 2021-11-25 19:16:56 +08:00
harveyshao
46b77638a3 opt some 2021-11-24 16:18:07 +08:00
harveyshao
d356dfe0ba opt some 2021-11-24 16:01:38 +08:00
harveyshao
9245526096 opt some 2021-11-24 11:14:31 +08:00
bergyu
8a87f1b41f opt some 2021-11-22 14:50:15 +08:00
harveyshao
60836fd0e3 opt some 2021-11-22 14:37:36 +08:00
512 changed files with 31207 additions and 16397 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2021 码神
Copyright (c) 2017-2025 shylinux
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

128
README.md
View File

@ -1,129 +1,3 @@
# icebergs
icebergs是一个应用框架通过模块化、集群化、自动化快速搭建起完整的个人云计算平台。
- 使用icebergs可以将各种模块或项目集成到一起快速开发出集中式的服务器。
- 使用icebergs可以将各种设备自由的组合在一起快速搭建起分布式的服务器。
## 0. 搭建服务
### 0.1 一键部署
```sh
mkdir miss; cd miss && curl -s https://shylinux.com/publish/ice.sh | sh
```
脚本会根据当前系统类型自动下载程序文件ice.bin并自动启动服务。
### 0.2 使用方式
**终端交互**
启动后的进程像bash一样是一个可交互的shell可以执行各种模块命令或系统命令。
**网页交互**
默认还会启动一个web服务访问地址 http://localhost:9020 ,就可以通过网页进行操作。
**重启服务**
在终端按Ctrl+C就可以重新启动服务。
**结束服务**
在终端按Ctrl+\,就可以停止服务。
### 0.3 使用示例
## 1. 项目开发
icebergs是一个应用框架如果官方模块无法满足使用需求还可以搜集第三方模块自行编译程序。
如果第三方模块也无法满足使用需求,还可以自己开发模块,
icebergs提供了模板可以一键创建新模块快速添加自己的功能模块。
### 1.1 部署环境
*开发环境需要提前安装好git和golang*
```sh
mkdir miss; cd miss && curl -s https://shylinux.com/publish/template.sh | sh
```
template.sh会自动创建出项目模板并自动编译生成程序然后启动服务。
为了方便以后创建项目与模块。
可以将辅助脚本template.sh下载并添加到可执行目录中。
### 1.2 添加第三方模块
在src/main.go文件中就可以import任意的第三方模块
执行一下make命令就会重新生成ice.bin。
重新启动服务,就可以使用第三方模块了。
### 1.3 开发模块
```sh
template.sh tutor hello
```
使用之前下载的template.sh调用tutor命令并指定模块名称hello就可以一键创建模块了。
在src/main.go 中import新加的模块
执行make命令程序编译完成后
重启服务,就可以使用新模块了。
### 1.4 开发框架
如果现有的框架,无法满足需求,还可以下载框架源码自行更改。
```sh
git clone https://shylinux.com/x/icebergs usr/icebergs
```
修改go.mod文件引用本地框架。
```go
replace shylinux.com/x/icebergs => ./usr/icebergs
```
## 2 原型 type.go
### 2.1 msg.Detail
### 2.2 msg.Option
### 2.3 msg.Append
### 2.4 msg.Result
### 2.5 msg.Travel
### 2.6 msg.Search
### 2.7 msg.Conf
### 2.8 msg.Cmd
### 2.9 msg.Cap
## 3 框架 base.go
### 3.1 注册模块 Register
### 3.2 创建资源 Begin
### 3.3 加载配置 _init
### 3.4 启动服务 Start
### 3.5 保存配置 _exit
### 3.6 释放资源 Close
## 4 基础模块 base/
### 4.1 模块中心 base/ctx/
### 4.2 命令中心 base/cli/
### 4.3 认证中心 base/aaa/
### 4.4 网页中心 base/web/
### 4.5 词法中心 base/lex/
### 4.6 语法中心 base/yac/
### 4.7 事件中心 base/gdb/
### 4.8 日志中心 base/log/
### 4.9 网络中心 base/tcp/
### 4.10 文件中心 base/nfs/
### 4.11 终端中心 base/ssh/
### 4.12 数据中心 base/mdb/
## 5 核心模块 core/
### 5.1 编程中心 core/code/
### 5.2 文档中心 core/wiki/
### 5.3 聊天中心 core/chat/
### 5.4 团队中心 core/team/
### 5.5 贸易中心 core/mall/
## 6 其它模块 misc/
### 6.1 终端管理 misc/zsh/
### 6.1 终端管理 misc/tmux/
### 6.1 代码管理 misc/git/
### 6.1 代码管理 misc/vim/
### 6.1 公众号 misc/mp/
### 6.1 小程序 misc/wx/
### 6.1 浏览器 misc/chrome/
### 6.1 机器人 misc/lark/
### 6.1 开发板 misc/pi/
icebergs 是一个后端框架,通过集群化、模块化、自动化方式,在各种设备上,即可一键启动完整的云计算服务与云研发环境。

View File

@ -1,21 +1,16 @@
package aaa
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
import ice "shylinux.com/x/icebergs"
const (
RSA = "rsa"
SIGN = "sign"
CERT = "cert"
VERIFY = "verify"
BASE64 = "base64"
)
const AAA = "aaa"
var Index = &ice.Context{Name: AAA, Help: "认证模块", Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Rich(ROLE, nil, kit.Dict(kit.MDB_NAME, VOID, WHITE, kit.Dict(), BLACK, kit.Dict()))
m.Rich(ROLE, nil, kit.Dict(kit.MDB_NAME, TECH, BLACK, kit.Dict(), WHITE, kit.Dict()))
m.Load()
}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Save()
}},
}}
var Index = &ice.Context{Name: AAA, Help: "认证模块"}
func init() { ice.Index.Register(Index, nil, ROLE, SESS, TOTP, USER) }
func init() { ice.Index.Register(Index, nil, APPLY, OFFER, EMAIL, USER, TOTP, SESS, ROLE, CERT, RSA) }

View File

@ -1,6 +0,0 @@
chapter "aaa"
field "角色" role
field "会话" sess
field "令牌" totp
field "用户" user

77
base/aaa/email.go Normal file
View File

@ -0,0 +1,77 @@
package aaa
import (
"net/smtp"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
const (
ADMIN = "admin"
SEND = "send"
DATE = "date"
FROM = "from"
TO = "to"
CC = "cc"
SUBJECT = "subject"
CONTENT = "content"
)
const EMAIL = "email"
func init() {
const (
ADMIN = "admin"
SERVICE = "service"
NL = "\r\n"
DF = ": "
)
Index.MergeCommands(ice.Commands{
EMAIL: {Help: "邮件", Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create name*=admin service*='smtp.163.com:25' username* password*"},
SEND: {Name: "send from=admin to*='shy@shylinux.com' cc subject*=hi content*:textarea=hello", Help: "发送", Icon: "bi bi-send-plus", Hand: func(m *ice.Message, arg ...string) {
msg := mdb.HashSelects(m.Spawn(), m.OptionDefault(FROM, ADMIN))
if m.WarnNotFound(msg.Append(SERVICE) == "", m.Option(FROM)) {
return
}
m.ToastProcess()
content := []byte(kit.JoinKV(DF, NL, kit.Simple(FROM, msg.Append(USERNAME), m.OptionSimple(TO, CC, SUBJECT), DATE, time.Now().Format(time.RFC1123Z), html.ContentType, "text/html; charset=UTF-8")...) + NL + NL + m.Option(CONTENT))
auth := smtp.PlainAuth("", msg.Append(USERNAME), msg.Append(PASSWORD), kit.Split(msg.Append(SERVICE), ice.DF)[0])
m.Logs(EMAIL, SEND, string(content))
if !m.WarnNotValid(smtp.SendMail(msg.Append(SERVICE), auth, msg.Append(USERNAME), kit.Split(m.Option(TO)), content)) {
m.ToastSuccess()
}
}},
}, mdb.DevDataAction("name,service,username,password"), mdb.HashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,service,username", ice.ACTION, SEND)), Hand: func(m *ice.Message, arg ...string) {
if mdb.HashSelect(m, arg...); len(arg) == 0 && m.Length() == 0 {
m.EchoInfoButton(m.Trans("please add admin email", "请配置管理员邮箱"), mdb.CREATE, mdb.DEV_REQUEST)
} else if len(arg) == 0 {
m.Action(mdb.CREATE, mdb.DEV_REQUEST)
}
}},
})
ice.Info.Inputs = append(ice.Info.Inputs, func(m *ice.Message, arg ...string) {
switch kit.TrimPrefix(arg[0], "extra.") {
case TO:
if m.Option(ice.ACTION) != EMAIL {
break
}
fallthrough
case EMAIL:
m.Push(arg[0], "shy@shylinux.com", "shylinux@163.com")
case PASSWORD:
m.SetAppend()
}
})
}
func SendEmail(m *ice.Message, from, to, cc string, arg ...string) {
m.Option(ice.MSG_USERHOST, strings.Split(m.Option(ice.MSG_USERHOST), "://")[1])
m.Cmdy(EMAIL, SEND, kit.Select(mdb.Config(m, EMAIL), from), kit.Select(m.Option(EMAIL), to), cc,
strings.TrimSpace(kit.Select(ice.Render(m, ice.RENDER_TEMPLATE, SUBJECT_HTML), arg, 0)),
kit.Select(ice.Render(m, ice.RENDER_TEMPLATE, CONTENT_HTML), arg, 1),
)
}

71
base/aaa/offer.go Normal file
View File

@ -0,0 +1,71 @@
package aaa
import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _offer_create(m *ice.Message, arg ...string) {
h := mdb.HashCreate(m.Spawn(), FROM, m.Option(ice.MSG_USERNAME), mdb.STATUS, INVITE, m.OptionSimple(EMAIL, SUBJECT, CONTENT))
SendEmail(m.Options("link", m.Cmdx("host", "publish", m.MergePodCmd("", "", mdb.HASH, h))), m.Option(FROM), "", "")
gdb.Event(m, OFFER_CREATE, mdb.HASH, h, EMAIL, m.Option(EMAIL))
}
func _offer_accept(m *ice.Message, arg ...string) {
msg := mdb.HashSelect(m.Spawn(), m.Option(mdb.HASH))
if ls := kit.Split(msg.Append(EMAIL), mdb.AT); !m.WarnNotFound(msg.Length() == 0 || len(ls) < 2, m.Option(mdb.HASH)) {
m.Spawn().AdminCmd(USER, mdb.CREATE, USERROLE, VOID, USERNAME, msg.Append(EMAIL), USERNICK, ls[0], USERZONE, ls[1])
mdb.HashModify(m, m.OptionSimple(mdb.HASH), mdb.STATUS, ACCEPT)
gdb.Event(m, OFFER_ACCEPT, mdb.HASH, m.Option(mdb.HASH), EMAIL, msg.Append(EMAIL))
m.ProcessLocation(m.MergePod("", ice.MSG_SESSID, SessValid(m.Options(ice.MSG_USERNAME, msg.Append(EMAIL)))))
}
}
const (
INVITE = "invite"
ACCEPT = "accept"
SUBJECT_HTML = "subject.html"
CONTENT_HTML = "content.html"
OFFER_CREATE = "offer.create"
OFFER_ACCEPT = "offer.accept"
)
const APPLY = "apply"
const OFFER = "offer"
func init() {
Index.MergeCommands(ice.Commands{
OFFER: {Help: "邀请", Role: VOID, Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict("from", "发自")),
), Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create from*=admin email*='shy@shylinux.com' subject content", Help: "邀请", Hand: func(m *ice.Message, arg ...string) {
_offer_create(m, arg...)
}},
ACCEPT: {Help: "接受", Role: VOID, Hand: func(m *ice.Message, arg ...string) {
if !m.WarnNotValid(m.Option(mdb.HASH), mdb.HASH) {
_offer_accept(m, arg...)
}
}},
}, mdb.ImportantHashAction(
mdb.SHORT, mdb.UNIQ, mdb.FIELD, "time,hash,from,status,email,subject,content"), EMAIL, ADMIN,
), Hand: func(m *ice.Message, arg ...string) {
if m.WarnNotRight(len(arg) == 0 && !IsTechOrRoot(m)) {
return
} else if mdb.HashSelect(m, arg...).FieldsIsDetail() {
if m.Option(ice.MSG_USERNAME) == "" {
m.Option(ice.MSG_USERHOST, strings.Split(m.Option(ice.MSG_USERHOST), "://")[1])
m.SetAppend().EchoInfoButton(m.Template(SUBJECT_HTML), ACCEPT)
} else if strings.Contains(m.Option(ice.MSG_USERWEB), "/c/offer") {
m.ProcessLocation(m.MergePod(""))
}
}
}},
})
}
func OfferAction() ice.Actions {
return gdb.EventsAction(OFFER_CREATE, OFFER_ACCEPT, USER_CREATE, USER_REMOVE)
}

124
base/aaa/portal/apply.go Normal file
View File

@ -0,0 +1,124 @@
package portal
import (
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web"
kit "shylinux.com/x/toolkits"
)
type apply struct {
ice.Hash
email string `data:"admin"`
checkbox string `data:"true"`
online string `data:"true"`
field string `data:"time,hash,status,email,userrole,username,usernick,icons,agent,system,ip,ua"`
apply string `name:"apply" help:"申请" role:"void"`
agree string `name:"agree userrole=tech,void" help:"同意" icon:"bi bi-check2-square"`
login string `name:"login" help:"登录" role:"void"`
list string `name:"list hash auto sso" help:"注册"`
}
func (s apply) Sso(m *ice.Message, arg ...string) {
m.AddHeaderLogin(cli.QRCODE, cli.QRCODE, "扫码登录", "10")
m.AddHeaderLogin(mdb.PLUGIN, aaa.EMAIL, "邮箱登录", "20", ctx.INDEX, m.ShortKey(), ctx.ARGS, kit.FuncName(s.Login))
m.AddHeaderLogin(mdb.PLUGIN, aaa.APPLY, "注册用户", "30", ctx.INDEX, m.ShortKey(), ctx.ARGS, kit.FuncName(s.Apply))
}
func (s apply) Apply(m *ice.Message, arg ...string) {
if m.IsGetMethod() {
if k := _cookie_key(m); m.Option(k) == "" || s.List(m, m.Option(k)).Length() == 0 && m.Result() == "" {
m.DisplayForm(m, "email*", aaa.USERNICK, s.Apply)
}
} else if !m.WarnAlreadyExists(m.Options(arg).Cmd(aaa.USER, m.Option(aaa.EMAIL)).Length() > 0, m.Option(aaa.EMAIL)) {
m.Options(ice.MSG_USERNAME, m.Option(aaa.EMAIL), ice.MSG_USERNICK, kit.Split(m.Option(aaa.EMAIL), "@")[0])
h := s.Hash.Create(m, kit.Simple(arg, mdb.STATUS, kit.FuncName(s.Apply), web.ParseUA(m.Message))...)
m.ProcessCookie(_cookie_key(m), h)
m.StreamPushRefreshConfirm()
m.ChatMessageInsertPlug(aaa.APPLY, "user.signup", "", m.PrefixKey(), h)
}
}
func (s apply) Agree(m *ice.Message, arg ...string) {
if m.WarnNotValid(m.Option(mdb.HASH) == "", mdb.HASH) {
return
}
msg := s.Hash.List(m.Spawn(), m.Option(mdb.HASH))
if m.WarnNotFound(msg.Length() == 0, m.Option(mdb.HASH)) {
return
}
s.Hash.Modify(m, kit.Simple(m.OptionSimple(mdb.HASH, aaa.USERROLE), mdb.STATUS, s.Agree)...)
// m.UserCreate(m.Option(aaa.USERROLE), msg.Append(aaa.USERNAME), msg.Append(aaa.USERNICK))
m.PushRefresh(msg.Append(cli.DAEMON))
}
func (s apply) Login(m *ice.Message, arg ...string) {
kit.If(m.Option(mdb.HASH) == kit.FuncName(s.Apply), func() { m.Options(mdb.HASH, "") })
if m.IsGetMethod() {
m.DisplayForm("email*", s.Login)
} else if m.Options(arg).Option(aaa.EMAIL) == "" {
if m.WarnNotValid(m.OptionDefault(mdb.HASH, m.Option(_cookie_key(m))) == "", mdb.HASH) {
m.ProcessCookie(_cookie_key(m), "")
return
}
msg := s.Hash.List(m.Spawn(), m.Option(mdb.HASH))
if m.WarnNotFound(msg.Length() == 0, m.Option(mdb.HASH)) {
m.ProcessCookie(_cookie_key(m), "")
return
}
m.UserCreate(m.Option(aaa.USERROLE), msg.Append(aaa.USERNAME), msg.Append(aaa.USERNICK))
web.RenderCookie(m.Message, m.Cmdx(aaa.SESS, mdb.CREATE, msg.Append(aaa.USERNAME)))
s.Hash.Modify(m, kit.Simple(m.OptionSimple(mdb.HASH), mdb.STATUS, s.Login)...)
m.ProcessLocation(nfs.PS)
m.StreamPushRefreshConfirm()
} else {
if m.WarnNotFound(m.Cmd(aaa.USER, m.Option(aaa.EMAIL)).Length() == 0, m.Option(aaa.EMAIL)) {
return
}
m.Options(ice.MSG_USERNAME, m.Option(aaa.EMAIL))
space := kit.Keys(kit.Slice(kit.Split(m.Option(ice.MSG_DAEMON), nfs.PT), 0, -1))
share := m.Cmd(web.SHARE, mdb.CREATE, mdb.TYPE, web.FIELD, mdb.NAME, web.CHAT_GRANT, mdb.TEXT, space, web.SPACE, ice.OPS).Append(mdb.LINK)
m.Options(web.LINK, share).SendEmail("", "", "")
// m.ProcessHold(m.Trans("please auth login in mailbox", "请注意查收邮件"))
m.Echo(m.Trans("please auth login in mailbox", "请注意查收邮件"))
m.ProcessInner()
}
}
func (s apply) List(m *ice.Message, arg ...string) *ice.Message {
kit.If(m.Option(_cookie_key(m)), func(p string) { arg = []string{p} })
kit.If(!m.IsTech(), func() { m.Option(ice.MSG_ONLINE, ice.FALSE) })
if m.IsTech() || (len(arg) > 0 && arg[0] != "") {
s.Hash.List(m, arg...).Table(func(value ice.Maps) {
switch value[mdb.STATUS] {
case kit.FuncName(s.Apply):
m.PushButton(s.Agree, s.Remove)
case kit.FuncName(s.Agree):
m.PushButton(s.Login, s.Remove)
default:
m.PushButton(s.Remove)
}
})
}
if m.Option(_cookie_key(m)) != "" || m.ActionKey() != "" {
switch m.Append(mdb.STATUS) {
case kit.FuncName(s.Login):
if m.ActionKey() == kit.FuncName(s.Apply) {
m.ProcessCookie(_cookie_key(m), "")
} else {
m.SetAppend().ProcessLocation(nfs.PS)
}
case kit.FuncName(s.Agree):
m.SetAppend().EchoInfoButton(m.Trans("please login", "请登录"), s.Login)
case kit.FuncName(s.Apply):
m.SetAppend().EchoInfoButton(m.Trans("please wait admin agree", "请等待管理员同意"), nil)
}
} else if len(arg) == 0 {
m.EchoQRCode(m.MergePodCmd("", "", ctx.ACTION, s.Apply))
}
return m
}
func init() { ice.Cmd("aaa.apply", apply{}) }
func _cookie_key(m *ice.Message) string { return kit.Keys(m.PrefixKey(), mdb.HASH) }

77
base/aaa/portal/asign.go Normal file
View File

@ -0,0 +1,77 @@
package portal
import (
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
type asign struct {
ice.Hash
export string `data:"true"`
short string `data:"role"`
field string `data:"time,role"`
shorts string `data:"index"`
fields string `data:"time,index,operate"`
insert string `name:"insert index"`
deploy string `name:"deploy" help:"部署"`
list string `name:"list role auto" help:"分配"`
confer string `name:"confer username" help:"授权"`
}
func (s asign) Inputs(m *ice.Message, arg ...string) {
if arg[0] == "operate" {
m.Search(m.Option(ctx.INDEX), func(key string, cmd *ice.Command) {
for sub, action := range cmd.Actions {
if kit.HasPrefix(sub, "_", "/") {
continue
}
m.Push(arg[0], sub)
m.Push(mdb.NAME, action.Name)
m.Push(mdb.HELP, action.Help)
}
m.Sort(arg[0])
m.Option(ice.TABLE_CHECKBOX, ice.TRUE)
})
} else if arg[0] == aaa.USERNAME {
m.Cmdy(aaa.USER).Cut(aaa.USERNAME, aaa.USERNICK, aaa.USERROLE)
} else {
s.Hash.Inputs(m, arg...)
}
}
func (s asign) Modify(m *ice.Message, arg ...string) {
if m.Option(ctx.INDEX) != "" {
s.Update(m, arg...)
} else {
s.Modify(m, arg...)
}
}
func (s asign) Deploy(m *ice.Message, arg ...string) {
defer m.ToastProcess()()
s.List(m.Spawn()).Table(func(val ice.Maps) {
m.Cmd(aaa.ROLE, mdb.REMOVE, val[aaa.ROLE])
m.Cmd(aaa.ROLE, mdb.CREATE, val[aaa.ROLE])
s.List(m.Spawn(), val[aaa.ROLE]).Table(func(value ice.Maps) {
m.Cmd(aaa.ROLE, aaa.WHITE, val[aaa.ROLE], value[ctx.INDEX])
m.Cmd(aaa.ROLE, aaa.BLACK, val[aaa.ROLE], value[ctx.INDEX], ctx.ACTION)
kit.For(kit.Split(value["operate"]), func(p string) {
m.Cmd(aaa.ROLE, aaa.WHITE, val[aaa.ROLE], value[ctx.INDEX], ctx.ACTION, p)
})
})
})
}
func (s asign) List(m *ice.Message, arg ...string) *ice.Message {
if len(arg) == 0 {
s.Hash.List(m, arg...).Action(s.Create, s.Deploy).PushAction(s.Confer, s.Remove)
} else {
s.Hash.SubList(m, arg[0], arg[1:]...).Action(s.Insert, s.Deploy).PushAction(s.Delete)
}
return m
}
func (s asign) Confer(m *ice.Message, arg ...string) {
m.Cmd(aaa.USER, mdb.MODIFY, aaa.USERNAME, m.Option(aaa.USERNAME), aaa.USERROLE, m.Option(aaa.ROLE))
}
func init() { ice.Cmd("aaa.asign", asign{}) }

View File

@ -1,144 +1,172 @@
package aaa
import (
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _role_user(m *ice.Message, userrole string, username ...string) {
m.Richs(ROLE, nil, userrole, func(key string, value map[string]interface{}) {
for _, user := range username {
kit.Value(value, kit.Keys(USER, user), true)
func _role_keys(key ...string) string {
if _key := kit.Select("", strings.Split(key[0], ice.PT), -1); _key != "" {
if c, ok := ice.Info.Index[_key].(*ice.Context); ok && kit.Keys(c.Prefix(), _key) == key[0] {
key[0] = _key
}
})
}
func _role_list(m *ice.Message, userrole string) {
m.Richs(ROLE, nil, kit.Select(kit.MDB_FOREACH, userrole), func(key string, value map[string]interface{}) {
kit.Fetch(value[WHITE], func(k string, v interface{}) {
m.Push(ROLE, kit.Value(value, kit.MDB_NAME))
m.Push(kit.MDB_ZONE, WHITE)
m.Push(kit.MDB_KEY, k)
})
kit.Fetch(value[BLACK], func(k string, v interface{}) {
m.Push(ROLE, kit.Value(value, kit.MDB_NAME))
m.Push(kit.MDB_ZONE, BLACK)
m.Push(kit.MDB_KEY, k)
})
})
}
func _role_chain(arg ...string) string {
return strings.ReplaceAll(kit.Keys(arg), "/", ice.PT)
}
func _role_black(m *ice.Message, userrole, chain string, status bool) {
m.Richs(ROLE, nil, userrole, func(key string, value map[string]interface{}) {
m.Log_CREATE(ROLE, userrole, BLACK, chain)
list := value[BLACK].(map[string]interface{})
list[chain] = status
})
}
func _role_white(m *ice.Message, userrole, chain string, status bool) {
m.Richs(ROLE, nil, userrole, func(key string, value map[string]interface{}) {
m.Log_CREATE(ROLE, userrole, WHITE, chain)
list := value[WHITE].(map[string]interface{})
list[chain] = status
})
}
func _role_right(m *ice.Message, userrole string, keys ...string) (ok bool) {
if userrole == ROOT {
return true // 超级用户
}
m.Richs(ROLE, nil, kit.Select(VOID, userrole), func(key string, value map[string]interface{}) {
ok = true
list := value[BLACK].(map[string]interface{})
for i := 0; i < len(keys); i++ {
if v, o := list[kit.Join(keys[:i+1], ice.PT)]; o && v == true {
ok = false
}
}
if m.Warn(!ok, ice.ErrNotRight, keys, USERROLE, userrole) {
return
}
if userrole == TECH {
return // 管理用户
}
ok = false
list = value[WHITE].(map[string]interface{})
for i := 0; i < len(keys); i++ {
if v, o := list[kit.Join(keys[:i+1], ice.PT)]; o && v == true {
ok = true
}
}
if m.Warn(!ok, ice.ErrNotRight, keys, USERROLE, userrole) {
return
}
// 普通用户
})
return strings.TrimPrefix(strings.TrimPrefix(strings.TrimSuffix(strings.ReplaceAll(path.Join(strings.ReplaceAll(kit.Keys(key), ice.PT, ice.PS)), ice.PS, ice.PT), ice.PT), ice.PT), "web.")
}
func _role_set(m *ice.Message, role, zone, key string, status bool) {
m.Logs(mdb.INSERT, mdb.KEY, ROLE, ROLE, role, zone, key)
mdb.HashSelectUpdate(m, role, func(value ice.Map) { value[zone].(ice.Map)[key] = status })
}
func _role_white(m *ice.Message, role, key string) { _role_set(m, role, WHITE, key, true) }
func _role_black(m *ice.Message, role, key string) { _role_set(m, role, BLACK, key, true) }
func _role_check(value ice.Map, key []string, ok bool) bool {
white, black := value[WHITE].(ice.Map), value[BLACK].(ice.Map)
for i := 0; i < len(key); i++ {
kit.If(white[kit.Join(key[:i+1], ice.PT)], func() { ok = true })
kit.If(black[kit.Join(key[:i+1], ice.PT)], func() { ok = false })
}
return ok
}
func RoleRight(m *ice.Message, userrole string, keys ...string) bool {
return _role_right(m, userrole, kit.Split(kit.Keys(keys), ice.PT)...)
func _role_right(m *ice.Message, role string, key ...string) (ok bool) {
return role == ROOT || len(mdb.HashSelectDetails(m, kit.Select(VOID, role), func(value ice.Map) bool { return _role_check(value, key, role == TECH) })) > 0
}
func _role_list(m *ice.Message, role string, arg ...string) *ice.Message {
mdb.HashSelectDetail(m, kit.Select(VOID, role), func(value ice.Map) {
kit.For(value[WHITE], func(k string, v ice.Any) {
if len(arg) == 0 || k == arg[0] {
m.Push(ROLE, kit.Value(value, mdb.NAME)).Push(mdb.ZONE, WHITE).Push(mdb.KEY, k).Push(mdb.STATUS, v)
}
})
kit.For(value[BLACK], func(k string, v ice.Any) {
if len(arg) == 0 || k == arg[0] {
m.Push(ROLE, kit.Value(value, mdb.NAME)).Push(mdb.ZONE, BLACK).Push(mdb.KEY, k).Push(mdb.STATUS, v)
}
})
})
return m.Sort(mdb.KEY)
}
const ( // 用户角色
const (
ROOT = "root"
TECH = "tech"
VOID = "void"
)
const ( // 角色操作
BLACK = "black"
const (
WHITE = "white"
BLACK = "black"
RIGHT = "right"
)
const (
AUTH = "auth"
ACCESS = "access"
PUBLIC = "public"
PRIVATE = "private"
CONFIRM = "confirm"
)
const ROLE = "role"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
ROLE: {Name: ROLE, Help: "角色", Value: kit.Data(kit.MDB_SHORT, kit.MDB_NAME)},
}, Commands: map[string]*ice.Command{
ROLE: {Name: "role role auto create", Help: "角色", Action: map[string]*ice.Action{
mdb.CREATE: {Name: "create role=void,tech zone=white,black key=", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
m.Richs(ROLE, nil, m.Option(ROLE), func(key string, value map[string]interface{}) {
list := value[m.Option(kit.MDB_ZONE)].(map[string]interface{})
m.Log_CREATE(ROLE, m.Option(ROLE), list[m.Option(kit.MDB_KEY)])
list[m.Option(kit.MDB_KEY)] = true
Index.MergeCommands(ice.Commands{
ROLE: {Name: "role name key auto", Help: "角色", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(ROLE, mdb.CREATE, VOID, TECH)
has := map[string]bool{VOID: true, TECH: true}
m.Travel(func(p *ice.Context, c *ice.Context, key string, cmd *ice.Command) {
role := map[string][]string{}
kit.For(kit.Split(cmd.Role), func(k string) { role[k] = []string{} })
for sub, action := range cmd.Actions {
kit.For(kit.Split(action.Role), func(k string) { role[k] = append(role[k], sub) })
}
kit.For(role, func(role string, list []string) {
kit.If(!has[role], func() { m.Cmd(ROLE, mdb.CREATE, role); has[role] = true })
roleHandle(m, role, list...)
})
})
m.Cmd(ROLE, WHITE, VOID, ROLE, "action", RIGHT)
}},
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.Richs(ROLE, nil, m.Option(ROLE), func(key string, value map[string]interface{}) {
list := value[m.Option(kit.MDB_ZONE)].(map[string]interface{})
m.Log_REMOVE(ROLE, m.Option(ROLE), list[m.Option(kit.MDB_KEY)])
delete(list, m.Option(kit.MDB_KEY))
})
}},
BLACK: {Name: "black role chain...", Help: "黑名单", Hand: func(m *ice.Message, arg ...string) {
_role_black(m, arg[0], _role_chain(arg[1:]...), true)
}},
WHITE: {Name: "white role chain...", Help: "白名单", Hand: func(m *ice.Message, arg ...string) {
_role_white(m, arg[0], _role_chain(arg[1:]...), true)
}},
RIGHT: {Name: "right role chain...", Help: "查看权限", Hand: func(m *ice.Message, arg ...string) {
if _role_right(m, arg[0], kit.Split(_role_chain(arg[1:]...), ice.PT)...) {
m.Echo(ice.OK)
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
if arg[0] == mdb.KEY {
mdb.HashInputs(m, ice.INDEX).CutTo(ice.INDEX, arg[0])
}
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) < 2 { // 角色列表
_role_list(m, kit.Select("", arg, 0))
m.PushAction(mdb.REMOVE)
return
mdb.CREATE: {Hand: func(m *ice.Message, arg ...string) {
kit.For(arg, func(role string) {
mdb.Rich(m, ROLE, nil, kit.Dict(mdb.NAME, role, BLACK, kit.Dict(), WHITE, kit.Dict()))
})
}},
mdb.INSERT: {Name: "insert role*=void zone*=white,black key*", Hand: func(m *ice.Message, arg ...string) {
_role_set(m, m.Option(ROLE), m.Option(mdb.ZONE), m.Option(mdb.KEY), true)
}},
mdb.DELETE: {Hand: func(m *ice.Message, arg ...string) {
_role_set(m, m.Option(ROLE), m.Option(mdb.ZONE), m.Option(mdb.KEY), false)
}},
WHITE: {Hand: func(m *ice.Message, arg ...string) { _role_white(m, arg[0], _role_keys(arg[1:]...)) }},
BLACK: {Hand: func(m *ice.Message, arg ...string) { _role_black(m, arg[0], _role_keys(arg[1:]...)) }},
RIGHT: {Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 2 {
m.Search(arg[1], func(key string, cmd *ice.Command) {
if _, ok := cmd.Actions[arg[2]]; ok {
arg = kit.Simple(arg[0], arg[1], ice.ACTION, arg[2:])
}
})
}
for _, role := range kit.AddUniq(kit.Split(arg[0]), VOID) {
if _role_right(m, role, kit.Split(_role_keys(arg[1:]...), ice.PT)...) {
m.Echo(ice.OK)
break
}
}
}},
}, mdb.HashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name")), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
mdb.HashSelect(m, arg...)
} else {
_role_list(m, kit.Select("", arg, 0), kit.Slice(arg, 1)...)
}
// 设置角色
_role_user(m, arg[0], arg[1:]...)
}},
}})
})
}
func roleHandle(m *ice.Message, role string, key ...string) {
cmd := m.ShortKey()
role = kit.Select(VOID, role)
m.Cmd(ROLE, WHITE, role, cmd)
if cmd == "header" {
return
}
m.Cmd(ROLE, BLACK, role, cmd, ice.ACTION)
kit.For(key, func(key string) { m.Cmd(ROLE, WHITE, role, cmd, ice.ACTION, key) })
}
func WhiteAction(role string, key ...string) ice.Actions {
role = kit.Select(VOID, role)
return ice.Actions{ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
cmd := m.CommandKey()
m.Cmd(ROLE, WHITE, role, cmd)
if cmd == "header" {
return
}
m.Cmd(ROLE, BLACK, role, cmd, ice.ACTION)
kit.For(key, func(key string) { m.Cmd(ROLE, WHITE, role, cmd, ice.ACTION, key) })
}}}
}
func White(m *ice.Message, key ...string) {
kit.For(key, func(key string) { m.Cmd(ROLE, WHITE, VOID, key) })
}
func Black(m *ice.Message, key ...string) {
kit.For(key, func(key string) { m.Cmd(ROLE, BLACK, VOID, key) })
}
func Right(m *ice.Message, key ...ice.Any) bool {
if key := kit.Simple(key); len(key) > 2 && key[1] == ice.ACTION && kit.IsIn(kit.Format(key[2]), ice.RUN, ice.COMMAND) {
return true
} else if len(key) > 0 && key[0] == ice.ETC_PATH {
return true
}
// m.Option(ice.MSG_TITLE, kit.Keys(m.Option(ice.MSG_USERPOD), m.CommandKey(), m.ActionKey())+" "+logs.FileLine(-2))
return !ice.Info.Important || m.Option(ice.MSG_USERROLE) == ROOT || !m.WarnNotRight(m.Cmdx(ROLE, RIGHT, m.Option(ice.MSG_USERROLE), key, logs.FileLineMeta(-1)) != ice.OK,
kit.Keys(key...), USERROLE, m.Option(ice.MSG_USERROLE))
}
func IsTechOrRoot(m *ice.Message) bool { return kit.IsIn(m.Option(ice.MSG_USERROLE), TECH, ROOT) }

View File

@ -4,44 +4,22 @@ import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _sess_create(m *ice.Message, username string, arg ...string) {
if msg := m.Cmd(USER, username); msg.Length() > 0 {
mdb.HashCreate(m, msg.AppendSimple(USERROLE, USERNAME, USERNICK, AVATAR), arg)
} else {
mdb.HashCreate(m, m.OptionSimple(USERROLE, USERNAME, USERNICK, AVATAR), arg)
}
}
func _sess_check(m *ice.Message, sessid string) {
m.Richs(SESS, nil, sessid, func(value map[string]interface{}) {
value = kit.GetMeta(value)
m.Richs(USER, nil, value[USERNAME], func(value map[string]interface{}) {
value = kit.GetMeta(value)
m.Log_AUTH(
USERROLE, m.Option(ice.MSG_USERROLE, UserRole(m, value[USERNAME])),
USERNAME, m.Option(ice.MSG_USERNAME, value[USERNAME]),
USERNICK, m.Option(ice.MSG_USERNICK, value[USERNICK]),
)
})
})
}
func _sess_create(m *ice.Message, username string) string {
if username == "" {
return ""
if val := mdb.HashSelectDetails(m, sessid, func(value ice.Map) bool {
return kit.Format(value[mdb.TIME]) > m.Time()
}); len(val) > 0 {
SessAuth(m, val)
}
if m.Richs(USER, nil, username, nil) == nil {
_user_create(m, username, kit.Hashs())
}
h := m.Cmdx(mdb.INSERT, SESS, "", mdb.HASH,
USERROLE, UserRole(m, username), USERNAME, username,
IP, m.Option(ice.MSG_USERIP), UA, m.Option(ice.MSG_USERUA),
kit.MDB_TIME, m.Time(m.Conf(SESS, kit.Keym(kit.MDB_EXPIRE))),
)
m.Event(SESS_CREATE, SESS, h, USERNAME, username)
return h
}
func SessCheck(m *ice.Message, sessid string) {
_sess_check(m, sessid)
}
func SessCreate(m *ice.Message, username string) string {
return m.Option(ice.MSG_SESSID, _sess_create(m, username))
}
const (
@ -49,25 +27,46 @@ const (
UA = "ua"
)
const (
CHECK = "check"
LOGIN = "login"
LOGOUT = "logout"
)
const (
SESS_CREATE = "sess.create"
)
const SESS = "sess"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SESS: {Name: SESS, Help: "会话", Value: kit.Data(
kit.MDB_SHORT, "uniq", kit.MDB_FIELD, "time,hash,userrole,username,ip,ua",
kit.MDB_EXPIRE, "720h",
)},
}, Commands: map[string]*ice.Command{
SESS: {Name: "sess hash auto prunes", Help: "会话", Action: ice.MergeAction(map[string]*ice.Action{
mdb.CREATE: {Name: "create username", Help: "创建"},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...)
}},
}})
Index.MergeCommands(ice.Commands{
SESS: {Name: "sess hash auto", Help: "会话", Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create username*", Hand: func(m *ice.Message, arg ...string) {
_sess_create(m, m.Option(USERNAME), UA, m.Option(ice.MSG_USERUA), IP, m.Option(ice.MSG_USERIP))
}},
CHECK: {Name: "check sessid*", Hand: func(m *ice.Message, arg ...string) { _sess_check(m, m.Option(ice.MSG_SESSID)) }},
}, mdb.ImportantHashAction("checkbox", ice.TRUE, mdb.EXPIRE, mdb.MONTH, mdb.SHORT, mdb.UNIQ, mdb.FIELD, "time,hash,userrole,username,usernick,avatar,ip,ua"))},
})
}
func SessCreate(m *ice.Message, username string) string {
return m.Option(ice.MSG_SESSID, m.Cmdx(SESS, mdb.CREATE, username))
}
func SessCheck(m *ice.Message, sessid string) bool {
m.Options(ice.MSG_USERNICK, "", ice.MSG_USERNAME, "", ice.MSG_USERROLE, VOID, ice.MSG_CHECKER, logs.FileLine(-1))
return sessid != "" && m.Cmdy(SESS, CHECK, sessid, logs.FileLineMeta(-1)).Option(ice.MSG_USERNAME) != ""
}
func SessValid(m *ice.Message) string {
if m.Option(ice.MSG_SESSID) == "" || m.Spawn().AdminCmd(SESS, m.Option(ice.MSG_SESSID)).Length() == 0 {
return m.Option(ice.MSG_SESSID, m.Spawn().AdminCmd(SESS, mdb.CREATE, m.Option(ice.MSG_USERNAME)).Result())
}
return m.Option(ice.MSG_SESSID)
}
func SessAuth(m *ice.Message, value ice.Any, arg ...string) *ice.Message {
return m.Auth(
USERROLE, m.Option(ice.MSG_USERROLE, kit.Format(kit.Value(value, USERROLE))),
USERNAME, m.Option(ice.MSG_USERNAME, kit.Format(kit.Value(value, USERNAME))),
USERNICK, m.Option(ice.MSG_USERNICK, kit.Format(kit.Value(value, USERNICK))),
LANGUAGE, m.OptionDefault(ice.MSG_LANGUAGE, kit.Format(kit.Value(value, LANGUAGE))),
AVATAR, m.Option(ice.MSG_AVATAR, kit.Format(kit.Value(value, AVATAR))),
arg, logs.FileLineMeta(kit.Select(logs.FileLine(-1), m.Option("aaa.checker"))),
)
}
func SessLogout(m *ice.Message, arg ...string) {
kit.If(m.Option(ice.MSG_SESSID), func(sessid string) { m.Cmd(SESS, mdb.REMOVE, mdb.HASH, sessid) })
}

View File

@ -21,69 +21,48 @@ func _totp_gen(per int64) string {
b := hmac.New(sha1.New, buf.Bytes()).Sum(nil)
return strings.ToUpper(base32.StdEncoding.EncodeToString(b[:]))
}
func _totp_get(key string, num int, per int64) string {
now := kit.Int64(time.Now().Unix() / per)
buf := []byte{}
for i := 0; i < 8; i++ {
buf = append(buf, byte((uint64(now) >> uint64(((7 - i) * 8)))))
}
if l := len(key) % 8; l != 0 {
key += strings.Repeat("=", 8-l)
}
func _totp_get(key string, per int64, num int) string {
buf, now := []byte{}, kit.Int64(time.Now().Unix()/per)
kit.For(8, func(i int) { buf = append(buf, byte((uint64(now) >> uint64(((7 - i) * 8))))) })
kit.If(len(key)%8, func(l int) { key += strings.Repeat(mdb.EQ, 8-l) })
s, _ := base32.StdEncoding.DecodeString(strings.ToUpper(key))
hm := hmac.New(sha1.New, s)
hm.Write(buf)
b := hm.Sum(nil)
n := b[len(b)-1] & 0x0F
res := int64(b[n]&0x7F)<<24 | int64(b[n+1]&0xFF)<<16 | int64(b[n+2]&0xFF)<<8 | int64(b[n+3]&0xFF)
return kit.Format(kit.Format("%%0%dd", num), res%int64(math.Pow10(num)))
}
func TOTP_GET(key string, num int, per int64) string { return _totp_get(key, num, per) }
const (
SECRET = "secret"
PERIOD = "period"
NUMBER = "number"
)
const TOTP = "totp"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
TOTP: {Name: TOTP, Help: "动态令牌", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_NAME, kit.MDB_FIELD, "time,name,secret,period,number",
kit.MDB_LINK, "otpauth://totp/%s?secret=%s",
)},
}, Commands: map[string]*ice.Command{
TOTP: {Name: "totp name auto create", Help: "动态令牌", Action: ice.MergeAction(map[string]*ice.Action{
mdb.CREATE: {Name: "create name secret period=30 number=6", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
if m.Option(SECRET) == "" { // 创建密钥
m.Option(SECRET, _totp_gen(kit.Int64(m.Option(PERIOD))))
}
m.Cmd(mdb.INSERT, TOTP, "", mdb.HASH, m.OptionSimple(kit.MDB_NAME, SECRET, PERIOD, NUMBER))
const (
NUMBER = "number"
PERIOD = "period"
SECRET = "secret"
)
Index.MergeCommands(ice.Commands{
TOTP: {Help: "令牌", Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create name*=hi number*=6 period*=30 secret", Hand: func(m *ice.Message, arg ...string) {
kit.If(m.Option(SECRET) == "", func() { m.Option(SECRET, _totp_gen(kit.Int64(m.Option(PERIOD)))) })
mdb.HashCreate(m, m.OptionSimple(mdb.NAME, NUMBER, PERIOD, SECRET))
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m.Spawn(c), arg...).Table(func(index int, value map[string]string, head []string) {
if len(arg) > 0 {
m.OptionFields(mdb.DETAIL)
}
m.Push(kit.MDB_TIME, m.Time())
m.Push(kit.MDB_NAME, value[kit.MDB_NAME])
}, mdb.HashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,number,period,secret", mdb.LINK, "otpauth://totp/%s?secret=%s")), Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m.Spawn(), arg...).Table(func(value ice.Maps) {
kit.If(len(arg) > 0, func() { m.OptionFields(ice.FIELDS_DETAIL) })
m.Push(mdb.TIME, m.Time()).Push(mdb.NAME, value[mdb.NAME])
period := kit.Int64(value[PERIOD])
m.Push("rest", period-time.Now().Unix()%period)
m.Push("code", _totp_get(value[SECRET], kit.Int(value[NUMBER]), period))
m.Push(mdb.EXPIRE, period-time.Now().Unix()%period)
m.Push(mdb.VALUE, _totp_get(value[SECRET], period, kit.Int(value[NUMBER])))
if len(arg) > 0 {
m.PushQRCode("scan", kit.Format(m.Config(kit.MDB_LINK), value[kit.MDB_NAME], value[SECRET]))
m.Echo(_totp_get(value[SECRET], kit.Int(value[NUMBER]), kit.Int64(value[PERIOD])))
m.PushQRCode(mdb.SCAN, kit.Format(mdb.Config(m, mdb.LINK), value[mdb.NAME], value[SECRET]))
m.Echo(m.Append(mdb.VALUE))
}
})
kit.If(len(arg) == 0, func() { m.PushAction(mdb.REMOVE).Action(mdb.CREATE, mdb.PRUNES) })
}},
}})
})
}
func TOTP_GET(key string, per int64, num int) string { return _totp_get(key, per, num) }

View File

@ -2,136 +2,111 @@ package aaa
import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _user_login(m *ice.Message, name, word string) (ok bool) {
if m.Richs(USER, nil, name, nil) == nil {
_user_create(m, name, word)
}
m.Richs(USER, nil, name, func(key string, value map[string]interface{}) {
ok = word == value[PASSWORD]
})
return ok
func _user_create(m *ice.Message, name string, arg ...string) {
mdb.HashCreate(m, USERNAME, name, arg)
gdb.Event(m, USER_CREATE, USER, name)
}
func _user_create(m *ice.Message, name, word string) {
m.Rich(USER, nil, kit.Dict(
USERNAME, name, PASSWORD, word, USERNICK, name,
USERZONE, m.Option(ice.MSG_USERZONE),
))
m.Event(USER_CREATE, USER, name)
}
func _user_search(m *ice.Message, name, text string) {
m.Richs(USER, nil, kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
if value = kit.GetMeta(value); name != "" && name != value[USERNAME] {
return
}
m.PushSearch(kit.MDB_TYPE, UserRole(m, value[USERNAME]), kit.MDB_NAME, kit.Format(value[USERNAME]), kit.MDB_TEXT, kit.Format(value[USERNICK]), value)
})
}
func UserRoot(m *ice.Message) {
m.Option(ice.MSG_USERROLE, ROOT)
m.Option(ice.MSG_USERNAME, ice.Info.UserName)
if m.Richs(USER, "", ice.Info.UserName, nil) == nil {
_user_create(m, ice.Info.UserName, kit.Hashs())
}
}
func UserRole(m *ice.Message, username interface{}) (role string) {
if role = VOID; username == ice.Info.UserName {
return ROOT
}
m.Richs(ROLE, nil, TECH, func(key string, value map[string]interface{}) {
if kit.Value(kit.GetMeta(value), kit.Keys(USER, username)) == true {
role = TECH
}
})
return
}
func UserNick(m *ice.Message, username interface{}) (nick string) {
m.Richs(USER, nil, kit.Format(username), func(key string, value map[string]interface{}) {
nick = kit.Format(kit.GetMeta(value)[USERNICK])
})
return
}
func UserZone(m *ice.Message, username interface{}) (zone string) {
m.Richs(USER, nil, kit.Format(username), func(key string, value map[string]interface{}) {
zone = kit.Format(kit.GetMeta(value)[USERZONE])
})
return
}
func UserLogin(m *ice.Message, username, password string) bool {
if password == "" || _user_login(m, username, password) {
m.Log_AUTH(
USERROLE, m.Option(ice.MSG_USERROLE, UserRole(m, username)),
USERNAME, m.Option(ice.MSG_USERNAME, username),
USERNICK, m.Option(ice.MSG_USERNICK, UserNick(m, username)),
)
return true
}
return false
func _user_remove(m *ice.Message, name string, arg ...string) {
gdb.Event(m, USER_REMOVE, m.OptionSimple(USERNAME, USERNICK))
mdb.HashRemove(m, m.OptionSimple(USERNAME))
}
const (
BACKGROUND = "background"
AVATAR_URL = "avatar_url"
AVATAR = "avatar"
GENDER = "gender"
MOBILE = "mobile"
PHONE = "phone"
SECRET = "secret"
THEME = "theme"
AVATAR = "avatar"
GENDER = "gender"
MOBILE = "mobile"
EMAIL = "email"
CITY = "city"
COUNTRY = "country"
PROVINCE = "province"
LANGUAGE = "language"
LANGUAGE = "language"
LOCATION = "location"
LONGITUDE = "longitude"
LATITUDE = "latitude"
COMPANY = "company"
PROVINCE = "province"
COUNTRY = "country"
CITY = "city"
)
const (
USERROLE = "userrole"
USERNICK = "usernick"
USERNAME = "username"
PASSWORD = "password"
USERNICK = "usernick"
USERROLE = "userrole"
USERZONE = "userzone"
)
const (
USER_CREATE = "user.create"
USER_REMOVE = "user.remove"
)
const (
INVITE = "invite"
)
const USER = "user"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
USER: {Name: USER, Help: "用户", Value: kit.Data(
kit.MDB_SHORT, USERNAME, kit.MDB_FIELD, "time,username,usernick,userzone",
)},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(mdb.SEARCH, mdb.CREATE, USER, m.Prefix(USER))
}},
USER: {Name: "user username auto create", Help: "用户", Action: ice.MergeAction(map[string]*ice.Action{
mdb.CREATE: {Name: "create userrole=void,tech username password", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
_user_create(m, m.Option(USERNAME), m.Option(PASSWORD))
_role_user(m, m.Option(USERROLE), m.Option(USERNAME))
}},
mdb.REMOVE: {Name: "remove username", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(mdb.DELETE, USER, "", mdb.HASH, m.OptionSimple(m.Config(kit.MDB_SHORT)))
m.Event(USER_REMOVE, USER, m.Option(m.Config(kit.MDB_SHORT)))
}},
mdb.SEARCH: {Name: "search type name text", Help: "搜索", Hand: func(m *ice.Message, arg ...string) {
if arg[0] == USER {
_user_search(m, arg[1], kit.Select("", arg, 2))
Index.MergeCommands(ice.Commands{
USER: {Help: "用户", Icon: "Contacts.png", Actions: ice.MergeActions(ice.Actions{
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch mdb.HashInputs(m, arg); arg[0] {
case USERNICK:
m.Push(arg[0], m.Option(ice.MSG_USERNICK))
case USERNAME:
m.Push(arg[0], m.Option(ice.MSG_USERNAME))
}
if arg[0] == USERROLE {
m.Option(ice.TABLE_CHECKBOX, ice.TRUE)
}
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...).Table(func(index int, value map[string]string, head []string) {
m.Push(USERROLE, UserRole(m, value[USERNAME]))
})
}},
}})
mdb.CREATE: {Name: "create userrole=void,tech username* usernick language userzone email avatar", Hand: func(m *ice.Message, arg ...string) {
_user_create(m, m.Option(USERNAME), m.OptionSimple(USERROLE, USERNICK, LANGUAGE, AVATAR, BACKGROUND, USERZONE, EMAIL)...)
}},
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) { _user_remove(m, m.Option(USERNAME)) }},
}, mdb.ImportantHashAction(mdb.SHORT, USERNAME, mdb.FIELD, "time,userrole,username,usernick,language,avatar,background,userzone", html.CHECKBOX, ice.TRUE))},
})
}
func UserInfo(m *ice.Message, name ice.Any, key, meta string) (value string) {
if m.Cmd(USER, kit.Select(m.Option(ice.MSG_USERNAME), name), func(val ice.Maps) { value = val[key] }).Length() == 0 || value == "" {
return m.Option(meta)
}
return
}
func UserRole(m *ice.Message, username ice.Any) (role string) {
if username == "" {
return VOID
} else if role = VOID; username == ice.Info.Username {
return ROOT
} else {
return UserInfo(m, username, USERROLE, ice.MSG_USERROLE)
}
}
func UserNick(m *ice.Message, username ice.Any) (nick string) {
return UserInfo(m, username, USERNICK, ice.MSG_USERNICK)
}
func UserLang(m *ice.Message, username ice.Any) (nick string) {
return UserInfo(m, username, LANGUAGE, ice.MSG_LANGUAGE)
}
func UserZone(m *ice.Message, username ice.Any) (zone string) {
return UserInfo(m, username, USERZONE, ice.MSG_USERZONE)
}
func UserEmail(m *ice.Message, username ice.Any) (nick string) {
return UserInfo(m, username, EMAIL, EMAIL)
}
func UserRoot(m *ice.Message, arg ...string) *ice.Message {
userrole := kit.Select(TECH, arg, 0)
username := kit.Select(ice.Info.Username, arg, 1)
usernick := kit.Select(UserNick(m, username), arg, 2)
language := kit.Select(UserLang(m, username), arg, 3)
userzone := kit.Select(ice.OPS, arg, 4)
email := kit.Select(UserEmail(m, username), arg, 5)
if len(arg) > 0 {
kit.If(username != ROOT, func() { ice.Info.Username = username })
m.Cmd(USER, mdb.CREATE, userrole, username, usernick, language, userzone, email)
}
return SessAuth(m, kit.Dict(USERROLE, userrole, USERNAME, username, USERNICK, usernick))
}

View File

@ -1,18 +1,18 @@
package shy
package base
import (
_ "shylinux.com/x/icebergs/base/aaa"
_ "shylinux.com/x/icebergs/base/cli"
_ "shylinux.com/x/icebergs/base/ctx"
_ "shylinux.com/x/icebergs/base/mdb"
_ "shylinux.com/x/icebergs/base/web"
_ "shylinux.com/x/icebergs/base/gdb"
_ "shylinux.com/x/icebergs/base/lex"
_ "shylinux.com/x/icebergs/base/log"
_ "shylinux.com/x/icebergs/base/ssh"
_ "shylinux.com/x/icebergs/base/yac"
_ "shylinux.com/x/icebergs/base/mdb"
_ "shylinux.com/x/icebergs/base/cli"
_ "shylinux.com/x/icebergs/base/log"
_ "shylinux.com/x/icebergs/base/nfs"
_ "shylinux.com/x/icebergs/base/ssh"
_ "shylinux.com/x/icebergs/base/tcp"
)

View File

@ -1,21 +0,0 @@
label `
ctx cli web aaa
lex yac gdb log
tcp nfs ssh mdb
`
source ctx/ctx.shy
source cli/cli.shy
source web/web.shy
source aaa/aaa.shy
source lex/lex.shy
source yac/yac.shy
source gdb/gdb.shy
source log/log.shy
source tcp/tcp.shy
source nfs/nfs.shy
source ssh/ssh.shy
source mdb/mdb.shy

View File

@ -2,15 +2,13 @@ package cli
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const (
USER = "USER"
HOME = "HOME"
PATH = "PATH"
)
const CLI = "cli"
var Index = &ice.Context{Name: CLI, Help: "命令模块"}
func init() { ice.Index.Register(Index, nil, RUNTIME, SYSTEM, DAEMON, QRCODE) }
func Prefix(arg ...string) string { return kit.Keys(CLI, arg) }
func init() { ice.Index.Register(Index, nil, RUNTIME, SYSTEM, DAEMON, FOREVER, MIRRORS, QRCODE, SUDO) }

View File

@ -1,7 +0,0 @@
chapter "cli"
field "环境" runtime
field "命令" system
field "守护" daemon
field "扫码" qrcode

92
base/cli/color.go Normal file
View File

@ -0,0 +1,92 @@
package cli
import (
"fmt"
"image/color"
"math/rand"
"strconv"
"strings"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const (
_DARK = 255
_LIGHT = 127
)
var _color_map = map[string]color.Color{
BLACK: color.RGBA{0, 0, 0, _DARK},
RED: color.RGBA{_DARK, 0, 0, _DARK},
GREEN: color.RGBA{0, _DARK, 0, _DARK},
YELLOW: color.RGBA{_DARK, _DARK, 0, _DARK},
BLUE: color.RGBA{0, 0, _DARK, _DARK},
PURPLE: color.RGBA{_DARK, 0, _DARK, _DARK},
CYAN: color.RGBA{0, _DARK, _DARK, _DARK},
WHITE: color.RGBA{_DARK, _DARK, _DARK, _DARK},
SILVER: color.RGBA{0xC0, 0xC0, 0xC0, _DARK},
}
var _color_list = map[string]string{
"navy": "#000080",
"aliceblue": "#f0f8ff",
"firebrick": "#b22222",
}
func _parse_color(str string) color.Color {
if str == RANDOM {
list := kit.SortedKey(_color_map)
str = list[rand.Intn(len(list))]
}
str = kit.Select(str, _color_list[str])
if strings.HasPrefix(str, "#") {
kit.If(len(str) == 7, func() { str += "ff" })
if u, e := strconv.ParseUint(str[1:], 16, 64); e == nil {
return color.RGBA{uint8((u & 0xFF000000) >> 24), uint8((u & 0x00FF0000) >> 16), uint8((u & 0x0000FF00) >> 8), uint8((u & 0x000000FF) >> 0)}
}
}
if color, ok := _color_map[str]; ok {
return color
}
return _color_map[WHITE]
}
func _parse_cli_color(str string) string {
res := 0
r, g, b, _ := _parse_color(str).RGBA()
kit.If(r > _LIGHT, func() { res += 1 })
kit.If(g > _LIGHT, func() { res += 2 })
kit.If(b > _LIGHT, func() { res += 4 })
return kit.Format(res)
}
const (
BG = "bg"
FG = "fg"
COLOR = "color"
BLACK = "black"
WHITE = "white"
BLUE = "blue"
RED = "red"
GRAY = "gray"
CYAN = "cyan"
GREEN = "green"
SILVER = "silver"
PURPLE = "purple"
YELLOW = "yellow"
RANDOM = "random"
TRANS = "#0000"
LIGHT = "light"
DARK = "dark"
)
func Color(m *ice.Message, c string, str ice.Any) string {
wrap, color := `<span style="color:%s">%v</span>`, c
kit.If(m.IsCliUA(), func() { wrap, color = "\033[3%sm%v\033[0m", _parse_cli_color(c) })
return fmt.Sprintf(wrap, color, str)
}
func ColorRed(m *ice.Message, str ice.Any) string { return Color(m, RED, str) }
func ColorGreen(m *ice.Message, str ice.Any) string { return Color(m, GREEN, str) }
func ColorYellow(m *ice.Message, str ice.Any) string { return Color(m, YELLOW, str) }
func ParseCliColor(color string) string { return _parse_cli_color(color) }
func ParseColor(color string) color.Color { return _parse_color(color) }

View File

@ -1,93 +1,75 @@
package cli
import (
"bytes"
"io"
"os/exec"
"runtime"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
kit "shylinux.com/x/toolkits"
)
func _daemon_exec(m *ice.Message, cmd *exec.Cmd) {
if r, ok := m.Optionv(CMD_INPUT).(io.Reader); ok {
cmd.Stdin = r
}
err := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS))
cmd.Stderr = err
if w := _system_out(m, CMD_OUTPUT); w != nil {
cmd.Stdout = w
cmd.Stderr = w
cmd.Stdout, cmd.Stderr = w, w
}
if w := _system_out(m, CMD_ERRPUT); w != nil {
cmd.Stderr = w
}
// 启动进程
if e := cmd.Start(); m.Warn(e, ice.ErrNotStart, cmd.Args) {
return // 启动失败
h := mdb.HashCreate(m.Spawn(), STATUS, START,
ice.CMD, strings.TrimPrefix(strings.TrimPrefix(kit.JoinWord(cmd.Args...), kit.Path("")+nfs.PS), cmd.Dir+nfs.PS), DIR, cmd.Dir, ENV, kit.Select("", cmd.Env),
m.OptionSimple(CMD_INPUT, CMD_OUTPUT, CMD_ERRPUT, mdb.CACHE_CLEAR_ONEXIT),
)
if e := cmd.Start(); m.WarnNotValid(e, cmd.Args, err.String()) {
mdb.HashModify(m, h, STATUS, ERROR, ERROR, e)
return
}
m.Echo("%d", cmd.Process.Pid)
m.Go(func() {
h := m.Cmdx(mdb.INSERT, DAEMON, "", mdb.HASH,
kit.MDB_STATUS, START, ice.CMD, kit.Join(cmd.Args, ice.SP),
PID, cmd.Process.Pid, DIR, cmd.Dir, ENV, kit.Select("", cmd.Env),
m.OptionSimple(CMD_OUTPUT, CMD_ERRPUT, mdb.CACHE_CLEAR_ON_EXIT),
)
if e := cmd.Wait(); m.Warn(e, ice.ErrNotStart, cmd.Args) {
if m.Conf(DAEMON, kit.Keys(kit.MDB_HASH, h, kit.Keym(kit.MDB_STATUS))) == START {
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, ERROR, ERROR, e)
}
mdb.HashSelectUpdate(m, h, func(value ice.Map) { value[PID] = cmd.Process.Pid })
m.Echo("%d", cmd.Process.Pid).Go(func() {
if e := cmd.Wait(); !m.WarnNotValid(e, cmd.Args, err.String()) && cmd.ProcessState != nil && cmd.ProcessState.Success() {
mdb.HashModify(m, mdb.HASH, h, STATUS, STOP)
m.Cost(CODE, "0", ctx.ARGS, cmd.Args)
} else {
m.Cost(kit.MDB_CODE, cmd.ProcessState.ExitCode(), kit.MDB_ARGS, cmd.Args)
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, STOP)
mdb.HashSelectUpdate(m, h, func(value ice.Map) { value[STATUS], value[ERROR] = ERROR, e })
}
switch cb := m.Optionv(kit.Keycb(DAEMON)).(type) {
switch status := mdb.HashSelectField(m.Sleep300ms(), h, STATUS); cb := m.OptionCB("").(type) {
case func(string) bool:
kit.If(!cb(status), func() { m.Cmdy(DAEMON, cmd.Path, cmd.Args) })
case func(string):
m.Sleep("1s")
cb(m.Conf(DAEMON, kit.Keys(kit.MDB_HASH, h, kit.Keym(kit.MDB_STATUS))))
cb(status)
case func():
m.Sleep("1s")
cb()
case nil:
default:
m.ErrorNotImplement(cb)
}
if w, ok := m.Optionv(CMD_INPUT).(io.Closer); ok {
w.Close()
}
if w, ok := m.Optionv(CMD_OUTPUT).(io.Closer); ok {
w.Close()
}
if w, ok := m.Optionv(CMD_ERRPUT).(io.Closer); ok {
w.Close()
}
kit.For(kit.Simple(CMD_INPUT, CMD_OUTPUT, CMD_ERRPUT), func(p string) { nfs.Close(m, m.Optionv(p)) })
})
}
func Inputs(m *ice.Message, field string) bool {
switch strings.TrimPrefix(field, "extra.") {
case ice.POD:
m.Cmdy("route")
case ice.CTX:
m.Cmdy(ctx.CONTEXT)
case ice.CMD:
m.Cmdy(ctx.CONTEXT, kit.Select(m.Option(ice.CTX), m.Option(kit.Keys(kit.MDB_EXTRA, ice.CTX))), ctx.COMMAND)
case ice.ARG:
default:
return false
}
return true
}
const (
DIR = "dir"
ENV = "env"
API = "api"
PID = "pid"
PWD = "pwd"
DIR = "dir"
ENV = "env"
API = "api"
MOD = "mod"
PWD = "pwd"
PID = "pid"
PPID = "ppid"
)
const (
ERROR = "error"
BUILD = "build"
ORDER = "order"
SPAWN = "spawn"
@ -95,66 +77,128 @@ const (
BENCH = "bench"
PPROF = "pprof"
START = "start"
RESTART = "restart"
RELOAD = "reload"
STOP = "stop"
TIMEOUT = "timeout"
STATUS = "status"
ERROR = "error"
CLEAR = "clear"
STASH = "stash"
DELAY = "delay"
RECORD = "record"
RELOAD = "reload"
REBOOT = "reboot"
RESTART = "restart"
INTERVAL = "interval"
OPTS = "opts"
ARGS = "args"
LOGS = "logs"
OPEN = "open"
CLOSE = "close"
BEGIN = "begin"
END = "end"
START = "start"
STOP = "stop"
OPEN = "open"
CLOSE = "close"
PLAY = "play"
MAIN = "main"
CODE = "code"
COST = "cost"
FROM = "from"
BACK = "back"
)
const DAEMON = "daemon"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
DAEMON: {Name: DAEMON, Help: "守护进程", Value: kit.Data(
kit.MDB_PATH, ice.USR_LOCAL_DAEMON, kit.MDB_FIELD, "time,hash,status,pid,cmd,dir,env",
)},
}, Commands: map[string]*ice.Command{
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(mdb.PRUNES, DAEMON, "", mdb.HASH, mdb.CACHE_CLEAR_ON_EXIT, ice.TRUE)
}},
DAEMON: {Name: "daemon hash auto start prunes", Help: "守护进程", Action: ice.MergeAction(map[string]*ice.Action{
START: {Name: "start cmd env dir", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
m.Option(CMD_DIR, m.Option(DIR))
m.Option(CMD_ENV, kit.Split(m.Option(ENV), " ="))
m.Cmdy(DAEMON, kit.Split(m.Option(ice.CMD)))
Index.MergeCommands(ice.Commands{
DAEMON: {Name: "daemon hash auto", Help: "守护进程", Actions: ice.MergeActions(ice.Actions{
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) {
mdb.HashPrunesValue(m, mdb.CACHE_CLEAR_ONEXIT, ice.TRUE)
}},
RESTART: {Name: "restart", Help: "重启", Hand: func(m *ice.Message, arg ...string) {
m.Cmd(DAEMON, STOP)
m.Sleep("3s")
m.Cmdy(DAEMON, START)
START: {Name: "start cmd* dir env", Hand: func(m *ice.Message, arg ...string) {
m.Options(CMD_DIR, m.Option(DIR), CMD_ENV, kit.Split(m.Option(ENV), " ="))
_daemon_exec(m, _system_cmd(m, kit.Split(m.Option(ice.CMD))...))
}},
STOP: {Name: "stop", Help: "停止", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmd(mdb.SELECT, DAEMON, "", mdb.HASH, m.OptionSimple(kit.MDB_HASH)).Table(func(index int, value map[string]string, head []string) {
m.Cmd(mdb.MODIFY, DAEMON, "", mdb.HASH, m.OptionSimple(kit.MDB_HASH), kit.MDB_STATUS, STOP)
m.Cmdy(SYSTEM, "kill", "-9", value[PID])
RESTART: {Hand: func(m *ice.Message, arg ...string) {
m.Cmdy("", STOP).Sleep3s().Cmdy("", START)
}},
STOP: {Hand: func(m *ice.Message, arg ...string) {
h, pid := m.Option(mdb.HASH), m.Option(PID)
mdb.HashSelects(m.Spawn(), h).Table(func(value ice.Maps) {
if h == "" && value[PID] != pid {
return
}
mdb.HashModify(m, mdb.HASH, kit.Select(h, value[mdb.HASH]), STATUS, STOP)
kit.If(value[PID], func() { m.Cmd(gdb.SIGNAL, gdb.KILL, value[PID]) })
})
}},
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, STOP)
m.Cmdy(mdb.PRUNES, DAEMON, "", mdb.HASH, kit.MDB_STATUS, ERROR)
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) {
h, pid := m.Option(mdb.HASH), m.Option(PID)
mdb.HashSelects(m.Spawn(), h).Table(func(value ice.Maps) {
if h == "" && value[PID] != pid {
return
}
mdb.HashRemove(m, kit.Select(h, value[mdb.HASH]))
})
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...).Table(func(index int, value map[string]string, head []string) {
switch value[kit.MDB_STATUS] {
}, mdb.StatusHashAction(mdb.FIELD, "time,hash,status,pid,cmd,dir,env")), Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m, arg...).Table(func(value ice.Maps) {
switch value[STATUS] {
case START:
m.PushButton(RESTART, STOP)
default:
m.PushButton(mdb.REMOVE)
m.PushButton(START, mdb.REMOVE)
}
})
if len(arg) == 0 || m.Length() > 0 {
return
kit.If(len(arg) == 0, func() { m.Action(START, mdb.PRUNES) })
if len(arg) > 0 && m.Length() == 0 {
if runtime.GOOS == WINDOWS {
_daemon_exec(m, _system_cmd(m, arg...))
} else {
_daemon_exec(m, _system_cmd(m, arg...))
// _daemon_exec(m, _system_cmd(m, kit.Simple(kit.Split(arg[0]), arg[1:])...))
}
kit.If(IsSuccess(m) && m.Append(CMD_ERR) == "", func() { m.SetAppend() })
}
_daemon_exec(m, _system_cmd(m, arg...))
}},
}})
})
}
func Opens(m *ice.Message, arg ...string) {
if !tcp.IsLocalHost(m, m.Option(ice.MSG_USERIP)) {
// return
} else if len(arg) == 0 || arg[0] == "" {
// return
}
switch runtime.GOOS {
case DARWIN:
if kit.Ext(arg[0]) == "app" {
m.Cmdy(SYSTEM, OPEN, "-a", arg[0])
} else {
m.Cmdy(SYSTEM, OPEN, arg[0])
}
case WINDOWS:
if kit.Ext(arg[0]) == "exe" {
m.Cmdy(SYSTEM, arg[0])
} else {
m.Cmdy(SYSTEM, "explorer", arg[0])
}
}
}
func OpenCmds(m *ice.Message, arg ...string) *ice.Message {
if !tcp.IsLocalHost(m, m.Option(ice.MSG_USERIP)) {
return m
} else if len(arg) == 0 || arg[0] == "" {
return m
}
TellApp(m, "Terminal", kit.Format(`do script %q`, strings.Join(arg, "; ")), "activate")
return m
}
func TellApp(m *ice.Message, app string, arg ...string) {
OSAScript(m, kit.Format(`
tell application "%s"
%s
end tell
`, app, strings.Join(arg, lex.NL)))
}
func OSAScript(m *ice.Message, arg ...string) { m.Cmd(SYSTEM, "osascript", "-e", arg) }

73
base/cli/forever.go Normal file
View File

@ -0,0 +1,73 @@
package cli
import (
"os"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _path_sep() string { return kit.Select(nfs.DF, ";", strings.Contains(os.Getenv(PATH), ";")) }
const FOREVER = "forever"
func init() {
Index.MergeCommands(ice.Commands{
FOREVER: {Help: "启动", Actions: ice.Actions{
START: {Hand: func(m *ice.Message, arg ...string) {
env := []string{PATH, BinPath(""), HOME, kit.Select(kit.Path(""), os.Getenv(HOME))}
kit.For(ENV_LIST, func(k string) { kit.If(kit.Env(k) != "", func() { env = append(env, k, kit.Env(k)) }) })
kit.For(os.Environ(), func(v string) {
if ls := kit.Split(v, mdb.EQ, mdb.EQ); kit.IndexOf(env, ls[0]) == -1 && len(ls) > 1 {
env = append(env, ls[0], ls[1])
}
})
m.Options(CMD_ENV, env, CMD_INPUT, os.Stdin, CMD_OUTPUT, os.Stdout, CMD_ERRPUT, os.Stderr)
kit.If(kit.Select("/dev/null", kit.Env(CTX_LOG)), func(p string) { m.Optionv(CMD_ERRPUT, p) })
m.Cmd(FOREVER, STOP)
if bin := kit.Select(os.Args[0], ice.BIN_ICE_BIN, nfs.Exists(m, ice.BIN_ICE_BIN)); len(arg) > 0 && arg[0] == ice.SPACE {
m.Cmdy(FOREVER, bin, ice.SPACE, START, ice.DEV, ice.OPS, arg[1:])
} else {
kit.If(len(arg) > 0 && arg[0] != ice.DEV, func() { arg = append([]string{ice.DEV, ""}, arg...) })
m.Cmdy(FOREVER, bin, ice.SERVE, START, arg)
}
}},
RESTART: {Hand: func(m *ice.Message, arg ...string) { m.Cmd(gdb.SIGNAL, gdb.RESTART) }},
STOP: {Hand: func(m *ice.Message, arg ...string) { m.Cmd(gdb.SIGNAL, gdb.STOP) }},
DELAY: {Hand: func(m *ice.Message, arg ...string) { m.Sleep(arg[0]).Cmdy(arg[1:]) }},
}, Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
m.Cmdy(RUNTIME, BOOTINFO)
return
}
for {
if logs.Println("run %s", kit.Join(arg, lex.SP)); IsSuccess(m.Cmd(SYSTEM, arg)) {
logs.Println(ice.EXIT)
break
}
logs.Println()
}
}},
})
}
func BinPath(arg ...string) string {
list := []string{}
push := func(p string) {
kit.If(kit.IndexOf(list, p) == -1, func() { list = append(list, kit.ReplaceAll(p, "\\", nfs.PS)) })
}
kit.For(arg, func(p string) {
list = append(list, kit.Path(p, ice.BIN), kit.Path(p, ice.USR_PUBLISH), kit.Path(p, ice.USR_LOCAL_BIN), kit.Path(p, ice.USR_LOCAL_GO_BIN))
kit.For(kit.Reverse(EtcPath(ice.Pulse)), func(l string) {
kit.If(strings.TrimSpace(l) != "" && !strings.HasPrefix(strings.TrimSpace(l), "#"), func() { push(kit.Path(p, l)) })
})
})
kit.For(strings.Split(kit.Env(PATH), _path_sep()), func(p string) { push(p) })
return kit.Join(list, _path_sep())
}

122
base/cli/mirrors.go Normal file
View File

@ -0,0 +1,122 @@
package cli
import (
"runtime"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
const (
CMD = "cmd"
ADD = "add"
OSID = "osid"
REPOS = "repos"
UBUNTU = "ubuntu"
CENTOS = "centos"
ALPINE = "alpine"
BUSYBOX = "busybox"
RELEASE = "release"
RHEL = "rhel"
ETC_OS_RELEASE = "/etc/os-release"
ETC_APK_REPOS = "/etc/apk/repositories"
)
const MIRRORS = "mirrors"
func init() {
Index.MergeCommands(ice.Commands{
MIRRORS: {Help: "软件镜像", Actions: ice.MergeActions(ice.Actions{
mdb.INSERT: {Name: "insert cli* osid cmd*"},
CMD: {Name: "cmd cli osid", Hand: func(m *ice.Message, arg ...string) {
osid := kit.Select(mdb.Conf(m, RUNTIME, kit.Keys(HOST, OSID)), m.Option(OSID))
mdb.ZoneSelectCB(m, m.Option(CLI), func(value ice.Map) {
kit.If(strings.Contains(osid, kit.Format(value[OSID])), func() {
m.Cmdy(kit.Split(kit.Format(value[CMD])))
})
})
}},
ADD: {Help: "安装", Hand: func(m *ice.Message, arg ...string) {
ice.Info.PushStream(m)
mdb.ZoneSelect(m, m.Option(CLI)).Table(func(value ice.Maps) {
m.ToastProcess()
if msg := m.Cmd(kit.Split(value[CMD])); IsSuccess(msg) {
m.ToastSuccess()
} else {
m.ToastFailure()
}
})
}},
REPOS: {Name: "repos proxy=mirrors.tencent.com", Help: "镜像", Hand: func(m *ice.Message, arg ...string) {
switch {
case strings.Contains(_release, ALPINE):
defer m.PushStream().ToastProcess()()
kit.If(m.Option("proxy"), func(p string) {
m.Cmd(nfs.SAVE, ETC_APK_REPOS, strings.ReplaceAll(m.Cmdx(nfs.CAT, ETC_APK_REPOS), "dl-cdn.alpinelinux.org", p))
})
m.Cmdy(SYSTEM, "apk", "update")
}
}},
ALPINE: {Name: "alpine cli cmd", Hand: func(m *ice.Message, arg ...string) { IsAlpine(m, arg...) }},
}, mdb.ZoneAction(mdb.SHORT, CLI, mdb.FIELDS, "time,id,osid,cmd"), mdb.ClearOnExitHashAction()), Hand: func(m *ice.Message, arg ...string) {
if mdb.ZoneSelect(m, arg...); len(arg) == 0 {
m.Table(func(value ice.Maps) {
p := SystemFind(m, value[CLI])
if m.Push(nfs.PATH, p); p == "" {
m.PushButton(ADD)
} else {
m.PushButton("")
}
}).Action(REPOS).StatusTimeCount("release", _release)
}
switch {
case strings.Contains(_release, ALPINE):
m.Cmdy(nfs.CAT, ETC_APK_REPOS)
}
// m.EchoScript(kit.Format("cd %s; %s", kit.Path(""), kit.JoinCmds(kit.Simple(kit.Path(os.Args[0]), os.Args[1:])...)))
}},
})
}
var _release = ""
func release(m *ice.Message) string {
list := []string{runtime.GOOS}
if list[0] != LINUX || !nfs.Exists(m, ETC_OS_RELEASE) {
return list[0]
}
m.Cmd(nfs.CAT, ETC_OS_RELEASE, kit.Dict(ice.MSG_USERROLE, aaa.ROOT), func(text string, _ int) string {
if ls := kit.Split(text, mdb.EQ); len(ls) > 1 {
kit.Switch(ls[0], []string{"ID", "ID_LIKE"}, func() { list = append(list, strings.TrimSpace(ls[1])) })
}
return text
})
_release = kit.JoinWord(list...)
return _release
}
func insert(m *ice.Message, sys, cmd string, arg ...string) bool {
if !strings.Contains(_release, sys) {
return false
}
if len(arg) > 0 {
m.GoSleep300ms(func() {
m.Cmd(mdb.INSERT, kit.Keys(CLI, MIRRORS), "", mdb.ZONE, arg[0], OSID, sys, CMD, cmd+lex.SP+kit.Select(arg[0], arg, 1))
})
}
return true
}
func IsAlpine(m *ice.Message, arg ...string) bool {
return insert(m, ALPINE, "system apk add", arg...)
}
func IsRedhat(m *ice.Message, arg ...string) bool {
return insert(m, RHEL, "system yum install -y", arg...)
}
func IsSystem(m *ice.Message, arg ...string) bool {
return IsAlpine(m, arg...) || IsRedhat(m, arg...)
}

View File

@ -2,71 +2,22 @@ package cli
import (
"encoding/base64"
"fmt"
"image/color"
"math/rand"
"strconv"
"strings"
"shylinux.com/x/go-qrcode"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/tcp"
"shylinux.com/x/icebergs/base/web/html"
"shylinux.com/x/icebergs/misc/qrcode"
kit "shylinux.com/x/toolkits"
)
var _trans_web = map[string]color.Color{
BLACK: color.RGBA{0, 0, 0, DARK},
RED: color.RGBA{DARK, 0, 0, DARK},
GREEN: color.RGBA{0, DARK, 0, DARK},
YELLOW: color.RGBA{DARK, DARK, 0, DARK},
BLUE: color.RGBA{0, 0, DARK, DARK},
PURPLE: color.RGBA{DARK, 0, DARK, DARK},
CYAN: color.RGBA{0, DARK, DARK, DARK},
WHITE: color.RGBA{DARK, DARK, DARK, DARK},
}
func _parse_color(str string) color.Color {
if str == RANDOM {
list := []string{}
for k := range _trans_web {
list = append(list, k)
}
str = list[rand.Intn(len(list))]
}
if strings.HasPrefix(str, "#") {
if len(str) == 7 {
str += "ff"
}
if u, e := strconv.ParseUint(str[1:], 16, 64); e == nil {
return color.RGBA{
uint8((u & 0xFF000000) >> 24),
uint8((u & 0x00FF0000) >> 16),
uint8((u & 0x0000FF00) >> 8),
uint8((u & 0x000000FF) >> 0),
}
}
}
return _trans_web[str]
}
func _trans_cli(str string) string {
res := 0
r, g, b, _ := _parse_color(str).RGBA()
if r > LIGHT {
res += 1
}
if g > LIGHT {
res += 2
}
if b > LIGHT {
res += 4
}
return kit.Format(res)
}
func _qrcode_cli(m *ice.Message, text string) {
qr, _ := qrcode.New(text, qrcode.Medium)
fg := _trans_cli(m.Option(FG))
bg := _trans_cli(m.Option(BG))
data := qr.Bitmap()
sc := qrcode.New(text)
fg := ParseCliColor(m.Option(FG))
bg := ParseCliColor(m.Option(BG))
data := sc.Bitmap()
for i, row := range data {
if n := len(data); i < 3 || i >= n-3 {
continue
@ -75,74 +26,63 @@ func _qrcode_cli(m *ice.Message, text string) {
if n := len(row); i < 3 || i >= n-3 {
continue
}
m.Echo("\033[4%sm \033[0m", kit.Select(bg, fg, col))
}
m.Echo(ice.NL)
m.Echo(lex.NL)
}
m.Echo(text)
m.Echo(text).Echo(lex.NL)
}
func _qrcode_web(m *ice.Message, text string) {
qr, _ := qrcode.New(text, qrcode.Medium)
qr.ForegroundColor = _parse_color(m.Option(FG))
qr.BackgroundColor = _parse_color(m.Option(BG))
if data, err := qr.PNG(kit.Int(m.Option(SIZE))); m.Assert(err) {
m.Echo(`<img src="data:image/png;base64,%s" title='%s'>`, base64.StdEncoding.EncodeToString(data), text)
func _qrcode_web(m *ice.Message, text string) string {
sc := qrcode.New(text)
sc.ForegroundColor = ParseColor(m.Option(FG))
sc.BackgroundColor = ParseColor(m.Option(BG))
if data, err := sc.PNG(kit.Int(m.Option(SIZE))); m.Assert(err) {
m.Echo(`<img class="qrcode" src="data:image/png;base64,%s" title='%s'>`, base64.StdEncoding.EncodeToString(data), text)
}
return text
}
func Color(m *ice.Message, c string, str interface{}) string {
wrap, color := `<span style="color:%s">%v</span>`, c
if m.IsCliUA() {
wrap, color = "\033[3%sm%v\033[0m", _trans_cli(c)
}
return fmt.Sprintf(wrap, color, str)
}
func ColorRed(m *ice.Message, str interface{}) string { return Color(m, RED, str) }
func ColorGreen(m *ice.Message, str interface{}) string { return Color(m, GREEN, str) }
func ColorYellow(m *ice.Message, str interface{}) string { return Color(m, YELLOW, str) }
const (
FG = "fg"
BG = "bg"
SIZE = "size"
DARK = 255
LIGHT = 127
)
const (
BLACK = "black"
RED = "red"
GREEN = "green"
YELLOW = "yellow"
BLUE = "blue"
PURPLE = "purple"
CYAN = "cyan"
WHITE = "white"
RANDOM = "random"
)
const QRCODE = "qrcode"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
QRCODE: {Name: QRCODE, Help: "二维码", Value: kit.Data()},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
ice.AddRender(ice.RENDER_QRCODE, func(m *ice.Message, cmd string, args ...interface{}) string {
return m.Cmd(QRCODE, kit.Simple(args...)).Result()
})
}},
QRCODE: {Name: "qrcode text fg bg size auto", Help: "二维码", Action: map[string]*ice.Action{}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Option(SIZE, kit.Select("240", arg, 3))
m.Option(BG, kit.Select(WHITE, arg, 2))
m.Option(FG, kit.Select(BLUE, arg, 1))
Index.MergeCommands(ice.Commands{
QRCODE: {Name: "qrcode text fg@key bg@key size auto", Help: "二维码", Icon: "Chess.png", Role: aaa.VOID, Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict(mdb.TEXT, "文本", BG, "背景色", FG, "字体色")),
), Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
ice.AddRender(ice.RENDER_QRCODE, func(m *ice.Message, args ...ice.Any) string {
if m.IsMobileUA() {
m.Option(SIZE, "280")
}
return m.Cmd(Prefix(QRCODE), kit.Simple(args...)).Result()
})
}},
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case FG, BG:
m.Push(arg[0], kit.SortedKey(_color_map))
}
}},
}), Hand: func(m *ice.Message, arg ...string) {
if m.IsCliUA() {
_qrcode_cli(m, kit.Select(m.Conf("share", kit.Keym(kit.MDB_DOMAIN)), arg, 0))
m.OptionDefault(FG, BLACK, BG, WHITE)
_qrcode_cli(m, kit.Select(kit.Select(ice.Info.Make.Domain, ice.Info.Domain), arg, 0))
} else {
_qrcode_web(m, kit.Select(m.Option(ice.MSG_USERWEB), arg, 0))
// m.OptionDefault(SIZE, kit.Select("360", "280", m.IsMobileUA()))
m.Option(FG, kit.Select(m.Option(ice.MSG_FG), arg, 1))
m.Option(BG, kit.Select(m.Option(ice.MSG_BG), arg, 2))
m.Option(SIZE, kit.Select(m.OptionDefault(SIZE, "320"), arg, 3))
switch m.Option(ice.MSG_THEME) {
case LIGHT, WHITE:
m.OptionDefault(FG, BLACK, BG, WHITE)
default:
m.OptionDefault(FG, WHITE, BG, BLACK)
}
m.StatusTime(mdb.LINK, _qrcode_web(m, tcp.PublishLocalhost(m, kit.Select(m.Option(ice.MSG_USERWEB), arg, 0))))
}
}},
}})
})
}

View File

@ -1,191 +1,307 @@
package cli
import (
"bytes"
"io/ioutil"
"os"
"os/user"
"path"
"runtime"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
kit "shylinux.com/x/toolkits"
)
func _runtime_init(m *ice.Message) {
// 版本信息
kit.Fetch(kit.UnMarshal(kit.Format(ice.Info.Make)), func(key string, value interface{}) {
m.Conf(RUNTIME, kit.Keys(MAKE, strings.ToLower(key)), value)
})
// 环境变量
for _, k := range []string{CTX_SHY, CTX_DEV, CTX_OPS, CTX_PID, CTX_USER, CTX_SHARE, CTX_RIVER} {
m.Conf(RUNTIME, kit.Keys(CONF, k), os.Getenv(k))
}
// 主机信息
count := kit.Int(m.Conf(RUNTIME, kit.Keys(BOOT, mdb.COUNT)))
defer m.Conf(RUNTIME, kit.Keys(BOOT, mdb.COUNT), count+1)
kit.For(kit.UnMarshal(kit.Format(ice.Info.Make)), func(k string, v ice.Any) { m.Conf(RUNTIME, kit.Keys(MAKE, strings.ToLower(k)), v) })
m.Conf(RUNTIME, kit.Keys(HOST, GOARCH), runtime.GOARCH)
m.Conf(RUNTIME, kit.Keys(HOST, GOOS), runtime.GOOS)
m.Conf(RUNTIME, kit.Keys(HOST, "pid"), os.Getpid())
// 启动信息
if name, e := os.Hostname(); e == nil {
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), kit.Select(name, os.Getenv("HOSTNAME")))
m.Conf(RUNTIME, kit.Keys(HOST, OSID), release(m))
m.Conf(RUNTIME, kit.Keys(HOST, PID), os.Getpid())
m.Conf(RUNTIME, kit.Keys(HOST, PWD), kit.Path(""))
m.Conf(RUNTIME, kit.Keys(HOST, HOME), kit.HomePath(""))
m.Conf(RUNTIME, kit.Keys(HOST, MAXPROCS), runtime.GOMAXPROCS(0))
ice.Info.System = m.Conf(RUNTIME, kit.Keys(HOST, OSID))
kit.For(ENV_LIST, func(k string) { m.Conf(RUNTIME, kit.Keys(CONF, k), kit.Env(k)) })
ice.Info.Lang = m.Conf(RUNTIME, kit.Keys(CONF, LANG))
m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME), kit.UserName())
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), kit.Env("HOSTNAME"))
m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME), path.Base(kit.Path("")))
if name, e := os.Hostname(); e == nil && name != "" {
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), name)
}
if name, e := os.Getwd(); e == nil {
name = path.Base(kit.Select(name, os.Getenv("PWD")))
ls := strings.Split(name, "/")
name = ls[len(ls)-1]
ls = strings.Split(name, "\\")
name = ls[len(ls)-1]
m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME), name)
ice.Info.Username = m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME))
ice.Info.Hostname = m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME))
ice.Info.Pathname = m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME))
kit.HashSeed = append(kit.HashSeed, ice.Info.Username)
kit.HashSeed = append(kit.HashSeed, ice.Info.Hostname)
kit.HashSeed = append(kit.HashSeed, ice.Info.Pathname)
aaa.UserRoot(ice.Pulse, aaa.TECH, ice.Info.Make.Author, "", "", ice.DEV, ice.Info.Make.Email)
aaa.UserRoot(ice.Pulse, aaa.TECH, ice.Info.Make.Username, "", "", ice.DEV, ice.Info.Make.Email)
aaa.UserRoot(ice.Pulse, aaa.ROOT, ice.Info.Username)
aaa.UserRoot(ice.Pulse, aaa.ROOT, aaa.ROOT)
ice.Info.Time = m.Time()
m.Conf(RUNTIME, kit.Keys(BOOT, mdb.TIME), ice.Info.Time)
if runtime.GOARCH != MIPSLE {
msg := m.Cmd(nfs.DIR, _system_find(m, os.Args[0]), "time,path,size,hash")
m.Conf(RUNTIME, kit.Keys(BOOT, mdb.HASH), msg.Append(mdb.HASH))
m.Conf(RUNTIME, kit.Keys(BOOT, nfs.SIZE), msg.Append(nfs.SIZE))
m.Conf(RUNTIME, kit.Keys(BOOT, ice.BIN), msg.Append(nfs.PATH))
ice.Info.Hash = msg.Append(mdb.HASH)
ice.Info.Size = msg.Append(nfs.SIZE)
}
if m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME), kit.Select(os.Getenv(USER), os.Getenv(CTX_USER))) == "" {
if user, e := user.Current(); e == nil && user.Name != "" {
m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME), kit.Select(user.Name, os.Getenv(CTX_USER)))
}
}
ice.Info.HostName = m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME))
ice.Info.PathName = m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME))
ice.Info.UserName = m.Conf(RUNTIME, kit.Keys(BOOT, USERNAME))
// 启动次数
count := kit.Int(m.Conf(RUNTIME, kit.Keys(BOOT, kit.MDB_COUNT))) + 1
m.Conf(RUNTIME, kit.Keys(BOOT, kit.MDB_COUNT), count)
// 节点信息
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_TIME), m.Time())
NodeInfo(m, "worker", m.Conf(RUNTIME, kit.Keys(BOOT, PATHNAME)))
runtime.GOMAXPROCS(kit.Int(kit.Select("1", m.Conf(RUNTIME, kit.Keys(HOST, "GOMAXPROCS")))))
nfs.Exists(m, "/proc/meminfo", func(p string) {
kit.For(kit.SplitLine(m.Cmdx(nfs.CAT, p)), func(p string) {
switch ls := kit.Split(p, ": "); kit.Select("", ls, 0) {
case "MemTotal", "MemFree", "MemAvailable":
m.Conf(RUNTIME, kit.Keys(HOST, ls[0]), kit.FmtSize(kit.Int(ls[1])*1024))
}
})
})
m.Conf(m.PrefixKey(), mdb.META, "")
}
func _runtime_hostinfo(m *ice.Message) {
if f, e := os.Open("/proc/cpuinfo"); e == nil {
defer f.Close()
if b, e := ioutil.ReadAll(f); e == nil {
m.Push("nCPU", bytes.Count(b, []byte("processor")))
}
}
if f, e := os.Open("/proc/meminfo"); e == nil {
defer f.Close()
if b, e := ioutil.ReadAll(f); e == nil {
for i, ls := range strings.Split(string(b), ice.NL) {
vs := kit.Split(ls, ": ")
m.Push(strings.TrimSpace(vs[0]), kit.FmtSize(kit.Int64(strings.TrimSpace(vs[1]))*1024))
if i > 1 {
m.Push("time", ice.Info.Make.Time)
m.Push("nCPU", runtime.NumCPU())
m.Push("GOMAXPROCS", runtime.GOMAXPROCS(0))
m.Push("NumGoroutine", runtime.NumGoroutine())
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
m.Push("Sys", kit.FmtSize(stats.Sys))
m.Push("Alloc", kit.FmtSize(stats.Alloc))
m.Push("TotalAlloc", kit.FmtSize(stats.TotalAlloc))
m.Push("StackSys", kit.FmtSize(stats.StackSys))
m.Push("StackInuse", kit.FmtSize(stats.StackInuse))
m.Push("HeapSys", kit.FmtSize(stats.HeapSys))
m.Push("HeapInuse", kit.FmtSize(stats.HeapInuse))
m.Push("HeapIdle", kit.FmtSize(stats.HeapIdle))
m.Push("HeapReleased", kit.FmtSize(stats.HeapReleased))
m.Push("NumGC", stats.NumGC)
m.Push("LastGC", time.Unix(int64(stats.LastGC)/int64(time.Second), int64(stats.LastGC)%int64(time.Second)))
m.Push("uptime", kit.Split(m.Cmdx(SYSTEM, "uptime"), mdb.FS)[0])
if runtime.GOOS == LINUX {
for i, ls := range strings.Split(m.Cmdx(nfs.CAT, "/proc/meminfo"), lex.NL) {
if vs := kit.Split(ls, ": "); len(vs) > 1 {
if m.Push(strings.TrimSpace(vs[0]), kit.FmtSize(kit.Int64(strings.TrimSpace(vs[1]))*1024)); i > 1 {
break
}
}
}
} else {
m.Push("MemAvailable", "")
m.Push("MemTotal", "")
m.Push("MemFree", "")
}
m.Push("uptime", kit.Split(m.Cmdx(SYSTEM, "uptime"), ",")[0])
}
func NodeInfo(m *ice.Message, kind, name string) {
name = strings.ReplaceAll(name, ice.PT, "_")
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_TYPE), kind)
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_NAME), name)
ice.Info.NodeName = name
ice.Info.NodeType = kind
func _runtime_diskinfo(m *ice.Message) {
m.Spawn().Split(kit.Replace(m.Cmdx(SYSTEM, "df", "-h"), "Mounted on", "Mountedon"), "", lex.SP, lex.NL).Table(func(value ice.Maps, index int, head []string) {
kit.If(strings.HasPrefix(value["Filesystem"], "/dev"), func() { m.Push("", value, head) })
})
m.RenameAppend("%iused", "piused", "Use%", "Usep")
ctx.DisplayStory(m, "pie.js?field=Size")
}
const (
MAKE = "make"
TEST = "test"
CONF = "conf"
HOST = "host"
CONF = "conf"
BOOT = "boot"
NODE = "node"
)
const (
SOURCE = "source"
TARGET = "target"
)
const (
GOARCH = "GOARCH"
GOOS = "GOOS"
X386 = "386"
AMD64 = "amd64"
ARM64 = "arm64"
X86 = "386"
ARM = "arm"
ARM64 = "arm64"
MIPSLE = "mipsle"
GOOS = "GOOS"
LINUX = "linux"
MACOS = "macos"
DARWIN = "darwin"
WINDOWS = "windows"
)
const (
CTX_SHY = "ctx_shy"
CTX_DEV = "ctx_dev"
CTX_OPS = "ctx_ops"
CTX_PID = "ctx_pid"
CTX_LOG = "ctx_log"
CTX_USER = "ctx_user"
CTX_SHARE = "ctx_share"
CTX_RIVER = "ctx_river"
COMMIT_TIME = "commitTime"
COMPILE_TIME = "compileTime"
BOOT_TIME = "bootTime"
KERNEL = "kernel"
ARCH = "arch"
CPU = "cpu"
OS = "os"
)
const (
PATH = "PATH"
HOME = "HOME"
USER = "USER"
TERM = "TERM"
SHELL = "SHELL"
LANG = "LANG"
TZ = "TZ"
)
const (
CTX_SHY = "ctx_shy"
CTX_DEV = "ctx_dev"
CTX_DEV_IP = "ctx_dev_ip"
CTX_OPS = "ctx_ops"
CTX_REPOS = "ctx_repos"
CTX_NAME = "ctx_name"
CTX_DEMO = "ctx_demo"
CTX_MAIL = "ctx_mail"
CTX_ROOT = "ctx_root"
CTX_DOMAIN = "ctx_domain"
CTX_PID = "ctx_pid"
CTX_LOG = "ctx_log"
CTX_POD = "ctx_pod"
CTX_ENV = "ctx_env"
CTX_CLI = "ctx_cli"
CTX_ARG = "ctx_arg"
)
var ENV_LIST = []string{TZ, LANG, TERM, SHELL, CTX_SHY, CTX_DEV, CTX_OPS, CTX_DEMO, CTX_MAIL, CTX_ROOT, CTX_PID}
const (
USERNAME = "username"
HOSTNAME = "hostname"
PATHNAME = "pathname"
USERNAME = "username"
)
const (
IFCONFIG = "ifconfig"
DISKINFO = "diskinfo"
HOSTINFO = "hostinfo"
USERINFO = "userinfo"
PROCINFO = "procinfo"
BOOTINFO = "bootinfo"
DISKINFO = "diskinfo"
MAXPROCS = "maxprocs"
)
const RUNTIME = "runtime"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
RUNTIME: {Name: RUNTIME, Help: "运行环境", Value: kit.Dict()},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
_runtime_init(m)
}},
RUNTIME: {Name: "runtime info=ifconfig,hostinfo,hostname,userinfo,procinfo,bootinfo,diskinfo auto", Help: "运行环境", Action: map[string]*ice.Action{
IFCONFIG: {Name: "ifconfig", Help: "网卡配置", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy("tcp.host")
}},
HOSTINFO: {Name: "hostinfo", Help: "主机信息", Hand: func(m *ice.Message, arg ...string) {
_runtime_hostinfo(m)
}},
HOSTNAME: {Name: "hostname", Help: "主机域名", Hand: func(m *ice.Message, arg ...string) {
Index.MergeCommands(ice.Commands{
RUNTIME: {Name: "runtime info=bootinfo,ifconfig,diskinfo,hostinfo,userinfo,bootinfo,role,api,cli,cmd,mod,env,path,chain auto upgrade reboot lock", Icon: "Infomation.png", Help: "环境", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { _runtime_init(m) }},
IFCONFIG: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(tcp.HOST) }},
DISKINFO: {Hand: func(m *ice.Message, arg ...string) { _runtime_diskinfo(m) }},
HOSTINFO: {Hand: func(m *ice.Message, arg ...string) { _runtime_hostinfo(m) }},
HOSTNAME: {Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 0 {
m.Conf(RUNTIME, kit.Keys(NODE, kit.MDB_NAME), arg[0])
m.Conf(RUNTIME, kit.Keys(BOOT, HOSTNAME), arg[0])
ice.Info.HostName = arg[0]
ice.Info.Hostname = mdb.Conf(m, RUNTIME, kit.Keys(NODE, mdb.NAME), mdb.Conf(m, RUNTIME, kit.Keys(BOOT, HOSTNAME), arg[0]))
}
m.Echo(ice.Info.HostName)
m.Echo(ice.Info.Hostname)
}},
USERINFO: {Name: "userinfo", Help: "用户信息", Hand: func(m *ice.Message, arg ...string) {
m.Split(m.Cmdx(SYSTEM, "who"), "user term time", ice.SP, ice.NL)
MAXPROCS: {Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) > 0, func() { runtime.GOMAXPROCS(kit.Int(mdb.Conf(m, RUNTIME, kit.Keys(HOST, MAXPROCS), arg[0]))) })
m.Echo("%d", runtime.GOMAXPROCS(0))
}},
PROCINFO: {Name: "procinfo", Help: "进程信息", Hand: func(m *ice.Message, arg ...string) {
m.Split(m.Cmdx(SYSTEM, "ps", "u"), "", ice.SP, ice.NL)
m.PushAction("prockill")
m.StatusTimeCount()
USERINFO: {Hand: func(m *ice.Message, arg ...string) { m.Split(m.Cmdx(SYSTEM, "who"), "user term time") }},
aaa.ROLE: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(aaa.ROLE, func(value ice.Maps) { m.Push(mdb.KEY, kit.Keys(value[aaa.ROLE], value[mdb.ZONE], value[mdb.KEY])) })
ctx.DisplayStorySpide(m.Options(nfs.DIR_ROOT, "ice."), mdb.FIELD, mdb.KEY, lex.SPLIT, nfs.PT)
}},
"prockill": {Name: "prockill", Help: "结束进程", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(SYSTEM, "prockill", m.Option("PID"))
m.ProcessRefresh30ms()
API: {Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 1 {
m.Cmdy(ctx.COMMAND, "inner").Push(ctx.ARGS, kit.Format(nfs.SplitPath(m, m.Option(nfs.FILE))))
return
}
ctx.DisplayStorySpide(m.Options(nfs.DIR_ROOT, nfs.PS), lex.PREFIX, kit.Fields(ctx.ACTION, m.ActionKey()))
kit.For(ice.Info.Route, func(k, v string) { m.Push(nfs.PATH, k).Push(nfs.FILE, v) })
m.Sort(nfs.PATH)
}},
DISKINFO: {Name: "diskinfo", Help: "磁盘信息", Hand: func(m *ice.Message, arg ...string) {
m.Spawn().Split(m.Cmdx(SYSTEM, "df", "-h"), "", ice.SP, ice.NL).Table(func(index int, value map[string]string, head []string) {
if strings.HasPrefix(value["Filesystem"], "/dev") {
m.Push("", value, head)
}
})
CLI: {Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 1 {
m.Cmdy(ctx.COMMAND, "inner").Push(ctx.ARGS, kit.Format(nfs.SplitPath(m, m.Option(nfs.FILE))))
return
}
ctx.DisplayStorySpide(m.Options(nfs.DIR_ROOT, "ice."), lex.PREFIX, kit.Fields(ctx.ACTION, m.ActionKey()), mdb.FIELD, mdb.NAME, lex.SPLIT, nfs.PT)
kit.For(ice.Info.File, func(k, v string) { m.Push(nfs.FILE, k).Push(mdb.NAME, v) })
m.Sort(mdb.NAME)
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 0 && arg[0] == BOOTINFO {
arg = arg[1:]
}
m.Cmdy(ctx.CONFIG, RUNTIME, arg)
CMD: {Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(ctx.INDEX, mdb.NAME, mdb.HELP, nfs.FILE)
m.Cmdy(ctx.COMMAND, mdb.SEARCH, ctx.COMMAND)
}},
MOD: {Hand: func(m *ice.Message, arg ...string) {
kit.For(ice.Info.Gomod, func(k string, v string) { m.Push(nfs.MODULE, k).Push(nfs.VERSION, v) })
}},
ENV: {Hand: func(m *ice.Message, arg ...string) {
kit.For(os.Environ(), func(v string) { ls := strings.SplitN(v, mdb.EQ, 2); m.Push(mdb.NAME, ls[0]).Push(mdb.VALUE, ls[1]) })
m.Sort(mdb.NAME)
}},
nfs.PATH: {Hand: func(m *ice.Message, arg ...string) {
kit.For(_path_split(os.Getenv(PATH)), func(p string) { m.Push(nfs.PATH, p) })
}},
"chain": {Hand: func(m *ice.Message, arg ...string) { m.Echo(m.FormatChain()) }},
"upgrade": {Help: "升级", Hand: func(m *ice.Message, arg ...string) {
if nfs.Exists(m, ice.SRC_MAIN_GO) && nfs.Exists(m, ".git") && SystemFind(m, "go") != "" {
m.Cmdy("vimer", "compile")
} else if nfs.Exists(m, ice.BIN_ICE_BIN) {
m.Cmdy("upgrade")
} else {
m.Cmdy("", REBOOT)
}
}},
REBOOT: {Help: "重启", Hand: func(m *ice.Message, arg ...string) {
m.Go(func() { m.Sleep30ms(ice.EXIT, 1) })
}},
"lock": {Help: "锁屏", Icon: "bi bi-file-lock", Hand: func(m *ice.Message, arg ...string) {
switch runtime.GOOS {
case DARWIN:
TellApp(m, "System Events", `keystroke "q" using {control down, command down}`)
}
}},
}, ctx.ConfAction("")), Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) > 0 && arg[0] == BOOTINFO, func() { arg = arg[1:] })
m.Cmdy(ctx.CONFIG, RUNTIME, arg).StatusTime(mdb.TIME, ice.Info.Make.Time,
mdb.HASH, kit.Cut(ice.Info.Hash, 6), nfs.SIZE, ice.Info.Size,
mdb.NAME, ice.Info.NodeName, nfs.VERSION, ice.Info.Make.Versions(),
).Action()
ctx.DisplayStoryJSON(m)
}},
}})
})
}
func NodeInfo(m *ice.Message, arg ...string) {
mdb.Conf(m, RUNTIME, kit.Keys(NODE, mdb.TIME), m.Time())
ice.Info.NodeName = mdb.Conf(m, RUNTIME, kit.Keys(NODE, mdb.NAME), kit.Select(ice.Info.NodeName, arg, 0))
ice.Info.NodeType = mdb.Conf(m, RUNTIME, kit.Keys(NODE, mdb.TYPE), kit.Select(ice.Info.NodeType, arg, 1))
ice.Info.NodeIcon = mdb.Conf(m, RUNTIME, kit.Keys(NODE, mdb.ICON), kit.Select(ice.Info.NodeIcon, arg, 2))
}
func IsWindows() bool { return runtime.GOOS == WINDOWS }
func ParseMake(str string) []string {
res := kit.UnMarshal(str)
data := kit.Value(res, MAKE)
version := kit.Format(kit.Value(data, nfs.VERSION))
if kit.Format(kit.Value(data, "forword")) != "0" {
version = kit.Join(kit.TrimArg(kit.Simple(
kit.Select("v0.0.0", kit.Format(kit.Value(data, nfs.VERSION))),
kit.Select("0", kit.Format(kit.Value(data, "forword"))),
kit.Cut(kit.Format(kit.Value(data, mdb.HASH)), 6),
)...), "-")
}
return kit.Simple(
mdb.TIME, kit.Format(kit.Value(data, mdb.TIME)),
ice.SPACE, kit.Format(kit.Value(res, kit.Keys(NODE, mdb.NAME))),
nfs.MODULE, kit.Format(kit.Value(data, nfs.MODULE)),
nfs.VERSION, version,
COMMIT_TIME, kit.Format(kit.Value(data, "when")),
COMPILE_TIME, kit.Format(kit.Value(data, mdb.TIME)),
BOOT_TIME, kit.Format(kit.Value(res, kit.Keys(BOOT, mdb.TIME))),
SHELL, kit.Format(kit.Value(res, kit.Keys(CONF, SHELL))),
KERNEL, kit.Format(kit.Value(res, kit.Keys(HOST, GOOS))),
ARCH, kit.Format(kit.Value(res, kit.Keys(HOST, GOARCH))),
)
}
func SimpleMake() []string {
return []string{
nfs.MODULE, ice.Info.Make.Module, nfs.VERSION, ice.Info.Make.Versions(),
COMMIT_TIME, ice.Info.Make.When, COMPILE_TIME, ice.Info.Make.Time, BOOT_TIME, ice.Info.Time,
KERNEL, runtime.GOOS, ARCH, runtime.GOARCH,
}
}

12
base/cli/sudo.go Normal file
View File

@ -0,0 +1,12 @@
package cli
import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
)
func init() {
Index.MergeCommands(ice.Commands{
SUDO: {Actions: mdb.HashAction(mdb.SHORT, "cmd", mdb.FIELD, "time,cmd")},
})
}

View File

@ -3,33 +3,70 @@ package cli
import (
"bytes"
"io"
"net/http"
"os"
"os/exec"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/file"
)
func _path_split(ps string) []string {
ps = kit.ReplaceAll(ps, "\\", nfs.PS)
return kit.Split(ps, lex.NL+kit.Select(nfs.DF, ";", strings.Contains(ps, ";")), lex.NL)
}
func _system_cmd(m *ice.Message, arg ...string) *exec.Cmd {
cmd := exec.Command(arg[0], arg[1:]...)
// 运行目录
if cmd.Dir = m.Option(CMD_DIR); len(cmd.Dir) > 0 {
m.Log_EXPORT(CMD_DIR, cmd.Dir)
if _, e := os.Stat(cmd.Dir); e != nil && os.IsNotExist(e) {
os.MkdirAll(cmd.Dir, ice.MOD_DIR)
bin, env := "", kit.Simple(m.Optionv(CMD_ENV))
kit.For(env, func(k, v string) {
if k == PATH {
if bin = _system_find(m, arg[0], _path_split(v)...); bin != "" {
m.Logs(FIND, "envpath cmd", bin)
}
}
})
if bin == "" {
if bin = _system_find(m, arg[0], EtcPath(m)...); bin != "" {
m.Logs(FIND, "etcpath cmd", bin)
}
}
// 环境变量
env := kit.Simple(m.Optionv(CMD_ENV))
for i := 0; i < len(env)-1; i += 2 {
cmd.Env = append(cmd.Env, kit.Format("%s=%s", env[i], env[i+1]))
if bin == "" {
if bin = _system_find(m, arg[0], m.Option(CMD_DIR), ice.BIN, nfs.PWD); bin != "" {
m.Logs(FIND, "contexts cmd", bin)
}
}
if len(cmd.Env) > 0 {
m.Log_EXPORT(CMD_ENV, cmd.Env)
if bin == "" && !strings.Contains(arg[0], nfs.PS) {
if bin = _system_find(m, arg[0]); bin != "" {
m.Logs(FIND, "systems cmd", bin)
}
}
if bin == "" && !strings.Contains(arg[0], nfs.PS) {
m.Cmd(MIRRORS, CMD, arg[0])
if bin = _system_find(m, arg[0]); bin != "" {
m.Logs(FIND, "mirrors cmd", bin)
}
}
arg[0] = kit.Select(arg[0], bin)
if m.Cmd(SUDO, arg[0]).Length() > 0 {
m.Logs(FIND, "sudo cmd", arg[0])
arg = kit.Simple(SUDO, arg)
}
cmd := exec.Command(arg[0], arg[1:]...)
if cmd.Dir = kit.TrimPath(m.Option(CMD_DIR)); len(cmd.Dir) > 0 {
if m.Logs(EXEC, CMD_DIR, cmd.Dir); !nfs.Exists(m, cmd.Dir) {
file.MkdirAll(cmd.Dir, ice.MOD_DIR)
}
}
kit.For(env, func(k, v string) { cmd.Env = append(cmd.Env, kit.Format("%s=%s", k, v)) })
kit.If(len(cmd.Env) > 0 && m.IsDebug(), func() { m.Logs(EXEC, CMD_ENV, kit.Format(cmd.Env)) })
kit.If(len(cmd.Env) > 0, func() { m.Logs(EXEC, CMD_ENV, kit.Format(cmd.Env)) })
_system_cmds(m, cmd, arg...)
return cmd
}
func _system_out(m *ice.Message, out string) io.Writer {
@ -37,20 +74,16 @@ func _system_out(m *ice.Message, out string) io.Writer {
return w
} else if m.Option(out) == "" {
return nil
} else if f, p, e := kit.Create(m.Option(out)); m.Assert(e) {
m.Log_EXPORT(out, p)
m.Optionv(out, f)
} else if f, p, e := file.CreateFile(m.Option(out)); m.Assert(e) {
m.Logs(nfs.SAVE, out, p).Optionv(out, f)
return f
}
return nil
}
func _system_exec(m *ice.Message, cmd *exec.Cmd) {
// 输入流
if r, ok := m.Optionv(CMD_INPUT).(io.Reader); ok {
cmd.Stdin = r
}
// 输出流
if w := _system_out(m, CMD_OUTPUT); w != nil {
cmd.Stdout, cmd.Stderr = w, w
if w := _system_out(m, CMD_ERRPUT); w != nil {
@ -59,27 +92,54 @@ func _system_exec(m *ice.Message, cmd *exec.Cmd) {
} else {
out := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS))
err := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS))
defer func() {
m.Push(CMD_OUT, out.String())
m.Push(CMD_ERR, err.String())
m.Echo(kit.Select(out.String(), err.String()))
}()
cmd.Stdout, cmd.Stderr = out, err
defer func() {
m.Push(CMD_OUT, out.String()).Push(CMD_ERR, err.String())
if m.Echo(out.String()).Echo(err.String()); m.IsErr() {
m.Option(ice.MSG_ARGS, kit.Simple(http.StatusBadRequest, cmd.Args, err.String()))
m.Echo(strings.TrimRight(err.String(), lex.NL))
m.Info("err: %v", err.String())
m.Info("out: %v", out.String())
}
}()
}
// 执行命令
if e := cmd.Run(); !m.Warn(e, ice.ErrNotFound, cmd.Args) {
m.Cost(kit.MDB_CODE, cmd.ProcessState.ExitCode(), kit.MDB_ARGS, cmd.Args)
if e := cmd.Run(); !m.WarnNotValid(e, cmd.Args) {
m.Cost(CODE, _system_code(cmd), EXEC, cmd.Args)
}
m.Push(kit.MDB_TIME, m.Time())
m.Push(kit.MDB_CODE, int(cmd.ProcessState.ExitCode()))
m.Push(mdb.TIME, m.Time()).Push(CODE, _system_code(cmd)).StatusTime()
}
func IsSuccess(m *ice.Message) bool {
return m.Append(kit.MDB_CODE) == "0" || m.Append(kit.MDB_CODE) == ""
func _system_code(cmd *exec.Cmd) string {
return kit.Select("1", "0", cmd.ProcessState != nil && cmd.ProcessState.Success())
}
func _system_find(m *ice.Message, bin string, dir ...string) string {
if strings.Contains(bin, nfs.DF) {
return bin
} else if strings.HasPrefix(bin, nfs.PS) {
return bin
} else if strings.HasPrefix(bin, nfs.PWD) {
return bin
}
kit.If(len(dir) == 0, func() { dir = append(dir, _path_split(kit.Env(PATH))...) })
for _, p := range dir {
if nfs.Exists(m, path.Join(p, bin)) {
return kit.Path(p, bin)
} else if IsWindows() && nfs.Exists(m, path.Join(p, bin)+".exe") {
return kit.Path(p, bin) + ".exe"
}
}
if nfs.Exists(m, bin) {
return kit.Path(bin)
}
return ""
}
const (
TIME_300ms = "300ms"
TIME_30ms = "30ms"
TIME_30s = "30s"
TIME_3s = "3s"
TIME_1s = "1s"
CMD_DIR = "cmd_dir"
CMD_ENV = "cmd_env"
@ -87,27 +147,103 @@ const (
CMD_OUTPUT = "cmd_output"
CMD_ERRPUT = "cmd_errput"
CMD_OUT = "cmd_out"
CMD_ERR = "cmd_err"
CMD_OUT = "cmd_out"
RUN = "run"
REST = "rest"
PARAM = "param"
OPENS = "opens"
RELAY = "relay"
)
const (
SH = "sh"
LN = "ln"
CP = "cp"
MV = "mv"
RM = "rm"
CD = "cd"
CAT = "cat"
FIND = "find"
GREP = "grep"
TAIL = "tail"
WGET = "wget"
CURL = "curl"
SUDO = "sudo"
EXEC = "exec"
EXIT = "exit"
ECHO = "echo"
KILL = "kill"
GO = "go"
GOTAGS = "gotags"
GIT = "git"
MAN = "man"
YUM = "yum"
)
const SYSTEM = "system"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SYSTEM: {Name: SYSTEM, Help: "系统命令", Value: kit.Data(kit.MDB_FIELD, "time,id,cmd")},
}, Commands: map[string]*ice.Command{
SYSTEM: {Name: "system cmd run:button", Help: "系统命令", Hand: func(m *ice.Message, c *ice.Context, key string, arg ...string) {
if len(arg) == 0 {
mdb.ListSelect(m, arg...)
return
Index.MergeCommands(ice.Commands{
SYSTEM: {Name: "system cmd", Help: "系统命令", Actions: ice.MergeActions(ice.Actions{
nfs.PUSH: {Hand: func(m *ice.Message, arg ...string) {
kit.For(arg, func(p string) {
kit.If(!kit.IsIn(p, EtcPath(m)...), func() {
m.Cmd(nfs.PUSH, ice.ETC_PATH, strings.TrimSpace(p)+lex.NL)
})
})
m.Cmdy(nfs.CAT, ice.ETC_PATH)
}},
FIND: {Hand: func(m *ice.Message, arg ...string) { m.Echo(_system_find(m, arg[0], arg[1:]...)) }},
MAN: {Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 1, func() { arg = append(arg, "") })
m.Echo(SystemCmds(m, "man %s %s|col -b", kit.Select("", arg[1], arg[1] != "1"), arg[0]))
}},
OPENS: {Hand: func(m *ice.Message, arg ...string) { Opens(m, arg...) }},
}), Hand: func(m *ice.Message, arg ...string) {
if _system_exec(m, _system_cmd(m, arg...)); IsSuccess(m) && m.Append(CMD_ERR) == "" {
m.SetAppend()
}
m.Grow(SYSTEM, "", kit.Dict(kit.MDB_TIME, m.Time(), ice.CMD, kit.Join(arg, ice.SP)))
if len(arg) == 1 {
arg = kit.Split(arg[0])
}
_system_exec(m, _system_cmd(m, arg...))
}},
}})
})
ice.Info.SystemCmd = func(m *ice.Message, arg ...ice.Any) *ice.Message {
msg := m.Cmd(append([]ice.Any{SYSTEM}, arg...)...)
if m.Warn(!IsSuccess(msg), msg.Append(CMD_ERR)) {
}
return msg
}
}
func SystemFindGit(m *ice.Message) bool { return SystemFind(m, GIT) != "" }
func SystemFindGo(m *ice.Message) bool { return SystemFind(m, GO) != "" }
func SystemFind(m *ice.Message, bin string, dir ...string) string {
dir = append(dir, EtcPath(m)...)
return _system_find(m, bin, append(dir, _path_split(kit.Env(PATH))...)...)
}
func SystemExec(m *ice.Message, arg ...string) string { return strings.TrimSpace(m.Cmdx(SYSTEM, arg)) }
func SystemCmds(m *ice.Message, cmds string, args ...ice.Any) string {
return strings.TrimRight(m.Cmdx(SYSTEM, SH, "-c", kit.Format(cmds, args...), ice.Option{CMD_OUTPUT, ""}), lex.NL)
}
func IsSuccess(m *ice.Message) bool { return m.Append(CODE) == "" || m.Append(CODE) == "0" }
var _cache_path []string
func Shell(m *ice.Message) string { return kit.Select(SH, os.Getenv(SHELL)) }
func EtcPath(m *ice.Message) (res []string) {
if len(_cache_path) > 0 {
return _cache_path
}
nfs.Exists(m, ice.ETC_PATH, func(p string) {
kit.For(strings.Split(m.Cmdx(nfs.CAT, p, kit.Dict(aaa.UserRole, aaa.ROOT)), lex.NL), func(p string) {
kit.If(p != "" && !strings.HasPrefix(p, "# "), func() {
res = append(res, p)
})
})
})
_cache_path = res
return
}

11
base/cli/system_darwin.go Normal file
View File

@ -0,0 +1,11 @@
package cli
import (
"os/exec"
ice "shylinux.com/x/icebergs"
)
func _system_cmds(m *ice.Message, cmd *exec.Cmd, arg ...string) *exec.Cmd {
return cmd
}

11
base/cli/system_linux.go Normal file
View File

@ -0,0 +1,11 @@
package cli
import (
"os/exec"
ice "shylinux.com/x/icebergs"
)
func _system_cmds(m *ice.Message, cmd *exec.Cmd, arg ...string) *exec.Cmd {
return cmd
}

View File

@ -0,0 +1,11 @@
package cli
import (
"os/exec"
ice "shylinux.com/x/icebergs"
)
func _system_cmds(m *ice.Message, cmd *exec.Cmd, arg ...string) *exec.Cmd {
return cmd
}

View File

@ -1,91 +1,223 @@
package ctx
import (
"path"
"runtime"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _command_list(m *ice.Message, name string) {
if name == "" { // 命令列表
for k, v := range m.Source().Commands {
if k[0] == '/' || k[0] == '_' {
continue // 内部命令
}
m.Push(kit.MDB_KEY, k)
m.Push(kit.MDB_NAME, v.Name)
m.Push(kit.MDB_HELP, v.Help)
}
m.Sort(kit.MDB_KEY)
return
func _command_list(m *ice.Message, name string) *ice.Message {
if strings.HasPrefix(name, "can.") {
return m.Push(mdb.INDEX, name).Push(mdb.NAME, name).Push(mdb.HELP, "").Push(mdb.META, "").Push(mdb.LIST, "")
}
// 命令详情
m.Option(ice.MSG_NODENAME, ice.Info.Titles)
m.Option(ice.MSG_NODEICON, m.Resource(ice.Info.NodeIcon))
m.Spawn(m.Source()).Search(name, func(p *ice.Context, s *ice.Context, key string, cmd *ice.Command) {
m.Push(kit.MDB_INDEX, kit.Keys(s.Cap(ice.CTX_FOLLOW), key))
m.Push(kit.MDB_NAME, kit.Format(cmd.Name))
m.Push(kit.MDB_HELP, kit.Format(cmd.Help))
m.Push(kit.MDB_META, kit.Format(cmd.Meta))
m.Push(kit.MDB_LIST, kit.Format(cmd.List))
icon := kit.Format(kit.Value(cmd.Meta, kit.Keys(ice.CTX_ICONS, key)))
m.Push(mdb.INDEX, kit.Keys(s.Prefix(), key))
m.Push(mdb.ICONS, kit.Select(cmd.Icon, icon, !kit.HasPrefix(icon, "bi ", "{")))
m.Push(mdb.NAME, kit.Format(cmd.Name)).Push(mdb.HELP, kit.Format(cmd.Help))
m.Push(mdb.LIST, kit.Format(cmd.List)).Push(mdb.META, kit.Format(cmd.Meta))
m.Push("_command", ShortCmd(kit.Keys(s.Prefix(), key)))
if !nfs.Exists(m, kit.Split(cmd.FileLine(), nfs.DF)[0], func(p string) {
m.Push("_fileline", m.FileURI(p))
}) {
m.Push("_fileline", "")
}
m.Push("_role", kit.Select("", ice.OK, aaa.Right(m.Spawn(), name)))
m.Push("_help", GetCmdHelp(m, name))
})
return m
}
func _command_search(m *ice.Message, kind, name, text string) {
m.Travel(func(p *ice.Context, s *ice.Context, key string, cmd *ice.Command) {
if key[0] == '/' || key[0] == '_' {
return // 内部命令
}
if name != "" && !strings.HasPrefix(key, name) && !strings.Contains(s.Name, name) {
if IsOrderCmd(key) || !strings.Contains(s.Prefix(key), name) {
return
}
m.PushSearch(ice.CTX, kit.PathName(1), ice.CMD, kit.FileName(1),
kit.MDB_TYPE, s.Cap(ice.CTX_FOLLOW), kit.MDB_NAME, cmd.Name, kit.MDB_TEXT, cmd.Help,
CONTEXT, s.Cap(ice.CTX_FOLLOW), COMMAND, key,
)
})
}
func CmdAction(fields ...string) map[string]*ice.Action {
return ice.SelectAction(map[string]*ice.Action{
COMMAND: {Name: "command", Help: "命令", Hand: func(m *ice.Message, arg ...string) {
if !m.PodCmd(COMMAND, arg) {
m.Cmdy(COMMAND, arg)
}
}},
ice.RUN: {Name: "run", Help: "执行", Hand: func(m *ice.Message, arg ...string) {
if m.Right(arg) && !m.PodCmd(arg) {
m.Cmdy(arg)
}
}},
}, fields...)
kit.SimpleKV("", s.Prefix(), kit.Select(key, cmd.Name), cmd.Help),
INDEX, kit.Keys(s.Prefix(), key))
}).Sort(m.OptionFields())
}
const (
ACTION = "action"
INDEX = "index"
CMDS = "cmds"
ARGS = "args"
OPTS = "opts"
STYLE = "style"
DISPLAY = "display"
PREVIEW = "preview"
ACTION = "action"
TOOLS = "tools"
RUN = "run"
SHIP = "ship"
ICONS = ice.CTX_ICONS
TRANS = ice.CTX_TRANS
TITLE = ice.CTX_TITLE
INPUT = html.INPUT
)
const COMMAND = "command"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(mdb.SEARCH, mdb.CREATE, COMMAND, m.Prefix(COMMAND))
}},
COMMAND: {Name: "command key auto", Help: "命令", Action: map[string]*ice.Action{
mdb.SEARCH: {Name: "search type name text", Help: "搜索", Hand: func(m *ice.Message, arg ...string) {
if arg[0] == COMMAND || arg[1] != "" {
_command_search(m, arg[0], arg[1], arg[2])
Index.MergeCommands(ice.Commands{
COMMAND: {Name: "command key auto", Help: "命令", Role: aaa.VOID, Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
TravelCmd(m, func(key, file, line string) { AddFileCmd(file, key) })
m.Travel(func(p *ice.Context, c *ice.Context, key string, cmd *ice.Command) {
kit.If(cmd.Actions == nil, func() { cmd.Actions = ice.Actions{} })
if _, ok := cmd.Actions[COMMAND]; !ok {
cmd.Actions[COMMAND] = &ice.Action{Hand: Command}
}
if _, ok := cmd.Actions[RUN]; !ok {
cmd.Actions[RUN] = &ice.Action{Hand: Run}
}
if _, ok := cmd.Actions[mdb.INPUTS]; !ok {
cmd.Actions[mdb.INPUTS] = &ice.Action{Hand: func(m *ice.Message, arg ...string) { mdb.HashInputs(m, arg) }}
}
})
}},
mdb.SEARCH: {Hand: func(m *ice.Message, arg ...string) {
if arg[0] == m.CommandKey() || len(arg) > 1 && arg[1] != "" {
_command_search(m, arg[0], kit.Select("", arg, 1), kit.Select("", arg, 2))
}
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 0 && arg[0] != "" && arg[0] != ice.EXIT {
m.Search(arg[0], func(key string, cmd *ice.Command) {
field := kit.Format(kit.Value(cmd.List, kit.Format("%d.name", len(arg)-1)))
if m.Cmdy(arg[0], mdb.INPUTS, field); m.Length() == 0 {
m.Cmdy(arg).Cut(field)
}
})
}
}},
"default": {Hand: func(m *ice.Message, arg ...string) {
m.Spawn(m.Source()).Search(arg[0], func(key string, cmd *ice.Command) {
if arg[1] == ACTION {
list := kit.Value(cmd.Meta, arg[2])
kit.For(arg[3:], func(k, v string) {
kit.For(list, func(value ice.Map) {
kit.If(value[mdb.NAME] == k, func() {
value[mdb.VALUE] = v
})
})
})
return
}
for i, v := range arg[1:] {
kit.Value(cmd.List, kit.Keys(i, mdb.VALUE), v)
}
})
}},
}), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
_command_list(m, "")
}
for _, key := range arg {
_command_list(m, key)
m.OptionFields(INDEX)
m.Cmdy("", mdb.SEARCH, COMMAND)
} else {
kit.For(arg, func(k string) { _command_list(m, k) })
}
}},
}})
})
}
var PodCmd = func(m *ice.Message, arg ...ice.Any) bool { return false }
func Run(m *ice.Message, arg ...string) {
kit.If(!PodCmd(m, arg) && aaa.Right(m, arg), func() { m.Cmdy(arg) })
}
func Command(m *ice.Message, arg ...string) {
kit.If(!PodCmd(m, COMMAND, arg), func() { m.Cmdy(COMMAND, arg) })
}
func FileCmd(dir string) string {
if strings.Index(dir, ":") == 1 {
return ice.Pulse.FileURI(kit.ExtChange(strings.Join(kit.Slice(strings.Split(dir, ":"), 0, 2), ":"), nfs.GO))
}
return ice.Pulse.FileURI(kit.ExtChange(strings.Split(dir, nfs.DF)[0], nfs.GO))
}
func AddFileCmd(dir, key string) {
if ls := strings.SplitN(path.Join(kit.Slice(kit.Split(FileCmd(dir), nfs.PS), 1, 4)...), mdb.AT, 2); len(ls) > 1 {
_ls := strings.Split(FileCmd(dir), mdb.AT+ls[1]+nfs.PS)
ice.Info.File[path.Join(nfs.P, nfs.USR, path.Base(_ls[0]), _ls[1])] = key
ice.Info.Gomod[ls[0]] = ls[1]
} else {
ice.Info.File[FileCmd(dir)] = key
}
}
func GetFileCmd(dir string) string {
if strings.HasPrefix(dir, ice.REQUIRE+nfs.PS) {
dir = nfs.PS + dir
} else if strings.HasPrefix(dir, ice.ISH_PLUGED) {
dir = path.Join(nfs.P, strings.TrimPrefix(dir, ice.ISH_PLUGED))
}
for _, dir := range []string{dir, path.Join(nfs.P, ice.Info.Make.Module, dir), path.Join(nfs.P, ice.Info.Make.Module, ice.SRC, dir)} {
if cmd, ok := ice.Info.File[FileCmd(dir)]; ok {
return cmd
}
p := path.Dir(dir)
if cmd, ok := ice.Info.File[FileCmd(path.Join(p, path.Base(p)+nfs.PT+nfs.GO))]; ok {
return cmd
}
}
return ""
}
func GetCmdHelp(m *ice.Message, cmds string) (file string) {
file = kit.TrimPrefix(m.FileURI(kit.ExtChange(GetCmdFile(m, cmds), nfs.SHY)), nfs.P, nfs.REQUIRE)
if !nfs.Exists(m, path.Join(nfs.USR_LEARNING_PORTAL, "commands", strings.TrimPrefix(file, nfs.USR_ICEBERGS)), func(p string) { file = p }) {
kit.If(!nfs.Exists(m, file), func() { file = "" })
}
return
}
func GetCmdFile(m *ice.Message, cmds string) (file string) {
m.Search(kit.Select(m.PrefixKey(), cmds), func(key string, cmd *ice.Command) {
if file = kit.TrimPrefix(m.FileURI(kit.Split(cmd.FileLine(), nfs.DF)[0]), nfs.P); !nfs.Exists(m, file) {
file = path.Join(nfs.P, file)
}
})
return
}
func TravelCmd(m *ice.Message, cb func(key, file, line string)) *ice.Message {
m.Travel(func(p *ice.Context, s *ice.Context, key string, cmd *ice.Command) {
if IsOrderCmd(key) {
return
}
if runtime.GOOS == ice.WINDOWS {
if ls := kit.Split(cmd.FileLine(), nfs.DF); len(ls) > 2 {
cb(kit.Keys(s.Prefix(), key), strings.TrimPrefix(strings.Join(kit.Slice(ls, 0, -1), nfs.DF), kit.Path("")+nfs.PS), kit.Select("1", ls, -1))
return
}
}
if ls := kit.Split(cmd.FileLine(), nfs.DF); len(ls) > 0 {
cb(kit.Keys(s.Prefix(), key), strings.TrimPrefix(ls[0], kit.Path("")+nfs.PS), kit.Select("1", ls, 1))
}
})
return m
}
func IsOrderCmd(key string) bool {
return key[0] == '/' || key[0] == '_'
}
func ShortCmd(key string) string {
_key := kit.Select("", kit.Split(key, nfs.PT), -1)
if _p, ok := ice.Info.Index[_key].(*ice.Context); ok && _p.Prefix(_key) == key {
return _key
}
return key
}
func ResourceFile(m *ice.Message, file string, arg ...string) string {
if kit.HasPrefix(file, nfs.PS, ice.HTTP) {
return file
} else if nfs.Exists(m, file) {
return file
} else {
return path.Join(path.Dir(GetCmdFile(m, m.PrefixKey())), file)
}
}

View File

@ -2,61 +2,75 @@ package ctx
import (
"encoding/json"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/miss"
)
func _config_list(m *ice.Message) {
for k, v := range m.Source().Configs {
if k[0] == '/' || k[0] == '_' {
continue // 内部配置
func _config_only(v ice.Any, arg ...string) bool {
switch v := v.(type) {
case nil:
return true
case ice.Map:
if len(v) > len(arg) {
return false
}
m.Push(kit.MDB_KEY, k)
m.Push(kit.MDB_NAME, v.Name)
m.Push(kit.MDB_VALUE, kit.Format(v.Value))
for k, v := range v {
if v, ok := v.(ice.Map); ok && len(v) == 0 {
continue
} else if kit.IndexOf(arg, k) == -1 {
return false
}
}
return true
}
m.Sort(kit.MDB_KEY)
return false
}
func _config_save(m *ice.Message, name string, arg ...string) {
name = path.Join(m.Conf(CONFIG, kit.META_PATH), name)
if f, p, e := kit.Create(name); m.Assert(e) {
if !ice.HasVar() {
return
}
data, msg := ice.Map{}, m.Spawn(m.Source())
for _, k := range arg {
if v := mdb.Confv(msg, k); _config_only(v, mdb.META) && _config_only(kit.Value(v, mdb.META),
mdb.IMPORTANT, mdb.EXPIRE, mdb.VENDOR, nfs.SOURCE, nfs.SCRIPT, nfs.PATH, lex.REGEXP,
mdb.SHORT, mdb.FIELD, mdb.SHORTS, mdb.FIELDS,
mdb.ACTION, mdb.SORT, mdb.TOOLS,
"link", "linux", "darwin", "windows",
) {
continue
} else {
data[k] = v
}
}
if len(data) == 0 {
return
}
if f, _, e := miss.CreateFile(path.Join(ice.VAR_CONF, name)); m.Assert(e) {
defer f.Close()
msg := m.Spawn(m.Source())
data := map[string]interface{}{}
for _, k := range arg {
if v := msg.Confv(k); v != "" {
data[k] = v
if s, e := json.MarshalIndent(data, "", " "); !m.WarnNotValid(e) {
if _, e := f.Write(s); !m.WarnNotValid(e) {
}
}
// 保存配置
if s, e := json.MarshalIndent(data, "", " "); m.Assert(e) {
if n, e := f.Write(s); m.Assert(e) {
m.Log_EXPORT(CONFIG, name, kit.MDB_FILE, p, kit.MDB_SIZE, n)
}
}
m.Echo(p)
}
}
func _config_load(m *ice.Message, name string, arg ...string) {
name = path.Join(m.Conf(CONFIG, kit.META_PATH), name)
if f, e := os.Open(name); e == nil {
if !ice.HasVar() {
return
}
if f, e := miss.OpenFile(path.Join(ice.VAR_CONF, name)); e == nil {
defer f.Close()
msg := m.Spawn(m.Source())
data := map[string]interface{}{}
data, msg := ice.Map{}, m.Spawn(m.Source())
json.NewDecoder(f).Decode(&data)
// 加载配置
for k, v := range data {
msg.Search(k, func(p *ice.Context, s *ice.Context, key string) {
m.Log_IMPORT(CONFIG, kit.Keys(s.Name, key), kit.MDB_FILE, name)
msg.Search(k, func(p *ice.Context, s *ice.Context, key string, conf *ice.Config) {
kit.If(s.Configs[key] == nil, func() { s.Configs[key] = &ice.Config{} })
s.Configs[key].Value = v
})
}
@ -65,68 +79,87 @@ func _config_load(m *ice.Message, name string, arg ...string) {
func _config_make(m *ice.Message, key string, arg ...string) {
msg := m.Spawn(m.Source())
if len(arg) > 1 {
if strings.HasPrefix(arg[1], "@") {
arg[1] = msg.Cmdx("nfs.cat", arg[1][1:])
}
// 修改配置
msg.Confv(key, arg[0], kit.Parse(nil, "", arg[1:]...))
kit.If(!kit.IsIn(strings.Split(arg[0], nfs.PT)[0], mdb.META, mdb.HASH, mdb.LIST), func() { arg[0] = kit.Keys(mdb.META, arg[0]) })
kit.If(strings.HasPrefix(arg[1], mdb.AT), func() { arg[1] = msg.Cmdx(nfs.CAT, arg[1][1:]) })
mdb.Confv(msg, key, arg[0], kit.Parse(nil, "", arg[1:]...))
}
if len(arg) > 0 {
m.Echo(kit.Formats(msg.Confv(key, arg[0])))
m.Echo(kit.Formats(mdb.Confv(msg, key, arg[0])))
} else {
m.Echo(kit.Formats(msg.Confv(key)))
m.Echo(kit.Formats(mdb.Confv(msg, key))).StatusTime(mdb.COUNT, kit.Length(mdb.Confv(msg, key, mdb.HASH)))
}
}
func _config_rich(m *ice.Message, key string, sub string, arg ...string) {
m.Rich(key, sub, kit.Data(arg))
}
func _config_grow(m *ice.Message, key string, sub string, arg ...string) {
m.Grow(key, sub, kit.Dict(arg))
func _config_list(m *ice.Message) {
for k, v := range m.Source().Configs {
if !IsOrderCmd(k) {
m.Push(mdb.KEY, k).Push(mdb.NAME, v.Name).Push(mdb.VALUE, kit.Format(v.Value))
}
}
m.Sort(mdb.KEY)
}
const (
SAVE = "save"
LOAD = "load"
RICH = "rich"
GROW = "grow"
)
const CONFIG = "config"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
CONFIG: {Name: CONFIG, Help: "配置", Value: kit.Data(kit.MDB_PATH, ice.VAR_CONF)},
}, Commands: map[string]*ice.Command{
CONFIG: {Name: "config key auto", Help: "配置", Action: map[string]*ice.Action{
SAVE: {Name: "save", Help: "保存", Hand: func(m *ice.Message, arg ...string) {
_config_save(m, arg[0], arg[1:]...)
Index.MergeCommands(ice.Commands{
CONFIG: {Name: "config key auto", Help: "配置", Actions: ice.Actions{
nfs.SAVE: {Hand: func(m *ice.Message, arg ...string) { _config_save(m, arg[0], arg[1:]...) }},
nfs.LOAD: {Hand: func(m *ice.Message, arg ...string) { _config_load(m, arg[0], arg[1:]...) }},
mdb.EXPORT: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(arg[0], mdb.EXPORT) }},
mdb.IMPORT: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(arg[0], mdb.IMPORT) }},
nfs.TRASH: {Hand: func(m *ice.Message, arg ...string) {
nfs.Trash(m, path.Join(ice.VAR_DATA, arg[0]))
nfs.Trash(m, m.Cmdx(arg[0], mdb.EXPORT))
mdb.Conf(m, arg[0], mdb.HASH, "")
}},
LOAD: {Name: "load", Help: "加载", Hand: func(m *ice.Message, arg ...string) {
_config_load(m, arg[0], arg[1:]...)
mdb.CREATE: {Name: "create name value", Hand: func(m *ice.Message, arg ...string) {
m.Confv(m.Option(mdb.KEY), kit.Keys(mdb.META, m.Option(mdb.NAME)), m.Option(mdb.VALUE))
}},
RICH: {Name: "rich", Help: "富有", Hand: func(m *ice.Message, arg ...string) {
_config_rich(m, arg[0], arg[1], arg[2:]...)
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) {
m.Confv(m.Option(mdb.KEY), kit.Keys(mdb.META, m.Option(mdb.NAME)), "")
}},
GROW: {Name: "grow", Help: "成长", Hand: func(m *ice.Message, arg ...string) {
_config_grow(m, arg[0], arg[1], arg[2:]...)
}},
"list": {Name: "list", Help: "列表", Hand: func(m *ice.Message, arg ...string) {
list := []interface{}{}
for _, v := range arg[2:] {
list = append(list, v)
mdb.MODIFY: {Hand: func(m *ice.Message, arg ...string) {
if arg[0] == mdb.VALUE {
m.Confv(m.Option(mdb.KEY), kit.Keys(mdb.META, m.Option(mdb.NAME)), arg[1])
}
m.Confv(arg[0], arg[1], kit.List(list...))
}},
"clear": {Name: "clear", Help: "清空", Hand: func(m *ice.Message, arg ...string) {
m.Conf(arg[0], "", "")
m.Cmd(ice.EXIT, 1)
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
}, Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
_config_list(m)
return
} else {
_config_make(m, arg[0], arg[1:]...)
m.Action(mdb.CREATE, mdb.IMPORT, mdb.EXPORT, nfs.TRASH)
kit.For(mdb.Confv(m, arg[0], mdb.META), func(k, v string) {
m.Push(mdb.NAME, k).Push(mdb.VALUE, v).PushButton(mdb.REMOVE)
})
DisplayStoryJSON(m)
}
_config_make(m, arg[0], arg[1:]...)
}},
}})
})
}
func init() { ice.Info.Save = Save; ice.Info.Load = Load }
func Save(m *ice.Message, arg ...string) *ice.Message {
kit.If(len(arg) == 0, func() { arg = kit.SortedKey(m.Target().Configs) })
kit.For(arg, func(i int, k string) { arg[i] = strings.Replace(m.Prefix(k), nfs.PS, "", 1) })
return m.Cmd(prefix(CONFIG), nfs.SAVE, m.Prefix(nfs.JSON), arg)
}
func Load(m *ice.Message, arg ...string) *ice.Message {
kit.If(len(arg) == 0, func() { arg = kit.SortedKey(m.Target().Configs) })
kit.For(arg, func(i int, k string) { arg[i] = strings.Replace(m.Prefix(k), nfs.PS, "", 1) })
return m.Cmd(prefix(CONFIG), nfs.LOAD, m.Prefix(nfs.JSON), arg)
}
func ConfAction(arg ...ice.Any) ice.Actions {
return ice.Actions{ice.CTX_INIT: mdb.AutoConfig(arg...)}
}
func ConfigFromOption(m *ice.Message, arg ...string) {
if len(arg) == 0 {
kit.For(m.Target().Commands[m.CommandKey()].Actions[m.ActionKey()].List, func(value ice.Any) {
arg = append(arg, kit.Format(kit.Value(value, mdb.NAME)))
})
}
kit.For(arg, func(k string) { mdb.Config(m, k, kit.Select(mdb.Config(m, k), m.Option(k))) })
}
func OptionFromConfig(m *ice.Message, arg ...string) string {
kit.For(arg, func(k string) { m.Option(k, mdb.Config(m, k)) })
return m.Option(arg[0])
}

View File

@ -4,49 +4,32 @@ import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func _context_list(m *ice.Message, sub *ice.Context, name string) {
m.Travel(func(p *ice.Context, s *ice.Context) {
if !strings.HasPrefix(s.Cap(ice.CTX_FOLLOW), name+ice.PT) {
if name != "" && name != ice.ICE && !strings.HasPrefix(s.Prefix(), name+nfs.PT) {
return
}
m.Push(kit.MDB_NAME, strings.TrimPrefix(s.Cap(ice.CTX_FOLLOW), name+ice.PT))
m.Push(kit.MDB_STATUS, s.Cap(ice.CTX_STATUS))
m.Push(kit.MDB_STREAM, s.Cap(ice.CTX_STREAM))
m.Push(kit.MDB_HELP, s.Help)
m.Push(mdb.NAME, s.Prefix()).Push(mdb.HELP, s.Help)
})
}
const CONTEXT = "context"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
CONTEXT: {Name: "context name=web.chat action=context,command,config key auto spide", Help: "模块", Action: ice.MergeAction(map[string]*ice.Action{
"spide": {Name: "spide", Help: "架构图", Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 || arg[1] == CONTEXT { // 模块列表
m.Cmdy(CONTEXT, kit.Select(ice.ICE, arg, 0), CONTEXT)
m.Display("/plugin/story/spide.js", "root", kit.Select(ice.ICE, arg, 0),
"field", "name", "split", ice.PT, "prefix", "spide")
return
}
if index := kit.Keys(arg[0], arg[1]); strings.HasSuffix(index, arg[2]) { // 命令列表
m.Cmdy(CONTEXT, index, COMMAND).Table(func(i int, value map[string]string, head []string) {
m.Push("file", arg[1])
})
} else { // 命令详情
m.Cmdy(COMMAND, kit.Keys(index, strings.Split(arg[2], " ")[0]))
}
}},
}, CmdAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Search(kit.Select(ice.ICE, arg, 0)+ice.PT, func(p *ice.Context, s *ice.Context, key string) {
Index.MergeCommands(ice.Commands{
CONTEXT: {Name: "context name=ice action=context,command,config key auto", Help: "模块", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 0, func() { arg = append(arg, m.Source().Prefix()) })
m.Search(arg[0]+nfs.PT, func(p *ice.Context, s *ice.Context) {
msg := m.Spawn(s)
defer m.Copy(msg)
switch kit.Select(CONTEXT, arg, 1) {
case CONTEXT:
_context_list(msg, s, kit.Select("", arg, 0))
_context_list(msg, s, arg[0])
case COMMAND:
msg.Cmdy(COMMAND, arg[2:])
case CONFIG:
@ -54,5 +37,5 @@ func init() {
}
})
}},
}})
})
}

View File

@ -2,10 +2,13 @@ package ctx
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const CTX = "ctx"
var Index = &ice.Context{Name: CTX, Help: "标准模块"}
func init() { ice.Index.Register(Index, nil, CONTEXT, COMMAND, CONFIG, MESSAGE) }
func init() { ice.Index.Register(Index, nil, CONTEXT, COMMAND, CONFIG) }
func prefix(arg ...string) string { return kit.Keys(CTX, arg) }

View File

@ -1,7 +0,0 @@
chapter "ctx"
field "模块" context
field "命令" command
field "配置" config
field "消息" message

94
base/ctx/display.go Normal file
View File

@ -0,0 +1,94 @@
package ctx
import (
"path"
"reflect"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func isLocalFile(p string) bool {
return !strings.HasPrefix(p, nfs.PS) && !strings.HasPrefix(p, ice.HTTP)
}
func DisplayTable(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayBase(m, ice.PLUGIN_TABLE_JS, arg...)
}
func DisplayTableCard(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayTable(m, STYLE, "card")
}
func DisplayStory(m *ice.Message, file string, arg ...ice.Any) *ice.Message {
kit.If(file == "", func() { file = kit.Keys(m.CommandKey(), nfs.JS) })
kit.If(isLocalFile(file), func() { file = path.Join(ice.PLUGIN_STORY, file) })
return DisplayBase(m, file, arg...)
}
func DisplayInput(m *ice.Message, file string, arg ...ice.Any) *ice.Message {
kit.If(file == "", func() { file = kit.Keys(m.CommandKey(), nfs.JS) })
kit.If(isLocalFile(file), func() { file = path.Join(ice.PLUGIN_INPUT, file) })
return DisplayBase(m, file, arg...)
}
func DisplayStoryForm(m *ice.Message, arg ...ice.Any) *ice.Message {
args := kit.List()
for i := range arg {
switch v := arg[i].(type) {
case string:
args = append(args, ice.SplitCmd("list "+v, nil)...)
default:
trans := kit.Value(m.Commands(m.CommandKey()).Meta, ice.CTX_TRANS)
if t := reflect.TypeOf(v); t.Kind() == reflect.Func {
name := kit.FuncName(arg[i])
args = append(args, kit.Dict(mdb.TYPE, html.BUTTON, mdb.NAME, name, mdb.VALUE, kit.Select(name, kit.Value(trans, name), !m.IsEnglish())))
}
}
}
kit.For(args, func(v ice.Map) { m.Push("", v, kit.Split("type,name,value,values,need,action")) })
return DisplayStory(m, "form")
}
func DisplayInputKey(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayInput(m, "key", arg...)
}
func DisplayStoryWeight(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayStory(m, "weight", arg...)
}
func DisplayStoryPie(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayStory(m, "pie", arg...)
}
func DisplayStoryJSON(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayStory(m, "json", arg...)
}
func DisplayStorySpide(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayStory(m, "spide", arg...)
}
func DisplayStoryChina(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayStory(m, "china", arg...)
}
func DisplayStudio(m *ice.Message, cmd ...string) *ice.Message {
for i, k := range cmd {
kit.If(!strings.Contains(cmd[i], nfs.PT), func() { cmd[i] = m.Prefix(k) })
}
return DisplayStory(m.Cmdy(COMMAND, cmd), "studio")
}
func DisplayLocal(m *ice.Message, file string, arg ...ice.Any) *ice.Message {
kit.If(file == "", func() { file = strings.ReplaceAll(strings.TrimPrefix(m.PrefixKey(), "web."), nfs.PT, nfs.PS) })
kit.If(isLocalFile(file), func() { file = path.Join(ice.PLUGIN_LOCAL, file) })
return DisplayBase(m, file, arg...)
}
func DisplayLocalInner(m *ice.Message, arg ...ice.Any) *ice.Message {
return DisplayLocal(m, "code/inner", arg...)
}
func DisplayBase(m *ice.Message, file string, arg ...ice.Any) *ice.Message {
m.Option(ice.MSG_DISPLAY, kit.MergeURL(kit.Select(kit.ExtChange(file, nfs.JS), file, strings.Contains(file, mdb.QS)), arg...))
return Toolkit(m, "")
}
func DisplayBaseCSS(m *ice.Message, file string, arg ...ice.Any) *ice.Message {
m.Option(ice.MSG_DISPLAY_CSS, kit.MergeURL(kit.Select(kit.ExtChange(file, nfs.CSS), file, strings.Contains(file, mdb.QS)), arg...))
return m
}
func Toolkit(m *ice.Message, arg ...string) *ice.Message {
m.OptionDefault(ice.MSG_ONLINE, mdb.Config(m, "online"))
return m.Options(ice.MSG_TOOLKIT, kit.Select(mdb.Config(m, mdb.TOOLS), kit.Fields(arg)))
}

View File

@ -1,25 +0,0 @@
package ctx
import (
"reflect"
"strings"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const MESSAGE = "message"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
MESSAGE: {Name: "message", Help: "消息", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
t := reflect.TypeOf(m)
for i := 0; i < t.NumMethod(); i++ {
method := t.Method(i)
p := kit.FileLine(method.Func.Interface(), 4)
m.Push(kit.MDB_NAME, method.Name)
m.Push(kit.MDB_TEXT, strings.Split(p, ice.ICEBERGS+"/")[1])
}
}},
}})
}

61
base/ctx/process.go Normal file
View File

@ -0,0 +1,61 @@
package ctx
import (
"path"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _process_args(m *ice.Message, args ice.Any) []string {
switch cb := args.(type) {
case []string:
return cb
case string:
return []string{cb}
case func() string:
return []string{cb()}
case func() []string:
return cb()
case func():
cb()
case nil:
default:
m.ErrorNotImplement(args)
}
return nil
}
func ProcessField(m *ice.Message, cmd string, args ice.Any, arg ...string) *ice.Message {
if cmd = kit.Select(m.ActionKey(), cmd); kit.HasPrefixList(arg, RUN) {
if !PodCmd(m, cmd, arg[1:]) && aaa.Right(m, cmd, arg[1:]) {
m.Cmdy(cmd, arg[1:])
}
return m
}
args = kit.Format(_process_args(m, args))
if PodCmd(m, COMMAND, cmd) {
m.Push(ice.SPACE, m.Option(ice.MSG_USERPOD))
} else {
m.Cmdy(COMMAND, cmd)
}
if m.Push(ARGS, args); m.IsMetaKey() {
m.Push(STYLE, html.FLOAT)
}
if m.ActionKey() == "" {
m.ProcessField(ACTION, RUN, cmd)
} else {
m.ProcessField(ACTION, m.ActionKey(), RUN)
}
return m
}
func ProcessFloat(m *ice.Message, cmd string, args ice.Any, arg ...string) *ice.Message {
if m.IsMetaKey() {
m.ProcessOpen(path.Join("/c/", cmd, path.Join(_process_args(m, args)...)))
return m
} else if !kit.HasPrefixList(arg, RUN) {
defer m.Push(STYLE, html.FLOAT)
}
return ProcessField(m, cmd, args, arg...)
}

View File

@ -1,40 +1,75 @@
package gdb
import (
"sync"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _event_listen(m *ice.Message, event string, cmd string) {
m.Cmdy(mdb.INSERT, EVENT, "", mdb.HASH, EVENT, event)
m.Cmdy(mdb.INSERT, EVENT, "", mdb.ZONE, event, ice.CMD, cmd)
}
func _event_action(m *ice.Message, event string, arg ...string) {
mdb.ZoneSelect(m, event).Table(func(index int, value map[string]string, head []string) {
m.Cmd(kit.Split(value[ice.CMD]), event, arg).Cost(EVENT, event, ice.ARG, arg)
})
}
const (
LISTEN = "listen"
HAPPEN = "happen"
)
const EVENT = "event"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
EVENT: {Name: EVENT, Help: "事件流", Value: kit.Data(
kit.MDB_SHORT, EVENT, kit.MDB_FIELD, "time,id,cmd",
)},
}, Commands: map[string]*ice.Command{
EVENT: {Name: "event event id auto listen", Help: "事件流", Action: ice.MergeAction(map[string]*ice.Action{
LISTEN: {Name: "listen event cmd", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
_event_listen(m, m.Option(EVENT), m.Option(ice.CMD))
Index.MergeCommands(ice.Commands{
EVENT: {Name: "event event id auto listen happen", Help: "事件流", Actions: ice.MergeActions(ice.Actions{
LISTEN: {Name: "listen event* cmd*", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
mdb.ZoneInsert(m, m.OptionSimple(EVENT, ice.CMD))
list[m.Option(EVENT)]++
}},
ACTION: {Name: "action event arg", Help: "触发", Hand: func(m *ice.Message, arg ...string) {
_event_action(m, m.Option(EVENT), arg[2:]...)
HAPPEN: {Name: "happen event*", Help: "触发", Hand: func(m *ice.Message, arg ...string) {
defer m.Cost()
m.OptionCB(mdb.SELECT, "")
mdb.ZoneSelectAll(m.Spawn(ice.OptionFields("")), arg[1]).Table(func(value ice.Maps) {
m.Cmdy(kit.Split(value[ice.CMD]), arg[1], arg[2:], ice.OptionFields(""))
})
_waitMap.Range(func(key, cb ice.Any) bool { cb.(func(*ice.Message, ...string))(m, arg...); return true })
}},
}, mdb.ZoneAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if mdb.ZoneSelect(m, arg...); len(arg) == 0 {
m.PushAction(ACTION, mdb.REMOVE)
}
}},
}})
}, mdb.ZoneAction(mdb.SHORT, EVENT, mdb.FIELDS, "time,id,cmd"), mdb.ClearOnExitHashAction())},
})
}
func EventsAction(arg ...string) ice.Actions {
list := kit.DictList(arg...)
return ice.Actions{ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
for sub := range m.Target().Commands[m.CommandKey()].Actions {
kit.If(list[sub] == ice.TRUE, func() { Watch(m, sub) })
}
}}}
}
var list map[string]int = map[string]int{}
func Watch(m *ice.Message, key string, arg ...string) *ice.Message {
kit.If(len(arg) == 0, func() { arg = append(arg, m.ShortKey()) })
return m.Cmd(Prefix(EVENT), LISTEN, EVENT, key, ice.CMD, kit.Join(arg, ice.SP))
}
func Event(m *ice.Message, key string, arg ...ice.Any) *ice.Message {
if key = kit.Select(kit.Keys(m.CommandKey(), m.ActionKey()), key); list[key] == 0 {
return m
}
return m.Cmdy(Prefix(EVENT), HAPPEN, EVENT, key, arg, logs.FileLineMeta(-1))
}
func EventDeferEvent(m *ice.Message, key string, arg ...ice.Any) func(string, ...ice.Any) {
Event(m, key, arg...)
return func(key string, args ...ice.Any) { Event(m, key, args...) }
}
var _waitMap = sync.Map{}
func WaitEvent(m *ice.Message, key string, cb func(*ice.Message, ...string) bool) {
wg := sync.WaitGroup{}
h := kit.HashsUniq()
defer _waitMap.Delete(h)
_waitMap.Store(h, func(m *ice.Message, arg ...string) {
m.Info("WaitEvent %v %v", key, kit.FileLine(cb, 3))
kit.If((key == "" || m.Option(EVENT) == key) && cb(m, arg...), func() { wg.Done() })
})
wg.Add(1)
defer wg.Wait()
}

View File

@ -1,57 +1,56 @@
package gdb
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
type Frame struct {
s chan os.Signal
t time.Duration
e chan bool
}
type Frame struct{ s chan os.Signal }
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server {
return &Frame{}
func (f *Frame) Begin(m *ice.Message, arg ...string) {
f.s = make(chan os.Signal, 10)
}
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
f.s = make(chan os.Signal, ice.MOD_CHAN)
f.e = make(chan bool, 1)
return f
}
func (f *Frame) Start(m *ice.Message, arg ...string) bool {
f.t = kit.Duration(m.Conf(TIMER, kit.Keym(TICK)))
for {
select {
case <-f.e:
return true
case <-time.Tick(f.t):
// m.Cmd(TIMER, ACTION)
case s := <-f.s:
m.Cmd(SIGNAL, ACTION, ACTION, SIGNAL, s)
func (f *Frame) Start(m *ice.Message, arg ...string) {
if ice.HasVar() {
if f, p, e := logs.CreateFile(ice.VAR_LOG_ICE_PID); e == nil {
m.Logs("save", "file", p, PID, os.Getpid())
fmt.Fprint(f, os.Getpid())
f.Close()
}
}
t := time.NewTicker(kit.Duration(mdb.Conf(m, TIMER, kit.Keym(TICK))))
for {
select {
case <-t.C:
m.Options(ice.LOG_DISABLE, ice.TRUE).Cmd(TIMER, HAPPEN)
case s, ok := <-f.s:
if !ok {
return
}
m.Cmd(SIGNAL, HAPPEN, SIGNAL, s)
}
}
return true
}
func (f *Frame) Close(m *ice.Message, arg ...string) bool {
f.e <- true
return true
func (f *Frame) Close(m *ice.Message, arg ...string) {
close(f.s)
}
func (f *Frame) listen(m *ice.Message, s int, arg ...string) {
signal.Notify(f.s, syscall.Signal(s))
mdb.HashCreate(m, SIGNAL, s, arg)
}
const GDB = "gdb"
var Index = &ice.Context{Name: GDB, Help: "事件模块", Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Load(TIMER)
}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Save(TIMER)
}},
}}
var Index = &ice.Context{Name: GDB, Help: "事件模块"}
func init() { ice.Index.Register(Index, &Frame{}, ROUTINE, SIGNAL, EVENT, TIMER) }
func Prefix(arg ...string) string { return kit.Keys(GDB, arg) }
func init() { ice.Index.Register(Index, &Frame{}, SIGNAL, EVENT, TIMER, ROUTINE) }

View File

@ -1,6 +0,0 @@
chapter "gdb"
field "协程池" routine
field "信号量" signal
field "事件流" event
field "定时器" timer

View File

@ -1,36 +1,46 @@
package gdb
import (
"path"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
const ROUTINE = "routine"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
ROUTINE: {Name: ROUTINE, Help: "协程池", Value: kit.Data(
kit.MDB_SHORT, "time,hash,status,fileline",
)},
}, Commands: map[string]*ice.Command{
ROUTINE: {Name: "routine hash auto prunes", Help: "协程池", Action: ice.MergeAction(map[string]*ice.Action{
mdb.CREATE: {Name: "create fileline status", Help: "创建"},
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_SHORT))
m.Cmdy(mdb.PRUNES, ROUTINE, "", mdb.HASH, kit.MDB_STATUS, cli.STOP)
m.Cmdy(mdb.PRUNES, ROUTINE, "", mdb.HASH, kit.MDB_STATUS, cli.ERROR)
Index.MergeCommands(ice.Commands{
ROUTINE: {Help: "协程池", Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create name cmd", Hand: func(m *ice.Message, arg ...string) {
m.Go(func() {
cb := m.OptionCB("")
m.OptionDefault(ice.CMD, logs.FileLine(cb))
h := mdb.HashCreate(m, m.OptionSimple(mdb.NAME, ice.CMD), mdb.STATUS, START)
defer func() {
if e := recover(); e == nil {
mdb.HashRemove(m, mdb.HASH, h)
} else {
mdb.HashModify(m, mdb.HASH, h, mdb.STATUS, ERROR, ERROR, e)
}
}()
switch cb := cb.(type) {
case string:
m.Cmd(kit.Split(cb))
case []string:
m.Cmd(kit.Split(kit.Join(cb)))
case func(*ice.Message):
cb(m.Spawn(m.Source()))
case func():
cb()
default:
m.ErrorNotImplement(cb)
}
}, m.Option(mdb.NAME))
}},
"inner": {Name: "inner", Help: "源码", Hand: func(m *ice.Message, arg ...string) {
ls := kit.Split(m.Option("fileline"), ":")
m.ProcessCommand("inner", []string{path.Dir(ls[0]), path.Base(ls[0]), ls[1]}, arg...)
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...)
m.PushAction("inner", mdb.REMOVE)
}},
}})
}, mdb.StatusHashAction(mdb.LIMIT, 1000, mdb.LEAST, 500, mdb.FIELD, "time,hash,status,name,cmd"), mdb.ClearOnExitHashAction())},
})
}
func Go(m *ice.Message, cb ice.Any, arg ...string) {
m.Cmd(ROUTINE, mdb.CREATE, kit.Select(m.ShortKey(), arg, 0), logs.FileLine(cb), cb)
}

View File

@ -3,32 +3,71 @@ package gdb
import (
"os"
"os/signal"
"path"
"syscall"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
log "shylinux.com/x/toolkits/logs"
"shylinux.com/x/toolkits/file"
)
func _signal_listen(m *ice.Message, s int, arg ...string) {
if f, ok := m.Target().Server().(*Frame); ok {
m.Cmdy(mdb.INSERT, SIGNAL, "", mdb.HASH, arg)
signal.Notify(f.s, syscall.Signal(s))
f.listen(m, s, arg...)
}
}
func _signal_action(m *ice.Message, arg ...string) {
mdb.HashSelect(m.Spawn(), arg...).Table(func(index int, value map[string]string, head []string) {
m.Cmdy(kit.Split(value[ice.CMD]))
mdb.HashSelect(m.Spawn(), arg...).Table(func(value ice.Maps) { m.Cmdy(kit.Split(value[ice.CMD])) })
}
func _signal_process(m *ice.Message, p string, s os.Signal) {
kit.If(p == "", func() { b, _ := file.ReadFile(ice.VAR_LOG_ICE_PID); p = string(b) })
if p, e := os.FindProcess(kit.Int(kit.Select(kit.Format(os.Getpid()), p))); e == nil {
p.Signal(s)
}
}
const (
PID = "pid"
)
const (
DEBUG = "debug"
ERROR = "error"
START = "start"
RESTART = "restart"
STOP = "stop"
KILL = "kill"
)
const SIGNAL = "signal"
func init() {
Index.MergeCommands(ice.Commands{
SIGNAL: {Help: "信号量", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { _signal_init(m, arg...) }},
LISTEN: {Name: "listen signal name cmd", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
_signal_listen(m, kit.Int(m.Option(SIGNAL)), arg...)
}},
HAPPEN: {Name: "happen signal", Help: "触发", Hand: func(m *ice.Message, arg ...string) {
_signal_action(m, m.Option(SIGNAL))
}},
RESTART: {Name: "restart pid", Hand: func(m *ice.Message, arg ...string) {
_signal_process(m, m.Option(PID), syscall.SIGINT)
}},
STOP: {Name: "stop pid", Hand: func(m *ice.Message, arg ...string) {
_signal_process(m, m.Option(PID), syscall.SIGQUIT)
}},
KILL: {Name: "kill pid signal", Hand: func(m *ice.Message, arg ...string) {
_signal_process(m, m.Option(PID), syscall.Signal(kit.Int(kit.Select("9", m.Option(SIGNAL)))))
}},
}, mdb.HashAction(mdb.SHORT, SIGNAL, mdb.FIELD, "time,signal,name,cmd", mdb.ACTION, HAPPEN), mdb.ClearOnExitHashAction()), Hand: func(m *ice.Message, arg ...string) {
defer kit.If(len(arg) == 0, func() { m.Action(LISTEN) })
mdb.HashSelect(m, arg...)
}},
})
}
func SignalNotify(m *ice.Message, sig int, cb func()) {
func SignalNotify(m *ice.Message, sig syscall.Signal, cb func()) {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.Signal(sig))
signal.Notify(ch, sig)
m.Go(func() {
for {
if _, ok := <-ch; ok {
@ -37,40 +76,3 @@ func SignalNotify(m *ice.Message, sig int, cb func()) {
}
})
}
const (
LISTEN = "listen"
ACTION = "action"
)
const SIGNAL = "signal"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SIGNAL: {Name: SIGNAL, Help: "信号器", Value: kit.Data(
kit.MDB_SHORT, SIGNAL, kit.MDB_FIELD, "time,signal,name,cmd",
kit.MDB_PATH, path.Join(ice.VAR_RUN, "ice.pid"),
)},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if log.LogDisable {
return // 禁用日志
}
m.Cmd(nfs.SAVE, kit.Select(m.Conf(SIGNAL, kit.META_PATH), m.Conf(cli.RUNTIME, kit.Keys(cli.CONF, cli.CTX_PID))),
m.Conf(cli.RUNTIME, kit.Keys(cli.HOST, cli.PID)))
m.Cmd(SIGNAL, LISTEN, SIGNAL, "3", kit.MDB_NAME, "退出", ice.CMD, "exit 0")
m.Cmd(SIGNAL, LISTEN, SIGNAL, "2", kit.MDB_NAME, "重启", ice.CMD, "exit 1")
}},
SIGNAL: {Name: "signal signal auto listen", Help: "信号器", Action: ice.MergeAction(map[string]*ice.Action{
LISTEN: {Name: "listen signal name cmd", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
_signal_listen(m, kit.Int(m.Option(SIGNAL)), arg...)
}},
ACTION: {Name: "action signal", Help: "触发", Hand: func(m *ice.Message, arg ...string) {
_signal_action(m, m.Option(SIGNAL))
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...).Sort(SIGNAL)
m.PushAction(ACTION, mdb.REMOVE)
}},
}})
}

24
base/gdb/signal_darwin.go Normal file
View File

@ -0,0 +1,24 @@
package gdb
import (
"os"
"syscall"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _signal_init(m *ice.Message, arg ...string) {
_signal_listen(m, 1, mdb.NAME, START, ice.CMD, "runtime")
_signal_listen(m, 2, mdb.NAME, RESTART, ice.CMD, "exit 1")
_signal_listen(m, 3, mdb.NAME, STOP, ice.CMD, "exit 0")
_signal_listen(m, int(syscall.SIGUSR1), mdb.NAME, "info", ice.CMD, "runtime")
}
func SignalProcess(m *ice.Message, pid string) bool {
if proc, err := os.FindProcess(kit.Int(pid)); err == nil && proc.Signal(syscall.SIGUSR1) == nil {
return true
}
return false
}

23
base/gdb/signal_linux.go Normal file
View File

@ -0,0 +1,23 @@
package gdb
import (
"os"
"syscall"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _signal_init(m *ice.Message, arg ...string) {
_signal_listen(m, 1, mdb.NAME, START, ice.CMD, "runtime")
_signal_listen(m, 2, mdb.NAME, RESTART, ice.CMD, "exit 1")
_signal_listen(m, 3, mdb.NAME, STOP, ice.CMD, "exit 0")
_signal_listen(m, int(syscall.SIGUSR1), mdb.NAME, "info", ice.CMD, "runtime")
}
func SignalProcess(m *ice.Message, pid string) bool {
if proc, err := os.FindProcess(kit.Int(pid)); err == nil && proc.Signal(syscall.SIGUSR1) == nil {
return true
}
return false
}

View File

@ -0,0 +1,15 @@
package gdb
import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
)
func _signal_init(m *ice.Message, arg ...string) {
_signal_listen(m, 1, mdb.NAME, START, ice.CMD, "runtime")
_signal_listen(m, 2, mdb.NAME, RESTART, ice.CMD, "exit 1")
_signal_listen(m, 3, mdb.NAME, STOP, ice.CMD, "exit 0")
}
func SignalProcess(m *ice.Message, pid string) bool {
return false
}

View File

@ -4,62 +4,49 @@ import (
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _timer_action(m *ice.Message, arg ...string) {
now := time.Now().UnixNano()
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Richs(TIMER, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
if value = kit.GetMeta(value); value[kit.MDB_STATUS] == cli.STOP {
func _timer_action(m *ice.Message, now time.Time, arg ...string) {
mdb.HashSelects(m).Table(func(value ice.Maps) {
count := kit.Int(value[mdb.COUNT])
if count == 0 || value[mdb.TIME] > now.Format(ice.MOD_TIME) {
return
}
order := kit.Int(value[ORDER])
if n := kit.Time(kit.Format(value[NEXT])); now > n && order > 0 {
m.Logs(TIMER, kit.MDB_KEY, key, ORDER, order)
msg := m.Cmd(value[ice.CMD])
m.Grow(TIMER, kit.Keys(kit.MDB_HASH, key), kit.Dict(ice.RES, msg.Result()))
if value[ORDER] = kit.Format(order - 1); order > 1 {
value[NEXT] = msg.Time(value[INTERVAL])
}
}
m.Options(ice.LOG_DISABLE, ice.FALSE)
m.Cmd(kit.Split(value[ice.CMD])).Cost()
kit.If(count < 0, func() { count++ })
mdb.HashModify(m, mdb.NAME, value[mdb.NAME], mdb.COUNT, count-1, mdb.TIME, m.Time(value[INTERVAL]))
})
}
const (
DELAY = "delay"
INTERVAL = "interval"
ORDER = "order"
NEXT = "next"
TICK = "tick"
)
const TIMER = "timer"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
TIMER: {Name: TIMER, Help: "定时器", Value: kit.Data(
kit.MDB_FIELD, "time,hash,delay,interval,order,next,cmd", TICK, "1s",
)},
}, Commands: map[string]*ice.Command{
TIMER: {Name: "timer hash id auto create action prunes", Help: "定时器", Action: ice.MergeAction(map[string]*ice.Action{
mdb.CREATE: {Name: "create delay=10ms interval=10s order=3 cmd=runtime", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(mdb.INSERT, TIMER, "", mdb.HASH, DELAY, "10ms", INTERVAL, "10m", ORDER, 1, NEXT, m.Time(m.Option(DELAY)), arg)
Index.MergeCommands(ice.Commands{
TIMER: {Help: "定时器", Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict(DELAY, "延时", INTERVAL, "间隔", TICK, "周期")),
), Actions: ice.MergeActions(ice.Actions{
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch mdb.HashInputs(m, arg); arg[0] {
case mdb.COUNT:
m.Push(arg[0], "-1")
case ice.CMD:
m.Push(arg[0], "cli.procstat insert")
}
}},
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(mdb.PRUNES, TIMER, "", mdb.HASH, ORDER, 0)
}},
ACTION: {Name: "action", Help: "执行", Hand: func(m *ice.Message, arg ...string) {
_timer_action(m, arg...)
}},
}, mdb.ZoneAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Fields(len(arg), m.Config(kit.MDB_FIELD), "time,id,res")
mdb.ZoneSelect(m, arg...)
mdb.CREATE: {Name: "create name*=hi delay=10ms interval=10s count=3 cmd*=runtime"},
mdb.PRUNES: {Hand: func(m *ice.Message, arg ...string) { mdb.HashPrunesValue(m, mdb.COUNT, "0") }},
HAPPEN: {Hand: func(m *ice.Message, arg ...string) { _timer_action(m, time.Now(), arg...) }},
}, mdb.HashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,delay,interval,count,cmd", TICK, "10s")), Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m, arg...).StatusTimeCount(mdb.ConfigSimple(m, TICK))
}},
}})
})
}

View File

@ -1,8 +1,6 @@
package lex
import (
ice "shylinux.com/x/icebergs"
)
import ice "shylinux.com/x/icebergs"
const LEX = "lex"

View File

@ -1,2 +0,0 @@
chapter "lex"

12
base/lex/regexp.go Normal file
View File

@ -0,0 +1,12 @@
package lex
const (
PATTERN = "pattern"
EXTREG = "extreg"
REGEXP = "regexp"
PREFIX = "prefix"
SUFFIX = "suffix"
SPACE = "space"
OPERATOR = "operator"
)

View File

@ -4,76 +4,118 @@ import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func _split_deep(m *ice.Message, text string) (deep int) {
func _split_tab(text string) (tab int) {
for _, c := range text {
switch c {
case '\t':
deep += 4
tab += 4
case ' ':
deep++
tab++
default:
return
}
}
return
}
func _split_list(m *ice.Message, file string, arg ...string) {
const DEEP = "_deep"
list := kit.List(kit.Data(DEEP, -1))
func _split_deep(stack []int, text string) ([]int, int) {
tab := _split_tab(text)
for i := len(stack) - 1; i >= 0; i-- {
kit.If(tab <= stack[i], func() { stack = stack[:len(stack)-1] })
}
stack = append(stack, tab)
return stack, len(stack)
}
func _split_list(m *ice.Message, file string, arg ...string) ice.Map {
const INDENT = "_indent"
stack, indent := []int{}, 0
list, line := kit.List(kit.Data(INDENT, -1)), ""
err := false
m.Cmd(nfs.CAT, file, func(text string) {
if text = kit.Split(text, "#", "#")[0]; strings.TrimSpace(text) == "" {
if strings.TrimSpace(text) == "" {
return
}
if line += text; strings.Count(text, "`")%2 == 1 {
return
}
if strings.HasPrefix(strings.TrimSpace(text), "# ") {
return
}
stack, indent = _split_deep(stack, text)
data := kit.Data(INDENT, indent)
ls := kit.Split(text, m.Option(SPLIT_SPACE), m.Option(SPLIT_BLOCK), m.Option(SPLIT_QUOTE), m.Option(SPLIT_TRANS))
if err {
return
}
deep := _split_deep(m, text)
data := kit.Data(DEEP, deep)
ls := kit.Split(text)
switch cb := m.OptionCB(SPLIT).(type) {
case func([]string, map[string]interface{}) []string:
case func(int, []string):
cb(indent, ls)
case func(int, []string) []string:
ls = cb(indent, ls)
case func(int, []string, ice.Map, ice.Map):
root, _ := kit.Value(list[0], "list.0").(ice.Map)
cb(indent, ls, data, root)
case func(int, []string, ice.Map) []string:
ls = cb(indent, ls, data)
case func([]string, ice.Map) []string:
ls = cb(ls, data)
case func([]string) []string:
ls = cb(ls)
case func([]string):
cb(ls)
case func(string, []string):
cb(text, ls)
case func(int, string, []string):
cb(indent, text, ls)
case nil:
default:
err = true
m.ErrorNotImplement(cb)
}
for _, k := range arg {
if kit.Value(data, kit.Keym(k), kit.Select("", ls, 0)); len(ls) > 0 {
ls = ls[1:]
}
}
for i := 0; i < len(ls)-1; i += 2 {
kit.Value(data, kit.Keym(ls[i]), ls[i+1])
}
kit.For(arg, func(k string) {
kit.Value(data, kit.Keym(k), kit.Select("", ls, 0))
kit.If(len(ls) > 0, func() { ls = ls[1:] })
})
kit.For(ls, func(k, v string) { kit.Value(data, kit.Keym(k), v) })
for i := len(list) - 1; i >= 0; i-- {
if deep > kit.Int(kit.Value(list[i], kit.Keym(DEEP))) {
kit.Value(list[i], "list.-2", data)
if indent > kit.Int(kit.Value(list[i], kit.Keym(INDENT))) {
kit.Value(list[i], kit.Keys(mdb.LIST, "-2"), data)
list = append(list, data)
break
}
list = list[:len(list)-1]
}
line = ""
})
m.Echo(kit.Format(list[0]))
return list[0].(ice.Map)
}
const (
TB = ice.TB
SP = ice.SP
NL = ice.NL
)
const (
SPLIT_SPACE = "split.space"
SPLIT_BLOCK = "split.block"
SPLIT_QUOTE = "split.quote"
SPLIT_TRANS = "split.trans"
)
const PARSE = "parse"
const SPLIT = "split"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SPLIT: {Name: "split", Help: "解析", Value: kit.Data()},
}, Commands: map[string]*ice.Command{
SPLIT: {Name: "split path key auto", Help: "解析", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) == 0 || strings.HasSuffix(arg[0], ice.PS) {
Index.MergeCommands(ice.Commands{
SPLIT: {Name: "split path key auto", Help: "分词", Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 || strings.HasSuffix(arg[0], nfs.PS) {
m.Cmdy(nfs.DIR, arg)
return
} else {
m.Echo(kit.Format(_split_list(m, arg[0], kit.Split(kit.Join(arg[1:]))...)))
}
_split_list(m, arg[0], arg[1:]...)
m.ProcessDisplay("/plugin/local/wiki/json.js")
}},
}})
})
}

13
base/log/bench.go Normal file
View File

@ -0,0 +1,13 @@
package log
import (
ice "shylinux.com/x/icebergs"
)
const BENCH = "bench"
func init() {
Index.MergeCommands(ice.Commands{
BENCH: {Help: "记录", Hand: func(m *ice.Message, arg ...string) {}},
})
}

94
base/log/debug.go Normal file
View File

@ -0,0 +1,94 @@
package log
import (
"regexp"
"strings"
"time"
"unicode"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func _debug_file(k string) string { return ice.VAR_LOG + k + ".log" }
const DEBUG = "debug"
func init() {
const (
LEVEL = "level"
)
Index.MergeCommands(ice.Commands{
DEBUG: {Name: "debug level=error,bench,debug,error,watch offset limit auto reset app doc", Help: "日志", Actions: ice.Actions{
"doc": {Help: "文档", Hand: func(m *ice.Message, arg ...string) { m.ProcessOpen("https://pkg.go.dev/std") }},
"reset": {Help: "重置", Hand: func(m *ice.Message, arg ...string) {
m.Cmd(nfs.CAT, _debug_file(arg[0]), func(line string, index int) { m.ProcessRewrite(mdb.OFFSET, index+2, mdb.LIMIT, 1000) })
}},
"app": {Hand: func(m *ice.Message, arg ...string) {
cli.OpenCmds(m, kit.Format("cd %s", kit.Path("")), "tail -f var/log/bench.log")
}},
}, Hand: func(m *ice.Message, arg ...string) {
r := regexp.MustCompile("{.*}")
offset, limit := kit.Int(kit.Select("0", arg, 1)), kit.Int(kit.Select("100", arg, 2))
switch arg[0] {
case BENCH, ERROR, DEBUG:
m.Cmd(nfs.CAT, _debug_file(arg[0]), func(text string, index int) {
if index < offset || index >= offset+limit || !strings.Contains(text, kit.Select("", arg, 3)) {
return
}
ls := strings.SplitN(strings.ReplaceAll(strings.ReplaceAll(text, " ", " "), " ", " "), lex.SP, 8)
if _, e := time.Parse(kit.Split(ice.MOD_TIMES)[0], ls[0]); e != nil || len(ls) < 8 {
m.Push(mdb.TIME, "").Push(ice.LOG_TRACEID, "").Push(mdb.ID, "")
m.Push(nfs.PATH, "").Push(nfs.FILE, "").Push(nfs.LINE, "")
m.Push(ctx.SHIP, "").Push(LEVEL, "").Push(nfs.CONTENT, text)
return
}
m.Push(mdb.TIME, ls[0]+lex.SP+ls[1]).Push(ice.LOG_TRACEID, ls[3]).Push(mdb.ID, ls[4])
m.Push(nfs.PATH, ice.USR_ICEBERGS)
if i := strings.LastIndex(ls[7], lex.SP); strings.HasPrefix(ls[7][i+1:], ice.BASE) || strings.HasPrefix(ls[7][i+1:], ice.CORE) || strings.HasPrefix(ls[7][i+1:], ice.MISC) {
m.Push(nfs.FILE, strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[0]))
m.Push(nfs.LINE, strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[1]))
ls[7] = ls[7][:i]
} else if strings.HasPrefix(ls[7][i+1:], ice.USR_ICEBERGS) {
m.Push(nfs.FILE, strings.TrimPrefix(strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[0]), ice.USR_ICEBERGS))
m.Push(nfs.LINE, strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[1]))
ls[7] = ls[7][:i]
} else {
m.Push(nfs.FILE, "base/web/serve.go").Push(nfs.LINE, "62")
}
if ls[6] == ice.LOG_CMDS {
_ls := strings.SplitN(ls[5], lex.SP, 2)
if ls[6], ls[7] = _ls[0], _ls[1]; !unicode.IsDigit(rune(ls[7][0])) {
_ls := strings.SplitN(ls[7], lex.SP, 2)
ls[6], ls[7] = ls[6]+lex.SP+_ls[0], _ls[1]
}
}
switch ls[6] {
case "recv", "done", "send", "echo":
ls[7] += "\n" + kit.Formats(kit.UnMarshal(r.FindString(ls[7])))
}
m.Push(ctx.SHIP, ls[5]).Push(LEVEL, ls[6]).Push(nfs.CONTENT, ls[7])
})
case WATCH:
m.Cmd(nfs.CAT, ice.VAR_LOG+arg[0]+".log", func(text string, index int) {
if index < offset || index >= offset+limit || !strings.Contains(text, kit.Select("", arg, 3)) {
return
}
ls := strings.SplitN(strings.ReplaceAll(strings.ReplaceAll(text, " ", " "), " ", " "), lex.SP, 8)
m.Push(mdb.TIME, ls[0]+lex.SP+ls[1]).Push(ice.LOG_TRACEID, ls[3]).Push(mdb.ID, ls[4])
i := strings.LastIndex(ls[7], lex.SP)
m.Push(nfs.PATH, ice.USR_ICEBERGS)
m.Push(nfs.FILE, strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[0]))
m.Push(nfs.LINE, strings.TrimSpace(strings.Split(ls[7][i:], nfs.DF)[1]))
m.Push(ctx.SHIP, ls[5]).Push(LEVEL, ls[6]).Push(nfs.CONTENT, ls[7][:i])
})
}
m.StatusTimeCountStats(LEVEL)
}},
})
}

18
base/log/error.go Normal file
View File

@ -0,0 +1,18 @@
package log
import (
"path"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/nfs"
)
const ERROR = "error"
func init() {
Index.MergeCommands(ice.Commands{
ERROR: {Help: "错误", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(nfs.CAT, path.Join(ice.VAR_LOG, "error.log"))
}},
})
}

View File

@ -2,142 +2,152 @@ package log
import (
"bufio"
"fmt"
"path"
"strings"
"sync/atomic"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
log "shylinux.com/x/toolkits/logs"
"shylinux.com/x/toolkits/logs"
)
type Log struct {
m *ice.Message
p string
l string
s string
c bool
p, l, s string
}
type Frame struct{ p chan *Log }
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server {
return &Frame{}
func (f *Frame) Begin(m *ice.Message, arg ...string) {
}
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
f.p = make(chan *Log, ice.MOD_BUFS)
ice.Info.Log = func(msg *ice.Message, p, l, s string) {
f.p <- &Log{m: msg, p: p, l: l, s: s}
func (f *Frame) Start(m *ice.Message, arg ...string) {
if !ice.HasVar() {
return
}
mdb.Confm(m, FILE, nil, func(k string, v ice.Map) {
if mdb.Conf(m, k, kit.Keym(mdb.DISABLE)) == ice.TRUE {
return
}
if f, p, e := logs.CreateFile(kit.Format(v[nfs.PATH])); e == nil {
m.Logs(nfs.SAVE, nfs.FILE, p)
v[FILE] = bufio.NewWriter(f)
}
})
f.p = make(chan *Log, ice.MOD_BUFS)
ice.Info.Log = func(m *ice.Message, p, l, s string) {
f.p <- &Log{c: m.Option(ice.LOG_DEBUG) == ice.TRUE, p: p, l: l, s: s}
}
return f
}
func (f *Frame) Start(m *ice.Message, arg ...string) bool {
for {
select {
case l, ok := <-f.p:
if !ok {
break
return
}
file := kit.Select(BENCH, m.Conf(SHOW, kit.Keys(l.l, FILE)))
view := m.Confm(VIEW, m.Conf(SHOW, kit.Keys(l.l, VIEW)))
bio := m.Confv(FILE, kit.Keys(file, FILE)).(*bufio.Writer)
if bio == nil {
continue
}
bio.WriteString(l.p)
bio.WriteString(ice.SP)
if p, ok := view[PREFIX].(string); ok {
bio.WriteString(p)
}
bio.WriteString(l.l)
bio.WriteString(ice.SP)
bio.WriteString(l.s)
if p, ok := view[SUFFIX].(string); ok {
bio.WriteString(p)
}
bio.WriteString(ice.NL)
bio.Flush()
kit.For([]string{m.Conf(SHOW, kit.Keys(l.l, FILE)), BENCH}, func(file string) {
if file == "" {
return
}
conf := m.Confv(FILE, file)
bio := kit.Value(conf, FILE).(*bufio.Writer)
if bio == nil {
return
}
defer bio.Flush()
defer fmt.Fprintln(bio)
fmt.Fprint(bio, l.p, lex.SP)
view := mdb.Confm(m, VIEW, m.Conf(SHOW, kit.Keys(l.l, VIEW)))
kit.If(ice.Info.Colors || l.c, func() { bio.WriteString(kit.Format(view[PREFIX])) })
defer kit.If(ice.Info.Colors || l.c, func() { bio.WriteString(kit.Format(view[SUFFIX])) })
fmt.Fprint(bio, l.l, lex.SP, l.s)
})
}
}
return true
}
func (f *Frame) Close(m *ice.Message, arg ...string) bool {
ice.Info.Log = nil
close(f.p)
return true
}
func (f *Frame) Close(m *ice.Message, arg ...string) { ice.Info.Log = nil; close(f.p) }
const (
PREFIX = "prefix"
SUFFIX = "suffix"
PREFIX = "prefix"
SUFFIX = "suffix"
TRACEID = "traceid"
)
const (
GREEN = "green"
YELLOW = "yellow"
RED = "red"
)
const (
BENCH = "bench"
WATCH = "watch"
ERROR = "error"
TRACE = "trace"
)
const (
FILE = "file"
VIEW = "view"
SHOW = "show"
)
const (
BENCH_LOG = "bench.log"
DEBUG_LOG = "debug.log"
ERROR_LOG = "error.log"
WATCH_LOG = "watch.log"
)
const LOG = "log"
var Index = &ice.Context{Name: "log", Help: "日志模块", Configs: map[string]*ice.Config{
var Index = &ice.Context{Name: LOG, Help: "日志模块", Configs: ice.Configs{
FILE: {Name: FILE, Help: "日志文件", Value: kit.Dict(
BENCH, kit.Dict(kit.MDB_PATH, path.Join(ice.VAR_LOG, "bench.log"), kit.MDB_LIST, []string{}),
WATCH, kit.Dict(kit.MDB_PATH, path.Join(ice.VAR_LOG, "watch.log"), kit.MDB_LIST, []string{
ice.LOG_CREATE, ice.LOG_REMOVE,
ice.LOG_INSERT, ice.LOG_DELETE,
ice.LOG_MODIFY, ice.LOG_SELECT,
ice.LOG_EXPORT, ice.LOG_IMPORT,
}),
ERROR, kit.Dict(kit.MDB_PATH, path.Join(ice.VAR_LOG, "error.log"), kit.MDB_LIST, []string{
ice.LOG_WARN, ice.LOG_ERROR,
}),
TRACE, kit.Dict(kit.MDB_PATH, path.Join(ice.VAR_LOG, "trace.log"), kit.MDB_LIST, []string{
ice.LOG_DEBUG,
}),
BENCH, kit.Dict(nfs.PATH, path.Join(ice.VAR_LOG, BENCH_LOG), mdb.LIST, []string{}),
DEBUG, kit.Dict(nfs.PATH, path.Join(ice.VAR_LOG, DEBUG_LOG), mdb.LIST, []string{ice.LOG_DEBUG}),
ERROR, kit.Dict(nfs.PATH, path.Join(ice.VAR_LOG, ERROR_LOG), mdb.LIST, []string{ice.LOG_WARN, ice.LOG_ERROR}),
WATCH, kit.Dict(nfs.PATH, path.Join(ice.VAR_LOG, WATCH_LOG), mdb.LIST, []string{mdb.CREATE, mdb.REMOVE, mdb.INSERT, mdb.DELETE, mdb.MODIFY, mdb.EXPORT, mdb.IMPORT}),
)},
VIEW: {Name: VIEW, Help: "日志格式", Value: kit.Dict(
GREEN, kit.Dict(PREFIX, "\033[32m", SUFFIX, "\033[0m", kit.MDB_LIST, []string{
ice.LOG_START, ice.LOG_SERVE, ice.LOG_CMDS,
}),
YELLOW, kit.Dict(PREFIX, "\033[33m", SUFFIX, "\033[0m", kit.MDB_LIST, []string{
ice.LOG_AUTH, ice.LOG_COST,
}),
RED, kit.Dict(PREFIX, "\033[31m", SUFFIX, "\033[0m", kit.MDB_LIST, []string{
ice.LOG_CLOSE, ice.LOG_WARN,
}),
GREEN, kit.Dict(PREFIX, "\033[32m", SUFFIX, "\033[0m", mdb.LIST, []string{ice.CTX_START, ice.LOG_CMDS}),
YELLOW, kit.Dict(PREFIX, "\033[33m", SUFFIX, "\033[0m", mdb.LIST, []string{ice.LOG_AUTH, ice.LOG_COST}),
RED, kit.Dict(PREFIX, "\033[31m", SUFFIX, "\033[0m", mdb.LIST, []string{ice.CTX_CLOSE, ice.LOG_WARN}),
)},
SHOW: {Name: SHOW, Help: "日志分流", Value: kit.Dict()},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if log.LogDisable {
return // 禁用日志
}
m.Confm(VIEW, nil, func(key string, value map[string]interface{}) {
kit.Fetch(value[kit.MDB_LIST], func(index int, k string) {
m.Conf(SHOW, kit.Keys(k, VIEW), key)
})
}, Commands: ice.Commands{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
ice.Info.Load(m)
mdb.Confm(m, FILE, nil, func(key string, value ice.Map) {
kit.For(value[mdb.LIST], func(index int, k string) { m.Conf(SHOW, kit.Keys(k, FILE), key) })
})
m.Confm(FILE, nil, func(key string, value map[string]interface{}) {
kit.Fetch(value[kit.MDB_LIST], func(index int, k string) {
m.Conf(SHOW, kit.Keys(k, FILE), key)
})
// 日志文件
if f, p, e := kit.Create(kit.Format(value[kit.MDB_PATH])); m.Assert(e) {
m.Cap(ice.CTX_STREAM, path.Base(p))
value[FILE] = bufio.NewWriter(f)
m.Log_CREATE(kit.MDB_FILE, p)
}
mdb.Confm(m, VIEW, nil, func(key string, value ice.Map) {
kit.For(value[mdb.LIST], func(index int, k string) { m.Conf(SHOW, kit.Keys(k, VIEW), key) })
})
}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) {
ice.Info.Save(m)
}},
}}
func init() { ice.Index.Register(Index, &Frame{}) }
func init() { ice.Index.Register(Index, &Frame{}, TAIL) }
func init() {
ice.Info.Traceid = "short"
ice.Pulse.Option("work.id", "0")
ice.Pulse.Option("task.id", "0")
ice.Pulse.Option(ice.LOG_TRACEID, Traceid(ice.Pulse))
}
var _trace_count int64
func Traceid(m *ice.Message) (traceid string) {
ls := []string{}
kit.For(kit.Split(ice.Info.Traceid), func(key string) {
switch key {
case "short":
if len(ls) == 0 {
ls = append(ls, kit.Hashs(mdb.UNIQ)[:6])
}
case "long":
if len(ls) == 0 {
ls = append(ls, kit.Hashs(mdb.UNIQ))
}
case "node":
ls = append(ls, ice.Info.NodeName)
case "hide":
ls = ls[:0]
}
})
kit.If(len(ls) > 0, func() { ls = append(ls, kit.Format(atomic.AddInt64(&_trace_count, 1))) })
return strings.Join(ls, "-")
}

View File

@ -1 +0,0 @@
chapter "log"

40
base/log/tail.go Normal file
View File

@ -0,0 +1,40 @@
package log
import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func _tail_create(m *ice.Message, arg ...string) {
h := mdb.HashCreate(m, arg)
kit.For(kit.Split(m.Option(nfs.FILE)), func(file string) {
m.Options(cli.CMD_OUTPUT, nfs.Pipe(m, func(text string) { mdb.ZoneInsert(m, h, nfs.FILE, file, nfs.SIZE, len(text), mdb.TEXT, text) }), mdb.CACHE_CLEAR_ONEXIT, ice.TRUE)
m.Cmd(cli.DAEMON, TAIL, "-n", "0", "-f", file)
})
}
const TAIL = "tail"
func init() {
Index.MergeCommands(ice.Commands{
TAIL: {Name: "tail name id auto page", Help: "日志流", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m.Spawn(ice.OptionFields("name,file"))).Table(func(value ice.Maps) {
m.Cmd("", mdb.CREATE, kit.SimpleKV("name,file", value))
})
}},
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case mdb.NAME:
m.Push(arg[0], kit.Split(m.Option(FILE), nfs.PS))
case nfs.FILE:
m.Cmdy(nfs.DIR, kit.Select(nfs.PWD, arg, 1), nfs.PATH).RenameAppend(nfs.PATH, nfs.FILE).ProcessAgain()
}
}},
mdb.CREATE: {Hand: func(m *ice.Message, arg ...string) { _tail_create(m, arg...) }},
}, mdb.PageZoneAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,file,count", mdb.FIELDS, "time,id,file,size,text"))},
})
}

29
base/log/watch.go Normal file
View File

@ -0,0 +1,29 @@
package log
import (
"path"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
const WATCH = "watch"
func init() {
Index.MergeCommands(ice.Commands{
WATCH: {Help: "记录", Hand: func(m *ice.Message, arg ...string) {
stats := map[string]int{}
m.Cmd(nfs.CAT, path.Join(ice.VAR_LOG, "watch.log"), func(text string) {
ls := kit.Split(text)
m.Push(mdb.TIME, ls[0]+lex.SP+ls[1]).Push(mdb.ID, ls[2]).Push(nfs.SOURCE, kit.Slice(ls, -1)[0])
m.Push(ctx.SHIP, ls[3]).Push("operate", ls[4]).Push(nfs.CONTENT, kit.Join(kit.Slice(ls, 5, -1), lex.SP))
stats[ls[4]]++
})
m.StatusTimeCount(stats)
}},
})
}

View File

@ -1,32 +1,7 @@
package mdb
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
import ice "shylinux.com/x/icebergs"
const ENGINE = "engine"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
ENGINE: {Name: ENGINE, Help: "引擎", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_TYPE, kit.MDB_FIELD, "time,type,name,text",
)},
}, Commands: map[string]*ice.Command{
ENGINE: {Name: "engine type name text auto", Help: "引擎", Action: ice.MergeAction(map[string]*ice.Action{
CREATE: {Name: "create type name text", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, m.OptionSimple("type,name,text"))
}},
}, HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 1 {
m.Optionv(kit.Keycb(SELECT), func(fields []string, value map[string]interface{}) {
m.Cmdy(kit.Keys(value[kit.MDB_TEXT], value[kit.MDB_NAME]),
m.CommandKey(), arg[0], arg[1], kit.Select("", arg, 2), kit.Slice(arg, 3))
})
}
if HashSelect(m, arg...); len(arg) == 0 {
m.Sort(kit.MDB_TYPE)
}
}},
}})
}
func init() { Index.MergeCommands(ice.Commands{ENGINE: {Help: "引擎", Actions: RenderAction()}}) }

View File

@ -2,207 +2,421 @@ package mdb
import (
"encoding/json"
"os"
"io"
"net/http"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
"shylinux.com/x/toolkits/miss"
)
func _hash_fields(m *ice.Message) []string {
return kit.Split(kit.Select("time,hash,type,name,text", m.OptionFields()))
return kit.Split(kit.Select(HASH_FIELD, m.OptionFields()))
}
func _hash_inputs(m *ice.Message, prefix, chain string, field, value string) {
list := map[string]int{}
m.Debug("what %v %v", prefix, chain)
m.Richs(prefix, chain, kit.MDB_FOREACH, func(key string, val map[string]interface{}) {
if val = kit.GetMeta(val); kit.Format(val[kit.MDB_COUNT]) != "" {
list[kit.Format(val[field])] = kit.Int(val[kit.MDB_COUNT])
} else {
list[kit.Format(val[field])]++
}
defer func() {
delete(list, "")
kit.For(list, func(k string, i int) { m.Push(field, k).Push(COUNT, i) })
m.SortIntR(COUNT)
}()
defer RLock(m, prefix)()
Richs(m, prefix, chain, FOREACH, func(key string, value Map) {
value = kit.GetMeta(value)
list[kit.Format(value[field])] += kit.Int(kit.Select("1", value[COUNT]))
})
for k, i := range list {
m.Push(field, k)
m.Push(kit.MDB_COUNT, i)
}
m.SortIntR(kit.MDB_COUNT)
}
func _hash_insert(m *ice.Message, prefix, chain string, arg ...string) {
if m.Option(ice.MSG_DOMAIN) != "" {
m.Conf(prefix, kit.Keys(chain, kit.Keym(kit.MDB_SHORT)), m.Conf(prefix, kit.Keym(kit.MDB_SHORT)))
func _hash_insert(m *ice.Message, prefix, chain string, arg ...string) string {
m.Logs(INSERT, KEY, path.Join(prefix, chain), arg)
defer Lock(m, prefix)()
if expire := m.Conf(prefix, kit.Keys(chain, kit.Keym(EXPIRE))); expire != "" && arg[0] != HASH {
arg = kit.Simple(TIME, m.Time(expire), arg)
}
m.Log_INSERT(kit.MDB_KEY, path.Join(prefix, chain), arg[0], arg[1])
m.Echo(m.Rich(prefix, chain, kit.Data(arg)))
if arg[0] == HASH {
m.Echo(arg[1]).Conf(prefix, kit.Keys(chain, HASH, arg[1]), kit.Data(arg[2:]))
} else {
if target, ok := m.Optionv(TARGET).([]string); ok && len(target) == 0 {
m.Echo(Rich(m, prefix, chain, kit.Data(arg)))
} else {
m.Echo(Rich(m, prefix, chain, kit.Data(arg, TARGET, m.Optionv(TARGET))))
}
}
saveImportant(m, prefix, chain, kit.Simple(INSERT, prefix, chain, HASH, HASH, m.Result(), TIME, m.Time(), arg)...)
return m.Result()
}
func _hash_delete(m *ice.Message, prefix, chain, field, value string) {
if field != kit.MDB_HASH {
field, value = kit.MDB_HASH, kit.Select(kit.Hashs(value), m.Option(kit.MDB_HASH))
}
m.Richs(prefix, chain, value, func(key string, val map[string]interface{}) {
m.Log_DELETE(kit.MDB_KEY, path.Join(prefix, chain), field, value, kit.MDB_VALUE, kit.Format(val))
m.Conf(prefix, kit.Keys(chain, kit.MDB_HASH, key), "")
defer Lock(m, prefix)()
Richs(m, prefix, chain, value, func(key string, val Map) {
if target, ok := kit.GetMeta(val)[TARGET].(io.Closer); ok {
target.Close()
}
m.Logs(DELETE, KEY, path.Join(prefix, chain), field, value, VALUE, kit.Format(val))
m.Conf(prefix, kit.Keys(chain, HASH, key), "")
saveImportant(m, prefix, chain, kit.Simple(DELETE, prefix, chain, HASH, HASH, key)...)
})
}
func _hash_modify(m *ice.Message, prefix, chain string, field, value string, arg ...string) {
m.Richs(prefix, chain, value, func(key string, val map[string]interface{}) {
val = kit.GetMeta(val)
m.Log_MODIFY(kit.MDB_KEY, path.Join(prefix, chain), field, value, arg)
for i := 0; i < len(arg); i += 2 {
if arg[i] == field {
continue
}
kit.Value(val, arg[i], kit.Select("", arg, i+1))
}
m.Logs(MODIFY, KEY, path.Join(prefix, chain), field, value, arg)
defer Lock(m, prefix)()
Richs(m, prefix, chain, value, func(key string, val Map) {
_mdb_modify(m, val, field, arg...)
saveImportant(m, prefix, chain, kit.Simple(MODIFY, prefix, chain, HASH, HASH, key, arg)...)
})
}
func _hash_select(m *ice.Message, prefix, chain, field, value string) {
if field == kit.MDB_HASH && value == RANDOM {
value = kit.MDB_RANDOMS
}
kit.If(field == HASH && value == RANDOM, func() { value = RANDOMS })
defer m.SortStrR(TIME)
fields := _hash_fields(m)
m.Richs(prefix, chain, value, func(key string, val map[string]interface{}) {
switch val = kit.GetMeta(val); cb := m.Optionv(kit.Keycb(SELECT)).(type) {
case func(fields []string, value map[string]interface{}):
cb(fields, val)
default:
if m.OptionFields() == DETAIL {
m.Push(DETAIL, val)
} else {
m.Push(key, val, fields)
}
}
})
if m.Option(FIELDS) != DETAIL {
m.SortTimeR(kit.MDB_TIME)
}
}
func _hash_export(m *ice.Message, prefix, chain, file string) {
f, p, e := kit.Create(kit.Keys(file, JSON))
m.Assert(e)
defer f.Close()
en := json.NewEncoder(f)
en.SetIndent("", " ")
m.Assert(en.Encode(m.Confv(prefix, kit.Keys(chain, HASH))))
m.Log_EXPORT(kit.MDB_KEY, path.Join(prefix, chain), kit.MDB_FILE, p)
m.Conf(prefix, kit.Keys(chain, kit.MDB_HASH), "")
m.Echo(p)
}
func _hash_import(m *ice.Message, prefix, chain, file string) {
f, e := os.Open(kit.Keys(file, JSON))
m.Assert(e)
defer f.Close()
list := map[string]interface{}{}
m.Assert(json.NewDecoder(f).Decode(&list))
count := 0
if m.Conf(prefix, kit.Keys(chain, kit.MDB_META, kit.MDB_SHORT)) == "" {
for k, data := range list {
m.Conf(prefix, kit.Keys(chain, kit.MDB_HASH, k), data)
count++
}
defer RLock(m, prefix)()
if strings.Contains(value, ",") {
kit.For(kit.Split(value), func(value string) {
Richs(m, prefix, chain, value, func(key string, value Map) { _mdb_select(m, m.OptionCB(""), key, value, fields, nil) })
})
} else {
for _, data := range list {
m.Rich(prefix, chain, data)
count++
}
Richs(m, prefix, chain, value, func(key string, value Map) { _mdb_select(m, m.OptionCB(""), key, value, fields, nil) })
}
m.Log_IMPORT(kit.MDB_KEY, path.Join(prefix, chain), kit.MDB_COUNT, count)
m.Echo("%d", count)
}
func _hash_select_field(m *ice.Message, prefix, chain string, key string, field string) (value string) {
defer RLock(m, prefix)()
Richs(m, prefix, chain, key, func(key string, val Map) { value = kit.Select(kit.Format(val[field]), key, field == HASH) })
return
}
func _hash_prunes(m *ice.Message, prefix, chain string, arg ...string) {
fields := _hash_fields(m)
m.Richs(prefix, chain, kit.MDB_FOREACH, func(key string, val map[string]interface{}) {
switch val = kit.GetMeta(val); cb := m.Optionv(kit.Keycb(PRUNES)).(type) {
case func(string, map[string]interface{}) bool:
if !cb(key, val) {
return
}
kit.If(kit.IndexOf(fields, HASH) == -1, func() { fields = append(fields, HASH) })
defer RLock(m, prefix)()
Richs(m, prefix, chain, FOREACH, func(key string, value Map) {
switch value = kit.GetMeta(value); cb := m.OptionCB("").(type) {
case func(string, Map) bool:
kit.If(cb(key, value), func() { m.Push(key, value, fields) })
default:
for i := 0; i < len(arg)-1; i += 2 {
if val[arg[i]] != arg[i+1] && kit.Value(val, arg[i]) != arg[i+1] {
return
}
}
kit.For(arg, func(k, v string) {
kit.If(value[k] == v || kit.Value(value, k) == v, func() { m.Push(key, value, fields) })
})
}
m.Push(key, val, fields)
})
m.Table(func(index int, value map[string]string, head []string) {
_hash_delete(m, prefix, chain, kit.MDB_HASH, value[kit.MDB_HASH])
})
}
func _hash_export(m *ice.Message, prefix, chain, file string) {
if !ice.HasUsr() {
return
}
defer Lock(m, prefix)()
if len(Confm(m, prefix, kit.Keys(chain, HASH))) == 0 {
return
}
f, p, e := miss.CreateFile(kit.Keys(file, JSON))
m.Assert(e)
defer f.Close()
defer m.Echo(p)
m.Logs(EXPORT, KEY, path.Join(prefix, chain), FILE, p)
en := json.NewEncoder(f)
if en.SetIndent("", " "); !m.WarnNotValid(en.Encode(m.Confv(prefix, kit.Keys(chain, HASH))), EXPORT, prefix) {
// m.Conf(prefix, kit.Keys(chain, HASH), "")
}
}
func _hash_import(m *ice.Message, prefix, chain, file string) {
if !ice.HasUsr() {
return
}
defer Lock(m, prefix)()
f, e := miss.OpenFile(kit.Keys(file, JSON))
if e != nil && !ice.Info.Important {
return
} else if m.WarnNotFound(e) {
return
}
defer f.Close()
data := Map{}
m.Warn(json.NewDecoder(f).Decode(&data))
m.Logs(IMPORT, KEY, path.Join(prefix, chain), FILE, kit.Keys(file, JSON), COUNT, len(data))
kit.If(m.Confv(prefix, kit.Keys(chain, HASH)) == nil, func() { m.Confv(prefix, kit.Keys(chain, HASH), ice.Map{}) })
kit.For(data, func(k string, v Any) { m.Confv(prefix, kit.Keys(chain, HASH, k), v) })
m.Echo("%d", len(data))
}
const (
MONTH = "720h"
DAYS = "72h"
HOUR = "1h"
CACHE_CLEAR_ONEXIT = "cache.clear.onexit"
)
const (
HASH_FIELD = "time,hash,type,name,text"
)
const HASH = "hash"
func HashAction(fields ...string) map[string]*ice.Action {
_key := func(m *ice.Message) string {
if m.Config(kit.MDB_HASH) == "uniq" {
return kit.MDB_HASH
}
return kit.Select(kit.MDB_HASH, m.Config(kit.MDB_SHORT))
func HashAction(arg ...Any) ice.Actions {
return ice.Actions{
ice.CTX_INIT: AutoConfig(append(kit.List(FIELD, HASH_FIELD), arg...)...),
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) { HashSelectClose(m) }},
INPUTS: {Hand: func(m *ice.Message, arg ...string) { HashInputs(m, arg) }},
CREATE: {Hand: func(m *ice.Message, arg ...string) { HashCreate(m, arg) }},
REMOVE: {Hand: func(m *ice.Message, arg ...string) { HashRemove(m, arg) }},
MODIFY: {Hand: func(m *ice.Message, arg ...string) { HashModify(m, arg) }},
SELECT: {Hand: func(m *ice.Message, arg ...string) { HashSelect(m, arg...) }},
PRUNES: {Name: "prunes before@date", Hand: func(m *ice.Message, arg ...string) { HashPrunes(m, nil) }},
EXPORT: {Hand: func(m *ice.Message, arg ...string) { HashExport(m, arg) }},
IMPORT: {Hand: func(m *ice.Message, arg ...string) { HashImport(m, arg) }},
}
return ice.SelectAction(map[string]*ice.Action{
INPUTS: {Name: "inputs", Help: "补全", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INPUTS, m.PrefixKey(), "", HASH, arg)
}},
CREATE: {Name: "create type name text", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, arg)
}},
REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(DELETE, m.PrefixKey(), "", HASH, m.OptionSimple(_key(m)), arg)
}},
MODIFY: {Name: "modify", Help: "编辑", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(MODIFY, m.PrefixKey(), "", HASH, m.OptionSimple(_key(m)), arg)
}},
EXPORT: {Name: "export", Help: "导出", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.META_FIELD))
m.Cmdy(EXPORT, m.PrefixKey(), "", HASH, arg)
}},
IMPORT: {Name: "import", Help: "导入", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(IMPORT, m.PrefixKey(), "", HASH, arg)
}},
PRUNES: &ice.Action{Name: "prunes before@date", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
HashPrunes(m, nil)
}},
}, fields...)
}
func HashActionStatus(fields ...string) map[string]*ice.Action {
list := HashAction(fields...)
list[PRUNES] = &ice.Action{Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(PRUNES, m.PrefixKey(), "", HASH, kit.MDB_STATUS, "error")
m.Cmdy(PRUNES, m.PrefixKey(), "", HASH, kit.MDB_STATUS, "close")
}}
return list
func StatusHashAction(arg ...Any) ice.Actions {
return ice.MergeActions(ice.Actions{
PRUNES: &ice.Action{Name: "prunes status", Hand: func(m *ice.Message, arg ...string) {
args := []string{}
kit.For(kit.Split(m.OptionDefault(STATUS, "error,close,stop,end")), func(s string) { args = append(args, STATUS, s) })
m.Cmdy(PRUNES, m.PrefixKey(), m.Option(SUBKEY), HASH, args, ice.OptionFields(HashField(m)))
}},
}, HashAction(arg...))
}
func ClearOnExitHashAction() ice.Actions {
return ice.Actions{ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) { Conf(m, m.PrefixKey(), HASH, "") }}}
}
func ExportHashAction(arg ...Any) ice.Actions {
return ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { Config(m, IMPORTANT, ice.TRUE); HashImport(m, arg) }},
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) { HashExport(m, arg) }},
}, HashAction(arg...))
}
const (
DEV_REQUEST = "devRequest"
DEV_CHOOSE = "devChoose"
DEV_RESPONSE = "devResponse"
DEV_CONFIRM = "devConfirm"
DEV_CREATE = "devCreate"
)
func DevDataAction(fields ...string) ice.Actions {
const (
DAEMON = "daemon"
ORIGIN = "origin"
BACK = "back"
)
return ice.Actions{
DEV_REQUEST: {Name: "request origin*", Help: "请求", Icon: "bi bi-cloud-download", Hand: func(m *ice.Message, arg ...string) {
back := m.Options(ice.MSG_USERWEB, m.Option(ice.MSG_USERHOST)).MergePod("")
m.ProcessOpen(m.Options(ice.MSG_USERWEB, m.Option(ORIGIN), ice.MSG_USERPOD, "").MergePodCmd("", m.PrefixKey(), ACTION, DEV_CHOOSE, BACK, back, DAEMON, m.Option(ice.MSG_DAEMON)))
}},
DEV_CHOOSE: {Hand: func(m *ice.Message, arg ...string) {
HashSelect(m.Options(ice.MSG_FIELDS, kit.Join(fields))).PushAction(DEV_RESPONSE).Options(ice.MSG_ACTION, "")
}},
DEV_RESPONSE: {Help: "选择", Hand: func(m *ice.Message, arg ...string) {
if !m.WarnNotAllow(m.Option(ice.MSG_METHOD) != http.MethodPost) {
m.ProcessReplace(m.ParseLink(m.Option(BACK)).MergePodCmd("", m.PrefixKey(), ACTION, DEV_CONFIRM, m.OptionSimple(DAEMON), m.OptionSimple(fields...)))
}
}},
DEV_CONFIRM: {Hand: func(m *ice.Message, arg ...string) {
m.EchoInfoButton(kit.JoinWord(m.PrefixKey(), m.Cmdx("nfs.cat", "src/template/mdb.hash/savefrom.html"), m.Option(kit.Split(fields[0])[0])), DEV_CREATE)
}},
DEV_CREATE: {Help: "创建", Hand: func(m *ice.Message, arg ...string) {
if !m.WarnNotAllow(m.Option(ice.MSG_METHOD) != http.MethodPost) {
defer kit.If(m.Option(DAEMON), func(p string) { m.Cmd("space", p, "refresh") })
HashCreate(m.ProcessClose(), m.OptionSimple(fields...))
}
}},
}
}
func HashKey(m *ice.Message) string {
if m.Option(HASH) != "" {
return HASH
}
return HashShort(m)
}
func HashShort(m *ice.Message) string {
if m.Option(SHORT) != "" {
return m.Option(SHORT)
}
short := ""
if m.Option(SUBKEY) != "" {
if short = Conf(m, m.PrefixKey(), kit.Keys(m.Option(SUBKEY), META, SHORT)); short == "" {
short = Config(m, SHORTS)
}
} else {
short = Config(m, SHORT)
}
return kit.Select(HASH, short, short != UNIQ)
}
func HashField(m *ice.Message) string {
if m.Option(FIELD) != "" {
return m.Option(FIELD)
}
field := ""
if m.Option(SUBKEY) != "" {
if field = Conf(m, m.PrefixKey(), kit.Keys(m.Option(SUBKEY), META, FIELDS)); field == "" {
field = Config(m, FIELDS)
}
} else {
field = Config(m, FIELD)
}
return kit.Select(HASH_FIELD, field)
}
func HashInputs(m *ice.Message, arg ...Any) *ice.Message {
return m.Cmdy(INPUTS, m.PrefixKey(), m.Option(SUBKEY), HASH, arg)
}
func HashCreate(m *ice.Message, arg ...Any) string {
kit.If(len(arg) == 0 || len(kit.Simple(arg...)) == 0, func() {
arg = append(arg, m.OptionSimple(kit.Filters(kit.Split(HashField(m)), TIME, HASH)...))
})
kit.If(m.Option(SUBKEY) == "", func() { kit.If(Config(m, SHORTS), func(p string) { arg = append([]ice.Any{SHORT, p}, arg) }) })
return m.Echo(m.Cmdx(append(kit.List(INSERT, m.PrefixKey(), m.Option(SUBKEY), HASH, logs.FileLineMeta(-1)), arg...)...)).Result()
}
func HashRemove(m *ice.Message, arg ...Any) *ice.Message {
if args := kit.Simple(arg...); len(args) == 0 {
arg = append(arg, m.OptionSimple(HashKey(m)))
} else if len(args) == 1 {
arg = kit.List(HashKey(m), args[0])
}
return m.Cmdy(DELETE, m.PrefixKey(), m.Option(SUBKEY), HASH, arg)
}
func HashModify(m *ice.Message, arg ...Any) *ice.Message {
if args := kit.Simple(arg...); args[0] != HASH && args[0] != HashShort(m) {
arg = append(kit.List(m.OptionSimple(HashKey(m))), arg...)
}
return m.Cmd(MODIFY, m.PrefixKey(), m.Option(SUBKEY), HASH, arg)
}
func HashSelect(m *ice.Message, arg ...string) *ice.Message {
m.Fields(len(arg), m.Config(kit.MDB_FIELD))
m.Cmdy(SELECT, m.PrefixKey(), "", HASH, m.Config(kit.MDB_SHORT), arg)
m.PushAction(REMOVE)
m.StatusTimeCount()
if len(arg) > 0 && (arg[0] == FOREACH || strings.Contains(arg[0], ",")) {
m.Fields(0, HashField(m))
} else {
m.Fields(len(kit.Slice(arg, 0, 1)), HashField(m))
}
m.Cmdy(SELECT, m.PrefixKey(), m.Option(SUBKEY), HASH, HashShort(m), arg, logs.FileLineMeta(-1))
kit.If(kit.Select(Config(m, SHORT), Config(m, SORT)), func(sort string) { kit.If(sort != UNIQ, func() { m.Sort(sort) }) })
if m.PushAction(Config(m, ACTION), REMOVE); !m.FieldsIsDetail() {
m.Options(ice.TABLE_CHECKBOX, Config(m, html.CHECKBOX))
return m.Action(CREATE)
}
return sortByField(m, HashField(m), arg...)
}
func HashPrunes(m *ice.Message, cb func(Map) bool) *ice.Message {
expire := kit.Select(m.Time("-"+kit.Select(DAYS, Config(m, EXPIRE))), m.Option("before"))
m.OptionCB(PRUNES, func(key string, value Map) bool { return kit.Format(value[TIME]) < expire && (cb == nil || cb(value)) })
return m.Cmdy(PRUNES, m.PrefixKey(), "", HASH, ice.OptionFields(HashField(m)))
}
func HashExport(m *ice.Message, arg ...Any) *ice.Message {
return m.Cmdy(EXPORT, m.PrefixKey(), "", HASH, arg)
}
func HashImport(m *ice.Message, arg ...Any) *ice.Message {
return m.Cmdy(IMPORT, m.PrefixKey(), "", HASH, arg)
}
func HashSelects(m *ice.Message, arg ...string) *ice.Message {
m.OptionFields(HashField(m))
return HashSelect(m, arg...)
}
func HashSelectValue(m *ice.Message, cb Any) *ice.Message {
m.OptionFields(Config(m, FIELD))
defer RLock(m, m.PrefixKey())()
Richs(m, m.PrefixKey(), nil, FOREACH, func(key string, value Map) { _mdb_select(m, cb, key, value, nil, nil) })
return m
}
func HashPrunes(m *ice.Message, cb func(map[string]string) bool) *ice.Message {
_key := func(m *ice.Message) string {
if m.Config(kit.MDB_HASH) == "uniq" {
return kit.MDB_HASH
func HashSelectUpdate(m *ice.Message, key string, cb Any) *ice.Message {
defer Lock(m, m.PrefixKey())()
Richs(m, m.PrefixKey(), nil, kit.Select(FOREACH, key), func(key string, value Map) { _mdb_select(m, cb, key, value, nil, nil) })
return m
}
func HashSelectDetail(m *ice.Message, key string, cb Any) (has bool) {
defer RLock(m, m.PrefixKey())()
Richs(m, m.PrefixKey(), nil, key, func(key string, value Map) { _mdb_select(m, cb, key, value, nil, nil); has = true })
return
}
func HashSelectDetails(m *ice.Message, key string, cb func(Map) bool) Map {
val := kit.Dict()
HashSelectDetail(m, key, func(value Map) { kit.If(cb(value), func() { kit.For(value, func(k string, v Any) { val[k] = v }) }) })
return val
}
func HashSelectField(m *ice.Message, key string, field string) (value string) {
HashSelectDetail(m, key, func(key string, val Map) { value = kit.Select(kit.Format(kit.Value(val, field)), key, field == HASH) })
return
}
func HashSelectTarget(m *ice.Message, key string, create Any) (target Any) {
HashSelectUpdate(m, key, func(value Map) {
target = value[TARGET]
if _target, ok := target.([]string); ok && len(_target) == 0 {
target = nil
}
return kit.Select(kit.MDB_HASH, m.Config(kit.MDB_SHORT))
}
before := kit.Time(kit.Select(m.Time("-72h"), m.Option(kit.MDB_BEFORE)))
m.Cmd(m.CommandKey()).Table(func(index int, value map[string]string, head []string) {
if kit.Time(value[kit.MDB_TIME]) > before {
if _target, ok := target.(List); ok && len(_target) == 0 {
target = nil
}
if _target, ok := target.(Map); ok && len(_target) == 0 {
target = nil
}
if target != nil || create == nil {
return
}
if cb != nil && cb(value) {
return
switch create := create.(type) {
case func(Maps) Any:
target = create(kit.ToMaps(value))
case func(Map) Any:
target = create(value)
case func() Any:
target = create()
default:
m.ErrorNotImplement(create)
}
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(DELETE, m.PrefixKey(), "", HASH, _key(m), value[_key(m)])
value[TARGET] = target
})
return
}
func HashSelectClose(m *ice.Message) *ice.Message {
HashSelectValue(m, func(value Map) {
if c, ok := value[TARGET].(io.Closer); ok {
m.WarnNotValid(c.Close())
}
delete(value, TARGET)
})
return m
}
func HashPrunesValue(m *ice.Message, field, value string) {
m.Cmdy(PRUNES, m.PrefixKey(), "", HASH, field, value, ice.OptionFields(HashField(m)))
}
func HashCreateDeferRemove(m *ice.Message, arg ...Any) func() {
h := HashCreate(m, arg...)
return func() { HashRemove(m.SetResult(), HASH, h) }
}
func HashModifyDeferRemove(m *ice.Message, arg ...Any) func() {
HashModify(m, arg...)
return func() { HashRemove(m, arg...) }
}
func Richs(m *ice.Message, prefix string, chain Any, raw Any, cb Any) (res Map) {
cache := Confm(m, prefix, chain)
if cache == nil {
return nil
}
if value := kit.Format(raw); strings.Contains(value, ",") {
kit.For(kit.Split(value), func(value string) {
res = miss.Richs(path.Join(prefix, kit.Keys(chain)), cache, value, cb)
})
return
}
return miss.Richs(path.Join(prefix, kit.Keys(chain)), cache, raw, cb)
}
func Rich(m *ice.Message, prefix string, chain Any, data Any) string {
cache := Confm(m, prefix, chain)
kit.If(cache == nil, func() { cache = kit.Data(); m.Confv(prefix, chain, cache) })
return miss.Rich(path.Join(prefix, kit.Keys(chain)), cache, data)
}
func sortByField(m *ice.Message, fields string, arg ...string) *ice.Message {
return m.Table(func(value ice.Maps) {
m.SetAppend().FieldsSetDetail()
kit.For(kit.Split(fields), func(key string) {
key = strings.TrimSuffix(key, "*")
if key == HASH {
m.Push(key, kit.Select(value[key], arg, 0))
} else {
m.Push(key, value[key])
}
delete(value, key)
})
kit.For(kit.SortedKey(value), func(k string) { m.Push(k, value[k]) })
})
}

View File

@ -4,176 +4,230 @@ import (
"encoding/csv"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/miss"
)
func _list_fields(m *ice.Message) []string {
return kit.Split(kit.Select("time,id,type,name,text", m.OptionFields()))
return kit.Split(kit.Select(LIST_FIELD, m.OptionFields()))
}
func _list_inputs(m *ice.Message, prefix, chain string, field, value string) {
list := map[string]int{}
m.Grows(prefix, chain, "", "", func(index int, val map[string]interface{}) {
if val = kit.GetMeta(val); kit.Format(val[kit.MDB_COUNT]) != "" {
list[kit.Format(val[field])] = kit.Int(val[kit.MDB_COUNT])
} else {
list[kit.Format(val[field])]++
}
defer func() {
delete(list, "")
kit.For(list, func(k string, i int) { m.Push(field, k).Push(COUNT, i) })
m.SortIntR(COUNT)
}()
defer RLock(m, prefix)()
Grows(m, prefix, chain, "", "", func(value ice.Map) {
value = kit.GetMeta(value)
list[kit.Format(value[field])] += kit.Int(kit.Select("1", value[COUNT]))
})
for k, i := range list {
m.Push(field, k)
m.Push(kit.MDB_COUNT, i)
}
m.SortIntR(kit.MDB_COUNT)
}
func _list_insert(m *ice.Message, prefix, chain string, arg ...string) {
m.Log_INSERT(kit.MDB_KEY, path.Join(prefix, chain), arg[0], arg[1])
m.Echo("%d", m.Grow(prefix, chain, kit.Dict(arg)))
}
func _list_delete(m *ice.Message, prefix, chain, field, value string) {
m.Logs(INSERT, KEY, path.Join(prefix, chain), arg)
defer Lock(m, prefix)()
m.Echo("%d", Grow(m, prefix, chain, kit.Dict(arg, TARGET, m.Optionv(TARGET))))
saveImportant(m, prefix, chain, kit.Simple(INSERT, prefix, chain, LIST, TIME, m.Time(), arg)...)
}
func _list_modify(m *ice.Message, prefix, chain string, field, value string, arg ...string) {
m.Grows(prefix, chain, field, value, func(index int, val map[string]interface{}) {
val = kit.GetMeta(val)
m.Log_MODIFY(kit.MDB_KEY, path.Join(prefix, chain), field, value, arg)
for i := 0; i < len(arg); i += 2 {
if arg[i] == field {
continue
}
kit.Value(val, arg[i], kit.Select("", arg, i+1))
}
})
m.Logs(MODIFY, KEY, path.Join(prefix, chain), field, value, arg)
defer Lock(m, prefix)()
Grows(m, prefix, chain, field, value, func(index int, val ice.Map) { _mdb_modify(m, val, field, arg...) })
saveImportant(m, prefix, chain, kit.Simple(MODIFY, prefix, chain, LIST, field, value, arg)...)
}
func _list_select(m *ice.Message, prefix, chain, field, value string) {
if value == "" {
field = ""
}
defer m.SortIntR(ID)
fields := _list_fields(m)
m.Grows(prefix, chain, kit.Select(m.Option(ice.CACHE_FIELD), field), kit.Select(m.Option(ice.CACHE_VALUE), value), func(index int, val map[string]interface{}) {
switch val = kit.GetMeta(val); cb := m.Optionv(kit.Keycb(SELECT)).(type) {
case func(fields []string, value map[string]interface{}):
cb(fields, val)
default:
if m.OptionFields() == DETAIL {
m.Push(DETAIL, val)
} else {
m.Push("", val, fields)
}
}
defer RLock(m, prefix)()
Grows(m, prefix, chain, kit.Select(m.Option(CACHE_FIELD), field), kit.Select(m.Option(CACHE_VALUE), value), func(value ice.Map) {
_mdb_select(m, m.OptionCB(""), "", value, fields, nil)
})
}
func _list_export(m *ice.Message, prefix, chain, file string) {
f, p, e := kit.Create(kit.Keys(file, CSV))
defer Lock(m, prefix)()
p := kit.Keys(file, CSV)
count := kit.Int(Conf(m, prefix, kit.Keys(chain, META, COUNT)))
if count == 0 {
if s, e := os.Stat(p); e == nil && !s.IsDir() {
os.Remove(p)
}
return
}
f, p, e := miss.CreateFile(p)
m.Assert(e)
defer f.Close()
defer m.Echo(p)
m.Logs(EXPORT, KEY, path.Join(prefix, chain), FILE, p, COUNT, count)
w := csv.NewWriter(f)
defer w.Flush()
count := 0
head := kit.Split(m.OptionFields())
m.Grows(prefix, chain, "", "", func(index int, val map[string]interface{}) {
if val = kit.GetMeta(val); index == 0 {
if len(head) == 0 || head[0] == "detail" { // 默认表头
for k := range val {
head = append(head, k)
}
kit.Sort(head)
}
w.Write(head) // 输出表头
head := kit.Split(ListField(m))
Grows(m, prefix, chain, "", "", func(index int, value ice.Map) {
if value = kit.GetMeta(value); index == 0 {
kit.If(len(head) == 0 || head[0] == ice.FIELDS_DETAIL, func() { head = kit.SortedKey(value) })
w.Write(head)
}
data := []string{}
for _, k := range head {
data = append(data, kit.Format(val[k]))
}
w.Write(data) // 输出数据
count++
w.Write(kit.Simple(head, func(k string) string { return kit.Format(value[k]) }))
})
m.Log_EXPORT(kit.MDB_KEY, path.Join(prefix, chain), kit.MDB_FILE, p, kit.MDB_COUNT, count)
m.Conf(prefix, kit.Keys(chain, kit.Keym(kit.MDB_COUNT)), 0)
m.Conf(prefix, kit.Keys(chain, kit.MDB_LIST), "")
m.Echo(p)
m.Conf(prefix, kit.Keys(chain, kit.Keym(COUNT)), 0)
m.Conf(prefix, kit.Keys(chain, LIST), "")
}
func _list_import(m *ice.Message, prefix, chain, file string) {
f, e := os.Open(kit.Keys(file, CSV))
m.Assert(e)
defer Lock(m, prefix)()
f, e := miss.OpenFile(kit.Keys(file, CSV))
if e != nil && !ice.Info.Important {
return
} else if m.WarnNotFound(e) {
return
}
defer f.Close()
r := csv.NewReader(f)
count := 0
head, _ := r.Read()
count := 0
for {
line, e := r.Read()
if e != nil {
break
}
data := kit.Dict()
for i, k := range head {
if k == kit.MDB_EXTRA {
if k == EXTRA {
kit.Value(data, k, kit.UnMarshal(line[i]))
} else {
kit.Value(data, k, line[i])
}
}
m.Grow(prefix, chain, data)
Grow(m, prefix, chain, data)
count++
}
m.Log_IMPORT(kit.MDB_KEY, kit.Keys(prefix, chain), kit.MDB_COUNT, count)
m.Logs(IMPORT, KEY, kit.Keys(prefix, chain), FILE, kit.Keys(file, CSV), COUNT, count)
m.Echo("%d", count)
}
func _list_prunes(m *ice.Message, prefix, chain string, arg ...string) {
}
const (
LIST_FIELD = "time,id,type,name,text"
)
const LIST = "list"
func ListAction(fields ...string) map[string]*ice.Action {
return ice.SelectAction(map[string]*ice.Action{
INPUTS: {Name: "inputs", Help: "补全", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INPUTS, m.PrefixKey(), "", LIST, arg)
}},
INSERT: {Name: "insert type=go name=hi text=hello", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", LIST, arg)
}},
DELETE: {Name: "delete", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(DELETE, m.PrefixKey(), "", LIST, m.OptionSimple(kit.MDB_ID), arg)
}},
MODIFY: {Name: "modify", Help: "编辑", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(MODIFY, m.PrefixKey(), "", LIST, m.OptionSimple(kit.MDB_ID), arg)
}},
EXPORT: {Name: "export", Help: "导出", Hand: func(m *ice.Message, arg ...string) {
m.Option(ice.CACHE_LIMIT, "-1")
m.OptionFields(m.Config(kit.MDB_FIELD))
m.Cmdy(EXPORT, m.PrefixKey(), "", LIST)
m.Conf(m.PrefixKey(), kit.MDB_LIST, "")
m.Config(kit.MDB_COUNT, 0)
}},
IMPORT: {Name: "import", Help: "导入", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(IMPORT, m.PrefixKey(), "", LIST, arg)
}},
PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(PRUNES, m.PrefixKey(), "", LIST, arg)
}},
PREV: {Name: "prev", Help: "上一页", Hand: func(m *ice.Message, arg ...string) {
PrevPage(m, m.Config(kit.MDB_COUNT), kit.Slice(arg, 1)...)
}},
NEXT: {Name: "next", Help: "下一页", Hand: func(m *ice.Message, arg ...string) {
NextPage(m, m.Config(kit.MDB_COUNT), kit.Slice(arg, 1)...)
}},
}, fields...)
}
func ListSelect(m *ice.Message, arg ...string) *ice.Message {
m.OptionPage(kit.Slice(arg, 1)...)
m.Fields(len(kit.Slice(arg, 0, 1)), m.Config(kit.MDB_FIELD))
m.Cmdy(SELECT, m.PrefixKey(), "", LIST, kit.MDB_ID, arg)
if !m.FieldsIsDetail() {
m.StatusTimeCountTotal(m.Config(kit.MDB_COUNT))
func ListAction(arg ...ice.Any) ice.Actions {
return ice.Actions{ice.CTX_INIT: AutoConfig(append(kit.List(FIELD, LIST_FIELD), arg...)...),
INPUTS: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(INPUTS, m.PrefixKey(), "", LIST, arg) }},
INSERT: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(INSERT, m.PrefixKey(), "", LIST, arg) }},
DELETE: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(DELETE, m.PrefixKey(), "", LIST, m.OptionSimple(ID), arg) }},
MODIFY: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(MODIFY, m.PrefixKey(), "", LIST, m.OptionSimple(ID), arg) }},
SELECT: {Name: "select id auto insert", Hand: func(m *ice.Message, arg ...string) { ListSelect(m, arg...) }},
PRUNES: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(PRUNES, m.PrefixKey(), "", LIST, arg) }},
EXPORT: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(EXPORT, m.PrefixKey(), "", LIST, arg) }},
IMPORT: {Hand: func(m *ice.Message, arg ...string) { m.Cmdy(IMPORT, m.PrefixKey(), "", LIST, arg) }},
}
return m
}
func PageListAction(arg ...ice.Any) ice.Actions {
return ice.MergeActions(ice.Actions{
SELECT: {Name: "select id auto insert page", Hand: func(m *ice.Message, arg ...string) { PageListSelect(m, arg...) }},
NEXT: {Hand: func(m *ice.Message, arg ...string) {
NextPage(m, kit.Select(Config(m, COUNT), arg, 0), kit.Slice(arg, 1)...)
}},
PREV: {Hand: func(m *ice.Message, arg ...string) {
PrevPageLimit(m, kit.Select(Config(m, COUNT), arg, 0), kit.Slice(arg, 1)...)
}},
}, ListAction(arg...))
}
func ListField(m *ice.Message) string { return kit.Select(LIST_FIELD, Config(m, FIELD)) }
func ListSelect(m *ice.Message, arg ...string) *ice.Message {
m.Fields(len(kit.Slice(arg, 0, 1)), ListField(m))
if m.Cmdy(SELECT, m.PrefixKey(), "", LIST, ID, arg); !m.FieldsIsDetail() {
return m.StatusTimeCountTotal(Config(m, COUNT))
}
return m.StatusTime()
}
func PageListSelect(m *ice.Message, arg ...string) *ice.Message {
OptionPage(m, kit.Slice(arg, 1)...)
return ListSelect(m, arg...)
}
func NextPageLimit(m *ice.Message, total string, arg ...string) {
if kit.Int(kit.Select("0", arg, 1)) > 0 {
NextPage(m, total, arg...)
} else {
m.ProcessHold("已经是最后一页啦!")
}
}
func NextPage(m *ice.Message, total string, arg ...string) {
limit, offend := kit.Select("10", arg, 0), kit.Select("0", arg, 1)
if offends := kit.Int(offend) - kit.Int(limit); total != "0" && (offends <= -kit.Int(total) || offends >= kit.Int(total)) {
m.ProcessHold("已经是最后一页啦!")
} else if offends == 0 {
m.ProcessRewrite("offend", "")
} else {
m.ProcessRewrite("offend", offends)
}
}
func PrevPage(m *ice.Message, total string, arg ...string) {
limit, offend := kit.Select("10", arg, 0), kit.Select("0", arg, 1)
if offends := kit.Int(offend) + kit.Int(limit); total != "0" && (offends <= -kit.Int(total) || offends >= kit.Int(total)) {
m.ProcessHold("已经是最前一页啦!")
} else if offends == 0 {
m.ProcessRewrite("offend", "")
} else {
m.ProcessRewrite("offend", offends)
}
}
func PrevPageLimit(m *ice.Message, total string, arg ...string) {
if kit.Int(kit.Select("0", arg, 1)) < 0 {
PrevPage(m, total, arg...)
} else {
m.ProcessHold("已经是最前一页啦!")
}
}
func OptionPages(m *ice.Message, arg ...string) (page int, size int) {
m.Option(CACHE_OFFEND, kit.Select(m.Option(CACHE_OFFEND), arg, 0))
m.Option(CACHE_LIMIT, kit.Select(m.Option(CACHE_LIMIT), arg, 1))
m.Option(CACHE_FILTER, kit.Select(m.Option(CACHE_FILTER), arg, 2))
m.Option(OFFEND, kit.Select(m.Option(OFFEND), arg, 0))
m.Option(LIMIT, kit.Select(m.Option(LIMIT), arg, 1))
size = kit.Int(kit.Select("10", m.Option(LIMIT)))
page = kit.Int(m.Option(OFFEND))/size + 1
return
}
func OptionPage(m *ice.Message, arg ...string) int {
page, _ := OptionPages(m, arg...)
return page
}
const (
CACHE_LIMIT = "cache.limit"
CACHE_BEGIN = "cache.begin"
CACHE_COUNT = "cache.count"
CACHE_OFFEND = "cache.offend"
CACHE_FILTER = "cache.filter"
CACHE_VALUE = "cache.value"
CACHE_FIELD = "cache.field"
)
func Grows(m *ice.Message, prefix string, chain Any, match string, value string, cb Any) Map {
cache, ok := m.Confv(prefix, chain).(ice.Map)
if cache == nil || !ok {
return nil
} else if begin, limit := kit.Int(m.Option(CACHE_BEGIN)), kit.Int(m.Option(CACHE_LIMIT)); begin != 0 && limit > 0 {
if count := kit.Int(m.Option(CACHE_COUNT, kit.Int(kit.Value(cache, kit.Keym(COUNT))))); count-begin < limit {
m.Option(CACHE_OFFEND, 0)
m.Option(CACHE_LIMIT, count-begin+1)
} else {
m.Option(CACHE_OFFEND, count-begin-limit+1)
}
}
return miss.Grows(path.Join(prefix, kit.Keys(chain)), cache,
kit.Int(kit.Select("0", strings.TrimPrefix(m.Option(CACHE_OFFEND), "-"))),
kit.Int(kit.Select("10", m.Option(CACHE_LIMIT))), match, value, cb)
}
func Grow(m *ice.Message, prefix string, chain Any, data Any) int {
cache, ok := m.Confv(prefix, chain).(ice.Map)
if cache == nil || !ok {
cache = kit.Data()
m.Confv(prefix, chain, cache)
}
return miss.Grow(path.Join(prefix, kit.Keys(chain)), cache, data)
}

75
base/mdb/lock.go Normal file
View File

@ -0,0 +1,75 @@
package mdb
import (
"sync"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/task"
)
var _lock = task.Lock{}
var _locks = map[string]*task.Lock{}
func getLock(m *ice.Message, arg ...string) *task.Lock {
key := kit.Select(m.PrefixKey(), kit.Keys(arg))
defer _lock.Lock()()
l, ok := _locks[key]
kit.If(!ok, func() { l = &task.Lock{}; _locks[key] = l })
return l
}
func Lock(m *ice.Message, arg ...string) func() {
return getLock(m, arg...).Lock()
}
func RLock(m *ice.Message, arg ...string) func() {
return getLock(m, arg...).RLock()
}
func ConfigSimple(m *ice.Message, key ...string) (res []string) {
for _, key := range key {
res = append(res, key, kit.Format(Configv(m, key)))
}
return
}
func Config(m *ice.Message, key string, arg ...Any) string {
return kit.Format(Configv(m, key, arg...))
}
func Configv(m *ice.Message, key string, arg ...Any) Any {
kit.If(len(arg) > 0, func() { Confv(m, m.PrefixKey(), kit.Keym(key), arg[0]) })
return Confv(m, m.PrefixKey(), kit.Keym(key))
}
func Confv(m *ice.Message, arg ...Any) Any {
key := kit.Select(m.PrefixKey(), kit.Format(arg[0]))
if ctx, ok := ice.Info.Index[key].(*ice.Context); ok {
key = ctx.Prefix(key)
}
if len(arg) > 2 {
defer Lock(m, key)()
} else {
defer RLock(m, key)()
}
return m.Confv(arg...)
}
func Conf(m *ice.Message, arg ...Any) string { return kit.Format(Confv(m, arg...)) }
func Confm(m *ice.Message, key string, sub Any, cbs ...Any) Map {
val := m.Confv(key, sub)
kit.If(len(cbs) > 0, func() { kit.For(val, cbs[0]) })
value, _ := val.(Map)
return value
}
var cache = sync.Map{}
func Cache(m *ice.Message, key string, add func() Any) Any {
if key = kit.Keys(m.PrefixKey(), key); add == nil {
cache.Delete(key)
return nil
} else if val, ok := cache.Load(key); ok {
return val
} else if val := add(); val != nil {
cache.Store(key, val)
return val
} else {
return nil
}
}

View File

@ -2,183 +2,324 @@ package mdb
import (
"path"
"strings"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
func _file_name(m *ice.Message, arg ...string) string {
return kit.Select(path.Join(ice.USR_LOCAL_EXPORT, m.Option(ice.MSG_DOMAIN), path.Join(arg[:2]...), arg[2]), arg, 3)
type Any = ice.Any
type List = ice.List
type Maps = ice.Maps
type Map = ice.Map
func _mdb_modify(m *ice.Message, value Map, field string, arg ...string) {
value = kit.GetMeta(value)
kit.For(arg, func(k, v string) { kit.If(k != field, func() { kit.Value(value, k, v) }) })
}
func _domain_chain(m *ice.Message, chain string) string {
return kit.Keys(m.Option(ice.MSG_DOMAIN), chain)
func _mdb_select(m *ice.Message, cb Any, key string, value Map, fields []string, val Map) {
switch value, val = kit.GetMeta(value), kit.GetMeta(val); cb := cb.(type) {
case func([]string, Map):
cb(fields, value)
case func(string, []string, Map, Map):
cb(key, fields, value, val)
case func(string, Map, Map):
cb(key, value, val)
case func(string, Map):
cb(key, value)
case func(Map):
cb(value)
case func(Any):
cb(value[TARGET])
case func(Maps):
cb(kit.ToMaps(value))
case string, []string, []Any, nil:
if m.FieldsIsDetail() {
// m.Push(ice.FIELDS_DETAIL, value, nil, kit.Dict(HASH, key))
m.Push(ice.FIELDS_DETAIL, value)
} else {
m.Push(key, value, fields, val)
}
default:
m.ErrorNotImplement(cb)
}
}
func _mdb_export_file(m *ice.Message, arg ...string) string {
if len(arg) > 3 && strings.Contains(arg[3], ice.PS) {
return arg[3]
}
return path.Join(ice.USR_LOCAL_EXPORT, path.Join(arg[:2]...), arg[2])
}
const (
CSV = "csv"
JSON = "json"
)
const (
DICT = "dict"
META = "meta"
)
const (
FIELDS = "fields"
DETAIL = "detail"
RANDOM = "random"
REPEAT = "repeat"
DICT = kit.MDB_DICT
META = kit.MDB_META
SHORT = kit.MDB_SHORT
FIELD = kit.MDB_FIELD
COUNT = kit.MDB_COUNT
TOTAL = kit.MDB_TOTAL
LIMIT = kit.MDB_LIMIT
LEAST = kit.MDB_LEAST
STORE = kit.MDB_STORE
FSIZE = kit.MDB_FSIZE
UNIQ = kit.MDB_UNIQ
FOREACH = kit.MDB_FOREACH
RANDOMS = kit.MDB_RANDOMS
)
const (
ID = kit.MDB_ID
TIME = kit.MDB_TIME
TYPE = kit.MDB_TYPE
NAME = kit.MDB_NAME
TEXT = kit.MDB_TEXT
ICON = kit.MDB_ICON
SCAN = kit.MDB_SCAN
LINK = kit.MDB_LINK
HELP = kit.MDB_HELP
FILE = kit.MDB_FILE
DATA = kit.MDB_DATA
VIEW = kit.MDB_VIEW
SHOW = kit.MDB_SHOW
KEY = kit.MDB_KEY
VALUE = kit.MDB_VALUE
INDEX = kit.MDB_INDEX
EXTRA = kit.MDB_EXTRA
ALIAS = kit.MDB_ALIAS
EXPIRE = kit.MDB_EXPIRE
STATUS = kit.MDB_STATUS
STREAM = kit.MDB_STREAM
TOOLS = "tools"
ICONS = "icons"
UNITS = "units"
ORDER = "order"
SCORE = "score"
GROUP = "group"
VALID = "valid"
ENABLE = "enable"
MEMBER = "member"
DISABLE = "disable"
EXPIRED = "expired"
INVALID = "invalid"
SOURCE = "_source"
TARGET = "_target"
IMPORTANT = "important"
)
const (
INPUTS = "inputs"
CREATE = "create"
REMOVE = "remove"
UPDATE = "update"
INSERT = "insert"
DELETE = "delete"
MODIFY = "modify"
SELECT = "select"
INPUTS = "inputs"
PRUNES = "prunes"
EXPORT = "export"
IMPORT = "import"
UPLOAD = "upload"
REVERT = "revert"
DETAIL = "detail"
FIELDS = "fields"
SHORTS = "shorts"
PARAMS = "params"
OFFEND = "offend"
OFFSET = "offset"
RANDOM = "random"
WEIGHT = "weight"
SUBKEY = "mdb.sub"
ACTION = "action"
UPLOAD = "upload"
RECENT = "recent"
REPEAT = "repeat"
REVERT = "revert"
RENAME = "rename"
VENDOR = "vendor"
PRUNE = "prune"
TABLE = "table"
CLASS = "class"
DATABASE = "database"
PAGE = "page"
NEXT = "next"
PREV = "prev"
)
const (
CACHE_CLEAR_ON_EXIT = "cache.clear.on.exit"
)
PLAY = "play"
func PrevPageLimit(m *ice.Message, total string, arg ...string) {
if kit.Int(kit.Select("0", arg, 1)) > 0 {
PrevPage(m, total, arg...)
} else {
m.Toast("已经是最前一页啦!")
m.ProcessHold()
}
}
func PrevPage(m *ice.Message, total string, arg ...string) {
limit, offend := kit.Select("10", arg, 0), kit.Select("0", arg, 1)
offends := kit.Int(offend) - kit.Int(limit)
if offends <= -kit.Int(total) || offends >= kit.Int(total) {
m.Toast("已经是最前一页啦!")
m.ProcessHold()
return
}
m.ProcessRewrite("offend", offends)
SORT = "sort"
JSON = "json"
CSV = "csv"
SUB = "sub"
}
func NextPage(m *ice.Message, total string, arg ...string) {
limit, offend := kit.Select("10", arg, 0), kit.Select("0", arg, 1)
offends := kit.Int(offend) + kit.Int(limit)
if offends <= -kit.Int(total) || offends >= kit.Int(total) {
m.Toast("已经是最后一页啦!")
m.ProcessHold()
return
}
m.ProcessRewrite("offend", offends)
}
func NextPageLimit(m *ice.Message, total string, arg ...string) {
if kit.Int(kit.Select("0", arg, 1)) < 0 {
NextPage(m, total, arg...)
} else {
m.Toast("已经是最后一页啦!")
m.ProcessHold()
}
}
QS = ice.QS
EQ = ice.EQ
AT = ice.AT
FS = ice.FS
)
const MDB = "mdb"
var Index = &ice.Context{Name: MDB, Help: "数据模块", Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {}},
INSERT: {Name: "insert key sub type arg...", Help: "添加", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE: // insert key sub type zone arg...
_list_insert(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), arg[4:]...)
case HASH:
_hash_insert(m, arg[0], _domain_chain(m, arg[1]), arg[3:]...)
case LIST:
_list_insert(m, arg[0], _domain_chain(m, arg[1]), arg[3:]...)
var Index = &ice.Context{Name: MDB, Help: "数据模块", Commands: ice.Commands{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {}},
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) {}},
INPUTS: {Name: "inputs key sub type field value", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() { _hash_inputs(m, arg[0], arg[1], kit.Select(NAME, arg, 3), kit.Select("", arg, 4)) },
ZONE, func() { _zone_inputs(m, arg[0], arg[1], arg[3], kit.Select(NAME, arg, 4), kit.Select("", arg, 5)) },
LIST, func() { _list_inputs(m, arg[0], arg[1], kit.Select(NAME, arg, 3), kit.Select("", arg, 4)) },
)
for _, inputs := range ice.Info.Inputs {
if arg[2] == ZONE {
inputs(m, arg[4])
} else {
inputs(m, arg[3])
}
}
}},
DELETE: {Name: "delete key sub type field value", Help: "删除", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE: // delete key sub type zone field value
_list_delete(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), arg[4], arg[5])
case HASH:
_hash_delete(m, arg[0], _domain_chain(m, arg[1]), arg[3], arg[4])
case LIST:
_list_delete(m, arg[0], _domain_chain(m, arg[1]), arg[3], arg[4])
}
INSERT: {Name: "insert key sub type arg...", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() { _hash_insert(m, arg[0], arg[1], arg[3:]...) },
ZONE, func() {
if arg[3] == ZONE {
_zone_insert(m, arg[0], arg[1], arg[4], arg[5:]...)
} else {
_zone_insert(m, arg[0], arg[1], arg[3], arg[4:]...)
}
},
LIST, func() { _list_insert(m, arg[0], arg[1], arg[3:]...) },
)
}},
MODIFY: {Name: "modify key sub type field value arg...", Help: "编辑", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE: // modify key sub type zone id field value
m.Debug("what %v %v", arg[3], kit.KeyHash(arg[3]))
_list_modify(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), kit.MDB_ID, arg[4], arg[5:]...)
case HASH:
_hash_modify(m, arg[0], _domain_chain(m, arg[1]), arg[3], arg[4], arg[5:]...)
case LIST:
_list_modify(m, arg[0], _domain_chain(m, arg[1]), arg[3], arg[4], arg[5:]...)
}
DELETE: {Name: "delete key sub type field value", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() { _hash_delete(m, arg[0], arg[1], arg[3], arg[4]) },
// ZONE, func() { _list_delete(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), arg[4], arg[5]) },
// LIST, func() { _list_delete(m, arg[0], arg[1], arg[3], arg[4]) },
)
}},
SELECT: {Name: "select key sub type field value", Help: "查询", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE:
_zone_select(m, arg[0], _domain_chain(m, arg[1]), kit.Select("", arg, 3), kit.Select("", arg, 4))
case HASH:
_hash_select(m, arg[0], _domain_chain(m, arg[1]), kit.Select("", arg, 3), kit.Select(kit.MDB_FOREACH, arg, 4))
case LIST:
_list_select(m, arg[0], _domain_chain(m, arg[1]), kit.Select("", arg, 3), kit.Select("", arg, 4))
}
MODIFY: {Name: "modify key sub type field value arg...", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() { _hash_modify(m, arg[0], arg[1], arg[3], arg[4], arg[5:]...) },
ZONE, func() { _zone_modify(m, arg[0], arg[1], arg[3], arg[4], arg[5:]...) },
LIST, func() { _list_modify(m, arg[0], arg[1], arg[3], arg[4], arg[5:]...) },
)
}},
INPUTS: {Name: "inputs key sub type field value", Help: "补全", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE: // inputs key sub type zone field value
_list_inputs(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), kit.Select(kit.MDB_NAME, arg, 4), kit.Select("", arg, 5))
case HASH:
_hash_inputs(m, arg[0], _domain_chain(m, arg[1]), kit.Select(kit.MDB_NAME, arg, 3), kit.Select("", arg, 4))
case LIST:
_list_inputs(m, arg[0], _domain_chain(m, arg[1]), kit.Select(kit.MDB_NAME, arg, 3), kit.Select("", arg, 4))
}
SELECT: {Name: "select key sub type field value", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() { _hash_select(m, arg[0], arg[1], kit.Select("", arg, 3), kit.Select(FOREACH, arg, 4)) },
ZONE, func() { _zone_select(m, arg[0], arg[1], kit.Select("", arg, 3), kit.Select("", arg, 4)) },
LIST, func() { _list_select(m, arg[0], arg[1], kit.Select("", arg, 3), kit.Select("", arg, 4)) },
)
}},
PRUNES: {Name: "prunes key sub type [field value]...", Help: "清理", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch arg[2] {
case ZONE: // prunes key sub type zone field value
_list_prunes(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), arg[4:]...)
case HASH:
_hash_prunes(m, arg[0], _domain_chain(m, arg[1]), arg[3:]...)
case LIST:
_list_prunes(m, arg[0], _domain_chain(m, arg[1]), arg[3:]...)
}
PRUNES: {Name: "prunes key sub type [field value]...", Hand: func(m *ice.Message, arg ...string) {
kit.Switch(arg[2],
HASH, func() {
_hash_prunes(m, arg[0], arg[1], arg[3:]...)
m.Table(func(value Maps) { _hash_delete(m, arg[0], arg[1], HASH, value[HASH]) })
},
// ZONE, func() { _list_prunes(m, arg[0], _domain_chain(m, kit.Keys(arg[1], kit.KeyHash(arg[3]))), arg[4:]...) },
// LIST, func() { _list_prunes(m, arg[0], arg[1], arg[3:]...) },
)
}},
EXPORT: {Name: "export key sub type file", Help: "导出", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch file := _file_name(m, arg...); arg[2] {
case ZONE:
_zone_export(m, arg[0], _domain_chain(m, arg[1]), file)
case HASH:
_hash_export(m, arg[0], _domain_chain(m, arg[1]), file)
case LIST:
_list_export(m, arg[0], _domain_chain(m, arg[1]), file)
EXPORT: {Name: "export index auto", Help: "导出数据", Actions: ice.MergeActions(ice.Actions{
IMPORT: {Hand: func(m *ice.Message, arg ...string) {
HashSelect(m).Table(func(value ice.Maps) {
if value[ENABLE] != ice.FALSE {
m.Cmd(IMPORT, value[INDEX], "", value[TYPE])
}
})
}},
EXPORT: {Hand: func(m *ice.Message, arg ...string) {
HashSelect(m).Table(func(value ice.Maps) {
if value[ENABLE] != ice.FALSE {
m.Cmd(EXPORT, value[INDEX], "", value[TYPE])
}
})
}},
}, ExportHashAction(SHORT, INDEX, FIELD, "time,index,type,enable")), Hand: func(m *ice.Message, arg ...string) {
if len(arg) < 2 {
HashSelect(m, arg...).PushAction(REMOVE)
return
}
m.OptionDefault(CACHE_LIMIT, "-1")
file := _mdb_export_file(m, arg...)
kit.Switch(arg[2],
HASH, func() { _hash_export(m, arg[0], arg[1], file) },
ZONE, func() { _zone_export(m, arg[0], arg[1], file); _hash_export(m, arg[0], arg[1], file) },
LIST, func() { _list_export(m, arg[0], arg[1], file) },
)
}},
IMPORT: {Name: "import key sub type file", Help: "导入", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
switch file := _file_name(m, arg...); arg[2] {
case ZONE:
_zone_import(m, arg[0], _domain_chain(m, arg[1]), file)
case HASH:
_hash_import(m, arg[0], _domain_chain(m, arg[1]), file)
case LIST:
_list_import(m, arg[0], _domain_chain(m, arg[1]), file)
}
IMPORT: {Name: "import key sub type file", Hand: func(m *ice.Message, arg ...string) {
file := _mdb_export_file(m, arg...)
kit.Switch(arg[2],
HASH, func() { _hash_import(m, arg[0], arg[1], file) },
ZONE, func() { _hash_import(m, arg[0], arg[1], file); _zone_import(m, arg[0], arg[1], file) },
LIST, func() { _list_import(m, arg[0], arg[1], file) },
)
}},
}}
func init() {
ice.Index.Register(Index, nil,
INSERT, DELETE, MODIFY, SELECT,
INPUTS, PRUNES, EXPORT, IMPORT,
SEARCH, ENGINE, PLUGIN, RENDER,
ice.Index.Register(Index, nil, INPUTS, INSERT, DELETE, MODIFY, SELECT, PRUNES, EXPORT, IMPORT, PLUGIN, RENDER, ENGINE, SEARCH)
}
func init() {
ice.Module(MDB,
HashInputs, HashCreate, HashRemove, func(m *ice.Message) { HashPrunes(m, nil) }, HashModify, HashSelect,
ZoneInputs, ZoneCreate, ZoneRemove, ZoneInsert, ZoneModify, ZoneSelect,
)
}
func AutoConfig(arg ...Any) *ice.Action {
return &ice.Action{Hand: func(m *ice.Message, args ...string) {
if cs := m.Target().Configs; cs[m.CommandKey()] == nil {
cs[m.CommandKey()] = &ice.Config{Value: kit.Data(arg...)}
} else {
kit.For(kit.Dict(arg...), func(k string, v Any) { Config(m, k, v) })
}
if cmd := m.Target().Commands[m.CommandKey()]; cmd == nil {
return
} else {
s := Config(m, SHORT)
kit.If(s == "" || s == UNIQ || strings.Contains(s, ","), func() { s = HASH })
if cmd.Name == "" {
cmd.Name = kit.Format("%s %s auto", m.CommandKey(), s)
cmd.List = ice.SplitCmd(cmd.Name, cmd.Actions)
}
add := func(list []string) (inputs []Any) {
kit.For(list, func(k string) {
kit.If(!kit.IsIn(k, TIME, HASH, COUNT, ID, ENABLE, DISABLE), func() {
inputs = append(inputs, k+kit.Select("", FOREACH, strings.Contains(s, k)))
})
})
return
}
kit.If(cmd.Meta[CREATE] == nil, func() { m.Design(CREATE, "", add(kit.Split(HashField(m)))...) })
return
if cmd.Actions[INSERT] != nil {
kit.If(cmd.Meta[INSERT] == nil, func() { m.Design(INSERT, "", add(kit.Simple(Config(m, SHORT), kit.Split(ListField(m))))...) })
kit.If(cmd.Meta[CREATE] == nil, func() { m.Design(CREATE, "", add(kit.Split(Config(m, SHORT)))...) })
} else if cmd.Actions[CREATE] != nil {
kit.If(cmd.Meta[CREATE] == nil, func() { m.Design(CREATE, "", add(kit.Split(HashField(m)))...) })
}
}
}}
}
func ImportantZoneAction(arg ...Any) ice.Actions {
return ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { Config(m, IMPORTANT, ice.TRUE) }},
}, ZoneAction(arg...))
}
func ImportantHashAction(arg ...Any) ice.Actions {
return ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { Config(m, IMPORTANT, ice.TRUE) }},
}, HashAction(arg...))
}
func saveImportant(m *ice.Message, key, sub string, arg ...string) {
if m.Option("skip.important") == ice.TRUE {
return
}
kit.If(m.Conf(key, kit.Keys(META, IMPORTANT)) == ice.TRUE, func() { ice.SaveImportant(m, arg...) })
}

View File

@ -1,7 +0,0 @@
chapter "mdb"
field "插件" mdb.plugin
field "渲染" mdb.render
field "引擎" mdb.engine
field "搜索" mdb.search

View File

@ -1,32 +1,7 @@
package mdb
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
import ice "shylinux.com/x/icebergs"
const PLUGIN = "plugin"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
PLUGIN: {Name: "plugin", Help: "插件", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_TYPE, kit.MDB_FIELD, "time,type,name,text",
)},
}, Commands: map[string]*ice.Command{
PLUGIN: {Name: "plugin type name text auto", Help: "插件", Action: map[string]*ice.Action{
CREATE: {Name: "create type name text", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, m.OptionSimple("type,name,text"))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 1 {
m.Optionv(kit.Keycb(SELECT), func(fields []string, value map[string]interface{}) {
m.Cmdy(kit.Keys(value[kit.MDB_TEXT], value[kit.MDB_NAME]),
m.CommandKey(), arg[0], arg[1], kit.Select("", arg, 2), kit.Slice(arg, 3))
})
}
if HashSelect(m, arg...); len(arg) == 0 {
m.Sort(kit.MDB_TYPE)
}
}},
}})
}
func init() { Index.MergeCommands(ice.Commands{PLUGIN: {Help: "插件", Actions: RenderAction()}}) }

View File

@ -7,26 +7,23 @@ import (
const RENDER = "render"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
RENDER: {Name: "render", Help: "渲染", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_TYPE, kit.MDB_FIELD, "time,type,name,text",
)},
}, Commands: map[string]*ice.Command{
RENDER: {Name: "render type name text auto", Help: "渲染", Action: map[string]*ice.Action{
CREATE: {Name: "create type name text", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, m.OptionSimple("type,name,text"))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 1 {
m.Optionv(kit.Keycb(SELECT), func(fields []string, value map[string]interface{}) {
m.Cmdy(kit.Keys(value[kit.MDB_TEXT], value[kit.MDB_NAME]),
m.CommandKey(), arg[0], arg[1], kit.Select("", arg, 2), kit.Slice(arg, 3))
func init() { Index.MergeCommands(ice.Commands{RENDER: {Help: "渲染", Actions: RenderAction()}}) }
func RenderAction(arg ...ice.Any) ice.Actions {
return ice.MergeActions(ice.Actions{ice.CTX_INIT: AutoConfig(SHORT, TYPE, FIELD, "time,type,name,text", arg),
CREATE: {Name: "create type name text", Hand: func(m *ice.Message, arg ...string) { Config(m, SHORT, TYPE); HashCreate(m) }},
SELECT: {Name: "select type name text auto create", Hand: func(m *ice.Message, arg ...string) {
if len(arg) < 2 || arg[0] == "" {
HashSelect(m, arg...)
return
}
m.OptionDefault(ice.MSG_FIELDS, kit.Select("type,name,text", arg, 2))
kit.For(kit.Split(arg[0]), func(k string) {
HashSelects(m.Spawn(), k).Table(func(value ice.Maps) {
m.Cmdy(kit.Keys(value[TEXT], value[NAME]), m.CommandKey(), k, arg[1], kit.Select("", arg, 2), kit.Slice(arg, 3))
})
}
if HashSelect(m, arg...); len(arg) == 0 {
m.Sort(kit.MDB_TYPE)
}
})
m.Sort(m.OptionFields())
}},
}})
}, ClearOnExitHashAction())
}

View File

@ -8,29 +8,24 @@ import (
const SEARCH = "search"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SEARCH: {Name: "search", Help: "搜索", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_TYPE, kit.MDB_FIELD, "time,type,name,text",
)},
}, Commands: map[string]*ice.Command{
SEARCH: {Name: "search type word text auto", Help: "搜索", Action: map[string]*ice.Action{
CREATE: {Name: "create type name text", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, m.OptionSimple("type,name,text"))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
msg := m.Spawn(c)
if len(arg) > 1 {
msg.Optionv(kit.Keycb(SELECT), func(fields []string, value map[string]interface{}) {
m.OptionFields(kit.Select("ctx,cmd,type,name,text", kit.Select(m.OptionFields(), arg, 2)))
m.Cmdy(kit.Keys(value[kit.MDB_TEXT], value[kit.MDB_NAME]), m.CommandKey(), arg[0], arg[1], kit.Select("", arg, 2))
})
}
if HashSelect(msg, arg...); len(arg) == 0 {
m.Copy(msg)
m.Sort(kit.MDB_TYPE)
} else if len(arg) == 1 {
m.Copy(msg)
}
}},
}})
Index.MergeCommands(ice.Commands{SEARCH: {Help: "搜索", Actions: RenderAction()}})
ice.AddMergeAction(func(c *ice.Context, key string, cmd *ice.Command, sub string, action *ice.Action) ice.Handler {
if sub == SEARCH {
return func(m *ice.Message, arg ...string) { m.Cmd(sub, CREATE, m.CommandKey(), m.PrefixKey()) }
}
return nil
})
}
func IsSearchPreview(m *ice.Message, arg []string, cb ...func() []string) bool {
if arg[0] == FOREACH && arg[1] == "" {
for _, cb := range cb {
if cb != nil {
if args := cb(); len(args) > 0 {
m.PushSearch(TYPE, kit.Select("", args, 0), NAME, kit.Select("", args, 1), TEXT, kit.Select("", args, 2))
}
}
}
return true
}
return false
}

View File

@ -8,187 +8,282 @@ import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
"shylinux.com/x/toolkits/miss"
)
func _zone_meta(m *ice.Message, prefix, chain, key string) string {
defer RLock(m, prefix)()
return m.Conf(prefix, kit.Keys(chain, kit.Keym(key)))
}
func _zone_fields(m *ice.Message) []string {
return kit.Split(kit.Select("zone,id,time,type,name,text", kit.Join(kit.Simple(m.Optionv(FIELDS)))))
return kit.Split(kit.Select(ZONE_FIELD, m.OptionFields()))
}
func _zone_inputs(m *ice.Message, prefix, chain, zone string, field, value string) {
if field == _zone_meta(m, prefix, chain, SHORT) {
_hash_inputs(m, prefix, chain, field, value)
return
}
h := _hash_select_field(m, prefix, chain, zone, HASH)
_list_inputs(m, prefix, kit.Keys(chain, HASH, h), field, value)
}
func _zone_insert(m *ice.Message, prefix, chain, zone string, arg ...string) {
h := _hash_select_field(m, prefix, chain, zone, HASH)
if h == "" {
h = _hash_insert(m, prefix, chain, kit.Select(ZONE, _zone_meta(m, prefix, chain, SHORT)), zone)
}
m.Assert(h != "")
_list_insert(m, prefix, kit.Keys(chain, HASH, h), arg...)
}
func _zone_modify(m *ice.Message, prefix, chain, zone, id string, arg ...string) {
h := _hash_select_field(m, prefix, chain, zone, HASH)
m.Assert(h != "")
_list_modify(m, prefix, kit.Keys(chain, HASH, h), ID, id, arg...)
}
func _zone_select(m *ice.Message, prefix, chain, zone string, id string) {
if zone == RANDOM {
zone = kit.MDB_RANDOMS
if zone == "" {
_hash_select(m, prefix, chain, HASH, FOREACH)
return
} else if zone == RANDOM {
zone = RANDOMS
}
defer m.SortIntR(ID)
fields := _zone_fields(m)
cb := m.Optionv(kit.Keycb(SELECT))
m.Richs(prefix, chain, kit.Select(kit.MDB_FOREACH, zone), func(key string, val map[string]interface{}) {
if val = kit.GetMeta(val); zone == "" {
if m.OptionFields() == DETAIL {
m.Push(DETAIL, val)
} else {
m.Push(key, val, fields)
}
return
}
m.Grows(prefix, kit.Keys(chain, kit.MDB_HASH, key), kit.MDB_ID, id, func(index int, value map[string]interface{}) {
switch value = kit.GetMeta(value); cb := cb.(type) {
case func(string, []string, map[string]interface{}, map[string]interface{}):
cb(key, fields, value, val)
case func(string, map[string]interface{}, map[string]interface{}):
cb(key, value, val)
case func(string, map[string]interface{}):
cb(key, value)
default:
if m.Option(FIELDS) == DETAIL {
m.Push(DETAIL, value)
} else {
m.Push(key, value, fields, val)
}
}
defer RLock(m, prefix)()
Richs(m, prefix, chain, kit.Select(FOREACH, zone), func(key string, val Map) {
chain := kit.Keys(chain, HASH, key)
Grows(m, prefix, chain, ID, id, func(value ice.Map) {
_mdb_select(m, m.OptionCB(""), key, value, fields, val)
})
m.StatusTimeCountTotal(kit.Value(val, "meta.count"), "step", "0")
})
}
func _zone_export(m *ice.Message, prefix, chain, file string) {
f, p, e := kit.Create(kit.Keys(file, CSV))
if !ice.HasUsr() {
return
}
defer Lock(m, prefix)()
if len(Confm(m, prefix, kit.Keys(chain, HASH))) == 0 {
return
}
f, p, e := miss.CreateFile(kit.Keys(file, CSV))
m.Assert(e)
defer f.Close()
defer m.Echo(p)
w := csv.NewWriter(f)
defer w.Flush()
fields := _zone_fields(m)
fields = append(fields, kit.MDB_EXTRA)
w.Write(fields)
head := kit.AddUniq(_zone_fields(m), EXTRA)
w.Write(head)
count := 0
m.Richs(prefix, chain, kit.MDB_FOREACH, func(key string, val map[string]interface{}) {
val = kit.GetMeta(val)
m.Grows(prefix, kit.Keys(chain, kit.MDB_HASH, key), "", "", func(index int, value map[string]interface{}) {
value = kit.GetMeta(value)
list := []string{}
for _, k := range fields {
list = append(list, kit.Select(kit.Format(kit.Value(val, k)), kit.Format(kit.Value(value, k))))
}
w.Write(list)
count++
for _, key := range kit.SortedKey(m.Confv(prefix, kit.Keys(chain, HASH))) {
Richs(m, prefix, chain, key, func(key string, val ice.Map) {
val = kit.GetMeta(val)
chain := kit.Keys(chain, HASH, key)
Grows(m, prefix, chain, "", "", func(value ice.Map) {
value = kit.GetMeta(value)
w.Write(kit.Simple(head, func(k string) string {
return kit.Select(kit.Format(kit.Value(val, k)), kit.Format(kit.Value(value, k)))
}))
count++
})
})
})
m.Log_EXPORT(kit.MDB_KEY, path.Join(prefix, chain), kit.MDB_FILE, p, kit.MDB_COUNT, count)
m.Conf(prefix, kit.Keys(chain, kit.MDB_HASH), "")
m.Echo(p)
m.Conf(prefix, kit.Keys(chain, HASH, key, LIST), "")
m.Conf(prefix, kit.Keys(chain, HASH, key, META, COUNT), "")
}
kit.If(count == 0, func() { os.Remove(p) })
m.Logs(EXPORT, KEY, path.Join(prefix, chain), FILE, p, COUNT, count)
}
func _zone_import(m *ice.Message, prefix, chain, file string) {
f, e := os.Open(kit.Keys(file, CSV))
m.Assert(e)
if !ice.HasUsr() {
return
}
defer Lock(m, prefix)()
f, e := miss.OpenFile(kit.Keys(file, CSV))
if e != nil && !ice.Info.Important {
return
} else if m.WarnNotFound(e) {
return
}
defer f.Close()
r := csv.NewReader(f)
head, _ := r.Read()
zkey := kit.Select(head[0], m.OptionFields())
list := ice.Maps{}
times := ice.Maps{}
kit.For(m.Confv(prefix, kit.Keys(chain, HASH)), func(key string, value ice.Any) {
times[key] = kit.Format(kit.Value(value, kit.Keys(META, TIME)))
})
count := 0
list := map[string]string{}
zkey := kit.Select(head[0], m.Option(FIELDS))
for {
line, e := r.Read()
if e != nil {
break
}
zone := ""
data := kit.Dict()
zone, data := "", kit.Dict()
for i, k := range head {
switch k {
case zkey:
zone = line[i]
case kit.MDB_ID:
case ID:
continue
case kit.MDB_EXTRA:
kit.Value(data, k, kit.UnMarshal(line[i]))
case EXTRA:
if line[i] != "" {
kit.Value(data, k, kit.UnMarshal(line[i]))
}
default:
kit.Value(data, k, line[i])
}
}
if list[zone] == "" {
list[zone] = m.Rich(prefix, chain, kit.Data(zkey, zone))
list[zone] = Rich(m, prefix, chain, kit.Data(zkey, zone))
kit.If(times[list[zone]], func(t string) { m.Confv(prefix, kit.Keys(chain, HASH, list[zone], META, TIME), t) })
}
m.Grow(prefix, kit.Keys(chain, kit.MDB_HASH, list[zone]), data)
func() { chain := kit.Keys(chain, HASH, list[zone]); Grow(m, prefix, chain, data) }()
count++
}
m.Log_IMPORT(kit.MDB_KEY, path.Join(prefix, chain), kit.MDB_COUNT, count)
m.Logs(IMPORT, KEY, path.Join(prefix, chain), FILE, kit.Keys(file, CSV), COUNT, count)
m.Echo("%d", count)
}
const (
ZONE_FIELD = "time,id,type,name,text"
)
const ZONE = "zone"
func ZoneAction(fields ...string) map[string]*ice.Action {
_zone := func(m *ice.Message) string { return kit.Select(kit.MDB_ZONE, m.Config(kit.MDB_SHORT)) }
return ice.SelectAction(map[string]*ice.Action{
INPUTS: {Name: "inputs", Help: "补全", Hand: func(m *ice.Message, arg ...string) {
arg[0] = strings.TrimPrefix(arg[0], "extra.")
arg[0] = kit.Select(arg[0], m.Config(kit.Keys(kit.MDB_ALIAS, arg[0])))
switch arg[0] {
case ice.POD:
m.Cmdy("route")
case ice.CTX:
m.Cmdy("context")
case ice.CMD:
m.Cmdy("context", kit.Select(m.Option(ice.CTX), m.Option(kit.Keys(kit.MDB_EXTRA, ice.CTX))), "command")
case ice.ARG:
case _zone(m):
m.Cmdy(INPUTS, m.PrefixKey(), "", HASH, arg)
default:
m.Cmdy(INPUTS, m.PrefixKey(), "", ZONE, m.Option(_zone(m)), arg)
func ZoneConfig(arg ...Any) *ice.Action {
return &ice.Action{Hand: func(m *ice.Message, args ...string) {
if cs := m.Target().Configs; cs[m.CommandKey()] == nil {
cs[m.CommandKey()] = &ice.Config{Value: kit.Data(arg...)}
} else {
kit.For(kit.Dict(arg...), func(k string, v Any) { Config(m, k, v) })
}
if cmd := m.Target().Commands[m.CommandKey()]; cmd == nil {
return
} else {
s := kit.Select(ZONE, Config(m, SHORT))
kit.If(s == UNIQ || strings.Contains(s, ","), func() { s = HASH })
if cmd.Name == "" {
cmd.Name = kit.Format("%s %s id auto", m.CommandKey(), s)
cmd.List = ice.SplitCmd(cmd.Name, cmd.Actions)
}
}},
CREATE: {Name: "create zone", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, arg)
}},
REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(DELETE, m.PrefixKey(), "", HASH, m.OptionSimple(_zone(m)), arg)
}},
INSERT: {Name: "insert zone type=go name=hi text=hello", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
arg = m.OptionSimple(_zone(m), m.Config(kit.MDB_FIELD))
add := func(list []string) (inputs []Any) {
kit.For(list, func(k string) {
kit.If(!kit.IsIn(k, TIME, HASH, COUNT, ID), func() {
inputs = append(inputs, k+kit.Select("", FOREACH, strings.Contains(s, k)))
})
})
return
}
m.Cmdy(INSERT, m.PrefixKey(), "", HASH, _zone(m), arg[1])
m.Cmdy(INSERT, m.PrefixKey(), "", ZONE, m.Option(_zone(m)), arg[2:])
}},
MODIFY: {Name: "modify", Help: "编辑", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(MODIFY, m.PrefixKey(), "", ZONE, m.Option(_zone(m)), m.Option(kit.MDB_ID), arg)
}},
EXPORT: {Name: "export", Help: "导出", Hand: func(m *ice.Message, arg ...string) {
m.Option(ice.CACHE_LIMIT, "-1")
m.OptionFields(_zone(m), m.Config(kit.MDB_FIELD))
m.Cmdy(EXPORT, m.PrefixKey(), "", ZONE)
}},
IMPORT: {Name: "import", Help: "导入", Hand: func(m *ice.Message, arg ...string) {
m.OptionFields(_zone(m))
m.Cmdy(IMPORT, m.PrefixKey(), "", ZONE)
}},
PLUGIN: {Name: "plugin extra.pod extra.ctx extra.cmd extra.arg", Help: "插件", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(MODIFY, m.PrefixKey(), "", ZONE, m.Option(_zone(m)), m.Option(kit.MDB_ID), arg)
}},
PREV: {Name: "prev", Help: "上一页", Hand: func(m *ice.Message, arg ...string) {
PrevPage(m, arg[0], arg[1:]...)
}},
NEXT: {Name: "next", Help: "下一页", Hand: func(m *ice.Message, arg ...string) {
NextPageLimit(m, arg[0], arg[1:]...)
}},
}, fields...)
kit.If(cmd.Meta[INSERT] == nil, func() { m.Design(INSERT, "", add(kit.Simple(kit.Split(s), kit.Split(ZoneField(m))))...) })
kit.If(cmd.Meta[CREATE] == nil, func() { m.Design(CREATE, "", add(kit.Split(kit.Select(s, Config(m, FIELD))))...) })
}
}}
}
func ZoneAction(arg ...ice.Any) ice.Actions {
return ice.Actions{ice.CTX_INIT: ZoneConfig(append(kit.List(SHORT, ZONE, FIELDS, ZONE_FIELD), arg...)...),
INPUTS: {Hand: func(m *ice.Message, arg ...string) { ZoneInputs(m, arg) }},
CREATE: {Hand: func(m *ice.Message, arg ...string) { ZoneCreate(m, arg) }},
REMOVE: {Hand: func(m *ice.Message, arg ...string) { ZoneRemove(m, arg) }},
INSERT: {Hand: func(m *ice.Message, arg ...string) { ZoneInsert(m, arg) }},
MODIFY: {Hand: func(m *ice.Message, arg ...string) { ZoneModify(m, arg) }},
SELECT: {Hand: func(m *ice.Message, arg ...string) { ZoneSelect(m, arg...) }},
EXPORT: {Hand: func(m *ice.Message, arg ...string) { ZoneExport(m, arg) }},
IMPORT: {Hand: func(m *ice.Message, arg ...string) { ZoneImport(m, arg) }},
}
}
func ExportZoneAction(arg ...ice.Any) ice.Actions {
return ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { Config(m, IMPORTANT, ice.TRUE); ZoneImport(m, arg) }},
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) { m.OptionFields(""); ZoneExport(m, arg) }},
}, ZoneAction(arg...))
}
func PageZoneAction(arg ...ice.Any) ice.Actions {
return ice.MergeActions(ice.Actions{
SELECT: {Hand: func(m *ice.Message, arg ...string) { PageZoneSelect(m, arg...) }},
PREV: {Hand: func(m *ice.Message, arg ...string) { PrevPageLimit(m, arg[0], arg[1:]...) }},
NEXT: {Hand: func(m *ice.Message, arg ...string) { NextPage(m, arg[0], arg[1:]...) }},
}, ZoneAction(arg...))
}
func ZoneKey(m *ice.Message) string {
if m.Option(HASH) != "" {
return HASH
}
return ZoneShort(m)
}
func ZoneShort(m *ice.Message) string {
return kit.Select(ZONE, Config(m, SHORT), Config(m, SHORT) != UNIQ)
}
func ZoneField(m *ice.Message) string { return kit.Select(ZONE_FIELD, Config(m, FIELDS)) }
func ZoneInputs(m *ice.Message, arg ...Any) {
m.Cmdy(INPUTS, m.PrefixKey(), "", ZONE, m.Option(ZoneKey(m)), arg)
}
func ZoneCreate(m *ice.Message, arg ...Any) { m.Cmdy(INSERT, m.PrefixKey(), "", HASH, arg) }
func ZoneRemove(m *ice.Message, arg ...Any) {
if args := kit.Simple(arg...); len(args) == 0 {
arg = append(arg, m.OptionSimple(ZoneKey(m)))
} else if len(args) == 1 {
arg = kit.List(ZoneKey(m), args[0])
}
m.Cmdy(DELETE, m.PrefixKey(), "", HASH, arg)
}
func ZoneInsert(m *ice.Message, arg ...Any) {
if args := kit.Simple(arg...); len(args) == 0 {
m.Cmdy(INSERT, m.PrefixKey(), "", ZONE, m.Option(ZoneShort(m)), m.OptionSimple(ZoneField(m)))
} else if args[0] == ZoneKey(m) {
m.Cmdy(INSERT, m.PrefixKey(), "", ZONE, args[1:])
} else {
m.Cmdy(INSERT, m.PrefixKey(), "", ZONE, arg)
}
}
func ZoneModify(m *ice.Message, arg ...Any) {
if args := kit.Simple(arg...); m.Option(ID) == "" {
HashModify(m, arg...)
} else if args[0] == HASH || args[0] == ZoneShort(m) {
m.Cmdy(MODIFY, m.PrefixKey(), "", ZONE, args[1], args[3], args[4:])
} else {
m.Cmdy(MODIFY, m.PrefixKey(), "", ZONE, m.Option(ZoneKey(m)), m.Option(ID), arg)
}
}
func ZoneSelect(m *ice.Message, arg ...string) *ice.Message {
m.Fields(len(arg), kit.Fields(kit.MDB_TIME, m.Config(kit.MDB_SHORT), kit.MDB_COUNT), m.Config(kit.MDB_FIELD))
if m.Cmdy(SELECT, m.PrefixKey(), "", ZONE, arg); kit.Select("", arg, 0) == "" {
m.Sort(m.Config(kit.MDB_SHORT))
m.PushAction(REMOVE)
arg = kit.Slice(arg, 0, 2)
short, field, fields := Config(m, SHORT), Config(m, FIELD), ZoneField(m)
m.Fields(len(arg), kit.Select(kit.Fields(TIME, short, COUNT), field), fields)
if m.Cmdy(SELECT, m.PrefixKey(), "", ZONE, arg, logs.FileLineMeta(-1)); len(arg) == 0 {
m.Sort(short).PushAction(REMOVE).Action(CREATE)
} else if len(arg) == 1 {
m.Action(INSERT)
} else {
sortByField(m, fields)
}
return m
}
func ZoneExport(m *ice.Message, arg ...Any) {
kit.If(m.OptionFields() == "", func() { m.OptionFields(Config(m, SHORT), ZoneField(m)) })
m.Cmdy(EXPORT, m.PrefixKey(), "", ZONE, arg)
}
func ZoneImport(m *ice.Message, arg ...Any) {
m.Cmdy(IMPORT, m.PrefixKey(), "", ZONE, arg)
}
func ZoneSelects(m *ice.Message, arg ...string) *ice.Message {
m.OptionFields(ZoneField(m))
return ZoneSelect(m, arg...)
}
func ZoneSelectAll(m *ice.Message, arg ...string) *ice.Message {
m.Option(CACHE_LIMIT, "-1")
return ZoneSelect(m, arg...)
}
func ZoneSelectCB(m *ice.Message, zone string, cb Any) *ice.Message {
m.OptionCB(SELECT, cb)
m.Option(CACHE_LIMIT, "-1")
return ZoneSelect(m, zone)
}
func PageZoneSelect(m *ice.Message, arg ...string) *ice.Message {
OptionPages(m, kit.Slice(arg, 2)...)
arg = kit.Slice(arg, 0, 2)
if ZoneSelect(m, arg...); len(arg) == 0 {
m.Action(CREATE)
} else if len(arg) == 1 {
m.Action(INSERT, PAGE)
}
return m
}

View File

@ -1,9 +1,11 @@
package nfs
import (
"bufio"
"bytes"
"encoding/csv"
"encoding/json"
"io"
"io/ioutil"
"os"
"path"
"strings"
@ -14,144 +16,221 @@ import (
kit "shylinux.com/x/toolkits"
)
type ReadCloser struct {
r io.Reader
}
func (r *ReadCloser) Read(buf []byte) (int, error) {
return r.r.Read(buf)
}
func (r *ReadCloser) Close() error {
if c, ok := r.r.(io.Closer); ok {
return c.Close()
}
return nil
}
func NewReadCloser(r io.Reader) *ReadCloser {
return &ReadCloser{r: r}
}
func _cat_right(m *ice.Message, name string) bool {
switch ls := strings.Split(name, "/"); ls[0] {
case ice.USR:
switch kit.Select("", ls, 1) {
case "local":
if m.Warn(m.Option(ice.MSG_USERROLE) == aaa.VOID, ice.ErrNotRight, name) {
return false
}
}
case ice.ETC, ice.VAR:
if m.Warn(m.Option(ice.MSG_USERROLE) == aaa.VOID, ice.ErrNotRight, name) {
return false
}
}
return true
}
func _cat_find(m *ice.Message, name string) io.ReadCloser {
func _cat_find(m *ice.Message, p string) (io.ReadCloser, error) {
if m.Option(CAT_CONTENT) != "" {
return NewReadCloser(bytes.NewBufferString(m.Option(CAT_CONTENT)))
return NewReadCloser(bytes.NewBufferString(m.Option(CAT_CONTENT))), nil
}
if f, e := os.Open(path.Join(m.Option(DIR_ROOT), name)); e == nil {
return f
}
if m.Option(CAT_LOCAL) == ice.TRUE {
return nil
}
if b, ok := ice.Info.Pack[name]; ok {
m.Logs("binpack", len(b), name)
return NewReadCloser(bytes.NewBuffer(b))
}
msg := m.Cmd("spide", ice.DEV, "raw", "GET", path.Join("/share/local/", name))
if msg.Result(0) == ice.ErrWarn {
return NewReadCloser(bytes.NewBufferString(""))
}
return NewReadCloser(bytes.NewBufferString(msg.Result()))
return OpenFile(m, path.Join(m.Option(DIR_ROOT), p))
}
func _cat_list(m *ice.Message, name string) {
if !_cat_right(m, name) {
return // 没有权限
func _cat_hash(m *ice.Message, p string) (h string) {
Open(m, p, func(r io.Reader) { h = kit.Hashs(r) })
return
}
func _cat_line(m *ice.Message, p string) (n int) {
Open(m, p, func(r io.Reader) { kit.For(r, func(s string) { n++ }) })
return
}
func _cat_list(m *ice.Message, p string) {
if m.Option(CAT_CONTENT) == "" && !kit.IsIn(kit.Ext(p), "css", "js") && !aaa.Right(m, path.Join(m.Option(DIR_ROOT), p)) {
return
}
f := _cat_find(m, name)
if f == nil {
return // 没有文件
f, e := _cat_find(m, p)
if m.WarnNotFound(e, FILE, p) {
return
}
defer f.Close()
switch cb := m.Optionv(kit.Keycb(CAT)).(type) {
switch cb := m.OptionCB("").(type) {
case func(string, int) string:
list := []string{}
for bio, i := bufio.NewScanner(f), 0; bio.Scan(); i++ {
list = append(list, cb(bio.Text(), i))
}
kit.For(f, func(s string, i int) { list = append(list, cb(s, i)) })
m.Echo(strings.Join(list, ice.NL) + ice.NL)
case func([]string, string) string:
list := []string{}
kit.For(f, func(s string, i int) { list = append(list, cb(kit.Split(s), s)) })
m.Echo(strings.Join(list, ice.NL) + ice.NL)
case func(string, int):
for bio, i := bufio.NewScanner(f), 0; bio.Scan(); i++ {
cb(bio.Text(), i)
}
kit.For(f, cb)
case func(string):
for bio := bufio.NewScanner(f); bio.Scan(); {
cb(bio.Text())
kit.For(f, cb)
case func([]string, string):
kit.For(f, cb)
case func([]string):
kit.For(f, cb)
case nil:
if b, e := ioutil.ReadAll(f); !m.WarnNotFound(e) {
m.Echo(string(b)).StatusTime(FILE, p, SIZE, len(b))
}
default:
buf := make([]byte, ice.MOD_BUFS)
for begin := 0; true; {
if n, e := f.Read(buf[begin:]); !m.Warn(e, ice.ErrNotFound, name) {
m.Log_IMPORT(kit.MDB_FILE, name, kit.MDB_SIZE, n)
if begin += n; begin < len(buf) {
buf = buf[:begin]
break
}
buf = append(buf, make([]byte, ice.MOD_BUFS)...)
}
}
m.Echo(string(buf))
m.ErrorNotImplement(cb)
}
}
const (
PATH = "path"
FILE = "file"
LINE = "line"
SIZE = "size"
CAT_LOCAL = "cat_local"
CAT_CONTENT = "cat_content"
CONFIGURE = "configure"
STDIO = "stdio"
TAGS = "tags"
MODULE = "module"
SOURCE = "source"
TARGET = "target"
BINARY = "binary"
SCRIPT = "script"
FORMAT = "format"
TRANS = "trans"
CLONE = "clone"
REPOS = "repos"
REMOTE = "remote"
ORIGIN = "origin"
COMMIT = "commit"
BRANCH = "branch"
MASTER = "master"
VERSION = "version"
COMPILE = "compile"
)
const (
HTML = ice.HTML
CSS = ice.CSS
SVG = ice.SVG
JS = ice.JS
GO = ice.GO
SH = ice.SH
SHY = ice.SHY
CSV = ice.CSV
JSON = ice.JSON
MOD = "mod"
PROTO = "proto"
YAML = "yaml"
CONF = "conf"
XML = "xml"
YML = "yml"
TXT = "txt"
MD = "md"
PY = "py"
IMAGE = "image"
JPEG = "jpeg"
JPG = "jpg"
PNG = "png"
MP4 = "mp4"
MOV = "mov"
PDF = "pdf"
DF = ice.DF
PS = ice.PS
PT = ice.PT
)
const CAT = "cat"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
CAT: {Name: CAT, Help: "文件", Value: kit.Data(
kit.SSH_SOURCE, kit.Dict(
"sh", ice.TRUE, "shy", ice.TRUE, "py", ice.TRUE,
"go", ice.TRUE, "vim", ice.TRUE, "js", ice.TRUE,
"json", ice.TRUE, "conf", ice.TRUE, "yml", ice.TRUE,
"makefile", ice.TRUE, "license", ice.TRUE, "md", ice.TRUE,
),
)},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(mdb.RENDER, mdb.CREATE, CAT, m.Prefix(CAT))
}},
CAT: {Name: "cat path auto", Help: "文件", Action: map[string]*ice.Action{
mdb.RENDER: {Name: "render type name text", Help: "渲染", Hand: func(m *ice.Message, arg ...string) {
_cat_list(m, path.Join(arg[2], arg[1]))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) == 0 || strings.HasSuffix(arg[0], ice.PS) {
m.Cmdy(DIR, arg)
return
Index.MergeCommands(ice.Commands{
CAT: {Name: "cat path auto", Help: "文件", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: mdb.AutoConfig(SOURCE, kit.DictList(
HTML, CSS, JS, GO, SH, PY, SHY, CSV, JSON, CONFIGURE, PROTO, YAML, CONF, XML, YML, TXT, MD, strings.ToLower(ice.LICENSE), strings.ToLower(ice.MAKEFILE),
)),
}, DIR), Hand: func(m *ice.Message, arg ...string) {
if !DirList(m, arg...) {
if arg[0] != "" {
m.Logs(FIND, m.OptionSimple(DIR_ROOT), FILE, arg[0])
}
_cat_list(m, arg[0])
}
m.Info("dir_root: %v", m.Option(DIR_ROOT))
_cat_list(m, arg[0])
}},
}})
})
}
func DirList(m *ice.Message, arg ...string) bool {
if len(arg) == 0 || strings.HasSuffix(arg[0], PS) {
m.Cmdy(DIR, kit.Slice(arg, 0, 1))
return true
} else {
return false
}
}
func IsSourceFile(m *ice.Message, ext string) bool {
return mdb.Conf(m, Prefix(CAT), kit.Keym(SOURCE, ext)) == ice.TRUE
}
func OptionLoad(m *ice.Message, p string) *ice.Message {
Open(m, p, func(r io.Reader) {
var data ice.Any
m.Assert(json.NewDecoder(r).Decode(&data))
kit.For(data, func(k string, v ice.Any) { m.Optionv(k, v) })
})
return m
}
func Open(m *ice.Message, p string, cb ice.Any) {
if p == "" {
return
} else if strings.HasSuffix(p, PS) {
kit.If(p == PS, func() { p = "" })
if ls, e := ReadDir(m, p); !m.WarnNotFound(e) {
switch cb := cb.(type) {
case func([]os.FileInfo):
cb(ls)
case func(os.FileInfo):
kit.For(ls, cb)
case func(io.Reader, string):
kit.For(ls, func(s os.FileInfo) { kit.If(!s.IsDir(), func() { Open(m, path.Join(p, s.Name()), cb) }) })
default:
m.ErrorNotImplement(cb)
}
}
} else if f, e := OpenFile(m, p); !m.WarnNotFound(e, p) {
defer f.Close()
switch cb := cb.(type) {
case func(io.Reader, os.FileInfo):
s, _ := StatFile(m, p)
cb(f, s)
case func(io.Reader, string):
cb(f, p)
case func(io.Reader):
cb(f)
case func(string):
if b, e := ioutil.ReadAll(f); !m.WarnNotFound(e) {
cb(string(b))
}
default:
m.ErrorNotImplement(cb)
}
}
}
func ReadAll(m *ice.Message, r io.Reader) []byte {
if b, e := ioutil.ReadAll(r); !m.WarnNotFound(e) {
return b
}
return nil
}
func ReadFile(m *ice.Message, p string) (b []byte, e error) {
Open(m, p, func(r io.Reader) { b, e = ioutil.ReadAll(r) })
return
}
func Rewrite(m *ice.Message, p string, cb func(string) string) {
m.Cmd(SAVE, p, m.Cmdx(CAT, p, func(s string, i int) string { return cb(s) }))
}
func ScanCSV(m *ice.Message, file string, cb func([]string), arg ...string) {
f, e := OpenFile(m, file)
if m.Warn(e) {
return
}
r := csv.NewReader(f)
head, err := r.Read()
if err != nil {
return
}
index := []int{}
kit.If(len(arg) == 0, func() { arg = append(arg, head...) })
kit.For(arg, func(h string) { index = append(index, kit.IndexOf(head, h)) })
for {
data, err := r.Read()
if err != nil {
break
}
res := []string{}
kit.For(index, func(i int) { res = append(res, data[i]) })
cb(res)
}
}

View File

@ -1,13 +1,12 @@
package nfs
import (
"bufio"
"crypto/sha1"
"io/ioutil"
"os"
"path"
"regexp"
"runtime"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
@ -15,215 +14,314 @@ import (
kit "shylinux.com/x/toolkits"
)
func _dir_list(m *ice.Message, root string, name string, level int, deep bool, dir_type string, dir_reg *regexp.Regexp, fields []string) *ice.Message {
if !_cat_right(m, path.Join(root, name)) {
return m // 没有权限
}
fs, e := ioutil.ReadDir(path.Join(root, name))
if e != nil { // 单个文件
ls, _ := ioutil.ReadDir(path.Dir(path.Join(root, name)))
for _, k := range ls {
if k.Name() == path.Base(name) {
fs = append(fs, k)
}
}
name = path.Dir(name)
}
for i := 0; i < len(fs)-1; i++ {
for j := i + 1; j < len(fs); j++ {
if fs[i].Name() > fs[j].Name() {
fs[i], fs[j] = fs[j], fs[i]
}
func _dir_size(m *ice.Message, p string) (n int) {
Open(m, p+PS, func(ls []os.FileInfo) { n = len(ls) })
return
}
func _dir_hash(m *ice.Message, p string) (h string) {
list := []string{}
Open(m, p+PS, func(s os.FileInfo) { list = append(list, kit.Format("%s%d%s", s.Name(), s.Size(), s.ModTime())) })
kit.If(len(list) > 0, func() { h = kit.Hashs(list) })
return ""
}
func _dir_list(m *ice.Message, root string, dir string, level int, deep bool, dir_type string, dir_reg *regexp.Regexp, fields []string) (total int64, last time.Time) {
ls, _ := ReadDir(m, path.Join(root, dir))
if len(ls) == 0 {
if s, e := StatFile(m, path.Join(root, dir)); e == nil && !s.IsDir() {
Open(m, path.Dir(path.Join(root, dir))+PS, func(s os.FileInfo) { kit.If(s.Name() == path.Base(dir), func() { ls = append(ls, s) }) })
dir, deep = path.Dir(dir), false
}
}
for _, f := range fs {
if f.Name() == ice.PT || f.Name() == ".." {
for _, s := range ls {
if s.Name() == PT || s.Name() == ".." || strings.HasPrefix(s.Name(), PT) && dir_type != TYPE_ALL {
continue
}
if strings.HasPrefix(f.Name(), ice.PT) && dir_type != TYPE_ALL {
continue
}
p := path.Join(root, name, f.Name())
if !(dir_type == TYPE_CAT && f.IsDir() || dir_type == TYPE_DIR && !f.IsDir()) && (dir_reg == nil || dir_reg.MatchString(f.Name())) {
switch cb := m.Optionv(kit.Keycb(DIR)).(type) {
case func(p string):
p, pp := path.Join(root, dir, s.Name()), path.Join(dir, s.Name())
isDir := s.IsDir() || kit.IsDir(p) && deep == false
isBin := s.Mode().String()[3] == 'x' || kit.Ext(s.Name()) == "exe"
if !(dir_type == TYPE_BIN && (!isBin || isDir) || dir_type == TYPE_CAT && isDir || dir_type == TYPE_DIR && !isDir) && (dir_reg == nil || dir_reg.MatchString(s.Name())) {
switch cb := m.OptionCB("").(type) {
case func(os.FileInfo, string):
cb(s, p)
continue
case func(string):
cb(p)
continue
case nil:
default:
m.ErrorNotImplement(cb)
}
kit.If(s.ModTime().After(last), func() { last = s.ModTime() })
for _, field := range fields {
switch field {
case kit.MDB_TIME:
m.Push(field, f.ModTime().Format(ice.MOD_TIME))
case kit.MDB_TYPE:
m.Push(field, kit.Select(CAT, DIR, f.IsDir()))
case "tree":
case mdb.TIME:
m.Push(field, s.ModTime().Format(ice.MOD_TIME))
case mdb.TYPE:
m.Push(field, kit.Select(CAT, DIR, isDir))
case TREE:
if level == 0 {
m.Push(field, f.Name())
m.Push(field, s.Name())
} else {
m.Push(field, strings.Repeat("| ", level-1)+"|-"+f.Name())
m.Push(field, strings.Repeat("| ", level-1)+"|-"+s.Name())
}
case "full":
m.Push(field, path.Join(root, name, f.Name())+kit.Select("", ice.PS, f.IsDir()))
case kit.MDB_PATH:
m.Push(field, path.Join(name, f.Name())+kit.Select("", ice.PS, f.IsDir()))
case kit.MDB_FILE:
m.Push(field, f.Name()+kit.Select("", ice.PS, f.IsDir()))
case kit.MDB_NAME:
m.Push(field, f.Name())
case kit.MDB_SIZE:
if f.IsDir() {
if ls, e := ioutil.ReadDir(path.Join(root, name, f.Name())); e == nil {
m.Push(field, len(ls))
case FULL:
m.Push(field, p+kit.Select("", PS, isDir))
case PATH:
m.Push(field, pp+kit.Select("", PS, isDir))
case FILE:
m.Push(field, s.Name()+kit.Select("", PS, isDir))
case NAME:
m.Push(field, s.Name())
case SIZE:
if isDir {
m.Push(field, _dir_size(m, p))
} else {
m.Push(field, kit.FmtSize(s.Size()))
total += s.Size()
}
case LINE:
if isDir {
m.Push(field, _dir_size(m, p))
} else {
m.Push(field, _cat_line(m, p))
}
case mdb.HASH, "hashs":
h := ""
if isDir {
h = _dir_hash(m, p)
} else {
h = _cat_hash(m, p)
}
m.Push(mdb.HASH, kit.Select(h[:6], h[:], field == mdb.HASH))
case mdb.LINK:
if isDir {
m.Push(mdb.LINK, "")
} else {
if strings.Contains(p, "ice.windows") {
m.PushDownload(mdb.LINK, "ice.exe", p)
} else {
m.Push(field, 0)
}
} else {
m.Push(field, kit.FmtSize(f.Size()))
}
case kit.MDB_LINE:
if f.IsDir() {
if ls, e := ioutil.ReadDir(path.Join(root, name, f.Name())); e == nil {
m.Push(field, len(ls))
} else {
m.Push(field, 0)
}
} else {
nline := 0
if f, e := os.Open(p); m.Assert(e) {
defer f.Close()
for bio := bufio.NewScanner(f); bio.Scan(); nline++ {
bio.Text()
}
}
m.Push(field, nline)
}
case kit.MDB_HASH, "hashs":
var h [20]byte
if f.IsDir() {
if d, e := ioutil.ReadDir(p); m.Assert(e) {
meta := []string{}
for _, v := range d {
meta = append(meta, kit.Format("%s%d%s", v.Name(), v.Size(), v.ModTime()))
}
kit.Sort(meta)
h = sha1.Sum([]byte(strings.Join(meta, "")))
}
} else {
if f, e := ioutil.ReadFile(path.Join(name, f.Name())); m.Assert(e) {
h = sha1.Sum(f)
m.PushDownload(mdb.LINK, p)
}
}
m.Push(kit.MDB_HASH, kit.Select(kit.Format(h[:6]), kit.Format(h[:]), field == kit.MDB_HASH))
case kit.MDB_LINK:
m.PushDownload(kit.MDB_LINK, kit.Select("", f.Name(), !f.IsDir()), path.Join(root, name, f.Name()))
case "show":
p := kit.MergeURL2(m.Option(ice.MSG_USERWEB), "/share/local/"+path.Join(name, f.Name()), ice.POD, m.Option(ice.MSG_USERPOD))
switch kit.Ext(f.Name()) {
case "jpg", "png":
case mdb.SHOW:
switch p := m.MergeLink(SHARE_LOCAL+p, ice.POD, m.Option(ice.MSG_USERPOD)); kit.Ext(s.Name()) {
case PNG, JPG:
m.PushImages(field, p)
case "mp4":
case MP4:
m.PushVideos(field, p)
default:
m.Push(field, "")
}
case kit.MDB_ACTION:
case mdb.ACTION:
if m.IsCliUA() || m.Option(ice.MSG_USERROLE) == aaa.VOID {
break
}
m.PushButton(kit.Select("", TRASH, !f.IsDir()))
m.PushButton(mdb.SHOW, "rename", TRASH)
default:
m.Push(field, "")
}
}
}
if f.IsDir() && deep {
_dir_list(m, root, path.Join(name, f.Name()), level+1, deep, dir_type, dir_reg, fields)
if deep && isDir {
switch s.Name() {
case "pluged", "node_modules":
continue
}
_total, _last := _dir_list(m, root, pp, level+1, deep, dir_type, dir_reg, fields)
if total += _total; _last.After(last) {
last = _last
}
}
}
return m
}
func _dir_search(m *ice.Message, kind, name string) {
msg := _dir_list(m.Spawn(), ice.PWD, "", 0, true, TYPE_BOTH, nil, kit.Split("time,type,name"))
msg.Table(func(index int, value map[string]string, head []string) {
if !strings.Contains(value[kit.MDB_NAME], name) {
return
}
if value[kit.MDB_TYPE] == CAT {
value[kit.MDB_TYPE] = kit.Ext(value[kit.MDB_NAME])
}
m.PushSearch(ice.CMD, CAT, value)
})
}
func Dir(m *ice.Message, sort string) *ice.Message {
m.Option(DIR_TYPE, TYPE_DIR)
m.Copy(m.Cmd(DIR, ice.PWD).Sort(sort))
m.Option(DIR_TYPE, TYPE_CAT)
m.Copy(m.Cmd(DIR, ice.PWD).Sort(sort))
return m
return
}
const (
PWD = "./"
SRC = "src/"
ETC = "etc/"
BIN = "bin/"
VAR = "var/"
USR = "usr/"
SCAN = "scan"
GOWORK = "gowork"
PORTAL_GO = "portal.go"
PORTAL_JSON = "portal.json"
ETC_LOCAL_SH = "etc/local.sh"
ETC_CERT_KEY = "etc/cert/cert.key"
ETC_CERT_PEM = "etc/cert/cert.pem"
SRC_DOCUMENT = "src/document/"
SRC_PRIVATE = "src/private/"
SRC_MAIN_PNG = "src/main.png"
SRC_OPTION_GO = "src/option.go"
SRC_TEMPLATE = ice.SRC_TEMPLATE
USR_TOOLKITS = ice.USR_TOOLKITS
USR_ICEBERGS = ice.USR_ICEBERGS
USR_RELEASE = ice.USR_RELEASE
USR_PUBLISH = ice.USR_PUBLISH
USR_LOCAL = ice.USR_LOCAL
USR_LOCAL_WORK = ice.USR_LOCAL_WORK
USR_IMAGE = "usr/image/"
USR_MATERIAL = "usr/material/"
USR_LOCAL_IMAGE = "usr/local/image/"
USR_LEARNING_PORTAL = "usr/learning/portal/"
USR_MODULES = "usr/node_modules/"
USR_PACKAGE = "usr/package.json"
VAR_LOG_BENCH_LOG = "var/log/bench.log"
USR_ICONS_AVATAR = "usr/icons/avatar.jpg"
USR_ICONS_CONTEXTS = "usr/icons/contexts.jpg"
USR_ICONS_ICEBERGS = "usr/icons/icebergs.png"
USR_ICONS_VOLCANOS = "usr/icons/volcanos.png"
USR_ICONS = "usr/icons/"
V = "/v/"
M = "/m/"
P = "/p/"
X = "/x/"
S = "/s/"
C = "/c/"
INTSHELL = "/intshell/"
VOLCANOS = "/volcanos/"
VOLCANOS_PLUGIN = "/volcanos/plugin/"
REQUIRE_MODULES = "/require/modules/"
REQUIRE_USR = "/require/usr/"
REQUIRE_SRC = "/require/src/"
REQUIRE = "/require/"
PLUGIN = "/plugin/"
SHARE_LOCAL = "/share/local/"
PATHNAME = "pathname"
FILENAME = "filename"
CONTEXTS = "contexts"
TYPE_ALL = "all"
TYPE_BIN = "bin"
TYPE_CAT = "cat"
TYPE_DIR = "dir"
TYPE_BOTH = "both"
)
const (
DIR_ROOT = "dir_root"
DIR_TYPE = "dir_type"
DIR_DEEP = "dir_deep"
DIR_TYPE = "dir_type"
DIR_REG = "dir_reg"
DIR_DEF_FIELDS = "time,path,size,action"
DIR_WEB_FIELDS = "time,path,size,link,action"
DIR_CLI_FIELDS = "path,size,time"
ROOT = "root"
TREE = "tree"
FULL = "full"
PATH = "path"
FILE = "file"
NAME = "name"
SIZE = "size"
LINE = "line"
)
const DIR = "dir"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
DIR: {Name: DIR, Help: "目录", Value: kit.Data()},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(mdb.SEARCH, mdb.CREATE, DIR, m.Prefix(DIR))
m.Cmd(mdb.RENDER, mdb.CREATE, DIR, m.Prefix(DIR))
}},
DIR: {Name: "dir path field... auto upload", Help: "目录", Action: map[string]*ice.Action{
mdb.SEARCH: {Name: "search type name", Help: "搜索", Hand: func(m *ice.Message, arg ...string) {
if arg[0] == kit.MDB_FOREACH {
return
Index.MergeCommands(ice.Commands{
DIR: {Name: "dir path auto upload app", Icon: "dir.png", Help: "文件夹", Actions: ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
aaa.White(m, ice.MAKEFILE, ice.README_MD, ice.LICENSE)
aaa.White(m, ice.SRC, ice.BIN, ice.USR)
aaa.Black(m, ice.SRC_PRIVATE)
aaa.Black(m, ice.USR_LOCAL)
}},
ice.APP: {Help: "本机", Hand: func(m *ice.Message, arg ...string) {
switch runtime.GOOS {
case "darwin":
m.System("open", kit.Path(m.Option(PATH)))
}
_dir_search(m, arg[0], arg[1])
}},
mdb.RENDER: {Name: "render type name text", Help: "渲染", Hand: func(m *ice.Message, arg ...string) {
_dir_list(m, arg[2], arg[1], 0, m.Option(DIR_DEEP) == ice.TRUE, kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)),
nil, kit.Split(kit.Select("time,size,type,path", m.OptionFields())))
mdb.SHOW: {Help: "预览", Hand: func(m *ice.Message, arg ...string) {
Show(m.ProcessInner(), path.Join(m.Option(DIR_ROOT), m.Option(PATH)))
}}, mdb.UPLOAD: {},
SIZE: {Hand: func(m *ice.Message, arg ...string) {
m.Echo(kit.Select("", kit.Split(m.System("du", "-sh").Result()), 0))
}},
mdb.UPLOAD: {Name: "upload", Help: "上传", Hand: func(m *ice.Message, arg ...string) {
m.Upload(m.Option(kit.MDB_PATH))
"rename": {Name: "rename to", Hand: func(m *ice.Message, arg ...string) {
m.Cmd(MOVE, path.Join(path.Dir(m.Option(PATH)), m.Option(TO)), m.Option(PATH))
}},
TRASH: {Name: "trash", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(TRASH, m.Option(kit.MDB_PATH))
TRASH: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(TRASH, mdb.CREATE, m.Option(PATH))
}},
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
os.Remove(m.Option(kit.MDB_PATH))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if m.Option(DIR_ROOT) != "" {
m.Info("dir_root: %v", m.Option(DIR_ROOT))
}, Hand: func(m *ice.Message, arg ...string) {
root, dir := kit.Select(PWD, m.Option(DIR_ROOT)), kit.Select(PWD, arg, 0)
kit.If(strings.HasPrefix(dir, PS), func() { root = "" })
if !aaa.Right(m, path.Join(root, dir)) {
return
}
_dir_list(m, kit.Select(ice.PWD, m.Option(DIR_ROOT)), kit.Select(ice.PWD, arg, 0),
0, m.Option(DIR_DEEP) == ice.TRUE, kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)), kit.Regexp(m.Option(DIR_REG)),
kit.Split(kit.Select(kit.Select("time,path,size,action", m.OptionFields()), kit.Join(kit.Slice(arg, 1)))))
m.SortTimeR(kit.MDB_TIME)
m.Logs(FIND, DIR_ROOT, root, PATH, dir, m.OptionSimple(DIR_TYPE, DIR_REG))
fields := kit.Split(kit.Select(kit.Select(DIR_DEF_FIELDS, m.OptionFields()), kit.Join(kit.Slice(arg, 1))))
size, last := _dir_list(m, root, dir, 0, m.Option(DIR_DEEP) == ice.TRUE, kit.Select(TYPE_BOTH, m.Option(DIR_TYPE)), regexp.MustCompile(m.Option(DIR_REG)), fields)
kit.If(m.Option(DIR_ROOT), func() { m.Option(DIR_ROOT, path.Join(m.Option(DIR_ROOT))+PS) })
m.StatusTimeCount(mdb.TIME, last, SIZE, kit.FmtSize(size), m.OptionSimple(DIR_ROOT))
}},
}})
})
}
func Relative(m *ice.Message, p string) string {
if _p := kit.ExtChange(p, JS); Exists(m, _p) {
return _p
} else if _p := kit.ExtChange(path.Join(ice.USR_VOLCANOS, ice.PLUGIN_LOCAL, path.Join(kit.Slice(kit.Split(p, PS), -2)...)), JS); Exists(m, kit.Split(_p, "?")[0]) {
return _p
} else {
return p
}
}
func SplitPath(m *ice.Message, p string) []string {
if kit.HasPrefix(p, REQUIRE_SRC, REQUIRE_USR) {
p = strings.TrimPrefix(p, REQUIRE)
} else if kit.HasPrefix(p, REQUIRE) {
ls := kit.Split(p, PS)
return []string{ice.USR_REQUIRE + path.Join(ls[1:4]...) + PS, path.Join(ls[4:]...)}
} else if kit.HasPrefix(p, P) {
p = strings.TrimPrefix(p, P)
}
line := kit.Select("1", strings.Split(p, DF), 1)
p = strings.Split(p, DF)[0]
p = strings.Split(p, "?")[0]
if ls := kit.Split(kit.Select(ice.SRC_MAIN_GO, p), PS); len(ls) == 1 {
return []string{PWD, ls[0], line}
} else if ls[0] == ice.USR {
return []string{strings.Join(ls[:2], PS) + PS, strings.Join(ls[2:], PS), line}
} else {
return []string{strings.Join(ls[:1], PS) + PS, strings.Join(ls[1:], PS), line}
}
}
func Dir(m *ice.Message, field string) *ice.Message {
m.Copy(m.Cmd(DIR, PWD, kit.Dict(DIR_TYPE, TYPE_DIR)).Sort(field))
m.Copy(m.Cmd(DIR, PWD, kit.Dict(DIR_TYPE, TYPE_CAT)).Sort(field))
return m
}
func DirDeepAll(m *ice.Message, root, dir string, cb func(ice.Maps), arg ...string) *ice.Message {
m.Options(DIR_TYPE, CAT, DIR_ROOT, root, DIR_DEEP, ice.TRUE)
defer m.Options(DIR_TYPE, "", DIR_ROOT, "", DIR_DEEP, "")
if msg := m.Cmd(DIR, dir, arg); cb == nil {
return m.Copy(msg)
} else {
return msg.Table(cb)
}
}
func Show(m *ice.Message, file string) bool {
p := SHARE_LOCAL + file
kit.If(m.Option(ice.MSG_USERPOD), func(pod string) { p = kit.MergeURL(p, ice.POD, pod) })
switch strings.ToLower(kit.Ext(file)) {
case PNG, JPG, JPEG, "gif":
m.EchoImages(p)
case MP4, MOV:
m.EchoVideos(p)
default:
if IsSourceFile(m, kit.Ext(file)) {
m.Cmdy(CAT, file)
} else {
m.ProcessOpen(p)
return false
}
}
return true
}

40
base/nfs/document.go Normal file
View File

@ -0,0 +1,40 @@
package nfs
import (
"path"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const DOCUMENT = "document"
func init() {
Index.MergeCommands(ice.Commands{
DOCUMENT: {Name: "document index path auto", Help: "文档", Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
m.Cmdy(ice.COMMAND).Option(ice.MSG_DISPLAY, "")
return
}
m.Search(arg[0], func(p *ice.Context, c *ice.Context, key string, cmd *ice.Command) {
if p := DocumentPath(m); p != "" {
if len(kit.Slice(arg, 0, 2)) == 1 {
m.Cmdy(DIR, p)
} else {
m.Cmdy(CAT, arg[1])
}
}
})
}},
})
}
func Document(m *ice.Message, p string, arg ...ice.Any) string {
return kit.Renders(kit.Format(DocumentText(m, p), arg...), m)
}
var DocumentText = func(m *ice.Message, p string) string {
return m.Cmdx(CAT, DocumentPath(m, path.Base(p)))
}
var DocumentPath = func(m *ice.Message, arg ...string) string {
return path.Join(USR_LEARNING_PORTAL, m.PrefixKey(), path.Join(arg...))
}

25
base/nfs/find.go Normal file
View File

@ -0,0 +1,25 @@
package nfs
import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
const FIND = "find"
func init() {
const CMD_DIR = "cmd_dir"
Index.MergeCommands(ice.Commands{
FIND: {Name: "find word file auto", Help: "搜索", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 0, func() { arg = append(arg, "main.go") })
m.Options(mdb.VALUE, arg[0], CMD_DIR, kit.Select("", arg, 2))
msg := m.System(FIND, kit.Select(SRC, arg, 1), "-name", arg[0])
m.Echo(msg.FormatsMeta(nil))
kit.For(strings.Split(msg.Result(), ice.NL), func(s string) { m.Push(FILE, s) })
m.StatusTimeCount(kit.Dict(PATH, m.Option(CMD_DIR)))
}},
})
}

33
base/nfs/grep.go Normal file
View File

@ -0,0 +1,33 @@
package nfs
import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
const (
OPENS = "opens"
)
const GREP = "grep"
func init() {
const CMD_DIR = "cmd_dir"
Index.MergeCommands(ice.Commands{
GREP: {Name: "grep word file auto", Help: "搜索", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 0, func() { arg = append(arg, ice.MAIN) })
kit.If(len(arg) == 1, func() { arg = append(arg, ice.SRC) })
m.Options(mdb.VALUE, arg[0])
kit.For(kit.SplitLine(m.System(GREP, "--exclude=.[a-z]*", "--exclude-dir=.[a-z]*", "-rni", arg[0], kit.AddUniq([]string{}, arg[1:]...)).Result()), func(s string) {
if ls := strings.SplitN(s, DF, 3); len(ls) > 2 {
_ls := SplitPath(m, ls[0])
m.Push(PATH, _ls[0]).Push(FILE, _ls[1]).Push(LINE, ls[1]).Push(mdb.TEXT, ls[2])
}
})
m.Sort("path,file,line")
}},
})
}

43
base/nfs/hex.go Normal file
View File

@ -0,0 +1,43 @@
package nfs
import (
"compress/gzip"
"compress/zlib"
"encoding/hex"
"io"
"os"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
const HEX = "hex"
func init() {
Index.MergeCommands(ice.Commands{HEX: {Name: "hex path compress=raw,gzip,zlib size auto", Help: "二进制", Hand: func(m *ice.Message, arg ...string) {
if DirList(m, arg...) {
return
}
Open(m, arg[0], func(r io.Reader, s os.FileInfo) {
switch arg[1] {
case "gzip":
if g, e := gzip.NewReader(r); !m.WarnNotFound(e) {
r = g
}
case "zlib":
if z, e := zlib.NewReader(r); !m.WarnNotFound(e) {
r = z
}
}
buf := make([]byte, kit.Int(kit.Select("1024", arg, 2)))
n, _ := r.Read(buf)
kit.For(n, func(i int) {
kit.If(i%8 == 0, func() { m.Push(OFFSET, kit.Format("%04d", i)) })
m.Push(kit.Format(i%8), hex.EncodeToString(buf[i:i+1]))
kit.If(i%8 == 7, func() { m.Push(mdb.TEXT, string(buf[i-7:i+1])) })
})
m.StatusTime(mdb.TIME, s.ModTime().Format(ice.MOD_TIME), FILE, arg[0], SIZE, kit.FmtSize(s.Size()))
})
}}})
}

View File

@ -2,8 +2,14 @@ package nfs
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
var Index = &ice.Context{Name: "nfs", Help: "存储模块"}
const NFS = "nfs"
func init() { ice.Index.Register(Index, nil, CAT, DIR, TAIL, TRASH, SAVE, PUSH, COPY, LINK, DEFS) }
var Index = &ice.Context{Name: NFS, Help: "存储模块"}
func init() {
ice.Index.Register(Index, nil, ZIP, TAR, CAT, DIR, PACK, DEFS, SAVE, PUSH, COPY, LINK, GREP, FIND, MOVE, MOVETO, TRASH)
}
func Prefix(arg ...string) string { return kit.Keys(NFS, arg) }

View File

@ -1,7 +0,0 @@
chapter "nfs"
field "文件" nfs.cat
field "目录" nfs.dir
field "跟踪" nfs.tail
field "删除" nfs.trash

134
base/nfs/pack.go Normal file
View File

@ -0,0 +1,134 @@
package nfs
import (
"io"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/file"
)
const PACK = "pack"
func init() {
Index.MergeCommands(ice.Commands{
PACK: {Name: "pack path auto create upload", Help: "文件系统", Actions: ice.Actions{
mdb.CREATE: {Name: "create path*=src/hi/hi.txt text*=hello", Hand: func(m *ice.Message, arg ...string) {
OptionFiles(m, PackFile)
Create(m, m.Option(PATH), func(w io.Writer, p string) {
Save(m, w, m.Option(mdb.TEXT), func(n int) { m.Logs(LOAD, FILE, p, SIZE, n) })
})
}},
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) { PackFile.Remove(path.Clean(m.Option(PATH))) }},
mdb.EXPORT: {Hand: func(m *ice.Message, arg ...string) {
OptionFiles(m, PackFile)
Open(m, path.Join(m.Option(PATH), m.Option(FILE)), func(r io.Reader, p string) {
OptionFiles(m, DiskFile)
Create(m, p, func(w io.Writer) { Copy(m, w, r, func(n int) { m.Logs(LOAD, FILE, p, SIZE, n) }) })
})
}},
mdb.IMPORT: {Hand: func(m *ice.Message, arg ...string) {
OptionFiles(m, DiskFile)
Open(m, path.Join(m.Option(PATH), m.Option(FILE)), func(r io.Reader, p string) {
OptionFiles(m, PackFile)
Create(m, p, func(w io.Writer) { Copy(m, w, r, func(n int) { m.Logs(LOAD, FILE, p, SIZE, n) }) })
})
}},
}, Hand: func(m *ice.Message, arg ...string) {
OptionFiles(m, PackFile)
if p := kit.Select("", arg, 0); p != "" && !strings.HasSuffix(p, PS) {
Open(m, p, func(r io.Reader) { m.Echo(string(ReadAll(m, r))) })
} else {
Open(m, path.Join(p)+PS, func(s os.FileInfo) {
m.Push(mdb.TIME, s.ModTime().Format(ice.MOD_TIME))
m.Push(PATH, path.Join(p, s.Name())+kit.Select("", PS, s.IsDir()))
m.Push(SIZE, kit.FmtSize(s.Size()))
})
m.PushAction(mdb.REMOVE)
}
}},
})
}
var DiskFile = file.NewDiskFile()
var PackFile = file.NewPackFile()
func init() { file.Init(OptionFiles(ice.Pulse, DiskFile, PackFile)) }
func OptionFiles(m *ice.Message, f ...file.File) file.File {
if len(f) > 1 {
m.Optionv(ice.MSG_FILES, file.NewMultiFile(f...))
} else if len(f) > 0 {
m.Optionv(ice.MSG_FILES, f[0])
}
return m.Optionv(ice.MSG_FILES).(file.File)
}
func StatFile(m *ice.Message, p string) (os.FileInfo, error) { return OptionFiles(m).StatFile(p) }
func OpenFile(m *ice.Message, p string) (io.ReadCloser, error) { return OptionFiles(m).OpenFile(p) }
func CreateFile(m *ice.Message, p string) (io.WriteCloser, string, error) {
return OptionFiles(m).CreateFile(p)
}
func AppendFile(m *ice.Message, p string) (io.ReadWriteCloser, string, error) {
w, e := OptionFiles(m).AppendFile(p)
return w, p, e
}
func WriteFile(m *ice.Message, p string, b []byte) error { return OptionFiles(m).WriteFile(p, b) }
func ReadDir(m *ice.Message, p string) ([]os.FileInfo, error) {
list, e := OptionFiles(m).ReadDir(p)
for i := 0; i < len(list)-1; i++ {
for j := i + 1; j < len(list); j++ {
if list[i].IsDir() && !list[j].IsDir() {
continue
} else if !list[i].IsDir() && list[j].IsDir() || list[i].Name() > list[j].Name() {
list[i], list[j] = list[j], list[i]
}
}
}
return list, e
}
func MkdirAll(m *ice.Message, p string) string {
OptionFiles(m).MkdirAll(p, ice.MOD_DIR)
return p
}
func RemoveAll(m *ice.Message, p string) error { return OptionFiles(m).RemoveAll(p) }
func Remove(m *ice.Message, p string) error { return OptionFiles(m).Remove(p) }
func Rename(m *ice.Message, oldname string, newname string) error {
MkdirAll(m, path.Dir(newname))
return OptionFiles(m).Rename(oldname, newname)
}
func Symlink(m *ice.Message, oldname string, newname string) error {
return OptionFiles(m).Symlink(oldname, newname)
}
func Link(m *ice.Message, oldname string, newname string) error {
return OptionFiles(m).Link(oldname, newname)
}
func Exists(m *ice.Message, p string, cb ...func(string)) bool {
if _, e := OptionFiles(m).StatFile(p); e == nil {
for _, cb := range cb {
cb(p)
}
return true
}
return false
}
func ExistsFile(m *ice.Message, p string) bool {
if s, e := OptionFiles(m).StatFile(p); e == nil && !s.IsDir() {
return true
}
return false
}
func NewReadCloser(r io.Reader) io.ReadCloser { return file.NewReadCloser(r) }
func NewWriteCloser(w func([]byte) (int, error), c func() error) io.WriteCloser {
return file.NewWriteCloser(w, c)
}
func Close(m *ice.Message, p ice.Any) {
if w, ok := p.(io.Closer); ok {
w.Close()
}
}

View File

@ -1,108 +1,228 @@
package nfs
import (
"fmt"
"io"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _save_file(m *ice.Message, name string, text ...string) {
if f, p, e := kit.Create(path.Join(m.Option(DIR_ROOT), name)); m.Assert(e) {
defer f.Close()
for _, v := range text {
if n, e := f.WriteString(v); m.Assert(e) {
m.Log_EXPORT(kit.MDB_FILE, p, kit.MDB_SIZE, n)
}
}
m.Echo(p)
}
}
func _defs_file(m *ice.Message, name string, text ...string) {
if _, e := os.Stat(path.Join(m.Option(DIR_ROOT), name)); os.IsNotExist(e) {
_save_file(m, name, text...)
}
}
func _push_file(m *ice.Message, name string, text ...string) {
p := path.Join(m.Option(DIR_ROOT), name)
if strings.Contains(p, ice.PS) {
os.MkdirAll(path.Dir(p), ice.MOD_DIR)
}
if f, e := os.OpenFile(p, os.O_WRONLY|os.O_APPEND|os.O_CREATE, ice.MOD_FILE); m.Assert(e) {
defer f.Close()
for _, k := range text {
if n, e := f.WriteString(k); m.Assert(e) {
m.Log_EXPORT(kit.MDB_FILE, p, kit.MDB_SIZE, n)
}
}
m.Echo(p)
}
}
func _copy_file(m *ice.Message, name string, from ...string) {
if f, p, e := kit.Create(path.Join(m.Option(DIR_ROOT), name)); m.Assert(e) {
defer f.Close()
for _, v := range from {
if s, e := os.Open(v); !m.Warn(e, ice.ErrNotFound, name) {
defer s.Close()
if n, e := io.Copy(f, s); !m.Warn(e, ice.ErrNotFound, name) {
m.Log_IMPORT(kit.MDB_FILE, v, kit.MDB_SIZE, n)
m.Log_EXPORT(kit.MDB_FILE, p, kit.MDB_SIZE, n)
}
}
}
m.Echo(p)
}
}
func _link_file(m *ice.Message, name string, from string) {
if from == "" {
if s, e := os.Stat(path.Join(m.Option(DIR_ROOT), name)); e == nil && s.Size() > 0 {
return
}
os.Remove(name)
os.MkdirAll(path.Dir(name), ice.MOD_DIR)
m.Warn(os.Link(from, name), ice.ErrNotFound, from)
m.Echo(name)
for i, v := range text {
if b, e := kit.Render(v, m); !m.WarnNotValid(e) {
text[i] = string(b)
}
}
_save_file(m, name, text...)
}
func _save_file(m *ice.Message, name string, text ...string) {
Create(m, path.Join(m.Option(DIR_ROOT), name), func(w io.Writer, p string) {
defer m.Echo(p)
kit.For(text, func(s string) { Save(m, w, s, func(n int) { m.Logs(SAVE, FILE, p, SIZE, n) }) })
})
}
func _push_file(m *ice.Message, name string, text ...string) {
Append(m, path.Join(m.Option(DIR_ROOT), name), func(w io.Writer, p string) {
defer m.Echo(p)
kit.For(text, func(s string) { Save(m, w, s, func(n int) { m.Logs(SAVE, FILE, p, SIZE, n) }) })
})
}
func _copy_file(m *ice.Message, name string, from ...string) {
Create(m, path.Join(m.Option(DIR_ROOT), name), func(w io.Writer, p string) {
defer m.Echo(p)
kit.For(from, func(f string) {
Open(m, path.Join(m.Option(DIR_ROOT), f), func(r io.Reader) {
Copy(m, w, r, func(n int) { m.Logs(LOAD, FILE, f, SIZE, n).Logs(SAVE, FILE, p, SIZE, n) })
})
})
})
}
func _link_file(m *ice.Message, name string, from string) {
if m.WarnNotValid(from == "", FROM) {
return
}
name = path.Join(m.Option(DIR_ROOT), name)
from = path.Join(m.Option(DIR_ROOT), from)
if m.WarnNotFound(!Exists(m, from), from) {
return
}
Remove(m, name)
if MkdirAll(m, path.Dir(name)); m.WarnNotValid(Link(m, from, name)) && m.WarnNotValid(Symlink(m, from, name), from) {
return
}
m.Logs(SAVE, FILE, name, FROM, from).Echo(name)
}
const SAVE = "save"
const (
CONTENT = "content"
OFFSET = "offset"
ALIAS = "alias"
FROM = "from"
TO = "to"
)
const DEFS = "defs"
const SAVE = "save"
const PUSH = "push"
const COPY = "copy"
const LINK = "link"
const LOAD = "load"
const MOVE = "move"
const MOVETO = "moveto"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
SAVE: {Name: "save file text...", Help: "保存", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) == 1 {
arg = append(arg, m.Option(kit.MDB_CONTENT))
}
_save_file(m, arg[0], arg[1:]...)
}},
DEFS: {Name: "defs file text...", Help: "默认", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
Index.MergeCommands(ice.Commands{
DEFS: {Name: "defs file text run", Help: "默认", Hand: func(m *ice.Message, arg ...string) {
OptionFiles(m, DiskFile)
_defs_file(m, arg[0], arg[1:]...)
}},
PUSH: {Name: "push file text...", Help: "追加", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) == 1 {
arg = append(arg, m.Option(kit.MDB_CONTENT))
}
SAVE: {Name: "save file text run", Help: "保存", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 1, func() { arg = append(arg, m.Option(CONTENT)) })
_save_file(m, arg[0], arg[1:]...)
}},
PUSH: {Name: "push file text run", Help: "追加", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) == 1, func() { arg = append(arg, m.Option(CONTENT)) })
_push_file(m, arg[0], arg[1:]...)
}},
COPY: {Name: "copy file from...", Help: "复制", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
for _, file := range arg[1:] {
if _, e := os.Stat(file); e == nil {
_copy_file(m, arg[0], arg[1:]...)
}
}
COPY: {Name: "copy file from run", Help: "复制", Hand: func(m *ice.Message, arg ...string) {
kit.If(len(arg) > 1 && Exists(m, arg[1]), func() { _copy_file(m, arg[0], arg[1:]...) })
}},
LINK: {Name: "link file from", Help: "链接", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
LINK: {Name: "link file from run", Help: "链接", Hand: func(m *ice.Message, arg ...string) {
_link_file(m, arg[0], arg[1])
}},
}})
MOVE: {Name: "move file from run", Help: "移动", Hand: func(m *ice.Message, arg ...string) {
arg[1] = path.Join(m.Option(DIR_ROOT), arg[1])
arg[0] = path.Join(m.Option(DIR_ROOT), arg[0])
Rename(m, arg[1], arg[0])
}},
MOVETO: {Name: "moveto path from run", Help: "移动到", Hand: func(m *ice.Message, arg ...string) {
kit.For(arg[1:], func(from string) { m.Cmd(MOVE, path.Join(arg[0], path.Base(from)), from) })
}},
})
}
func Create(m *ice.Message, p string, cb ice.Any) string {
if f, p, e := CreateFile(m, p); !m.WarnNotValid(e) {
defer f.Close()
switch cb := cb.(type) {
case func(io.Writer, string):
cb(f, p)
case func(io.Writer):
cb(f)
default:
m.ErrorNotImplement(cb)
}
}
return p
}
func Append(m *ice.Message, p string, cb ice.Any) {
if f, p, e := AppendFile(m, p); !m.WarnNotValid(e) {
defer f.Close()
switch cb := cb.(type) {
case func(io.Writer, string):
cb(f, p)
case func(io.Writer):
cb(f)
default:
m.ErrorNotImplement(cb)
}
}
}
func Save(m *ice.Message, w io.Writer, s string, cb ice.Any) {
switch content := m.Optionv(CONTENT).(type) {
case io.Reader:
io.Copy(w, content)
return
}
if n, e := fmt.Fprint(w, s); !m.WarnNotValid(e) {
switch cb := cb.(type) {
case func(int):
cb(n)
default:
m.ErrorNotImplement(cb)
}
}
}
func Copy(m *ice.Message, w io.Writer, r io.Reader, cb ice.Any) {
if n, e := io.Copy(w, r); !m.WarnNotValid(e) {
switch cb := cb.(type) {
case func(int):
cb(int(n))
default:
m.ErrorNotImplement(cb)
}
}
}
func CopyStream(m *ice.Message, to io.Writer, from io.Reader, cache, total int, cb ice.Any) {
kit.If(total == 0, func() { total = 1 })
count, buf := 0, make([]byte, cache)
for {
n, e := from.Read(buf)
to.Write(buf[0:n])
if count += n; count > total {
total = count
}
switch value := count * 100 / total; cb := cb.(type) {
case func(int, int, int):
cb(count, total, value)
case func(int, int):
cb(count, total)
case nil:
default:
m.ErrorNotImplement(cb)
}
if e == io.EOF || m.WarnNotValid(e) {
break
}
}
}
func CopyFile(m *ice.Message, to, from string, cb func([]byte, int) []byte) {
Open(m, from, func(r io.Reader) {
Create(m, to, func(w io.Writer) {
offset, buf := 0, make([]byte, 1024*1024)
for {
n, _ := r.Read(buf)
if n, _ = w.Write(cb(buf[:n], offset)); n == 0 {
break
}
offset += n
}
m.Logs(SAVE, FILE, to, FROM, from, SIZE, offset)
})
})
}
func Pipe(m *ice.Message, cb ice.Any) io.WriteCloser {
r, w := io.Pipe()
switch cb := cb.(type) {
case func(string):
m.Go(func() { kit.For(r, cb) })
case func([]byte):
m.Go(func() {
buf := make([]byte, ice.MOD_BUFS)
for {
n, e := r.Read(buf)
if cb(buf[:n]); e != nil {
break
}
}
})
default:
}
return w
}
func TempName(m *ice.Message) string {
return m.Cmdx(SAVE, path.Join(ice.VAR_TMP, kit.Hashs(mdb.UNIQ)), "")
}
func Temp(m *ice.Message, cb func(p string)) {
p := TempName(m)
defer os.Remove(p)
cb(p)
}
var ImageResize = func(m *ice.Message, p string, height, width uint) bool { return false }

View File

@ -1,82 +0,0 @@
package nfs
import (
"bufio"
"io"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _tail_create(m *ice.Message, arg ...string) {
h := m.Cmdx(mdb.INSERT, m.PrefixKey(), "", mdb.HASH, arg)
kit.ForEach(kit.Split(m.Option(FILE)), func(file string) {
r, w := io.Pipe()
m.Go(func() {
for bio := bufio.NewScanner(r); bio.Scan(); {
m.Log_IMPORT(kit.MDB_FILE, file, kit.MDB_SIZE, len(bio.Text()))
m.Grow(TAIL, kit.Keys(kit.MDB_HASH, h), kit.Dict(
kit.MDB_FILE, file, kit.MDB_SIZE, len(bio.Text()), kit.MDB_TEXT, bio.Text(),
))
}
})
m.Option(cli.CMD_OUTPUT, w)
m.Option(cli.CMD_ERRPUT, w)
m.Option(mdb.CACHE_CLEAR_ON_EXIT, ice.TRUE)
m.Cmd(cli.DAEMON, TAIL, "-n", "0", "-f", file)
})
}
func _tail_count(m *ice.Message, name string) string {
return m.Conf(TAIL, kit.KeyHash(name, kit.Keym(kit.MDB_COUNT)))
}
const TAIL = "tail"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
TAIL: {Name: TAIL, Help: "日志流", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_NAME, kit.MDB_FIELD, "time,id,file,text",
)},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Richs(TAIL, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
value, _ = kit.GetMeta(value), m.Option(kit.MDB_HASH, key)
m.Cmd(TAIL, mdb.CREATE, kit.MDB_FILE, kit.Format(value[kit.MDB_FILE]), kit.MDB_NAME, kit.Format(value[kit.MDB_NAME]))
})
}},
TAIL: {Name: "tail name id auto page filter:text create", Help: "日志流", Action: ice.MergeAction(map[string]*ice.Action{
mdb.INPUTS: {Name: "inputs", Help: "补全", Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case FILE:
m.Cmdy(DIR, kit.Select(ice.PWD, arg, 1), PATH).RenameAppend(PATH, FILE)
m.ProcessAgain()
case kit.MDB_NAME:
m.Push(arg[0], kit.Split(m.Option(FILE), ice.PS))
case kit.MDB_LIMIT:
m.Push(arg[0], kit.List("10", "20", "30", "50"))
}
}},
mdb.CREATE: {Name: "create file name", Help: "创建", Hand: func(m *ice.Message, arg ...string) {
_tail_create(m, arg...)
}},
}, mdb.ZoneAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Fields(len(kit.Slice(arg, 0, 2)), "time,name,count,file", m.Config(kit.MDB_FIELD))
m.OptionPage(kit.Slice(arg, 2)...)
mdb.ZoneSelect(m.Spawn(c), arg...).Table(func(index int, value map[string]string, head []string) {
if strings.Contains(value[kit.MDB_TEXT], m.Option(ice.CACHE_FILTER)) {
m.Push("", value, head)
}
})
if len(arg) > 0 {
m.StatusTimeCountTotal(_tail_count(m, arg[0]))
}
}},
}})
}

112
base/nfs/tar.go Normal file
View File

@ -0,0 +1,112 @@
package nfs
import (
"archive/tar"
"compress/gzip"
"io"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _tar_list(m *ice.Message, p string, cb func(*tar.Header, io.Reader, int)) {
Open(m, p, func(r io.Reader) {
for {
switch kit.Ext(p) {
case TGZ:
p = kit.Keys(kit.TrimExt(p, kit.Ext(p)), TAR, GZ)
case GZ:
if f, e := gzip.NewReader(r); m.WarnNotValid(e, p) {
return
} else {
defer f.Close()
r, p = f, kit.TrimExt(p, GZ)
}
case TAR:
i := 0
for r := tar.NewReader(r); ; i++ {
h, e := r.Next()
if m.WarnNotValid(e) || e == io.EOF {
break
}
if h.Size == 0 {
i--
continue
}
cb(h, r, i)
}
m.StatusTimeCount(mdb.TOTAL, i)
return
default:
return
}
}
})
}
const (
XZ = "xz"
GZ = "gz"
TGZ = "tgz"
)
const TAR = "tar"
func init() {
Index.MergeCommands(ice.Commands{
TAR: {Name: "tar path file auto page", Help: "打包", Actions: ice.MergeActions(ice.Actions{
mdb.NEXT: {Hand: func(m *ice.Message, arg ...string) { mdb.PrevPage(m, arg[0], kit.Slice(arg, 1)...) }},
mdb.PREV: {Hand: func(m *ice.Message, arg ...string) { mdb.NextPageLimit(m, arg[0], kit.Slice(arg, 1)...) }},
mdb.EXPORT: {Hand: func(m *ice.Message, arg ...string) {
if kit.Ext(m.Option(PATH)) == ZIP {
m.Cmdy(ZIP, mdb.EXPORT, arg)
return
}
list, size := kit.Dict(), 0
_tar_list(m, m.Option(PATH), func(h *tar.Header, r io.Reader, i int) {
if h.Name == m.Option(FILE) || m.Option(FILE) == "" {
p := path.Join(path.Dir(m.Option(PATH)), h.Name)
if strings.HasSuffix(h.Name, PS) {
MkdirAll(m, p)
return
}
kit.IfNoKey(list, path.Dir(p), func(p string) { MkdirAll(m, p) })
Create(m, p, func(w io.Writer) {
os.Chmod(p, os.FileMode(h.Mode))
Copy(m, w, r, func(n int) { size += n })
kit.If(m.Option(FILE), func() { m.Cmdy(DIR, p).Cmdy(CAT, p) })
})
}
})
}},
}, mdb.PageListAction()), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 || strings.HasSuffix(arg[0], PS) {
m.Cmdy(DIR, arg)
return
}
if kit.Ext(arg[0]) == ZIP {
m.Cmdy(ZIP, arg)
return
}
page, size := mdb.OptionPages(m, kit.Slice(arg, 2)...)
_tar_list(m, arg[0], func(h *tar.Header, r io.Reader, i int) {
if len(kit.Slice(arg, 0, 2)) > 1 {
if h.Name != arg[1] {
return
}
m.Echo(string(ReadAll(m, r)[:]))
}
if i >= (page-1)*size && i < page*size {
m.Push(mdb.TIME, h.ModTime.Format(ice.MOD_TIME)).Push(FILE, h.Name).Push(SIZE, kit.FmtSize(h.Size))
}
})
m.PushAction(mdb.EXPORT)
}},
})
}
func TarExport(m *ice.Message, path string, file ...string) {
m.Cmd(TAR, mdb.EXPORT, ice.Maps{PATH: path, FILE: kit.Select("", file, 0)})
}

55
base/nfs/template.go Normal file
View File

@ -0,0 +1,55 @@
package nfs
import (
"path"
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const TEMPLATE = "template"
func init() {
Index.MergeCommands(ice.Commands{
TEMPLATE: {Name: "template index path auto", Help: "模板", Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
ice.AddRender(ice.RENDER_TEMPLATE, func(m *ice.Message, args ...ice.Any) string {
return Template(m, kit.Format(args[0]), args[1:]...)
})
}},
}), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
m.Cmdy(ice.COMMAND).Option(ice.MSG_DISPLAY, "")
return
}
m.Search(arg[0], func(p *ice.Context, c *ice.Context, key string, cmd *ice.Command) {
if p := TemplatePath(m); p != "" {
if len(kit.Slice(arg, 0, 2)) == 1 {
m.Cmdy(DIR, p)
} else {
m.Cmdy(CAT, arg[1])
}
}
})
}},
})
}
func init() { ice.Info.Template = Template }
func Template(m *ice.Message, p string, data ...ice.Any) string {
if text := TemplateText(m, p); text == "" {
return ""
} else if len(data) == 0 {
return kit.Renders(text, m)
} else {
return kit.Renders(text, data[0])
}
}
var TemplateText = func(m *ice.Message, p string) string {
return m.Cmdx(CAT, kit.Select(TemplatePath(m, path.Base(p)), m.Option("_template")))
}
var TemplatePath = func(m *ice.Message, arg ...string) string {
return path.Join(ice.SRC_TEMPLATE, m.PrefixKey(), path.Join(arg...))
}

View File

@ -1,65 +1,51 @@
package nfs
import (
"os"
"io"
"path"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _trash_create(m *ice.Message, name string) {
if s, e := os.Stat(name); e == nil {
if s.IsDir() {
tar := path.Base(name) + ".tar.gz"
m.Cmd(cli.SYSTEM, "tar", "zcf", tar, name)
name = tar
}
if f, e := os.Open(name); m.Assert(e) {
defer f.Close()
h := kit.Hashs(f)
p := path.Join(m.Config(kit.MDB_PATH), h[:2], h)
os.MkdirAll(path.Dir(p), ice.MOD_DIR)
os.Rename(name, p)
m.Cmdy(mdb.INSERT, TRASH, "", mdb.HASH, kit.MDB_FILE, p, kit.MDB_FROM, name)
}
func _trash_create(m *ice.Message, from string) {
if m.WarnNotValid(from == "", FROM) {
return
}
s, e := StatFile(m, from)
if m.WarnNotFound(e, from) {
return
}
defer Remove(m, from)
p := path.Join(ice.VAR_TRASH, path.Base(from))
kit.If(!s.IsDir(), func() { Open(m, from, func(r io.Reader) { p = path.Join(ice.VAR_TRASH, kit.HashsPath(r)) }) })
RemoveAll(m, p)
kit.If(!m.WarnNotValid(Rename(m, from, p)), func() { mdb.HashCreate(m, FROM, kit.Paths(from), FILE, p) })
}
const TRASH = "trash"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
TRASH: {Name: TRASH, Help: "回收站", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_FROM, kit.MDB_FIELD, "time,hash,file,from",
kit.MDB_PATH, ice.VAR_TRASH,
)},
}, Commands: map[string]*ice.Command{
TRASH: {Name: "trash hash auto prunes", Help: "回收站", Action: ice.MergeAction(map[string]*ice.Action{
mdb.REVERT: {Name: "revert", Help: "恢复", Hand: func(m *ice.Message, arg ...string) {
os.Rename(m.Option(kit.MDB_FILE), m.Option(kit.MDB_FROM))
m.Cmd(mdb.DELETE, TRASH, "", mdb.HASH, m.OptionSimple(kit.MDB_HASH))
Index.MergeCommands(ice.Commands{
TRASH: {Name: "trash hash auto", Help: "回收站", Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Hand: func(m *ice.Message, arg ...string) {
_trash_create(m, kit.Paths(m.Option(FROM)))
}},
mdb.REMOVE: {Name: "remove", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
os.Remove(m.Option(kit.MDB_FILE))
m.Cmd(mdb.DELETE, TRASH, "", mdb.HASH, m.OptionSimple(kit.MDB_HASH))
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) {
Remove(m, m.Option(FILE))
mdb.HashRemove(m, m.OptionSimple(mdb.HASH))
}},
mdb.PRUNES: {Name: "prunes before@date", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
mdb.HashPrunes(m, func(value map[string]string) bool {
os.Remove(value[kit.MDB_FILE])
return false
})
mdb.REVERT: {Help: "恢复", Icon: "bi bi-folder-symlink", Hand: func(m *ice.Message, arg ...string) {
msg := mdb.HashSelect(m.Spawn(), m.Option(mdb.HASH))
Rename(m, msg.Append(FILE), msg.Append(FROM))
mdb.HashRemove(m, m.OptionSimple(mdb.HASH))
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if mdb.HashSelect(m, arg...); len(arg) == 0 || m.Length() > 0 {
m.PushAction(mdb.REVERT, mdb.REMOVE)
return
}
_trash_create(m, arg[0])
}},
}})
mdb.PRUNES: {Hand: func(m *ice.Message, arg ...string) {
mdb.HashPrunes(m, nil).Table(func(value ice.Maps) { Remove(m, value[FILE]) })
}},
}, mdb.HashAction(mdb.SHORT, FROM, mdb.FIELD, "time,hash,from,file", mdb.ACTION, mdb.REVERT))},
})
}
func Trash(m *ice.Message, p string, arg ...string) *ice.Message { return m.Cmd(TRASH, mdb.CREATE, p) }

72
base/nfs/zip.go Normal file
View File

@ -0,0 +1,72 @@
package nfs
import (
"archive/zip"
"io"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
func _zip_list(m *ice.Message, p string, cb func(zip.FileHeader, io.Reader, int)) {
if f, e := zip.OpenReader(p); m.WarnNotFound(e, p) {
return
} else {
defer f.Close()
for i, f := range f.File {
if r, e := f.Open(); e == nil {
defer r.Close()
cb(f.FileHeader, r, i)
}
}
}
}
const ZIP = "zip"
func init() {
Index.MergeCommands(ice.Commands{
ZIP: {Name: "zip path file auto page", Help: "打包", Actions: ice.MergeActions(ice.Actions{
mdb.NEXT: {Hand: func(m *ice.Message, arg ...string) { mdb.PrevPage(m, arg[0], kit.Slice(arg, 1)...) }},
mdb.PREV: {Hand: func(m *ice.Message, arg ...string) { mdb.NextPageLimit(m, arg[0], kit.Slice(arg, 1)...) }},
mdb.EXPORT: {Hand: func(m *ice.Message, arg ...string) {
list, size := kit.Dict(), 0
_zip_list(m, m.Option(PATH), func(h zip.FileHeader, r io.Reader, i int) {
p := path.Join(path.Dir(m.Option(PATH)), kit.Split(path.Base(m.Option(PATH)), "_-.")[0], h.Name)
if strings.HasSuffix(h.Name, PS) {
MkdirAll(m, p)
return
}
kit.IfNoKey(list, path.Dir(p), func(p string) { MkdirAll(m, p) })
Create(m, p, func(w io.Writer) {
os.Chmod(p, os.FileMode(h.Mode()))
Copy(m, w, r, func(n int) { size += n })
kit.If(m.Option(FILE), func() { m.Cmdy(DIR, p).Cmdy(CAT, p) })
})
})
}},
}, mdb.PageListAction()), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 || strings.HasSuffix(arg[0], PS) {
m.Cmdy(DIR, arg)
return
}
page, size := mdb.OptionPages(m, kit.Slice(arg, 2)...)
_zip_list(m, arg[0], func(h zip.FileHeader, r io.Reader, i int) {
if len(kit.Slice(arg, 0, 2)) > 1 {
if h.Name != arg[1] {
return
}
m.Echo(string(ReadAll(m, r)[:]))
}
if i >= (page-1)*size && i < page*size {
m.Push(mdb.TIME, h.ModTime().Format(ice.MOD_TIME)).Push(FILE, h.Name).Push(SIZE, kit.FmtSize(int64(h.UncompressedSize)))
}
})
m.PushAction(mdb.EXPORT)
}},
})
}

32
base/ssh/render.go Normal file
View File

@ -0,0 +1,32 @@
package ssh
import (
"fmt"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/lex"
kit "shylinux.com/x/toolkits"
)
func Render(m *ice.Message, cmd string, arg ...ice.Any) (res string) {
switch args := kit.Simple(arg...); cmd {
case ice.RENDER_RESULT:
kit.If(len(args) > 0, func() { m.Resultv(args) })
res = m.Result()
case ice.RENDER_VOID:
return res
default:
if res = m.Result(); res == "" {
if m.IsCliUA() {
res = m.TableEchoWithStatus().Result()
} else {
res = m.TableEcho().Result()
}
}
}
if fmt.Fprint(m.O, res); !strings.HasSuffix(res, lex.NL) {
fmt.Fprint(m.O, lex.NL)
}
return res
}

235
base/ssh/script.go Normal file
View File

@ -0,0 +1,235 @@
package ssh
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/log"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
kit "shylinux.com/x/toolkits"
)
type Frame struct {
source string
target *ice.Context
stdout io.Writer
stdin io.Reader
pipe io.Writer
ps1 []string
ps2 []string
res string
count int
}
func (f *Frame) prompt(m *ice.Message, arg ...string) *Frame {
if f.source != STDIO {
return f
}
kit.If(len(arg) == 0, func() { arg = append(arg, f.ps1...) })
fmt.Fprintf(f.stdout, kit.Select("\r", "\r\033[2K", ice.Info.Colors))
kit.For(arg, func(k string) {
switch k {
case mdb.COUNT:
fmt.Fprintf(f.stdout, "%d", f.count)
case tcp.HOSTNAME:
fmt.Fprintf(f.stdout, ice.Info.NodeName)
case mdb.TIME:
fmt.Fprintf(f.stdout, kit.Slice(kit.Split(time.Now().Format(ice.MOD_TIME)), -1)[0])
case TARGET:
fmt.Fprintf(f.stdout, f.target.Name)
default:
kit.If(ice.Info.Colors || k[0] != '\033', func() { fmt.Fprintf(f.stdout, k) })
}
})
return f
}
func (f *Frame) printf(m *ice.Message, str string, arg ...ice.Any) *Frame {
if f.source != STDIO {
return f
}
fmt.Fprint(f.stdout, kit.Format(str, arg...))
return f
}
func (f *Frame) change(m *ice.Message, ls []string) []string {
if len(ls) == 1 && ls[0] == "~" {
ls = []string{ctx.CONTEXT}
} else if len(ls) > 0 && strings.HasPrefix(ls[0], "~") {
target := ls[0][1:]
if ls = ls[1:]; len(target) == 0 && len(ls) > 0 {
target, ls = ls[0], ls[1:]
}
kit.If(target == "~", func() { target = "" })
m.Spawn(f.target).Search(target+nfs.PT, func(p *ice.Context, s *ice.Context) { f.target = s })
}
return ls
}
func (f *Frame) alias(m *ice.Message, ls []string) []string {
if len(ls) == 0 {
return ls
} else if alias := kit.Simple(kit.Value(m.Optionv(ice.SSH_ALIAS), ls[0])); len(alias) > 0 {
ls = append(alias, ls[1:]...)
}
return ls
}
func (f *Frame) parse(m *ice.Message, h, line string) string {
ls := kit.Split(strings.TrimSpace(line))
for i, v := range ls {
if v == "#" {
ls = ls[:i]
break
}
}
if ls = f.change(m, f.alias(m, ls)); len(ls) == 0 {
return ""
}
msg := m.Spawn(f.target)
kit.If(h == STDIO, func() { msg.Option(ice.LOG_TRACEID, log.Traceid(m)) })
if msg.Cmdy(ls); h == STDIO && msg.IsErrNotFoundIndex() {
msg.SetResult().Cmdy(cli.SYSTEM, ls)
}
kit.If(m.Option(ice.MSG_STATUS) == "", func() { m.StatusTimeCount() })
f.res = Render(msg, msg.Option(ice.MSG_OUTPUT), kit.List(msg.Optionv(ice.MSG_ARGS))...)
return ""
}
func (f *Frame) scan(m *ice.Message, h, line string) *Frame {
kit.If(f.source == STDIO, func() { m.Option(ice.LOG_DISABLE, ice.TRUE) })
f.ps1 = kit.Simple(mdb.Confv(m, PROMPT, kit.Keym(PS1)))
f.ps2 = kit.Simple(mdb.Confv(m, PROMPT, kit.Keym(PS2)))
ps, bio := f.ps1, bufio.NewScanner(f.stdin)
m.I, m.O = f.stdin, f.stdout
for f.prompt(m.Sleep300ms(), ps...); f.stdin != nil && bio.Scan(); f.prompt(m, ps...) {
if len(bio.Text()) == 0 && h == STDIO {
continue
}
f.count++
if line += bio.Text(); strings.Count(line, "`")%2 == 1 {
line += lex.NL
ps = f.ps2
continue
} else if len(bio.Text()) == 0 {
continue
} else if strings.HasSuffix(bio.Text(), "\\") {
line += bio.Text()[:len(bio.Text())-1]
ps = f.ps2
continue
} else if strings.HasPrefix(strings.TrimSpace(line), "#") {
line = ""
continue
} else if ps = f.ps1; f.stdout == os.Stdout && ice.Info.Colors {
f.printf(m, "\033[0m")
}
line = f.parse(m, h, line)
}
return f
}
func (f *Frame) Begin(m *ice.Message, arg ...string) {
ice.Info.Colors = kit.IsIn(strings.Split(os.Getenv(cli.TERM), "-")[0], "xterm", "screen")
}
func (f *Frame) Start(m *ice.Message, arg ...string) {
m.Optionv(FRAME, f)
switch f.source = kit.Select(STDIO, arg, 0); f.source {
case STDIO:
r, w, _ := os.Pipe()
go func() { io.Copy(w, os.Stdin) }()
f.pipe, f.stdin, f.stdout = w, r, os.Stdout
kit.If(f.target == nil, func() { f.target = m.Target() })
m.Optionv(ice.MSG_OPTS, ice.MSG_USERNAME, ice.MSG_USERROLE)
m.Option(ice.MSG_USERWEB, "http://localhost:9020")
f.scan(m, STDIO, "")
default:
if m.Option(ice.MSG_SCRIPT) != "" {
ls := kit.Split(m.Option(ice.MSG_SCRIPT), nfs.PS)
for i := len(ls) - 1; i > 0; i-- {
if p := path.Join(path.Join(ls[:i]...), f.source); nfs.Exists(m, p) {
f.source = p
}
}
}
if msg := m.Cmd(nfs.CAT, m.Option(ice.MSG_SCRIPT, f.source)); msg.IsErr() {
return
} else {
kit.If(m.Option(nfs.CAT_CONTENT), func() { m.Option(nfs.CAT_CONTENT, "") })
buf := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS))
f.stdin, f.stdout = bytes.NewBufferString(msg.Result()), buf
defer func() { m.Echo(buf.String()) }()
}
if target, ok := m.Optionv(ice.SSH_TARGET).(*ice.Context); ok {
f.target = target
} else {
f.target = m.Source()
}
f.scan(m, "", "")
}
}
func (f *Frame) Close(m *ice.Message, arg ...string) {
nfs.Close(m, f.stdin)
f.stdin = nil
}
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server { return &Frame{} }
const (
FRAME = "frame"
SHELL = "shell"
STDIO = "stdio"
PS1 = "PS1"
PS2 = "PS2"
)
const (
SOURCE = "source"
RETURN = "return"
TARGET = "target"
PROMPT = "prompt"
PRINTF = "printf"
)
func init() {
Index.MergeCommands(ice.Commands{
SOURCE: {Name: "source file run", Help: "脚本解析", Actions: mdb.HashAction(), Hand: func(m *ice.Message, arg ...string) {
if f, ok := m.Target().Server().(*Frame); ok {
f.Spawn(m, m.Target()).Start(m, arg...)
}
}},
RETURN: {Name: "return run", Help: "结束脚本", Hand: func(m *ice.Message, arg ...string) {
if f, ok := m.Optionv(FRAME).(*Frame); ok {
f.Close(m, arg...)
}
}},
TARGET: {Name: "target name run", Help: "当前模块", Hand: func(m *ice.Message, arg ...string) {
if f, ok := m.Target().Server().(*Frame); ok {
m.Search(arg[0]+nfs.PT, func(p *ice.Context, s *ice.Context) { f.target = s })
f.prompt(m)
}
}},
PROMPT: {Name: "prompt arg run", Help: "命令提示", Actions: ctx.ConfAction(
PS1, ice.List{"\033[33;44m", mdb.COUNT, mdb.AT, tcp.HOSTNAME, "[", mdb.TIME, "]", "\033[5m", TARGET, "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"},
PS2, ice.List{mdb.COUNT, lex.SP, TARGET, "> "},
), Hand: func(m *ice.Message, arg ...string) {
if f, ok := m.Target().Server().(*Frame); ok {
f.prompt(m, arg...)
}
}},
PRINTF: {Name: "printf run text", Help: "输出显示", Hand: func(m *ice.Message, arg ...string) {
if f, ok := m.Target().Server().(*Frame); ok {
f.printf(m, kit.Select(m.Option(nfs.CONTENT), arg, 0))
}
}},
})
}
func PrintQRCode(m *ice.Message, url string) {
m.Spawn(ice.OptionSilent()).Cmd(PRINTF, kit.Dict(nfs.CONTENT, lex.NL+ice.Render(m, ice.RENDER_QRCODE, url))).Cmd(PROMPT)
}

View File

@ -1,302 +0,0 @@
package ssh
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func Render(msg *ice.Message, cmd string, args ...interface{}) (res string) {
switch arg := kit.Simple(args...); cmd {
case ice.RENDER_VOID:
return res
case ice.RENDER_RESULT:
if len(arg) > 0 {
msg.Resultv(arg)
}
res = msg.Result()
default:
if res = msg.Result(); res == "" {
res = msg.Table().Result()
}
}
if fmt.Fprint(msg.O, res); !strings.HasSuffix(res, ice.NL) {
fmt.Fprint(msg.O, ice.NL)
}
return res
}
type Frame struct {
source string
target *ice.Context
stdout io.Writer
stdin io.Reader
pipe io.Writer
ps1 []string
ps2 []string
res string
count int
}
func (f *Frame) prompt(m *ice.Message, list ...string) *Frame {
if f.source != STDIO {
return f
}
if len(list) == 0 {
list = append(list, f.ps1...)
}
fmt.Fprintf(f.stdout, "\r")
for _, v := range list {
switch v {
case kit.MDB_COUNT:
fmt.Fprintf(f.stdout, "%d", f.count)
case kit.MDB_TIME:
fmt.Fprintf(f.stdout, time.Now().Format("15:04:05"))
case TARGET:
fmt.Fprintf(f.stdout, f.target.Name)
default:
fmt.Fprintf(f.stdout, v)
}
}
return f
}
func (f *Frame) printf(m *ice.Message, res string, arg ...interface{}) *Frame {
if len(arg) > 0 {
fmt.Fprintf(f.stdout, res, arg...)
} else {
fmt.Fprint(f.stdout, res)
}
return f
}
func (f *Frame) change(m *ice.Message, ls []string) []string {
if len(ls) == 1 && ls[0] == "~" { // 模块列表
ls = []string{ctx.CONTEXT}
} else if len(ls) > 0 && strings.HasPrefix(ls[0], "~") { // 切换模块
target := ls[0][1:]
if ls = ls[1:]; len(target) == 0 && len(ls) > 0 {
target, ls = ls[0], ls[1:]
}
if target == "~" {
target = ""
}
m.Spawn(f.target).Search(target+ice.PT, func(p *ice.Context, s *ice.Context, key string) {
m.Log_SELECT(ctx.CONTEXT, s.Name)
f.target = s
})
}
return ls
}
func (f *Frame) alias(m *ice.Message, ls []string) []string {
if alias, ok := m.Optionv(ice.MSG_ALIAS).(map[string]interface{}); ok {
if len(ls) > 0 {
if a := kit.Simple(alias[ls[0]]); len(a) > 0 {
ls = append(append([]string{}, a...), ls[1:]...)
}
}
}
return ls
}
func (f *Frame) parse(m *ice.Message, line string) string {
for _, one := range kit.Split(line, ";", ";", ";") {
msg := m.Spawn(f.target)
ls := f.change(msg, f.alias(msg, kit.Split(strings.TrimSpace(one))))
if len(ls) == 0 {
continue
}
if msg.Cmdy(ls[0], ls[1:]); msg.Result(1) == ice.ErrNotFound {
msg.Set(ice.MSG_RESULT).Cmdy(cli.SYSTEM, ls)
}
_args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{})
f.res = Render(msg, msg.Option(ice.MSG_OUTPUT), _args...)
}
return ""
}
func (f *Frame) scan(m *ice.Message, h, line string) *Frame {
f.ps1 = kit.Simple(m.Confv(PROMPT, kit.Keym(PS1)))
f.ps2 = kit.Simple(m.Confv(PROMPT, kit.Keym(PS2)))
ps := f.ps1
m.Sleep("300ms")
m.I, m.O = f.stdin, f.stdout
bio := bufio.NewScanner(f.stdin)
for f.prompt(m, ps...); bio.Scan() && f.stdin != nil; f.prompt(m, ps...) {
if h == STDIO && len(bio.Text()) == 0 {
continue // 空行
}
m.Cmdx(mdb.INSERT, SOURCE, kit.Keys(kit.MDB_HASH, h), mdb.LIST, kit.MDB_TEXT, bio.Text())
f.count++
if len(bio.Text()) == 0 {
if strings.Count(line, "`")%2 == 1 {
line += ice.NL
}
continue // 空行
}
if strings.HasSuffix(bio.Text(), "\\") {
line += bio.Text()[:len(bio.Text())-1]
ps = f.ps2
continue // 续行
}
if line += bio.Text(); strings.Count(line, "`")%2 == 1 {
line += ice.NL
ps = f.ps2
continue // 多行
}
if strings.HasPrefix(strings.TrimSpace(line), "#") {
line = ""
continue
}
// if line = strings.Split(line, " # ")[0]; len(line) == 0 {
// continue // 注释
// }
if ps = f.ps1; f.stdout == os.Stdout {
f.printf(m, "\033[0m") // 清空格式
}
line = f.parse(m, line)
}
return f
}
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
return f
}
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server {
return &Frame{}
}
func (f *Frame) Start(m *ice.Message, arg ...string) bool {
m.Optionv(FRAME, f)
switch f.source = kit.Select(STDIO, arg, 0); f.source {
case STDIO: // 终端交互
m.Cap(ice.CTX_STREAM, f.source)
if f.target == nil {
f.target = m.Target()
}
r, w, _ := os.Pipe()
m.Go(func() { io.Copy(w, os.Stdin) })
f.stdin, f.stdout = r, os.Stdout
f.pipe = w
aaa.UserRoot(m)
m.Option(ice.MSG_OPTS, ice.MSG_USERNAME)
m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_NAME)), STDIO)
m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_TIME)), m.Time())
f.count = kit.Int(m.Conf(SOURCE, kit.Keys(kit.MDB_HASH, STDIO, kit.Keym(kit.MDB_COUNT)))) + 1
f.scan(m, STDIO, "")
default: // 脚本文件
if strings.Contains(m.Option(ice.MSG_SCRIPT), ice.PS) {
f.source = path.Join(path.Dir(m.Option(ice.MSG_SCRIPT)), f.source)
}
m.Option(ice.MSG_SCRIPT, f.source)
f.target = m.Source()
if msg := m.Cmd(nfs.CAT, f.source); msg.Result(0) == ice.ErrWarn {
return true // 查找失败
} else {
buf := bytes.NewBuffer(make([]byte, 0, ice.MOD_BUFS))
defer func() { m.Echo(buf.String()) }()
f.stdin, f.stdout = bytes.NewBuffer([]byte(msg.Result())), buf
}
f.count = 1
f.scan(m, m.Cmdx(mdb.INSERT, SOURCE, "", mdb.HASH, kit.MDB_NAME, f.source), "")
}
return true
}
func (f *Frame) Close(m *ice.Message, arg ...string) bool {
if stdin, ok := f.stdin.(io.Closer); ok {
stdin.Close()
}
f.stdin = nil
return true
}
const (
FRAME = "frame"
STDIO = "stdio"
PS1 = "PS1"
PS2 = "PS2"
)
const (
SCRIPT = "script"
SOURCE = "source"
TARGET = "target"
PROMPT = "prompt"
PRINTF = "printf"
SCREEN = "screen"
RETURN = "return"
)
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SOURCE: {Name: SOURCE, Help: "加载脚本", Value: kit.Data()},
PROMPT: {Name: PROMPT, Help: "命令提示", Value: kit.Data(
PS1, []interface{}{"\033[33;44m", kit.MDB_COUNT, "[", kit.MDB_TIME, "]", "\033[5m", TARGET, "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"},
PS2, []interface{}{kit.MDB_COUNT, " ", TARGET, "> "},
)},
}, Commands: map[string]*ice.Command{
SOURCE: {Name: "source file", Help: "脚本解析", Action: ice.MergeAction(map[string]*ice.Action{
mdb.REPEAT: {Name: "repeat", Help: "执行", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(SCREEN, m.Option(kit.MDB_TEXT))
m.ProcessInner()
}},
}, mdb.ZoneAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) > 0 && kit.Ext(arg[0]) == ice.SHY {
(&Frame{}).Start(m, arg...)
return // 脚本解析
}
}},
TARGET: {Name: "target name run:button", Help: "当前模块", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
f := c.Server().(*Frame)
m.Search(arg[0]+ice.PT, func(p *ice.Context, s *ice.Context, key string) { f.target = s })
f.prompt(m)
}},
PROMPT: {Name: "prompt arg run:button", Help: "命令提示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
f := m.Optionv(FRAME).(*Frame)
f.ps1 = arg
f.prompt(m)
}},
PRINTF: {Name: "printf run:button text", Help: "输出显示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
f := m.Optionv(FRAME).(*Frame)
f.printf(m, arg[0])
}},
SCREEN: {Name: "screen run:button text", Help: "输出命令", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
f := m.Optionv(FRAME).(*Frame)
for _, line := range kit.Split(arg[0], ice.NL, ice.NL) {
fmt.Fprintf(f.pipe, line+ice.NL)
f.printf(m, line+ice.NL)
m.Sleep("300ms")
}
m.Echo(f.res)
}},
RETURN: {Name: "return", Help: "结束脚本", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
f := m.Optionv(FRAME).(*Frame)
f.Close(m, arg...)
}},
}})
}

View File

@ -1,11 +1,9 @@
package ssh
import (
ice "shylinux.com/x/icebergs"
)
import ice "shylinux.com/x/icebergs"
const SSH = "ssh"
var Index = &ice.Context{Name: SSH, Help: "终端模块"}
func init() { ice.Index.Register(Index, &Frame{}, SOURCE, TARGET, PROMPT, PRINTF, SCREEN, RETURN) }
func init() { ice.Index.Register(Index, &Frame{}, SOURCE, RETURN, TARGET, PROMPT, PRINTF) }

View File

@ -1,8 +0,0 @@
chapter "ssh"
field "脚本" ssh.source
field "模块" ssh.target
field "提示" ssh.prompt
field "输出" ssh.printf
field "屏显" ssh.screen

58
base/tcp/broad.go Normal file
View File

@ -0,0 +1,58 @@
package tcp
import (
"net"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _server_udp(m *ice.Message, arg ...string) {
l, e := net.ListenUDP(UDP4, UDPAddr(m, "0.0.0.0", m.Option(PORT)))
defer kit.If(e == nil, func() { l.Close() })
mdb.HashCreate(m, arg, kit.Dict(mdb.TARGET, l), STATUS, kit.Select(ERROR, OPEN, e == nil), ERROR, kit.Format(e))
switch cb := m.OptionCB("").(type) {
case func(*net.UDPAddr, []byte):
m.Assert(e)
buf := make([]byte, 2*ice.MOD_BUFS)
for {
if n, from, e := l.ReadFromUDP(buf[:]); !m.WarnNotValid(e) {
cb(from, buf[:n])
} else {
break
}
}
default:
m.ErrorNotImplement(cb)
}
}
func _client_dial_udp4(m *ice.Message, arg ...string) {
c, e := net.DialUDP(UDP4, nil, UDPAddr(m, kit.Select("255.255.255.255", m.Option(HOST)), m.Option(PORT)))
defer kit.If(e == nil, func() { c.Close() })
switch cb := m.OptionCB("").(type) {
case func(*net.UDPConn):
kit.If(!m.WarnNotValid(e), func() { cb(c) })
default:
m.ErrorNotImplement(cb)
}
}
const (
UDP4 = "udp4"
SEND = "send"
RECV = "recv"
ECHO = "echo"
DONE = "done"
DIRECT = "direct"
)
func UDPAddr(m *ice.Message, host, port string) *net.UDPAddr {
if addr, e := net.ResolveUDPAddr(UDP4, host+nfs.DF+port); !m.WarnNotValid(e, host, port, logs.FileLineMeta(2)) {
return addr
}
return nil
}
func HostPort(host, port string) string { return host + nfs.DF + port }

View File

@ -2,22 +2,19 @@ package tcp
import (
"net"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
type Stat struct {
nc, nr, nw int
}
type Conn struct {
net.Conn
m *ice.Message
h string
s *Stat
net.Conn
}
func (c *Conn) Read(b []byte) (int, error) {
@ -30,72 +27,36 @@ func (c *Conn) Write(b []byte) (int, error) {
c.s.nw += n
return n, e
}
func (c *Conn) Close() error {
return c.Conn.Close()
}
func (c *Conn) Close() error { return c.Conn.Close() }
func _client_dial(m *ice.Message, arg ...string) {
c, e := net.Dial(TCP, m.Option(HOST)+":"+m.Option(PORT))
c = &Conn{m: m, s: &Stat{}, Conn: c}
if e == nil {
defer c.Close()
}
switch cb := m.Optionv(kit.Keycb(DIAL)).(type) {
case func(net.Conn, error):
cb(c, e)
c, e := net.DialTimeout(TCP, m.Option(HOST)+nfs.DF+m.Option(PORT), 3*time.Second)
c = &Conn{Conn: c, m: m, s: &Stat{}}
defer kit.If(e == nil, func() { c.Close() })
switch cb := m.OptionCB("").(type) {
case func(net.Conn):
m.Assert(e)
cb(c)
case func(net.Conn, []byte, error):
b := make([]byte, ice.MOD_BUFS)
for {
n, e := c.Read(b)
if cb(c, b[:n], e); e != nil {
break
}
}
kit.If(!m.WarnNotValid(e), func() { cb(c) })
default:
c.Write([]byte("hello world\n"))
m.ErrorNotImplement(cb)
}
}
const (
OPEN = "open"
CLOSE = "close"
ERROR = "error"
START = "start"
STOP = "stop"
)
const (
DIAL = "dial"
)
const CLIENT = "client"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
CLIENT: {Name: CLIENT, Help: "客户端", Value: kit.Data(
kit.MDB_FIELD, "time,hash,status,type,name,host,port,error,nread,nwrite",
)},
}, Commands: map[string]*ice.Command{
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Richs(CLIENT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
kit.Value(value, kit.Keym(kit.MDB_STATUS), CLOSE)
})
}},
CLIENT: {Name: "client hash auto prunes", Help: "客户端", Action: ice.MergeAction(map[string]*ice.Action{
Index.MergeCommands(ice.Commands{
CLIENT: {Help: "客户端", Actions: ice.MergeActions(ice.Actions{
DIAL: {Name: "dial type name port=9010 host=", Help: "连接", Hand: func(m *ice.Message, arg ...string) {
_client_dial(m, arg...)
switch m.Option(mdb.TYPE) {
case UDP4:
_client_dial_udp4(m, arg...)
default:
_client_dial(m, arg...)
}
}},
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(mdb.PRUNES, CLIENT, "", mdb.HASH, kit.MDB_STATUS, ERROR)
m.Cmdy(mdb.PRUNES, CLIENT, "", mdb.HASH, kit.MDB_STATUS, CLOSE)
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...).Table(func(index int, value map[string]string, head []string) {
m.PushButton(kit.Select("", mdb.REMOVE, value[kit.MDB_STATUS] == OPEN))
})
}},
}})
}, mdb.StatusHashAction(mdb.FIELD, "time,hash,status,type,name,host,port,error"), mdb.ClearOnExitHashAction())},
})
}

View File

@ -2,98 +2,123 @@ package tcp
import (
"net"
"os"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _host_list(m *ice.Message, name string) {
func _host_domain(m *ice.Message) string {
return kit.GetValid(
func() string { return m.Option(ice.TCP_DOMAIN) },
func() string { return mdb.Config(m, DOMAIN) },
func() string { return os.Getenv(ice.TCP_DOMAIN) },
func() string {
if !kit.IsIn(m.ActionKey(), "", ice.LIST) {
return m.Cmdv(HOST, mdb.Config(m, ice.MAIN), aaa.IP)
}
return ""
},
func() string {
return LOCALHOST
},
)
}
func _host_list(m *ice.Message, name string) *ice.Message {
if ifs, e := net.Interfaces(); m.Assert(e) {
for _, v := range ifs {
if name != "" && !strings.Contains(v.Name, name) {
if !strings.Contains(v.Name, name) || len(v.HardwareAddr.String()) == 0 {
continue
}
if len(v.HardwareAddr.String()) == 0 {
continue
}
if ips, e := v.Addrs(); m.Assert(e) {
for _, x := range ips {
ip := strings.Split(x.String(), ice.PS)
if strings.Contains(ip[0], ":") || len(ip) == 0 {
ip := strings.Split(x.String(), nfs.PS)
if strings.Contains(ip[0], nfs.DF) || len(ip) == 0 {
continue
}
m.Push(kit.MDB_INDEX, v.Index)
m.Push(kit.MDB_NAME, v.Name)
m.Push(aaa.IP, ip[0])
m.Push("mask", ip[1])
m.Push("hard", v.HardwareAddr.String())
m.Push(mdb.INDEX, v.Index).Push(mdb.NAME, v.Name).Push(aaa.IP, ip[0]).Push(MASK, ip[1]).Push(MAC_ADDRESS, v.HardwareAddr.String())
}
}
}
}
if len(m.Appendv(aaa.IP)) == 0 {
m.Push(kit.MDB_INDEX, -1)
m.Push(kit.MDB_NAME, LOCALHOST)
m.Push(aaa.IP, "127.0.0.1")
m.Push("mask", "255.0.0.0")
m.Push("hard", "")
}
}
func _islocalhost(m *ice.Message, ip string) (ok bool) {
if ip == "::1" || strings.HasPrefix(ip, "127.") {
return true
}
if m.Richs(HOST, kit.Keym(aaa.BLACK), ip, nil) != nil {
return false
}
if m.Richs(HOST, kit.Keym(aaa.WHITE), ip, nil) != nil {
m.Log_AUTH(aaa.WHITE, ip)
return true
}
return false
}
func IsLocalHost(m *ice.Message, ip string) bool { return _islocalhost(m, ip) }
func ReplaceLocalhost(m *ice.Message, url string) string {
if strings.Contains(url, "://"+LOCALHOST) {
url = strings.Replace(url, "://"+LOCALHOST, "://"+m.Cmd(HOST).Append(aaa.IP), 1)
}
return url
return m.SortInt(mdb.INDEX).StatusTimeCount(DOMAIN, _host_domain(m))
}
const (
LOCALHOST = "localhost"
LOCALHOST = "localhost"
MAC_ADDRESS = "mac-address"
MASK = "mask"
DOMAIN = "domain"
GATEWAY = "gateway"
MACHINE = "machine"
ISLOCAL = "islocal"
PUBLISH = "publish"
)
const HOST = "host"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
HOST: {Name: HOST, Help: "主机", Value: kit.Data(
aaa.BLACK, kit.Data(kit.MDB_SHORT, kit.MDB_TEXT),
aaa.WHITE, kit.Data(kit.MDB_SHORT, kit.MDB_TEXT),
)},
}, Commands: map[string]*ice.Command{
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Cmd(HOST).Table(func(index int, value map[string]string, head []string) {
m.Cmd(HOST, aaa.WHITE, value[aaa.IP])
Index.MergeCommands(ice.Commands{
HOST: {Name: "host name auto domain", Help: "主机", Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict(
aaa.IP, "网络地址", MASK, "子网掩码", MAC_ADDRESS, "物理地址",
)),
), Actions: ice.MergeActions(ice.Actions{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd("", func(value ice.Maps) { m.Cmd("", aaa.WHITE, LOCALHOST, value[aaa.IP]) })
ice.Info.Host = mdb.Config(m, DOMAIN)
}},
mdb.SEARCH: {Hand: func(m *ice.Message, arg ...string) {
if mdb.IsSearchPreview(m, arg) && m.Cmd(HOST).Length() > 0 {
ip := m.Cmdv(HOST, GATEWAY, aaa.IP)
m.PushSearch(mdb.TYPE, GATEWAY, mdb.NAME, ip, mdb.TEXT, "http://"+ip)
}
}},
aaa.WHITE: {Name: "white name text", Help: "白名单", Hand: func(m *ice.Message, arg ...string) {
mdb.HashCreate(m, mdb.TYPE, m.ActionKey(), m.OptionSimple(mdb.NAME, mdb.TEXT))
}},
aaa.BLACK: {Name: "black name text", Help: "黑名单", Hand: func(m *ice.Message, arg ...string) {
mdb.HashCreate(m, mdb.TYPE, m.ActionKey(), m.OptionSimple(mdb.NAME, mdb.TEXT))
}},
ISLOCAL: {Hand: func(m *ice.Message, arg ...string) {
if arg[0] = strings.Split(strings.TrimPrefix(arg[0], "["), "]")[0]; arg[0] == "::1" || strings.HasPrefix(arg[0], "127.") || arg[0] == LOCALHOST {
m.Echo(ice.OK)
} else if mdb.HashSelectField(m, strings.Split(arg[0], nfs.DF)[0], mdb.TYPE) == aaa.WHITE {
m.Echo(ice.OK)
}
}},
PUBLISH: {Hand: func(m *ice.Message, arg ...string) {
for _, p := range []string{LOCALHOST, "127.0.0.1", m.Option("tcp_localhost")} {
if p != "" && strings.Contains(arg[0], p) {
arg[0] = strings.Replace(arg[0], p, _host_domain(m), 1)
break
}
}
m.Echo(arg[0])
}},
GATEWAY: {Hand: func(m *ice.Message, arg ...string) {
m.Push(aaa.IP, kit.Keys(kit.Slice(strings.Split(m.Cmdv(HOST, aaa.IP), nfs.PT), 0, 3), "1"))
}},
DOMAIN: {Name: "domain ip", Help: "主机", Icon: "bi bi-house-check", Hand: func(m *ice.Message, arg ...string) {
kit.If(m.Option(aaa.IP), func(p string) { ice.Info.Host = p; mdb.Config(m, DOMAIN, p) })
m.Echo(mdb.Config(m, DOMAIN))
}},
}, mdb.HashAction(mdb.SHORT, mdb.TEXT)), Hand: func(m *ice.Message, arg ...string) {
_host_list(m, kit.Select("", arg, 0)).Table(func(value ice.Maps) {
if value[aaa.IP] == mdb.Config(m, DOMAIN) {
m.Push(mdb.STATUS, "current").PushButton("")
} else {
m.Push(mdb.STATUS, "").PushButton(DOMAIN)
}
})
}},
HOST: {Name: "host name auto", Help: "主机", Action: map[string]*ice.Action{
aaa.BLACK: {Name: "black", Help: "黑名单", Hand: func(m *ice.Message, arg ...string) {
m.Log_CREATE(aaa.BLACK, arg[0])
m.Rich(HOST, kit.Keym(aaa.BLACK), kit.Dict(kit.MDB_TEXT, arg[0]))
}},
aaa.WHITE: {Name: "white", Help: "白名单", Hand: func(m *ice.Message, arg ...string) {
m.Log_CREATE(aaa.WHITE, arg[0])
m.Rich(HOST, kit.Keym(aaa.WHITE), kit.Dict(kit.MDB_TEXT, arg[0]))
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
_host_list(m, kit.Select("", arg, 0))
}},
}})
})
}
func IsLocalHost(m *ice.Message, ip string) bool { return m.Cmdx(HOST, ISLOCAL, ip) == ice.OK }
func PublishLocalhost(m *ice.Message, url string) string { return m.Cmdx(HOST, PUBLISH, url) }

52
base/tcp/peek.go Normal file
View File

@ -0,0 +1,52 @@
package tcp
import (
"bytes"
"net"
"net/http"
"strings"
kit "shylinux.com/x/toolkits"
)
type Buf struct {
buf []byte
}
type PeekConn struct {
net.Conn
*Buf
}
func (s PeekConn) Read(b []byte) (n int, err error) {
if len(s.buf) == 0 {
return s.Conn.Read(b)
}
if len(s.buf) < len(b) {
copy(b, s.buf)
s.buf = s.buf[:0]
return len(s.buf), nil
}
copy(b, s.buf)
s.buf = s.buf[len(b):]
return len(b), nil
}
func (s PeekConn) Peek(n int) (res []byte) {
b := make([]byte, n)
_n, _ := s.Conn.Read(b)
s.Buf.buf = b[:_n]
return b[:_n]
}
func (s PeekConn) IsHTTP() bool {
if head := s.Peek(4); bytes.Equal(head, []byte("GET ")) {
return true
}
return false
}
func (s PeekConn) Redirect(status int, location string) {
DF, NL := ": ", "\r\n"
s.Conn.Write([]byte(strings.Join([]string{
kit.Format("HTTP/1.1 %d %s", status, http.StatusText(status)),
kit.JoinKV(DF, NL, "Location", location, "Content-Length", "0"),
}, NL) + NL + NL))
}
func NewPeekConn(c net.Conn) PeekConn { return PeekConn{Conn: c, Buf: &Buf{}} }

View File

@ -2,70 +2,169 @@ package tcp
import (
"net"
"os"
"path"
"runtime"
"strconv"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
func _port_right(m *ice.Message, arg ...string) string {
current := kit.Int(kit.Select(m.Config(CURRENT), arg, 0))
end := kit.Int(m.Config(END))
if current >= end {
current = kit.Int(m.Config(BEGIN))
}
func _port_right(m *ice.Message, current, begin, end int) string {
kit.If(current >= end, func() { current = begin })
for i := current; i < end; i++ {
if c, e := net.Dial(TCP, kit.Format(":%d", i)); e == nil {
if p := path.Join(ice.USR_LOCAL_DAEMON, kit.Format(i)); nfs.Exists(m, p) {
} else if c, e := net.Dial(TCP, kit.Format(":%d", i)); e == nil {
m.Info("port exists %v", i)
c.Close()
continue
} else {
nfs.MkdirAll(m, p)
m.Logs(mdb.SELECT, PORT, i)
return mdb.Config(m, CURRENT, i)
}
p := path.Join(m.Conf(cli.DAEMON, kit.META_PATH), kit.Format(i))
if _, e := os.Stat(p); e == nil {
continue
}
os.MkdirAll(p, ice.MOD_DIR)
m.Log_SELECT(PORT, i)
return m.Config(CURRENT, i)
}
return ""
}
const (
RANDOM = "random"
CURRENT = "current"
PORT_22 = "22"
PORT_80 = "80"
PORT_443 = "443"
PORT_9020 = "9020"
PORT_9022 = "9022"
SOCKET = "socket"
BEGIN = "begin"
CURRENT = "current"
RANDOM = "random"
END = "end"
PID = "pid"
SPACE = "space"
)
const PORT = "port"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
PORT: {Name: PORT, Help: "端口", Value: kit.Data(BEGIN, 10000, CURRENT, 10000, END, 20000)},
}, Commands: map[string]*ice.Command{
PORT: {Name: "port port path auto", Help: "端口", Action: map[string]*ice.Action{
aaa.RIGHT: {Name: "right", Help: "分配", Hand: func(m *ice.Message, arg ...string) {
m.Echo(_port_right(m, arg...))
Index.MergeCommands(ice.Commands{
PORT: {Name: "port port path auto socket", Help: "端口", Actions: ice.MergeActions(ice.Actions{
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case HOST, SERVER:
m.Cmd(PORT, SOCKET, func(value ice.Maps) {
switch value[mdb.STATUS] {
case "LISTEN":
m.Push(arg[0], strings.Replace(value["local"], "0.0.0.0", "127.0.0.1", 1))
}
})
case PORT:
if runtime.GOOS == "darwin" {
ls := kit.SplitLine(m.Cmd("system", "sh", "-c", `lsof -nP -i4TCP | grep LISTEN | awk '{print $1 " " $9 }'`).Result())
kit.For(ls, func(p string) {
ls := kit.SplitWord(p)
m.Push(arg[0], kit.Split(ls[1], ":")[1]).Push(SERVER, ls[0])
})
m.Sort(arg[0], ice.INT)
return
}
m.Cmd(PORT, SOCKET, func(value ice.Maps) {
switch value[mdb.STATUS] {
case "LISTEN":
m.Push(arg[0], strings.TrimPrefix(value["local"], "0.0.0.0:"))
m.Push(mdb.NAME, "listen")
}
})
}
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
if len(arg) == 0 {
m.Option(nfs.DIR_ROOT, m.Conf(cli.DAEMON, kit.META_PATH))
m.Cmd(nfs.DIR, ice.PWD, "time,path,size").Table(func(index int, value map[string]string, head []string) {
m.Push(kit.MDB_TIME, value[kit.MDB_TIME])
m.Push(PORT, path.Base(value[kit.MDB_PATH]))
m.Push(kit.MDB_SIZE, value[kit.MDB_SIZE])
SOCKET: {Help: "端口", Hand: func(m *ice.Message, arg ...string) {
parse := func(str string) int64 { port, _ := strconv.ParseInt(str, 16, 32); return port }
trans := func(str string) string {
switch str {
case "01":
return "ESTABLISHED"
case "02":
return "TCP_SYNC_SEND"
case "03":
return "TCP_SYNC_RECV"
case "04":
return "TCP_FIN_WAIT1"
case "05":
return "TCP_FIN_WAIT2"
case "06":
return "TIME_WAIT"
case "07":
return "TCP_CLOSE"
case "08":
return "TCP_CLOSE_WAIT"
case "0A":
return "LISTEN"
default:
return str
}
}
stats := map[string]int{}
m.Spawn().Split(m.Cmdx(nfs.CAT, "/proc/net/tcp")).Table(func(value ice.Maps) {
stats[trans(value["st"])]++
m.Push(mdb.STATUS, trans(value["st"]))
ls := kit.Split(value["local_address"], ":")
m.Push("local", kit.Format("%d.%d.%d.%d:%d", parse(ls[0][6:8]), parse(ls[0][4:6]), parse(ls[0][2:4]), parse(ls[0][:2]), parse(ls[1])))
ls = kit.Split(value["rem_address"], ":")
m.Push("remote", kit.Format("%d.%d.%d.%d:%d", parse(ls[0][6:8]), parse(ls[0][4:6]), parse(ls[0][2:4]), parse(ls[0][:2]), parse(ls[1])))
})
m.SortInt(PORT)
m.Spawn().Split(m.Cmdx(nfs.CAT, "/proc/net/tcp6")).Table(func(value ice.Maps) {
stats[trans(value["st"])]++
m.Push(mdb.STATUS, trans(value["st"]))
ls := kit.Split(value["local_address"], ":")
m.Push("local", kit.Format("%d.%d.%d.%d:%d", parse(ls[0][30:32]), parse(ls[0][28:30]), parse(ls[0][26:28]), parse(ls[0][24:26]), parse(ls[1])))
ls = kit.Split(value["remote_address"], ":")
m.Push("remote", kit.Format("%d.%d.%d.%d:%d", parse(ls[0][30:32]), parse(ls[0][28:30]), parse(ls[0][26:28]), parse(ls[0][24:26]), parse(ls[1])))
})
m.Sort("status,local", []string{"LISTEN", "ESTABLISHED", "TIME_WAIT"}).StatusTimeCount(stats)
}},
nfs.TRASH: {Hand: func(m *ice.Message, arg ...string) {
m.Assert(m.Option(PORT) != "")
nfs.Trash(m, path.Join(ice.USR_LOCAL_DAEMON, m.Option(PORT)))
mdb.HashRemove(m)
}},
aaa.RIGHT: {Hand: func(m *ice.Message, arg ...string) { m.Echo(PortRight(m, arg...)) }},
CURRENT: {Hand: func(m *ice.Message, arg ...string) { m.Echo(mdb.Config(m, CURRENT)) }},
STOP: {Hand: func(m *ice.Message, arg ...string) { PortCmds(m, arg...); mdb.HashModify(m, PID, "") }},
START: {Hand: func(m *ice.Message, arg ...string) { PortCmds(m, arg...); mdb.HashModify(m, PID, m.Append(PID)) }},
}, mdb.HashAction(BEGIN, 10000, END, 20000,
mdb.SHORT, PORT, mdb.FIELD, "time,port,pid,cmd,name,text,icon,space,index",
)), Hand: func(m *ice.Message, arg ...string) {
if len(arg) > 0 {
m.Cmdy(nfs.DIR, arg[1:], kit.Dict(nfs.DIR_ROOT, path.Join(ice.USR_LOCAL_DAEMON, arg[0])))
return
}
m.Option(nfs.DIR_ROOT, path.Join(m.Conf(cli.DAEMON, kit.META_PATH), arg[0]))
m.Cmdy(nfs.DIR, arg[1:])
current := kit.Int(mdb.Config(m, BEGIN))
mdb.HashSelect(m, arg...).Table(func(value ice.Maps) {
current = kit.Max(current, kit.Int(value[PORT]))
if value[PID] == "" {
m.PushButton(START, nfs.TRASH)
} else {
m.PushButton(STOP)
}
})
mdb.Config(m, CURRENT, current)
m.StatusTimeCount(mdb.ConfigSimple(m, BEGIN, CURRENT, END)).SortInt(PORT)
}},
}})
})
ice.Info.Inputs = append(ice.Info.Inputs, func(m *ice.Message, arg ...string) {
switch arg[0] {
case PORT:
m.SetAppend().Cmdy(PORT, mdb.INPUTS, arg)
}
})
}
func PortRight(m *ice.Message, arg ...string) string {
current, begin, end := kit.Select("20000", mdb.Config(m, CURRENT)), kit.Select("20000", mdb.Config(m, BEGIN)), kit.Select("30000", mdb.Config(m, END))
return _port_right(m, kit.Int(kit.Select(kit.Select(begin, current), arg, 0)), kit.Int(kit.Select(begin, arg, 1)), kit.Int(kit.Select(end, arg, 2)))
}
func PortCmds(m *ice.Message, arg ...string) {
m.Cmdy(SPACE, m.Option(SPACE), m.Option(ctx.INDEX), m.ActionKey())
}

View File

@ -5,109 +5,87 @@ import (
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
kit "shylinux.com/x/toolkits"
)
type Stat struct{ nc, nr, nw int }
type Listener struct {
net.Listener
m *ice.Message
h string
s *Stat
net.Listener
}
func (l Listener) Accept() (net.Conn, error) {
c, e := l.Listener.Accept()
l.s.nc += 1
return &Conn{m: l.m, h: "", s: &Stat{}, Conn: c}, e
return &Conn{m: l.m, s: &Stat{}, Conn: c}, e
}
func (l Listener) Close() error {
l.m.Cmd(mdb.MODIFY, SERVER, "", mdb.HASH, kit.MDB_HASH, l.h, kit.MDB_STATUS, CLOSE)
l.m.Cmd(mdb.MODIFY, SERVER, "", mdb.HASH, mdb.HASH, l.h, STATUS, CLOSE)
return l.Listener.Close()
}
func _server_listen(m *ice.Message, arg ...string) {
l, e := net.Listen(TCP, m.Option(HOST)+":"+m.Option(PORT))
h := m.Cmdx(mdb.INSERT, SERVER, "", mdb.HASH, arg,
kit.MDB_STATUS, kit.Select(ERROR, OPEN, e == nil), kit.MDB_ERROR, kit.Format(e))
l = &Listener{m: m, h: h, s: &Stat{}, Listener: l}
if e == nil {
defer l.Close()
l, e := net.Listen(TCP, m.Option(HOST)+nfs.DF+m.Option(PORT))
if m.WarnNotValid(e) {
return
}
switch cb := m.Optionv(kit.Keycb(LISTEN)).(type) {
case func(net.Listener, error):
cb(l, e)
l = &Listener{Listener: l, m: m, h: mdb.HashCreate(m, arg, kit.Dict(mdb.TARGET, l), STATUS, kit.Select(ERROR, OPEN, e == nil), ERROR, kit.Format(e)), s: &Stat{}}
defer kit.If(e == nil, func() { l.Close() })
switch cb := m.OptionCB("").(type) {
case func(net.Listener):
m.Assert(e)
cb(l)
case func(net.Conn):
for {
if c, e := l.Accept(); e == nil {
if c, e := l.Accept(); !m.WarnNotValid(e) {
cb(c)
} else {
break
}
}
case func(net.Conn, error):
for {
c, e := l.Accept()
if cb(c, e); e != nil {
break
}
}
default:
for {
c, e := l.Accept()
if e != nil {
break
}
b := make([]byte, ice.MOD_BUFS)
if n, e := c.Read(b); e == nil {
m.Info("nonce", string(b[:n]))
c.Write(b[:n])
}
c.Close()
}
m.ErrorNotImplement(cb)
}
}
const (
PROTOCOL = "protocol"
HOSTPORT = "hostport"
HOSTNAME = "hostname"
PROTOCOL = "protocol"
HOSTPORT = "hostport"
HOSTNAME = "hostname"
NODENAME = "nodename"
NODETYPE = "nodetype"
BANDWIDTH = "bandwidth"
ADDRESS = "address"
)
const (
PROTO = "proto"
STATUS = "status"
ERROR = "error"
START = "start"
OPEN = "open"
CLOSE = "close"
STOP = "stop"
)
const (
LISTEN = "listen"
UNIX = "unix"
)
const SERVER = "server"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
SERVER: {Name: SERVER, Help: "服务器", Value: kit.Data(
kit.MDB_FIELD, "time,hash,status,type,name,host,port,error,nconn",
)},
}, Commands: map[string]*ice.Command{
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Richs(SERVER, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
kit.Value(value, kit.Keym(kit.MDB_STATUS), CLOSE)
})
m.Cmdy(SERVER, mdb.PRUNES)
}},
SERVER: {Name: "server hash auto prunes", Help: "服务器", Action: ice.MergeAction(map[string]*ice.Action{
LISTEN: {Name: "LISTEN type name port=9030 host=", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
_server_listen(m, arg...)
Index.MergeCommands(ice.Commands{
SERVER: {Help: "服务器", Actions: ice.MergeActions(ice.Actions{
LISTEN: {Name: "listen type name port=9030 host=", Hand: func(m *ice.Message, arg ...string) {
switch m.Option(mdb.TYPE) {
case UDP4:
_server_udp(m, arg...)
default:
_server_listen(m, arg...)
}
}},
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(mdb.PRUNES, SERVER, "", mdb.HASH, kit.MDB_STATUS, ERROR)
m.Cmdy(mdb.PRUNES, SERVER, "", mdb.HASH, kit.MDB_STATUS, CLOSE)
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.HashSelect(m, arg...).Table(func(index int, value map[string]string, head []string) {
m.PushButton(kit.Select("", mdb.REMOVE, value[kit.MDB_STATUS] == CLOSE))
})
}},
}})
}, mdb.StatusHashAction(mdb.FIELD, "time,hash,status,type,name,host,port,error"), mdb.ClearOnExitHashAction())},
})
}

View File

@ -2,10 +2,13 @@ package tcp
import (
ice "shylinux.com/x/icebergs"
kit "shylinux.com/x/toolkits"
)
const TCP = "tcp"
var Index = &ice.Context{Name: TCP, Help: "通信模块"}
func init() { ice.Index.Register(Index, nil, HOST, PORT, CLIENT, SERVER) }
func init() { ice.Index.Register(Index, nil, WIFI, HOST, PORT, CLIENT, SERVER) }
func Prefix(arg ...ice.Any) string { return kit.Keys(TCP, kit.Keys(arg...)) }

View File

@ -1,7 +0,0 @@
chapter "tcp"
field "主机" tcp.host
field "端口" tcp.port
field "服务器" tcp.server
field "客户端" tcp.client

47
base/tcp/wifi.go Normal file
View File

@ -0,0 +1,47 @@
package tcp
import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
kit "shylinux.com/x/toolkits"
)
const (
SSID = "ssid"
)
const WIFI = "wifi"
func init() {
const (
NETWORKSETUP = "networksetup"
CONNECT = "connect"
)
Index.MergeCommands(ice.Commands{
WIFI: {Help: "无线", Icon: "AirPort Utility.png", Actions: ice.MergeActions(ice.Actions{
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case SSID:
kit.For(kit.Slice(kit.SplitLine(m.System(NETWORKSETUP, "-listpreferredwirelessnetworks", "en0").Result()), 1), func(name string) {
m.Push(arg[0], strings.TrimSpace(name))
})
}
}},
CONNECT: {Help: "连接", Hand: func(m *ice.Message, arg ...string) {
m.ToastProcess()
msg := mdb.HashSelect(m.Spawn(), m.Option(SSID, strings.TrimSpace(m.Option(SSID))))
if res := m.System(NETWORKSETUP, "-setairportnetwork", "en0", kit.Select(m.Option(SSID), msg.Append(SSID)), msg.Append(aaa.PASSWORD)); res.Result() != "" {
m.Echo(res.Result()).ToastFailure(res.Result())
} else {
m.ProcessHold()
}
}},
}, mdb.ExportHashAction(mdb.SHORT, SSID, mdb.FIELD, "time,ssid,password")), Hand: func(m *ice.Message, arg ...string) {
if mdb.HashSelect(m, arg...).PushAction(CONNECT, mdb.REMOVE); len(arg) > 0 {
m.EchoQRCode(kit.Format("WIFI:T:WPA;S:%s;P:%s;H:false;;", m.Append(SSID), m.Append(aaa.PASSWORD)))
}
}},
})
}

67
base/web/admin.go Normal file
View File

@ -0,0 +1,67 @@
package web
import (
"net/http"
"os"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
const ADMIN = "admin"
func init() {
Index.MergeCommands(ice.Commands{
ADMIN: {Name: "admin index list", Help: "后台", Role: aaa.VOID, Actions: ice.MergeActions(ice.Actions{
DREAM_ACTION: {Hand: func(m *ice.Message, arg ...string) { DreamProcessIframe(m, arg...) }},
}, DreamTablesAction()), Hand: func(m *ice.Message, arg ...string) {
if m.Option(ice.MSG_SOURCE) != "" {
RenderMain(m)
} else {
kit.If(len(arg) == 0, func() { arg = append(arg, SPACE, DOMAIN) })
m.Cmd(SPIDE, mdb.CREATE, HostPort(m, tcp.LOCALHOST, kit.GetValid(
func() string { return m.Cmdx(nfs.CAT, ice.VAR_LOG_ICE_PORT) },
func() string { return m.Cmdx(nfs.CAT, kit.Path(os.Args[0], "../", ice.VAR_LOG_ICE_PORT)) },
func() string { return m.Cmdx(nfs.CAT, kit.Path(os.Args[0], "../../", ice.VAR_LOG_ICE_PORT)) },
func() string { return tcp.PORT_9020 },
)), ice.OPS)
args := []string{}
for i := range arg {
if arg[i] == "--" {
arg, args = arg[:i], arg[i+1:]
break
}
}
kit.If(os.Getenv(cli.CTX_OPS), func(p string) { m.Optionv(SPIDE_HEADER, html.XHost, p) })
m.Cmdy(SPIDE, ice.OPS, SPIDE_RAW, http.MethodPost, C(arg...), cli.PWD, kit.Path(""), args)
}
}},
})
}
func AdminCmd(m *ice.Message, cmd string, arg ...ice.Any) *ice.Message {
if ice.Info.NodeType == WORKER {
return m.Cmd(append([]ice.Any{SPACE, ice.OPS, cmd}, arg...)...)
} else {
return m.Cmd(append([]ice.Any{cmd}, arg...)...)
}
}
func OpsCmd(m *ice.Message, cmd string, arg ...ice.Any) *ice.Message {
if ice.Info.NodeType == WORKER {
return m.Cmd(append([]ice.Any{SPACE, ice.OPS, cmd}, arg...)...)
} else {
return m.Cmd(append([]ice.Any{cmd}, arg...)...)
}
}
func DevCmd(m *ice.Message, cmd string, arg ...ice.Any) *ice.Message {
if ice.Info.NodeType == WORKER {
return m.Cmd(append([]ice.Any{SPACE, ice.OPS, SPACE, ice.DEV, cmd}, arg...)...)
} else {
return m.Cmd(append([]ice.Any{SPACE, ice.DEV, cmd}, arg...)...)
}
}

52
base/web/basic.go Normal file
View File

@ -0,0 +1,52 @@
package web
import (
"encoding/base64"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func init() {
Index.MergeCommands(ice.Commands{
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { aaa.White(m, "basic") }},
"/basic/check": {Hand: func(m *ice.Message, arg ...string) {
kit.For(m.R.Header, func(key string, value []string) { m.Debug("what %v %v", key, value) })
if BasicSess(m); m.Option(ice.MSG_USERNAME) == "" {
BasicCheck(m, "请输入账号密码")
}
}},
"/basic/login": {Hand: func(m *ice.Message, arg ...string) { RenderMain(m) }},
"/basic/auths": {Hand: func(m *ice.Message, arg ...string) {
kit.If(m.R.URL.Query().Get(ice.MSG_SESSID), func(p string) { RenderCookie(m, m.Option(ice.MSG_SESSID, p)) })
RenderRedirect(m, kit.Select(nfs.PS, m.R.URL.Query().Get("redirect_uri")))
}},
})
}
func BasicSess(m *ice.Message) {
m.Options(ice.MSG_USERWEB, _serve_domain(m))
m.Options(ice.MSG_SESSID, kit.Select(m.Option(ice.MSG_SESSID), m.Option(CookieName(m.Option(ice.MSG_USERWEB)))))
aaa.SessCheck(m, m.Option(ice.MSG_SESSID))
}
func BasicCheck(m *ice.Message, realm string, check ...func(*ice.Message) bool) bool {
switch ls := kit.Split(m.R.Header.Get(html.Authorization)); kit.Select("", ls, 0) {
case html.Basic:
if buf, err := base64.StdEncoding.DecodeString(kit.Select("", ls, 1)); !m.WarnNotValid(err) {
if ls := strings.SplitN(string(buf), ":", 2); !m.WarnNotValid(len(ls) < 2 || ls[1] == "", html.Basic) {
if msg := m.Cmd(TOKEN, ls[1]); !m.WarnNotValid(msg.Time() > msg.Append(mdb.TIME)) {
if len(check) == 0 || check[0](msg) {
return true
}
}
}
}
}
m.W.Header().Add("WWW-Authenticate", kit.Format(`Basic realm="%s"`, realm))
m.RenderStatusUnauthorized()
return false
}

89
base/web/broad.go Normal file
View File

@ -0,0 +1,89 @@
package web
import (
"net"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/logs"
)
func _broad_send(m *ice.Message, to_host, to_port string, host, port string, arg ...string) {
m.Cmd(tcp.CLIENT, tcp.DIAL, mdb.TYPE, tcp.UDP4, tcp.HOST, to_host, tcp.PORT, kit.Select(tcp.PORT_9020, to_port), func(s *net.UDPConn) {
msg := m.Spawn(kit.Dict(tcp.HOST, host, tcp.PORT, port, arg))
msg.Logs(tcp.SEND, BROAD, msg.FormatsMeta(nil), nfs.TO, tcp.HostPort(to_host, to_port)).FormatsMeta(s)
})
}
func _broad_serve(m *ice.Message) {
if m.Cmd(tcp.HOST).Length() == 0 {
return
}
m.GoSleep300ms(func() {
m.Cmd(tcp.HOST, func(value ice.Maps) {
_broad_send(m, "", "", value[aaa.IP], m.Option(tcp.PORT), kit.Simple(gdb.EVENT, tcp.LISTEN, mdb.NAME, ice.Info.NodeName, mdb.TYPE, ice.Info.NodeType, mdb.TIME, m.Time(), cli.SimpleMake())...)
})
})
m.Cmd(tcp.SERVER, tcp.LISTEN, mdb.TYPE, tcp.UDP4, mdb.NAME, logs.FileLine(1), m.OptionSimple(tcp.HOST, tcp.PORT), func(from *net.UDPAddr, buf []byte) {
if m.WarnNotValid(len(buf) > 1024, "broad recv buf size too large") {
return
}
msg := m.Spawn(buf).Logs(tcp.RECV, BROAD, string(buf), nfs.FROM, from)
if strings.HasPrefix(msg.Option(tcp.HOST), "169.254") {
return
}
if m.Cmd(BROAD, mdb.CREATE, msg.OptionSimple(kit.Simple(msg.Optionv(ice.MSG_OPTION))...)); msg.Option(gdb.EVENT) == tcp.LISTEN {
m.Cmds(BROAD, func(value ice.Maps) {
_broad_send(m, msg.Option(tcp.HOST), msg.Option(tcp.PORT), value[tcp.HOST], value[tcp.PORT], kit.Simple(value)...)
})
}
})
}
const BROAD = "broad"
func init() {
Index.MergeCommands(ice.Commands{
BROAD: {Help: "广播台", Icon: "Podcasts.png", Actions: ice.MergeActions(ice.Actions{
SERVE_START: {Hand: func(m *ice.Message, arg ...string) { gdb.Go(m, _broad_serve) }},
mdb.SEARCH: {Hand: func(m *ice.Message, arg ...string) {
if mdb.IsSearchPreview(m, arg) {
host, domain := m.Cmdv(tcp.HOST, aaa.IP), UserWeb(m).Hostname()
m.Cmds("", func(value ice.Maps) {
switch kit.If(value[tcp.HOST] == host, func() { value[tcp.HOST] = domain }); value[mdb.TYPE] {
case "sshd":
m.PushSearch(mdb.NAME, Script(m, "ssh -p %s %s@%s", value[tcp.PORT], m.Option(ice.MSG_USERNAME), value[tcp.HOST]), mdb.TEXT, HostPort(m, value[tcp.HOST], value[tcp.PORT]), value)
default:
m.PushSearch(mdb.TEXT, HostPort(m, value[tcp.HOST], value[tcp.PORT]), value)
}
})
}
}},
SERVE: {Name: "serve port=9020 host", Hand: func(m *ice.Message, arg ...string) { gdb.Go(m, _broad_serve) }},
ADMIN: {Hand: func(m *ice.Message, arg ...string) { broadOpen(m) }},
DREAM: {Hand: func(m *ice.Message, arg ...string) { broadOpen(m) }},
VIMER: {Hand: func(m *ice.Message, arg ...string) { broadOpen(m) }},
SPIDE: {Name: "spide name type=repos", Icon: "bi bi-house-add", Hand: func(m *ice.Message, arg ...string) {
m.Cmd(SPIDE, mdb.CREATE, HostPort(m, m.Option(tcp.HOST), m.Option(tcp.PORT)), m.Option(mdb.NAME))
}},
OPEN: {Hand: func(m *ice.Message, arg ...string) {
m.ProcessOpen(HostPort(m, m.Option(tcp.HOST), m.Option(tcp.PORT)))
}},
tcp.SEND: {Hand: func(m *ice.Message, arg ...string) { _broad_send(m, "", "", "", "", arg...) }},
}, gdb.EventsAction(SERVE_START), mdb.HashAction(mdb.SHORT, "host,port",
mdb.FIELD, "time,hash,type,name,host,port,module,version,commitTime,compileTime,bootTime,kernel,arch",
mdb.ACTION, "admin,dream,vimer,spide,open", mdb.SORT, "type,name,host,port"), mdb.ClearOnExitHashAction()), Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m, arg...)
m.StatusTimeCount("nodename", ice.Info.NodeName)
}},
})
}
func broadOpen(m *ice.Message) {
m.ProcessOpen(HostPort(m, m.Option(mdb.NAME), m.Option(tcp.PORT)) + C(m.ActionKey()))
}

View File

@ -5,128 +5,98 @@ import (
"net/http"
"os"
"path"
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/toolkits/miss"
)
func _cache_name(m *ice.Message, h string) string {
return path.Join(m.Config(kit.MDB_PATH), h[:2], h)
}
func _cache_save(m *ice.Message, kind, name, text string, arg ...string) { // file size
if name == "" {
return
func _cache_name(m *ice.Message, h string) string { return path.Join(ice.VAR_FILE, h[:2], h) }
func _cache_mime(m *ice.Message, mime, name string) string {
if mime == html.ApplicationOctet {
if kit.ExtIsImage(name) {
mime = IMAGE + nfs.PS + kit.Ext(name)
} else if kit.ExtIsVideo(name) {
mime = VIDEO + nfs.PS + kit.Ext(name)
}
} else if mime == "" {
return kit.Ext(name)
}
if len(text) > 512 || kind == "go" { // 存入文件
p := m.Cmdx(nfs.SAVE, _cache_name(m, kit.Hashs(text)), text)
return mime
}
func _cache_save(m *ice.Message, mime, name, text string, arg ...string) {
if m.WarnNotValid(name == "", mdb.NAME) {
return
} else if len(text) > 512 {
p := m.Cmdx(nfs.SAVE, _cache_name(m, kit.Hashs(text)), kit.Dict(nfs.CONTENT, text))
text, arg = p, kit.Simple(p, len(text))
}
// 添加数据
size := kit.Int(kit.Select(kit.Format(len(text)), arg, 1))
file := kit.Select("", arg, 0)
text = kit.Select(file, text)
h := m.Cmdx(mdb.INSERT, CACHE, "", mdb.HASH,
kit.MDB_TYPE, kind, kit.MDB_NAME, name, kit.MDB_TEXT, text,
kit.MDB_FILE, file, kit.MDB_SIZE, size)
// 返回结果
m.Push(kit.MDB_TIME, m.Time())
m.Push(kit.MDB_TYPE, kind)
m.Push(kit.MDB_NAME, name)
m.Push(kit.MDB_TEXT, text)
m.Push(kit.MDB_SIZE, size)
m.Push(kit.MDB_FILE, file)
m.Push(kit.MDB_HASH, h)
m.Push(DATA, h)
file, size := kit.Select("", arg, 0), kit.Int(kit.Select(kit.Format(len(text)), arg, 1))
mime, text = _cache_mime(m, mime, name), kit.Select(file, text)
m.Push(mdb.TIME, m.Time()).Push(mdb.HASH, mdb.HashCreate(m.Spawn(), kit.SimpleKV("", mime, name, text), nfs.FILE, file, nfs.SIZE, size))
m.Push(mdb.TYPE, mime).Push(mdb.NAME, name).Push(mdb.TEXT, text).Push(nfs.FILE, file).Push(nfs.SIZE, size)
}
func _cache_watch(m *ice.Message, key, file string) {
mdb.HashSelect(m.Spawn(), key).Table(func(index int, value map[string]string, head []string) {
if value[kit.MDB_FILE] == "" {
m.Cmdy(nfs.SAVE, file, value[kit.MDB_TEXT])
func _cache_watch(m *ice.Message, key, path string) {
mdb.HashSelect(m.Spawn(), key).Table(func(value ice.Maps) {
if value[nfs.FILE] == "" {
m.Cmdy(nfs.SAVE, path, value[mdb.TEXT])
} else {
m.Cmdy(nfs.LINK, file, value[kit.MDB_FILE])
m.Cmdy(nfs.LINK, path, value[nfs.FILE])
}
})
}
func _cache_catch(m *ice.Message, name string) (file, size string) {
if f, e := os.Open(name); m.Assert(e) {
defer f.Close()
if s, e := f.Stat(); m.Assert(e) {
return m.Cmdx(nfs.LINK, _cache_name(m, kit.Hashs(f)), name), kit.Format(s.Size())
}
func _cache_catch(m *ice.Message, path string) (file string, size string) {
if msg := m.Cmd(nfs.DIR, path, "hash,size"); msg.Length() > 0 {
return m.Cmdx(nfs.LINK, _cache_name(m, msg.Append(mdb.HASH)), path), msg.Append(nfs.SIZE)
}
return "", "0"
}
func _cache_upload(m *ice.Message, r *http.Request) (kind, name, file, size string) {
if buf, h, e := r.FormFile(UPLOAD); e == nil {
defer buf.Close()
// 创建文件
if f, p, e := kit.Create(_cache_name(m, kit.Hashs(buf))); m.Assert(e) {
func _cache_upload(m *ice.Message, r *http.Request) (mime, name, file, size string) {
if b, h, e := r.FormFile(UPLOAD); !m.WarnNotValid(e, UPLOAD) {
defer b.Close()
if f, p, e := miss.CreateFile(_cache_name(m, kit.Hashs(b))); !m.WarnNotValid(e, UPLOAD) {
defer f.Close()
// 导入数据
buf.Seek(0, os.SEEK_SET)
if n, e := io.Copy(f, buf); m.Assert(e) {
m.Log_IMPORT(kit.MDB_FILE, p, kit.MDB_SIZE, kit.FmtSize(int64(n)))
return h.Header.Get(ContentType), h.Filename, p, kit.Format(n)
b.Seek(0, os.SEEK_SET)
if n, e := io.Copy(f, b); !m.WarnNotValid(e, UPLOAD) {
m.Logs(nfs.SAVE, nfs.FILE, p, nfs.SIZE, kit.FmtSize(int64(n)))
return h.Header.Get(html.ContentType), h.Filename, p, kit.Format(n)
}
}
}
return "", "", "", "0"
}
func _cache_download(m *ice.Message, r *http.Response) (file, size string) {
defer r.Body.Close()
if f, p, e := kit.Create(path.Join(ice.VAR_TMP, kit.Hashs("uniq"))); m.Assert(e) {
step, total := 0, kit.Int(kit.Select("1", r.Header.Get(ContentLength)))
size, buf := 0, make([]byte, ice.MOD_BUFS)
for {
if n, _ := r.Body.Read(buf); n > 0 {
size += n
f.Write(buf[0:n])
s := size * 100 / total
switch cb := m.OptionCB(SPIDE).(type) {
case func(int, int):
cb(size, total)
case []string:
m.Richs(cb[0], cb[1], cb[2], func(key string, value map[string]interface{}) {
value = kit.GetMeta(value)
value[kit.MDB_STEP], value[kit.MDB_SIZE], value[kit.MDB_TOTAL] = kit.Format(s), size, total
})
default:
if s != step && s%10 == 0 {
m.Debug("what %v", m.OptionCB(SPIDE))
m.Debug("what %v", m.OptionCB(SPIDE))
m.Debug("what %v", kit.FileLine(m.OptionCB(SPIDE), 3))
m.Log_IMPORT(kit.MDB_FILE, p, kit.MDB_STEP, s,
kit.MDB_SIZE, kit.FmtSize(int64(size)), kit.MDB_TOTAL, kit.FmtSize(int64(total)))
}
}
step = s
continue
func _cache_download(m *ice.Message, r *http.Response, file string, cb ice.Any) string {
m.Option(ice.MSG_USERROLE, aaa.TECH)
if f, p, e := miss.CreateFile(file); !m.WarnNotValid(e, DOWNLOAD) {
defer func() {
if s, e := os.Stat(file); e == nil && s.Size() == 0 {
nfs.Remove(m, file)
}
f.Close()
break
}
if f, e := os.Open(p); m.Assert(e) {
defer f.Close()
m.Log_IMPORT(kit.MDB_FILE, p, kit.MDB_SIZE, kit.FmtSize(int64(size)))
c := _cache_name(m, kit.Hashs(f))
m.Cmd(nfs.LINK, c, p)
return c, kit.Format(size)
}
}()
defer f.Close()
last, base := 0, 10
nfs.CopyStream(m, f, r.Body, base*ice.MOD_BUFS, kit.Int(kit.Select("100", r.Header.Get(html.ContentLength))), func(count, total, value int) {
if value/base == last {
return
}
last = value / base
switch m.Logs(nfs.SAVE, nfs.FILE, p, mdb.COUNT, kit.FmtSize(int64(count)), mdb.TOTAL, kit.FmtSize(int64(total)), mdb.VALUE, value); cb := cb.(type) {
case func(int, int, int):
kit.If(cb != nil, func() { cb(count, total, value) })
case nil:
default:
m.ErrorNotImplement(cb)
}
})
return p
}
return "", "0"
return ""
}
const (
@ -135,57 +105,160 @@ const (
WRITE = "write"
UPLOAD = "upload"
DOWNLOAD = "download"
PREVIEW = "preview"
PAGES = "pages"
IMAGE = "image"
VIDEO = "video"
)
const CACHE = "cache"
func init() {
Index.Merge(&ice.Context{Configs: map[string]*ice.Config{
CACHE: {Name: CACHE, Help: "缓存池", Value: kit.Data(
kit.MDB_SHORT, kit.MDB_TEXT, kit.MDB_FIELD, "time,hash,size,type,name,text",
kit.MDB_STORE, ice.VAR_DATA, kit.MDB_PATH, ice.VAR_FILE, kit.MDB_FSIZE, "200000",
kit.MDB_LIMIT, "50", kit.MDB_LEAST, "30",
)},
}, Commands: map[string]*ice.Command{
"/cache/": {Name: "/cache/", Help: "缓存池", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
m.Richs(CACHE, nil, arg[0], func(key string, value map[string]interface{}) {
if kit.Format(value[kit.MDB_FILE]) == "" {
m.RenderResult(value[kit.MDB_TEXT])
} else {
m.RenderDownload(value[kit.MDB_FILE])
}
})
}},
CACHE: {Name: "cache hash auto", Help: "缓存池", Action: ice.MergeAction(map[string]*ice.Action{
WATCH: {Name: "watch key file", Help: "释放", Hand: func(m *ice.Message, arg ...string) {
_cache_watch(m, arg[0], arg[1])
Index.MergeCommands(ice.Commands{
CACHE: {Name: "cache hash auto upload", Help: "缓存池", Actions: ice.MergeActions(ice.Actions{
ice.RENDER_DOWNLOAD: {Hand: func(m *ice.Message, arg ...string) {
m.Echo(_share_link(m, kit.Select(arg[0], arg, 1), ice.POD, m.Option(ice.MSG_USERPOD), nfs.FILENAME, kit.Select("", arg[0], len(arg) > 1)))
}},
CATCH: {Name: "catch type name", Help: "捕获", Hand: func(m *ice.Message, arg ...string) {
file, size := _cache_catch(m, arg[1])
_cache_save(m, arg[0], arg[1], "", file, size)
WRITE: {Name: "write type name* text*", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
_cache_save(m, m.Option(mdb.TYPE), m.Option(mdb.NAME), m.Option(mdb.TEXT))
}},
WRITE: {Name: "write type name text", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
_cache_save(m, arg[0], arg[1], arg[2])
CATCH: {Name: "catch path* type", Help: "导入", Hand: func(m *ice.Message, arg ...string) {
file, size := _cache_catch(m, m.Option(nfs.PATH))
_cache_save(m, m.Option(mdb.TYPE), m.Option(nfs.PATH), "", file, size)
}},
UPLOAD: {Name: "upload", Help: "上传", Hand: func(m *ice.Message, arg ...string) {
kind, name, file, size := _cache_upload(m, m.R)
_cache_save(m, kind, name, "", file, size)
WATCH: {Name: "watch hash* path*", Help: "导出", Hand: func(m *ice.Message, arg ...string) {
_cache_watch(m, m.Option(mdb.HASH), m.Option(nfs.PATH))
}},
DOWNLOAD: {Name: "download type name", Help: "下载", Hand: func(m *ice.Message, arg ...string) {
if r, ok := m.Optionv(RESPONSE).(*http.Response); ok {
file, size := _cache_download(m, r)
_cache_save(m, arg[0], arg[1], "", file, size)
UPLOAD: {Hand: func(m *ice.Message, arg ...string) {
mime, name, file, size := _cache_upload(m, m.R)
_cache_save(m, mime, name, "", file, size)
}},
DOWNLOAD: {Name: "download type name*", Hand: func(m *ice.Message, arg ...string) {
if res, ok := m.Optionv(RESPONSE).(*http.Response); !m.WarnNotValid(!ok, RESPONSE) {
nfs.Temp(m, func(p string) {
file, size := _cache_catch(m, _cache_download(m, res, p, m.OptionCB("")))
_cache_save(m, m.Option(mdb.TYPE), m.Option(mdb.NAME), "", file, size)
})
}
}},
}, mdb.HashAction()), Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
nfs.PS: {Hand: func(m *ice.Message, arg ...string) {
if mdb.HashSelectDetail(m, arg[0], func(value ice.Map) {
kit.If(kit.Format(value[nfs.FILE]), func() { m.RenderDownload(value[nfs.FILE]) }, func() { m.RenderResult(value[mdb.TEXT]) })
}) {
return
}
if pod := m.Option(ice.POD); pod != "" {
msg := m.Options(ice.POD, "").Cmd(SPACE, pod, CACHE, arg[0])
kit.If(kit.Format(msg.Append(nfs.FILE)), func() {
m.RenderDownload(path.Join(ice.USR_LOCAL_WORK, pod, msg.Append(nfs.FILE)))
}, func() { m.RenderResult(msg.Append(mdb.TEXT)) })
}
}},
}, mdb.HashAction(mdb.SHORT, mdb.TEXT, mdb.FIELD, "time,hash,size,type,name,text,file"), ice.RenderAction(ice.RENDER_DOWNLOAD)), Hand: func(m *ice.Message, arg ...string) {
if mdb.HashSelect(m, arg...); len(arg) == 0 {
return
}
if m.Append(kit.MDB_FILE) == "" {
m.PushScript("inner", m.Append(kit.MDB_TEXT))
if m.Length() == 0 {
return
} else if m.Append(nfs.FILE) == "" {
m.PushScript(mdb.TEXT, m.Append(mdb.TEXT))
} else {
m.PushDownload(m.Append(kit.MDB_NAME), kit.MergeURL2(m.Option(ice.MSG_USERWEB), "/share/cache/"+arg[0]))
PushDisplay(m, m.Append(mdb.TYPE), m.Append(mdb.NAME), m.MergeLink(P(SHARE, CACHE, arg[0])))
}
}},
}})
})
ice.AddMergeAction(func(c *ice.Context, key string, cmd *ice.Command, sub string, action *ice.Action) {
switch sub {
case UPLOAD:
if kit.FileLines(action.Hand) == kit.FileLines(1) {
break
}
watch := action.Hand == nil
action.Hand = ice.MergeHand(func(m *ice.Message, arg ...string) {
up := Upload(m)
m.Assert(len(up) > 1)
msg := m.Cmd(CACHE, m.Option(ice.MSG_UPLOAD))
// if m.Cmd(CACHE, m.Option(ice.MSG_UPLOAD)).Table(func(value ice.Maps) { m.Options(value) }).Length() == 0 {
if msg.Length() == 0 {
SpideCache(m.Spawn(), m.MergeLink(SHARE_CACHE+up[0]))
}
// if m.Options(mdb.HASH, up[0], mdb.NAME, up[1]); watch {
if watch {
m.Cmdy(CACHE, WATCH, up[0], path.Join(msg.Append(nfs.PATH), up[1]))
}
}, action.Hand)
}
})
}
func UploadSave(m *ice.Message, p string) string {
up := kit.Simple(m.Optionv(ice.MSG_UPLOAD))
kit.If(strings.HasSuffix(p, nfs.PS), func() { p = path.Join(p, up[1]) })
m.Cmd(CACHE, WATCH, up[0], p)
return p
}
func Upload(m *ice.Message) []string {
if up := kit.Simple(m.Optionv(ice.MSG_UPLOAD)); len(up) == 1 {
msg := m.Cmd(CACHE, UPLOAD)
if m.Optionv(ice.MSG_UPLOAD, kit.Simple(msg.Append(mdb.HASH), msg.Append(mdb.NAME), msg.Append(nfs.SIZE))); m.Option(ice.MSG_USERPOD) != "" {
if nfs.Exists(m, nfs.USR_LOCAL_WORK+m.Option(ice.MSG_USERPOD)) {
m.Cmd(nfs.LINK, path.Join(nfs.USR_LOCAL_WORK+m.Option(ice.MSG_USERPOD), msg.Append(nfs.FILE)), msg.Append(nfs.FILE))
m.Cmd(SPACE, m.Option(ice.MSG_USERPOD), CACHE, mdb.CREATE, msg.AppendSimple(mdb.NAME, mdb.TEXT, nfs.FILE, nfs.SIZE))
} else {
m.Cmd(SPACE, m.Option(ice.MSG_USERPOD), SPIDE, ice.DEV, SPIDE_CACHE, http.MethodGet, tcp.PublishLocalhost(m, m.MergeLink(PP(SHARE, CACHE, msg.Append(mdb.HASH)))))
}
}
return kit.Simple(m.Optionv(ice.MSG_UPLOAD))
} else {
return up
}
}
func Download(m *ice.Message, link string, cb func(count, total, value int)) *ice.Message {
return m.Cmdy(Prefix(SPIDE), ice.DEV, SPIDE_CACHE, http.MethodGet, link, cb)
}
func PushDisplay(m *ice.Message, mime, name, link string) {
if html.IsImage(name, mime) {
m.PushImages(nfs.FILE, link)
} else if html.IsVideo(name, mime) {
m.PushVideos(nfs.FILE, link)
} else if html.IsAudio(name, mime) {
m.PushAudios(nfs.FILE, link)
} else {
m.PushDownload(nfs.FILE, name, link)
}
}
func RenderCache(m *ice.Message, h string) {
if msg := m.Cmd(CACHE, h); msg.Append(nfs.FILE) == "" {
m.RenderResult(msg.Append(mdb.TEXT))
} else {
m.RenderDownload(msg.Append(mdb.FILE), msg.Append(mdb.TYPE), msg.Append(mdb.NAME))
}
}
func ExportCacheAction(field string) ice.Actions {
return ice.Actions{
ice.CTX_EXIT: {Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m.Spawn(kit.Dict(ice.MSG_FIELDS, field))).Table(func(value ice.Maps) {
kit.For(kit.Split(value[field]), func(h string) {
msg := m.Cmd(CACHE, h)
m.Cmd(nfs.LINK, kit.Keys(path.Join(ice.USR_LOCAL_EXPORT, m.PrefixKey(), field, h), kit.Select("", kit.Split(msg.Append(mdb.TYPE), nfs.PS), -1)), msg.Append(nfs.FILE))
})
})
}},
ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) {
list := map[string]string{}
m.Cmd(nfs.DIR, path.Join(ice.USR_LOCAL_EXPORT, m.PrefixKey(), field), func(value ice.Maps) {
list[kit.TrimExt(value[nfs.PATH])] = m.Cmd(CACHE, CATCH, value[nfs.PATH]).Append(mdb.HASH)
})
mdb.HashSelectUpdate(m, "", func(value ice.Map) {
value[field] = kit.Join(kit.Simple(kit.For(kit.Split(kit.Format(value[field])), func(p string) string { return kit.Select(p, list[p]) })))
})
}},
UPLOAD: {Hand: func(m *ice.Message, arg ...string) {
nfs.Temp(m, func(p string) {
msg := m.Cmd(CACHE, Upload(m)[0])
if os.Link(msg.Append(nfs.FILE), p); nfs.ImageResize(m, p, 390, 390) {
m.Echo(m.Cmd(CACHE, CATCH, p, msg.Append(mdb.TYPE)).Append(mdb.HASH))
}
})
}},
}
}

114
base/web/count.go Normal file
View File

@ -0,0 +1,114 @@
package web
import (
"strings"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _count_stat(m *ice.Message, arg ...string) map[string]int {
stat := map[string]int{}
m.Table(func(value ice.Maps) {
count := kit.Int(value[mdb.COUNT])
stat[mdb.TOTAL] += count
for _, agent := range []string{"美国", "电信", "联通", "移动", "阿里云", "腾讯云"} {
if strings.Contains(value[aaa.LOCATION], agent) {
stat[agent] += count
break
}
}
for _, agent := range []string{"GoModuleMirror", "Go-http-client", "git", "compatible"} {
if strings.Contains(value[mdb.TEXT], agent) {
stat[agent] += count
return
}
}
for _, agent := range html.AgentList {
if strings.Contains(value[mdb.TEXT], agent) {
stat[agent] += count
break
}
}
for _, agent := range html.SystemList {
if strings.Contains(value[mdb.TEXT], agent) {
stat[agent] += count
break
}
}
})
return stat
}
const COUNT = "count"
func init() {
Index.MergeCommands(ice.Commands{
COUNT: &ice.Command{Name: "count hash auto group valid location", Help: "计数器", Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict(aaa.LOCATION, "地理位置")),
), Actions: ice.MergeActions(ice.Actions{
mdb.CREATE: {Name: "create type name text", Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelectUpdate(m, mdb.HashCreate(m), func(value ice.Map) { value[mdb.COUNT] = kit.Int(value[mdb.COUNT]) + 1 })
// m.Cmd("count", mdb.CREATE, OFFER, m.Option(FROM), kit.Dict(ice.LOG_DISABLE, ice.TRUE))
}},
mdb.VALID: {Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m.Spawn(), arg...).Table(func(value ice.Maps) {
if !strings.HasPrefix(value[mdb.TEXT], html.Mozilla) {
return
} else if count := kit.Int(value[mdb.COUNT]); count < 1 {
return
} else {
m.Push("", value, kit.Split(mdb.Config(m, mdb.FIELD)))
}
})
m.StatusTimeCount(_count_stat(m))
}},
mdb.GROUP: {Hand: func(m *ice.Message, arg ...string) {
count := map[string]int{}
list := map[string]map[string]string{}
m.Cmd("", mdb.VALID).Table(func(value ice.Maps) {
count[value[aaa.LOCATION]] += kit.Int(value[mdb.COUNT])
list[value[aaa.LOCATION]] = value
})
stat := map[string]int{}
for _, v := range list {
func() {
for _, agent := range []string{"美国", "电信", "联通", "移动", "阿里云", "腾讯云", "北京市", "香港"} {
if strings.Contains(v[aaa.LOCATION], agent) {
stat[agent] += kit.Int(v[mdb.COUNT])
return
}
}
m.Push("", v, kit.Split(mdb.Config(m, mdb.FIELD)))
}()
}
m.StatusTimeCount(stat)
}},
aaa.LOCATION: {Hand: func(m *ice.Message, arg ...string) {
GoToast(mdb.HashSelects(m).Sort(mdb.COUNT, ice.INT_R), func(toast func(string, int, int)) []string {
m.Table(func(value ice.Maps, index, total int) {
if value[aaa.LOCATION] == "" {
location := kit.Format(kit.Value(SpideGet(m, "http://opendata.baidu.com/api.php?co=&resource_id=6006&oe=utf8", "query", value[mdb.NAME]), "data.0.location"))
mdb.HashModify(m, mdb.HASH, value[mdb.HASH], aaa.LOCATION, location)
toast(kit.Select(value[mdb.NAME], location), index, total)
m.Sleep300ms()
}
})
return nil
})
}},
}, mdb.HashAction(mdb.LIMIT, 1000, mdb.LEAST, 500, mdb.SHORT, "type,name", mdb.FIELD, "time,hash,count,location,type,name,text")), Hand: func(m *ice.Message, arg ...string) {
mdb.HashSelect(m, arg...).Sort(mdb.TIME, ice.STR_R).StatusTimeCount(_count_stat(m))
}},
})
}
func Count(m *ice.Message, arg ...string) *ice.Message {
kit.If(len(arg) > 0 && arg[0] == "", func() { arg[0] = m.ShortKey() })
kit.If(len(arg) > 1 && arg[1] == "", func() { arg[1] = m.ActionKey() })
m.Cmd(COUNT, mdb.CREATE, arg, kit.Dict(ice.LOG_DISABLE, ice.TRUE))
return m
}

View File

@ -1,163 +1,601 @@
package web
import (
"io/ioutil"
"os"
"path"
"regexp"
"runtime"
"strings"
"time"
ice "shylinux.com/x/icebergs"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/cli"
"shylinux.com/x/icebergs/base/ctx"
"shylinux.com/x/icebergs/base/gdb"
"shylinux.com/x/icebergs/base/lex"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/nfs"
"shylinux.com/x/icebergs/base/tcp"
"shylinux.com/x/icebergs/base/web/html"
kit "shylinux.com/x/toolkits"
)
func _dream_list(m *ice.Message) {
m.Cmdy(nfs.DIR, m.Config(kit.MDB_PATH), "time,size,name").Table(func(index int, value map[string]string, head []string) {
if m.Richs(SPACE, nil, value[kit.MDB_NAME], func(key string, value map[string]interface{}) {
m.Push(kit.MDB_TYPE, value[kit.MDB_TYPE])
m.Push(kit.MDB_STATUS, cli.START)
m.PushButton(cli.STOP)
}) == nil {
m.Push(kit.MDB_TYPE, WORKER)
m.Push(kit.MDB_STATUS, cli.STOP)
m.PushButton(cli.START)
func _dream_list(m *ice.Message) *ice.Message {
list := m.CmdMap(SPACE, mdb.NAME)
mdb.HashSelects(m.Spawn()).Table(func(value ice.Maps, index int, head []string) {
if value[aaa.ACCESS] == aaa.PRIVATE && (m.Option(ice.FROM_SPACE) != "" || !aaa.IsTechOrRoot(m)) {
return
}
if space, ok := list[value[mdb.NAME]]; ok {
value[ice.MAIN] = space[ice.MAIN]
value[mdb.ICONS] = space[mdb.ICONS]
m.Push("", value, kit.Slice(head, 0, -1))
m.Push(mdb.TYPE, space[mdb.TYPE]).Push(cli.STATUS, cli.START)
m.Push(nfs.MODULE, space[nfs.MODULE]).Push(nfs.VERSION, space[nfs.VERSION])
button := []ice.Any{PORTAL, DESKTOP, ADMIN, WORD}
text := space[nfs.MODULE]
kit.If(m.Option(ice.DREAM_SIMPLE) != ice.TRUE && aaa.IsTechOrRoot(m), func() {
kit.If(m.IsDebug(), func() {
button = append(button, VIMER, STATUS, COMPILE, cli.RUNTIME, XTERM)
text += "\n" + DreamStat(m, value[mdb.NAME])
})
button = append(button, "settings", cli.STOP)
})
m.Push(mdb.TEXT, text)
m.PushButton(append(button, OPEN)...)
} else if aaa.IsTechOrRoot(m) {
m.Push("", value, kit.Slice(head, 0, -1))
m.Push(nfs.MODULE, "").Push(nfs.VERSION, "").Push(mdb.TEXT, "")
if m.Push(mdb.TYPE, WORKER); nfs.Exists(m, path.Join(ice.USR_LOCAL_WORK, value[mdb.NAME])) {
m.Push(cli.STATUS, cli.STOP).PushButton(cli.START, nfs.TRASH)
} else {
m.Push(cli.STATUS, cli.BEGIN).PushButton(cli.START, mdb.REMOVE)
}
}
})
m.SortStrR(kit.MDB_TIME)
m.RewriteAppend(func(value, key string, index int) string {
if key == mdb.TIME {
if space, ok := list[m.Appendv(mdb.NAME)[index]]; ok {
return space[mdb.TIME]
}
} else if key == mdb.ICONS {
if kit.HasPrefix(value, HTTP, nfs.PS) {
return value
} else if nfs.ExistsFile(m, path.Join(ice.USR_LOCAL_WORK, m.Appendv(mdb.NAME)[index], value)) {
return m.Spawn(kit.Dict(ice.MSG_USERPOD, m.Appendv(mdb.NAME)[index])).FileURI(value)
} else if nfs.ExistsFile(m, value) {
return m.FileURI(value)
}
}
return value
})
return m
}
func _dream_show(m *ice.Message, name string) {
if !strings.Contains(name, "-") || !strings.HasPrefix(name, "20") {
name = m.Time("20060102-") + strings.ReplaceAll(name, "-", "_")
func _dream_list_more(m *ice.Message) *ice.Message {
field := kit.Split(mdb.Config(m, mdb.FIELD) + ",type,status,module,version,text")
m.Cmds(SPACE).Table(func(value ice.Maps) {
value[nfs.REPOS] = "https://" + value[nfs.MODULE]
value[aaa.ACCESS] = kit.Select("", value[aaa.USERROLE], value[aaa.USERROLE] != aaa.VOID)
value[mdb.STATUS] = cli.START
button := []ice.Any{PORTAL, DESKTOP, ADMIN, WORD}
kit.If(m.IsDebug(), func() { button = append(button, VIMER, STATUS, COMPILE, cli.RUNTIME, XTERM) })
switch value[mdb.TYPE] {
case ORIGIN:
if m.IsCliUA() {
return
}
value[mdb.TEXT] = kit.JoinLine(value[nfs.MODULE], value[mdb.TEXT])
button = append(button, GETTOKEN, OPEN)
kit.If(value[aaa.ACCESS] == "", func() { button = []ice.Any{PORTAL, OPEN} })
case SERVER:
if !m.IsCliUA() {
value[mdb.TEXT] = kit.JoinLine(value[nfs.MODULE], value[mdb.TEXT])
} else if !strings.HasPrefix(value[mdb.TEXT], ice.HTTP) {
return
}
button = append(button, SETTOKEN, OPEN)
case aaa.LOGIN:
if m.IsCliUA() {
return
}
value[mdb.TEXT] = kit.JoinWord(value[AGENT], value[cli.SYSTEM], value[aaa.IP], kit.Format(PublicIP(m, value[aaa.IP])))
button = []ice.Any{GRANT}
default:
return
}
m.Push("", value, field)
m.PushButton(button...)
})
return m
}
func _dream_start(m *ice.Message, name string) {
if m.WarnNotValid(name == "", mdb.NAME) {
return
}
m.Option(kit.MDB_NAME, name)
// 任务目录
p := path.Join(m.Config(kit.MDB_PATH), name)
if m.Option(kit.SSH_REPOS) != "" { // 下载源码
m.Cmd("web.code.git.repos", mdb.CREATE, m.OptionSimple(kit.SSH_REPOS), kit.MDB_PATH, p)
} else { // 创建目录
os.MkdirAll(p, ice.MOD_DIR)
if !m.IsCliUA() {
defer m.ProcessRefresh()
}
m.ProcessOpen(kit.MergeURL2(m.Option(ice.MSG_USERWEB), "/chat/pod/"+name))
// 任务模板
if m.Option(kit.MDB_TEMPLATE) != "" {
for _, file := range []string{ice.ETC_MISS_SH, ice.SRC_MAIN_SHY, ice.SRC_MAIN_GO, ice.GO_MOD, ice.MAKEFILE} {
if _, e := os.Stat(path.Join(p, file)); os.IsNotExist(e) {
switch m.Cmdy(nfs.COPY, path.Join(p, file), path.Join(m.Option(kit.MDB_TEMPLATE), file)); file {
case ice.GO_MOD:
kit.Rewrite(path.Join(p, file), func(line string) string {
return kit.Select(line, "module "+name, strings.HasPrefix(line, "module"))
})
defer mdb.Lock(m, m.PrefixKey(), cli.START, name)()
p := _dream_check(m, name)
if p == "" {
return
}
if !nfs.Exists(m, p) {
gdb.Event(m, DREAM_CREATE, m.OptionSimple(mdb.NAME))
}
defer m.Options(cli.CMD_DIR, "", cli.CMD_ENV, "", cli.CMD_OUTPUT, "")
m.Options(cli.CMD_DIR, kit.Path(p), cli.CMD_ENV, kit.EnvList(kit.Simple(m.OptionSimple(ice.TCP_DOMAIN),
cli.CTX_OPS, HostPort(m, tcp.LOCALHOST, m.Cmdv(SERVE, tcp.PORT)), cli.CTX_LOG, ice.VAR_LOG_BOOT_LOG,
cli.CTX_ROOT, kit.Path(""), cli.PATH, cli.BinPath(p, ""), cli.USER, ice.Info.Username,
)...), cli.CMD_OUTPUT, path.Join(p, ice.VAR_LOG_BOOT_LOG), mdb.CACHE_CLEAR_ONEXIT, ice.TRUE)
kit.If(m.Option(nfs.BINARY) == "" && !cli.SystemFindGo(m), func(p string) { m.Option(nfs.BINARY, S(name)) })
kit.If(m.Option(nfs.BINARY), func(p string) { _dream_binary(m, p) })
kit.If(m.Option(nfs.TEMPLATE), func(p string) { _dream_template(m, p) })
bin := kit.Select(kit.Path(os.Args[0]), cli.SystemFind(m, ice.ICE_BIN, nfs.PWD+path.Join(p, ice.BIN), nfs.PWD+ice.BIN))
if cli.IsSuccess(m.Cmd(cli.DAEMON, bin, SPACE, tcp.DIAL, ice.DEV, ice.OPS, cli.DAEMON, ice.OPS)) {
gdb.WaitEvent(m, DREAM_OPEN, func(m *ice.Message, arg ...string) bool { return m.Option(mdb.NAME) == name })
m.Sleep300ms()
}
}
func _dream_check(m *ice.Message, name string) string {
p := path.Join(ice.USR_LOCAL_WORK, name)
msg := m.Spawn(kit.Dict(ice.MSG_USERROLE, aaa.ROOT))
if pp := path.Join(p, ice.VAR_LOG_ICE_PID); nfs.Exists(m, pp) {
for i := 0; i < 5; i++ {
pid := msg.Cmdx(nfs.CAT, pp)
if pid == "" {
return p
}
m.Sleep("1s")
if m.Cmd(SPACE, name).Length() > 0 {
m.Info("already exists %v", name)
return ""
}
if runtime.GOOS == cli.LINUX && !nfs.Exists(m, "/proc/"+pid) {
return p
}
if nfs.Exists(m, "/proc/"+pid) && runtime.GOOS == cli.LINUX {
if !kit.HasPrefix(msg.Cmdx(nfs.CAT, "/proc/"+pid+"/cmdline"), kit.Path(ice.BIN_ICE_BIN), kit.Path(p, ice.BIN_ICE_BIN)) {
return p
} else {
return ""
}
}
if gdb.SignalProcess(m, pid) {
m.Info("already exists %v", pid)
return ""
}
}
}
// 任务脚本
miss := path.Join(p, ice.ETC_MISS_SH)
if _, e := os.Stat(miss); os.IsNotExist(e) {
m.Cmd(nfs.SAVE, miss, m.Config("miss"))
return p
}
func _dream_binary(m *ice.Message, p string) {
if bin := path.Join(m.Option(cli.CMD_DIR), ice.BIN_ICE_BIN); nfs.Exists(m, bin) {
return
} else if kit.IsUrl(p) || strings.HasPrefix(p, S()) {
// m.Cmd(DREAM, DOWNLOAD, bin, kit.MergeURL2(p, kit.Format("/publish/ice.%s.%s", runtime.GOOS, runtime.GOARCH), ice.POD, m.Option(mdb.NAME)))
m.Cmd(DREAM, DOWNLOAD, bin, kit.MergeURL(p, cli.GOOS, runtime.GOOS, cli.GOARCH, runtime.GOARCH))
} else {
m.Cmd(nfs.LINK, bin, kit.Path(p))
}
if b, e := ioutil.ReadFile(path.Join(p, m.Conf(gdb.SIGNAL, kit.Keym(cli.PID)))); e == nil {
if s, e := os.Stat("/proc/" + string(b)); e == nil && s.IsDir() {
m.Info("already exists %v", string(b))
return // 已经启动
}
func _dream_template(m *ice.Message, p string) {
kit.For([]string{
ice.LICENSE, ice.README_MD, ice.MAKEFILE, ice.GO_MOD, ice.GO_SUM,
ice.SRC_MAIN_SH, ice.SRC_MAIN_SHY, ice.SRC_MAIN_GO, ice.SRC_MAIN_JS,
ice.ETC_MISS_SH, ice.ETC_INIT_SHY, ice.ETC_EXIT_SHY,
}, func(file string) {
if nfs.Exists(m, kit.Path(m.Option(cli.CMD_DIR), file)) {
return
}
}
if m.Richs(SPACE, nil, name, nil) == nil {
m.Option(cli.CMD_DIR, p)
m.Optionv(cli.CMD_ENV, kit.Simple(
cli.CTX_DEV, "http://:"+m.Cmd(SERVE).Append(tcp.PORT),
cli.PATH, kit.Path(path.Join(p, ice.BIN))+":"+kit.Path(ice.BIN)+":"+os.Getenv(cli.PATH),
cli.USER, ice.Info.UserName, m.Confv(DREAM, kit.Keym(cli.ENV)),
))
// 启动任务
m.Optionv(cli.CMD_OUTPUT, path.Join(p, m.Config(kit.Keys(cli.ENV, cli.CTX_LOG))))
m.Cmd(cli.DAEMON, m.Confv(DREAM, kit.Keym(ice.CMD)), ice.DEV, ice.DEV, kit.MDB_NAME, name, m.OptionSimple(RIVER))
m.Sleep(ice.MOD_TICK)
m.Event(DREAM_CREATE, kit.MDB_TYPE, m.Option(kit.MDB_TYPE), kit.MDB_NAME, name)
}
m.Cmdy(nfs.DIR, p)
switch m.Cmdy(nfs.COPY, kit.Path(m.Option(cli.CMD_DIR), file), kit.Path(ice.USR_LOCAL_WORK, p, file)); file {
case ice.GO_MOD:
nfs.Rewrite(m, path.Join(p, file), func(line string) string {
return kit.Select(line, nfs.MODULE+lex.SP+m.Option(mdb.NAME), strings.HasPrefix(line, nfs.MODULE))
})
}
})
}
const (
ALWAYS = "always"
STARTALL = "startall"
STOPALL = "stopall"
FOR_EACH = "forEach"
FOR_FLOW = "forFlow"
GETTOKEN = "gettoken"
SETTOKEN = "settoken"
DREAM_INPUTS = "dream.inputs"
DREAM_CREATE = "dream.create"
DREAM_REMOVE = "dream.remove"
DREAM_TRASH = "dream.trash"
DREAM_START = "dream.start"
DREAM_STOP = "dream.stop"
DREAM_OPEN = "dream.open"
DREAM_CLOSE = "dream.close"
DREAM_TABLES = "dream.tables"
DREAM_ACTION = "dream.action"
OPS_DREAM_CREATE = "ops.dream.create"
OPS_DREAM_REMOVE = "ops.dream.remove"
)
const DREAM = "dream"
func init() {
Index.Merge(&ice.Context{Commands: map[string]*ice.Command{
DREAM: {Name: "dream name path auto start create", Help: "梦想家", Action: map[string]*ice.Action{
mdb.INPUTS: {Name: "inputs", Help: "补全", Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case kit.MDB_NAME:
m.Cmdy(nfs.DIR, m.Config(kit.MDB_PATH), "name,time")
case kit.MDB_TEMPLATE:
m.Cmdy(nfs.DIR, m.Config(kit.MDB_PATH), "path,size,time")
m.SortStrR(kit.MDB_PATH)
Index.MergeCommands(ice.Commands{
DREAM: {Name: "dream refresh", Help: "梦想家", Icon: "Launchpad.png", Role: aaa.VOID, Meta: kit.Dict(
ice.CTX_TRANS, kit.Dict(html.INPUT, kit.Dict(WORKER, "空间", SERVER, "门户", ORIGIN, "主机")),
), Actions: ice.MergeActions(ice.Actions{
ice.AFTER_INIT: {Hand: func(m *ice.Message, arg ...string) {
AddPortalProduct(m, "云空间", `
比虚拟机和容器更加轻量每个空间都是一个完整的系统拥有各种软件与独立的环境
空间内所有的软件配置数据以源码库形式保存每个空间都可以随时启动停止上传下载分享
每个空间都自带软件开发工具也可以随时编程添加新的功能
`, 200.0)
}},
mdb.SEARCH: {Hand: func(m *ice.Message, arg ...string) {
if mdb.IsSearchPreview(m, arg) {
mdb.HashSelects(m.Spawn()).Table(func(value ice.Maps) { m.PushSearch(mdb.TYPE, WORKER, mdb.TEXT, m.MergePod(value[mdb.NAME]), value) })
}
}},
mdb.CREATE: {Name: "create main=src/main.go@key name=hi@key from=usr/icebergs/misc/bash/bash.go@key", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(SPACE, m.Option(ROUTE), "web.code.autogen", mdb.CREATE, arg)
m.ProcessInner()
mdb.INPUTS: {Hand: func(m *ice.Message, arg ...string) {
switch arg[0] {
case mdb.NAME:
DreamEach(m, "", kit.Select(cli.START, cli.STOP, m.Option(ctx.ACTION) == STARTALL), func(name string) { m.Push(arg[0], name) })
case tcp.NODENAME:
m.Cmdy(SPACE, m.Option(mdb.NAME), SPACE, ice.INFO).CutTo(mdb.NAME, arg[0])
case aaa.USERNAME:
if aaa.IsTechOrRoot(m) && m.Option(ctx.ACTION) == GRANT {
m.Cmdy(aaa.USER).Cut(aaa.USERNAME, aaa.USERNICK).Option(ice.TABLE_CHECKBOX, ice.FALSE)
} else {
m.Push(arg[0], m.Option(tcp.NODENAME))
m.Push(arg[0], m.Option(ice.MSG_USERNAME))
}
case nfs.REPOS:
case nfs.BINARY:
default:
gdb.Event(m, DREAM_INPUTS, arg)
}
}},
cli.START: {Name: "start name repos river", Help: "启动", Hand: func(m *ice.Message, arg ...string) {
_dream_show(m, m.Option(kit.MDB_NAME, kit.Select(path.Base(m.Option(kit.SSH_REPOS)), m.Option(kit.MDB_NAME))))
nfs.SCAN: {Hand: func(m *ice.Message, arg ...string) {
list := m.CmdMap(CODE_GIT_REPOS, nfs.REPOS)
GoToastTable(m.Cmd(nfs.DIR, nfs.USR_LOCAL_WORK, mdb.NAME), mdb.NAME, func(value ice.Maps) {
if repos, ok := list[value[mdb.NAME]]; ok {
m.Cmd("", mdb.CREATE, value[mdb.NAME], repos[ORIGIN])
}
})
}},
cli.STOP: {Name: "stop", Help: "停止", Hand: func(m *ice.Message, arg ...string) {
m.Cmdy(SPACE, m.Option(kit.MDB_NAME), "exit", "0")
mdb.CREATE: {Name: "create name*=hi repos binary", Hand: func(m *ice.Message, arg ...string) {
kit.If(!strings.Contains(m.Option(mdb.NAME), "-") || !strings.HasPrefix(m.Option(mdb.NAME), "20"), func() { m.Option(mdb.NAME, m.Time("20060102-")+m.Option(mdb.NAME)) })
m.Option(nfs.REPOS, kit.Select("", kit.Split(m.Option(nfs.REPOS)), -1))
if mdb.HashCreate(m); ice.Info.Important == true {
_dream_start(m, m.Option(mdb.NAME))
SpaceEvent(m, OPS_DREAM_CREATE, m.Option(mdb.NAME), m.OptionSimple(mdb.NAME, nfs.REPOS, nfs.BINARY)...)
}
}},
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
mdb.REMOVE: {Hand: func(m *ice.Message, arg ...string) {
gdb.Event(m, DREAM_REMOVE, m.OptionSimple(mdb.NAME))
mdb.HashRemove(m)
}},
STARTALL: {Name: "startall name", Help: "启动", Icon: "bi bi-play-circle", Hand: func(m *ice.Message, arg ...string) {
DreamEach(m, m.Option(mdb.NAME), cli.STOP, func(name string) {
m.Cmd("", cli.START, ice.Maps{mdb.NAME: name, ice.MSG_DAEMON: ""})
})
}},
STOPALL: {Name: "stopall name", Help: "停止", Icon: "bi bi-stop-circle", Hand: func(m *ice.Message, arg ...string) {
DreamEach(m, m.Option(mdb.NAME), cli.START, func(name string) {
m.Cmd("", cli.STOP, ice.Maps{mdb.NAME: name, ice.MSG_DAEMON: ""})
})
}},
cli.BUILD: {Name: "build name", Hand: func(m *ice.Message, arg ...string) {
compile := cli.SystemFindGo(m)
m.Option(ice.MSG_TITLE, kit.Keys(m.Option(ice.MSG_USERPOD0), m.Option(ice.MSG_USERPOD), m.CommandKey(), m.ActionKey()))
m.Cmd("", FOR_FLOW, m.Option(mdb.NAME), kit.JoinWord(cli.SH, ice.ETC_MISS_SH), func(p string) bool {
if compile && nfs.Exists(m, path.Join(p, ice.SRC_MAIN_GO)) {
return false
} else {
m.Cmd(SPACE, path.Base(p), cli.RUNTIME, UPGRADE)
return true
}
}).Sleep3s()
m.ProcessHold()
}},
PUBLISH: {Name: "publish name", Hand: func(m *ice.Message, arg ...string) {
m.Option(ice.MSG_TITLE, kit.Keys(m.Option(ice.MSG_USERPOD0), m.Option(ice.MSG_USERPOD), m.CommandKey(), m.ActionKey()))
list := []string{cli.LINUX, cli.DARWIN, cli.WINDOWS}
msg := m.Spawn(ice.Maps{ice.MSG_DAEMON: ""})
func() {
if m.Option(mdb.NAME) != "" {
return
}
defer ToastProcess(m, PUBLISH, ice.Info.Pathname)()
m.Cmd(AUTOGEN, BINPACK)
kit.For(list, func(goos string) {
list := []string{cli.AMD64}
kit.If(goos == cli.DARWIN, func() { list = append(list, cli.ARM64) })
kit.For(list, func(arch string) {
PushNoticeRich(m, mdb.NAME, ice.Info.NodeName, msg.Cmd(COMPILE, goos, arch).AppendSimple())
})
})
}()
DreamEach(m, m.Option(mdb.NAME), "", func(name string) {
m.Cmd(SPACE, name, AUTOGEN, BINPACK)
kit.For(list, func(goos string) {
list := []string{cli.AMD64}
kit.If(goos == cli.DARWIN, func() { list = append(list, cli.ARM64) })
kit.For(list, func(arch string) {
PushNoticeRich(m.Options(ice.MSG_COUNT, "0", ice.LOG_DISABLE, ice.TRUE), mdb.NAME, name, msg.Cmd(SPACE, name, COMPILE, goos, arch, kit.Dict(ice.MSG_USERPOD, name)).AppendSimple())
})
})
})
m.ProcessHold()
}},
FOR_FLOW: {Name: "forFlow name cmd*='sh etc/miss.sh'", Help: "流程", Icon: "bi bi-terminal", Hand: func(m *ice.Message, arg ...string) {
m.Options(ctx.DISPLAY, html.PLUGIN_XTERM, cli.CMD_OUTPUT, nfs.NewWriteCloser(func(buf []byte) (int, error) {
PushNoticeGrow(m.Options(ice.MSG_COUNT, "0", ice.LOG_DEBUG, ice.FALSE, ice.LOG_DISABLE, ice.TRUE), strings.ReplaceAll(string(buf), lex.NL, "\r\n"))
return len(buf), nil
}, nil))
msg := m.Spawn(ice.Maps{ice.MSG_DEBUG: ice.FALSE})
DreamEach(m, m.Option(mdb.NAME), "", func(name string) {
p := path.Join(ice.USR_LOCAL_WORK, name)
if cb, ok := m.OptionCB("").(func(string) bool); ok && cb(p) {
return
}
defer PushNoticeGrow(msg, "\r\n\r\n")
PushNoticeGrow(msg, kit.Format("\033[33m[%s]%s$\033[0m %s\r\n", time.Now().Format(ice.MOD_TIME_ONLY), name, m.Option(ice.CMD)))
m.Cmd(cli.SYSTEM, kit.Split(m.Option(ice.CMD)), kit.Dict(cli.CMD_DIR, p)).Sleep300ms()
})
}},
cli.START: {Hand: func(m *ice.Message, arg ...string) {
_dream_start(m, m.Option(mdb.NAME))
gdb.Event(m, DREAM_START, arg)
}},
cli.STOP: {Hand: func(m *ice.Message, arg ...string) {
gdb.Event(m, DREAM_STOP, arg)
m.Cmd(SPACE, mdb.MODIFY, m.OptionSimple(mdb.NAME), mdb.STATUS, cli.STOP)
m.Cmd(SPACE, m.Option(mdb.NAME), ice.EXIT).Sleep3s()
}},
nfs.TRASH: {Hand: func(m *ice.Message, arg ...string) {
gdb.Event(m, DREAM_TRASH, arg)
nfs.Trash(m, path.Join(ice.USR_LOCAL_WORK, m.Option(mdb.NAME)))
}},
cli.RUNTIME: {Hand: func(m *ice.Message, arg ...string) {
ProcessPodCmd(m, m.Option(mdb.NAME), "", nil, arg...)
}},
"settings": {Name: "settings restart=manual,always access=public,private", Help: "设置", Style: html.DANGER, Hand: func(m *ice.Message, arg ...string) {
kit.If(m.Option(cli.RESTART) == "manual", func() { m.Option(cli.RESTART, "") })
kit.If(m.Option(aaa.ACCESS) == aaa.PUBLIC, func() { m.Option(aaa.ACCESS, "") })
mdb.HashModify(m, m.OptionSimple(mdb.NAME, cli.RESTART, aaa.ACCESS))
}},
SETTOKEN: {Name: "settoken nodename* username*", Help: "令牌", Style: html.DANGER, Hand: func(m *ice.Message, arg ...string) {
token := m.Cmdx(TOKEN, mdb.CREATE, mdb.TYPE, SERVER, mdb.NAME, m.Option(aaa.USERNAME), mdb.TEXT, m.Option(tcp.NODENAME))
m.Cmd(SPACE, m.Option(mdb.NAME), SPIDE, DEV_CREATE_TOKEN, ice.Maps{TOKEN: token})
}},
GETTOKEN: {Help: "令牌", Style: html.DANGER, Hand: func(m *ice.Message, arg ...string) {
m.Options(m.Cmd(SPIDE, m.Option(mdb.NAME)).AppendSimple()).Cmdy(SPIDE, mdb.DEV_REQUEST)
}},
GRANT: {Name: "grant username", Role: aaa.VOID, Hand: func(m *ice.Message, arg ...string) {
if aaa.IsTechOrRoot(m) && m.Option(aaa.USERNAME) != "" {
m.Option(ice.MSG_USERNAME, m.Option(aaa.USERNAME))
}
m.Cmd(CHAT_GRANT, aaa.CONFIRM, kit.Dict(SPACE, m.Option(mdb.NAME)))
}},
OPEN: {Style: html.NOTICE, Role: aaa.VOID, Hand: func(m *ice.Message, arg ...string) {
if strings.HasSuffix(m.Option(ice.MAIN), ".portal") || kit.HasPrefixList(arg, ctx.RUN) {
if !kit.HasPrefixList(arg, ctx.RUN) {
defer m.Push(TITLE, m.Option(mdb.NAME))
defer m.Push("_icon", m.Option(mdb.ICON))
defer m.Push("_style", "portal")
defer m.Push("_height", "844")
defer m.Push("_width", "390")
}
ctx.ProcessFloat(m, CHAT_IFRAME, S(m.Option(mdb.NAME)), arg...)
} else if m.Option(mdb.TYPE) == ORIGIN {
m.ProcessOpen(SpideOrigin(m, m.Option(mdb.NAME)))
} else if p := ProxyDomain(m, m.Option(mdb.NAME)); p != "" {
m.ProcessOpen(p)
} else {
m.ProcessOpen(S(kit.Keys(m.Option(ice.MSG_USERPOD), m.Option(mdb.NAME))))
}
}},
DREAM_OPEN: {Hand: func(m *ice.Message, arg ...string) {}},
DREAM_CLOSE: {Hand: func(m *ice.Message, arg ...string) {
kit.For(arg, func(k, v string) {
if k == cli.DAEMON && v == ice.OPS && m.Cmdv(SPACE, m.Option(mdb.NAME), mdb.STATUS) != cli.STOP {
m.GoSleep300ms(func() { m.Cmd(DREAM, cli.START, m.OptionSimple(mdb.NAME)) })
}
})
}},
DREAM_TABLES: {Hand: func(m *ice.Message, arg ...string) {
button := []ice.Any{}
if aaa.IsTechOrRoot(m) {
switch m.Option(mdb.TYPE) {
case ORIGIN:
button = append(button, DREAM, GETTOKEN)
case SERVER:
button = append(button, DREAM, SETTOKEN)
case WORKER:
button = append(button, "settings")
}
}
m.PushButton(append(button, OPEN)...)
}},
SERVE_START: {Hand: func(m *ice.Message, arg ...string) {
for _, cmd := range kit.Reverse(kit.Split(mdb.Config(m, html.BUTTON))) {
m.Cmd(gdb.EVENT, gdb.LISTEN, gdb.EVENT, DREAM_TABLES, ice.CMD, cmd)
m.Cmd(gdb.EVENT, gdb.LISTEN, gdb.EVENT, DREAM_ACTION, ice.CMD, cmd)
aaa.White(m, kit.Keys(m.ShortKey(), ctx.ACTION, cmd))
}
mdb.HashSelects(m.Spawn()).SortStrR(mdb.NAME).Table(func(value ice.Maps) {
if value[cli.RESTART] == ALWAYS && nfs.Exists(m, path.Join(ice.USR_LOCAL_WORK+value[mdb.NAME])) {
m.Cmd(DREAM, cli.START, kit.Dict(mdb.NAME, value[mdb.NAME]))
}
})
}},
STATS_TABLES: {Hand: func(m *ice.Message, arg ...string) {
if msg := _dream_list(m.Spawn()); msg.Length() > 0 {
stat := map[string]int{}
msg.Table(func(value ice.Maps) { stat[value[mdb.TYPE]]++; stat[value[mdb.STATUS]]++ })
PushStats(m, kit.Keys(m.CommandKey(), cli.START), stat[cli.START], "", "已启动空间")
PushStats(m, kit.Keys(m.CommandKey(), SERVER), stat[SERVER], "", "已连接机器")
PushStats(m, kit.Keys(m.CommandKey(), ORIGIN), stat[ORIGIN], "", "已连接主机")
}
}},
ORIGIN: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(SPACE).Table(func(value ice.Maps, index int, head []string) {
kit.If(value[mdb.TYPE] == m.ActionKey(), func() { m.PushRecord(value, head...) })
})
m.SortStrR(mdb.NAME)
kit.If(len(arg) > 0, func() { m.Cut(arg...) })
}},
SERVER: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(SPACE).Table(func(value ice.Maps, index int, head []string) {
kit.If(value[mdb.TYPE] == m.ActionKey(), func() { m.PushRecord(value, head...) })
})
m.SortStrR(mdb.NAME)
kit.If(len(arg) > 0, func() { m.Cut(arg...) })
}},
WORKER: {Hand: func(m *ice.Message, arg ...string) {
m.Cmd(SPACE).Table(func(value ice.Maps, index int, head []string) {
kit.If(value[mdb.TYPE] == m.ActionKey(), func() { m.PushRecord(value, head...) })
})
m.SortStrR(mdb.NAME)
kit.If(len(arg) > 0, func() { m.Cut(arg...) })
}},
DOWNLOAD: {Name: "download path link", Hand: func(m *ice.Message, arg ...string) {
GoToast(m, func(toast func(string, int, int)) []string {
SpideSave(m, m.Option(nfs.PATH), kit.MergeURL(m.Option(mdb.LINK), cli.GOOS, runtime.GOOS, cli.GOARCH, runtime.GOARCH), func(count, total, value int) {
toast(m.Option(mdb.NAME), count, total)
})
return nil
})
os.Chmod(m.Option(nfs.PATH), ice.MOD_DIR)
}},
VERSION: {Hand: func(m *ice.Message, arg ...string) {
m.Cmdy("web.code.version")
}},
nfs.GOWORK: {Name: "gowork name", Help: "工作区", Icon: "bi bi-exclude", Hand: func(m *ice.Message, arg ...string) {
m.Cmd(cli.SYSTEM, cli.GO, "work", "init")
kit.For([]string{".", nfs.USR_RELEASE, nfs.USR_ICEBERGS, nfs.USR_TOOLKITS}, func(p string) { m.Cmd(cli.SYSTEM, cli.GO, "work", "use", p) })
DreamEach(m, m.Option(mdb.NAME), "", func(name string) { m.Cmd(cli.SYSTEM, cli.GO, "work", "use", path.Join(ice.USR_LOCAL_WORK, name)) })
m.Cmdy(nfs.CAT, "go.work")
}},
}, StatsAction(), DreamAction(), DreamTablesAction(), mdb.ImportantHashAction(
mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,main,icons,repos,binary,template,restart,access",
html.BUTTON, kit.JoinWord(PORTAL, DESKTOP, ADMIN, WORD, VIMER, STATUS, COMPILE, XTERM, DREAM),
)), Hand: func(m *ice.Message, arg ...string) {
if len(arg) == 0 {
if ice.Info.NodeType == WORKER {
return
}
_dream_list(m)
return
if _dream_list_more(m); !aaa.IsTechOrRoot(m) || m.IsCliUA() {
m.Action()
} else if m.IsDebug() && cli.SystemFindGo(m) {
m.Action(mdb.CREATE, STARTALL, STOPALL, cli.BUILD, PUBLISH)
} else {
m.Action(mdb.CREATE, STARTALL, STOPALL)
}
if m.Length() == 0 {
m.EchoInfoButton(m.Trans("please scan or create new dream", "请扫描或创建新空间"), mdb.CREATE, nfs.SCAN)
return
}
ctx.DisplayTableCard(m)
m.Options(ice.MSG_TOOLKIT, "web.code.compose.insight")
m.Sort("type,status,name", []string{aaa.LOGIN, WORKER, SERVER, ORIGIN}, []string{cli.START, cli.STOP, cli.BEGIN}, ice.STR_R)
m.StatusTimeCountStats(mdb.TYPE, mdb.STATUS)
} else if arg[0] == ctx.ACTION {
m.Cmdy(arg[1], DREAM_ACTION, arg)
// gdb.Event(m, DREAM_ACTION, arg)
}
m.Option(nfs.DIR_ROOT, path.Join(m.Config(kit.MDB_PATH), arg[0]))
m.Cmdy(nfs.CAT, arg[1:])
}},
}, Configs: map[string]*ice.Config{
DREAM: {Name: DREAM, Help: "梦想家", Value: kit.Data(kit.MDB_PATH, ice.USR_LOCAL_WORK,
ice.CMD, []interface{}{"ice.bin", SPACE, tcp.DIAL},
cli.ENV, kit.Dict(cli.CTX_LOG, ice.BIN_BOOT_LOG),
"miss", `#!/bin/bash
if [ "$ISH_CONF_PRE" = "" ]; then
[ -f $PWD/.ish/plug.sh ] || [ -f $HOME/.ish/plug.sh ] || git clone ${ISH_CONF_HUB_PROXY:="https://"}shylinux.com/x/intshell $PWD/.ish
source $PWD/.ish/plug.sh || source $HOME/.ish/plug.sh
fi
require miss.sh
ish_miss_prepare_compile
ish_miss_prepare_develop
ish_miss_prepare_install
# ish_miss_prepare wubi-dict
# ish_miss_prepare word-dict
# ish_miss_prepare linux-story
# ish_miss_prepare mysql-story
ish_miss_prepare release
ish_miss_prepare_contexts
# ish_miss_prepare_intshell
# ish_miss_prepare_icebergs
# ish_miss_prepare_toolkits
# ish_miss_prepare_volcanos
# ish_miss_prepare_learning
make
`,
)},
}})
})
}
func DreamTablesAction(arg ...string) ice.Actions {
return ice.Actions{ice.CTX_INIT: {Hand: DreamWhiteHandle},
DREAM_TABLES: {Hand: func(m *ice.Message, _ ...string) {
m.PushButton(kit.Dict(m.CommandKey(), kit.Select(m.Commands("").Help, arg, 0)))
}},
DREAM_ACTION: {Hand: func(m *ice.Message, arg ...string) { DreamProcess(m, "", nil, arg...) }},
}
}
func DreamAction() ice.Actions {
return gdb.EventsAction(
DREAM_INPUTS, DREAM_CREATE, DREAM_REMOVE, DREAM_TRASH, DREAM_OPEN, DREAM_CLOSE,
OPS_ORIGIN_OPEN, OPS_SERVER_OPEN, OPS_DREAM_CREATE, OPS_DREAM_REMOVE,
SERVE_START, SPACE_LOGIN,
)
}
func DreamWhiteHandle(m *ice.Message, arg ...string) {
aaa.White(m, kit.Keys(DREAM, ctx.ACTION, m.ShortKey()))
aaa.White(m, kit.Keys(m.ShortKey(), ctx.ACTION, DREAM_ACTION))
}
func DreamProcessIframe(m *ice.Message, arg ...string) {
if !kit.HasPrefixList(arg, ctx.ACTION, m.ShortKey()) && !kit.HasPrefixList(arg, ctx.ACTION, m.CommandKey()) {
return
}
if len(arg) == 2 {
defer m.Push(TITLE, kit.Keys(m.Option(mdb.NAME), m.ShortKey())+kit.Format("(%s)", m.Command().Help))
defer m.Push("_icon", m.Option(mdb.ICON))
}
DreamProcess(m, CHAT_IFRAME, func() string {
p := S(kit.Keys(m.Option(ice.MSG_USERPOD), m.Option(mdb.NAME)))
kit.If(m.Option(mdb.TYPE) == ORIGIN && m.CommandKey() == PORTAL, func() { p = SpideOrigin(m, m.Option(mdb.NAME)) })
return kit.MergeURL(p+C(m.ShortKey()), ice.MSG_DEBUG, m.Option(ice.MSG_DEBUG))
}, arg...)
}
func DreamProcess(m *ice.Message, cmd string, args ice.Any, arg ...string) {
if !kit.HasPrefixList(arg, ctx.ACTION, m.ShortKey()) && !kit.HasPrefixList(arg, ctx.ACTION, m.CommandKey()) {
return
} else if arg = arg[2:]; len(arg) == 0 {
arg = append(arg, m.Option(mdb.NAME))
defer m.ProcessField(ctx.ACTION, m.ShortKey(), arg[0], ctx.RUN)
defer processSpace(m, arg[0], arg[0], m.ShortKey())
}
ctx.ProcessFloat(m.Options(ice.POD, arg[0]), kit.Select(m.ShortKey(), cmd), args, arg[1:]...)
}
func DreamEach(m *ice.Message, name string, status string, cb func(string)) *ice.Message {
reg, err := regexp.Compile(name)
if m.WarnNotValid(err) {
return m
}
msg := m.Spawn()
m.Cmds(DREAM, kit.Dict(ice.DREAM_SIMPLE, ice.TRUE)).Table(func(value ice.Maps) {
if value[mdb.STATUS] == kit.Select(cli.START, status) && value[mdb.TYPE] == WORKER && (value[mdb.NAME] == name || reg.MatchString(kit.Format("%s:%s=%s@%d", value[mdb.NAME], value[mdb.TYPE], value[nfs.MODULE], value[nfs.VERSION]))) {
msg.Push(mdb.NAME, value[mdb.NAME])
}
})
return GoToastTable(msg, mdb.NAME, func(value ice.Maps) { cb(value[mdb.NAME]) })
}
func DreamListSpide(m *ice.Message, list []string, types string, cb func(dev, origin string)) {
msg := m.Spawn()
kit.For(list, func(name string) { msg.Push(mdb.NAME, name) })
m.Cmds(SPACE).Table(func(value ice.Maps) { kit.If(value[mdb.TYPE] == types, func() { msg.Push(mdb.NAME, value[mdb.NAME]) }) })
has := map[string]bool{}
GoToastTable(msg, mdb.NAME, func(value ice.Maps) {
origin := SpideOrigin(m, value[mdb.NAME])
kit.If(!has[origin], func() { has[origin] = true; cb(value[mdb.NAME], origin) })
})
}
func DreamList(m *ice.Message) *ice.Message {
return AdminCmd(m.Options(ice.DREAM_SIMPLE, ice.TRUE), DREAM)
}
func DreamStat(m *ice.Message, name string) (res string) {
if cli.SystemFindGit(m) {
text := []string{}
for _, line := range kit.Split(m.Cmdx(cli.SYSTEM, cli.GIT, "diff", "--shortstat", kit.Dict(cli.CMD_DIR, path.Join(ice.USR_LOCAL_WORK, name))), mdb.FS, mdb.FS) {
if list := kit.Split(line); strings.Contains(line, nfs.FILE) {
text = append(text, kit.Format("<span class='files'>%s file</span>", list[0]))
} else if strings.Contains(line, "ins") {
text = append(text, kit.Format("<span class='add'>%s+++</span>", list[0]))
} else if strings.Contains(line, "del") {
text = append(text, kit.Format("<span class='del'>%s---</span>", list[0]))
}
}
res = strings.Join(text, "")
}
return
}

Some files were not shown because too many files have changed in this diff Show More