Compare commits

...

160 Commits

Author SHA1 Message Date
github-actions[bot]
8db427309a @HrushikeshAnandSarangi has signed the CLA in windmill-labs/windmill#8739 2026-04-06 16:44:54 +00:00
github-actions[bot]
47103527c4 @jkker has signed the CLA in windmill-labs/windmill#8732 2026-04-06 08:48:45 +00:00
github-actions[bot]
5389e3b802 @masterkain has signed the CLA in windmill-labs/windmill#8690 2026-04-03 05:18:53 +00:00
github-actions[bot]
0162d69f66 @Archie0125 has signed the CLA in windmill-labs/windmill#8670 2026-04-02 11:35:14 +00:00
github-actions[bot]
3cefcdfc1e @royalcala has signed the CLA in windmill-labs/windmill#8472 2026-03-21 22:20:39 +00:00
github-actions[bot]
308eebd14b @travisp has signed the CLA in windmill-labs/windmill#8375 2026-03-15 02:46:44 +00:00
github-actions[bot]
43d4e9b7c1 @ClumsyAdmin has signed the CLA in windmill-labs/windmill#8331 2026-03-11 21:08:22 +00:00
github-actions[bot]
9ba962a42b @sascha-egerer has signed the CLA in windmill-labs/windmill#8330 2026-03-11 20:47:17 +00:00
github-actions[bot]
f77de8447f @colinlienard has signed the CLA in windmill-labs/windmill#8259 2026-03-07 14:29:51 +00:00
github-actions[bot]
a8380f8f80 @lubu0 has signed the CLA in windmill-labs/windmill#8192 2026-03-02 16:32:13 +00:00
github-actions[bot]
ae289bdd92 @zhzy0077 has signed the CLA in windmill-labs/windmill#7946 2026-02-15 03:18:46 +00:00
github-actions[bot]
c6d43a6165 @paluigi has signed the CLA in windmill-labs/windmill#7871 2026-02-10 09:46:20 +00:00
github-actions[bot]
0e7207d09d @tammoippen has signed the CLA in windmill-labs/windmill#7854 2026-02-09 11:21:49 +00:00
github-actions[bot]
4181270800 @N48LL has signed the CLA in windmill-labs/windmill#7751 2026-02-07 12:06:39 +00:00
github-actions[bot]
dda92e84f6 @j-o-br has signed the CLA in windmill-labs/windmill#7653 2026-01-22 14:16:37 +00:00
github-actions[bot]
ec467932f0 @xflowos has signed the CLA in windmill-labs/windmill#7620 2026-01-20 05:53:02 +00:00
github-actions[bot]
e414c7c6d0 @AvigailRoyzenberg has signed the CLA in windmill-labs/windmill#7400 2025-12-17 03:09:55 +00:00
github-actions[bot]
5e7c284cda @fcrozatier has signed the CLA in windmill-labs/windmill#7370 2025-12-15 11:11:23 +00:00
github-actions[bot]
72a2583ad4 @miguelmanlyx has signed the CLA in windmill-labs/windmill#7367 2025-12-15 02:19:00 +00:00
github-actions[bot]
327c9c4ec9 @devdattatalele has signed the CLA in windmill-labs/windmill#7267 2025-12-02 10:02:08 +00:00
github-actions[bot]
4d126f83f0 @codenio has signed the CLA in windmill-labs/windmill#7221 2025-11-25 15:47:00 +00:00
github-actions[bot]
5a7df29e1d @skrzyneckik has signed the CLA in windmill-labs/windmill#6999 2025-10-30 08:23:43 +00:00
github-actions[bot]
0ebb8328ba @vivekchavan14 has signed the CLA in windmill-labs/windmill#6889 2025-10-22 05:00:02 +00:00
github-actions[bot]
cc2dc62c80 @tristantr has signed the CLA in windmill-labs/windmill#6874 2025-10-21 09:12:01 +00:00
github-actions[bot]
f2264a35f5 @drobnikj has signed the CLA in windmill-labs/windmill#6679 2025-10-03 06:53:25 +00:00
github-actions[bot]
846f1e4ee8 @osmanmesutozcan has signed the CLA in windmill-labs/windmill#6696 2025-09-29 06:04:29 +00:00
github-actions[bot]
85955eb83c @zachwill has signed the CLA in windmill-labs/windmill#6687 2025-09-27 15:55:49 +00:00
github-actions[bot]
38428028a8 @iamramtin has signed the CLA in windmill-labs/windmill#6603 2025-09-12 19:31:00 +00:00
github-actions[bot]
ad6ca120e5 @Roderik-WU has signed the CLA in windmill-labs/windmill#6349 2025-08-08 16:34:34 +00:00
github-actions[bot]
c5923e42e9 @gpeppers has signed the CLA in windmill-labs/windmill#6306 2025-07-30 19:39:08 +00:00
github-actions[bot]
f5d67c0809 @zobar has signed the CLA in windmill-labs/windmill#6277 2025-07-25 14:31:24 +00:00
github-actions[bot]
ca677c0bd0 @iqdecay has signed the CLA in windmill-labs/windmill#6263 2025-07-23 12:01:08 +00:00
github-actions[bot]
93e51c025a @xosnrdev has signed the CLA in windmill-labs/windmill#6195 2025-07-16 03:14:37 +00:00
github-actions[bot]
923d6cea51 @JonasGruenwald has signed the CLA in windmill-labs/windmill#5944 2025-06-14 13:39:44 +00:00
github-actions[bot]
737bd152ea @pancernik has signed the CLA in windmill-labs/windmill#5792 2025-05-21 20:41:29 +00:00
github-actions[bot]
e6a47b2bc9 @0xThiebaut has signed the CLA in windmill-labs/windmill#5683 2025-04-29 19:53:19 +00:00
github-actions[bot]
0317c17484 @PiyushXCoder has signed the CLA in windmill-labs/windmill#5575 2025-04-05 17:09:54 +00:00
github-actions[bot]
33c4eddbe8 @BaptisteMoureaux has signed the CLA in windmill-labs/windmill#5555 2025-04-02 15:38:44 +00:00
github-actions[bot]
3e0d43be70 @centdix has signed the CLA in windmill-labs/windmill#5507 2025-03-26 11:05:21 +00:00
github-actions[bot]
4b9956d0a0 @diegoimbert has signed the CLA in windmill-labs/windmill#4813 2025-03-10 07:43:11 +00:00
github-actions[bot]
d1fcd03e62 @lephattan has signed the CLA in windmill-labs/windmill#5372 2025-02-25 03:00:13 +00:00
github-actions[bot]
0f328d4e50 @abutbul has signed the CLA in windmill-labs/windmill#5349 2025-02-21 15:57:42 +00:00
github-actions[bot]
9079b26b93 @rudokemper has signed the CLA in windmill-labs/windmill#5249 2025-02-10 03:35:48 +00:00
github-actions[bot]
a80c18edaa @arjun-1 has signed the CLA in windmill-labs/windmill#5173 2025-01-30 09:36:51 +00:00
github-actions[bot]
1326c65a17 @cherusk has signed the CLA in windmill-labs/windmill#4967 2024-12-22 14:25:33 +00:00
github-actions[bot]
22ef2ec755 @hkader-tl has signed the CLA in windmill-labs/windmill#4924 2024-12-13 18:08:54 +00:00
github-actions[bot]
ca9ecce9b3 @mzhang28 has signed the CLA in windmill-labs/windmill#4826 2024-12-01 04:51:47 +00:00
github-actions[bot]
6c20e9b63c @dieriba has signed the CLA in windmill-labs/windmill#4672 2024-11-10 10:19:50 +00:00
github-actions[bot]
712d4f67ab @J1ufan has signed the CLA in windmill-labs/windmill#4671 2024-11-08 07:42:37 +00:00
github-actions[bot]
93f1f7e9ca @marcus-crane has signed the CLA in windmill-labs/windmill#4658 2024-11-07 00:30:58 +00:00
github-actions[bot]
38d3f78566 @uael has signed the CLA in windmill-labs/windmill#4631 2024-11-05 09:18:38 +00:00
github-actions[bot]
6b3e134137 @BretzelLudique has signed the CLA in windmill-labs/windmill#4612 2024-10-31 13:45:03 +00:00
github-actions[bot]
b8a5a90ebe @witalloliveira has signed the CLA in windmill-labs/windmill#4609 2024-10-31 01:37:46 +00:00
github-actions[bot]
359096c49a @minchingtonak has signed the CLA in windmill-labs/windmill#4581 2024-10-25 05:27:12 +00:00
github-actions[bot]
2b499f21f2 @sbeckstrand has signed the CLA in windmill-labs/windmill#4578 2024-10-24 15:02:35 +00:00
github-actions[bot]
1ce1ec14d6 @alpetric has signed the CLA in windmill-labs/windmill#4407 2024-09-19 15:45:09 +00:00
github-actions[bot]
fc89027ec8 @tennox has signed the CLA in windmill-labs/windmill#4382 2024-09-12 15:15:08 +00:00
github-actions[bot]
67fc77dec1 @pyranota has signed the CLA in windmill-labs/windmill#4373 2024-09-11 19:55:56 +00:00
github-actions[bot]
513de69113 @Bert-Proesmans has signed the CLA in windmill-labs/windmill#4304 2024-08-29 14:26:30 +00:00
github-actions[bot]
5b68113e70 @Guilhem-lm has signed the CLA in windmill-labs/windmill#4218 2024-08-09 09:57:15 +00:00
github-actions[bot]
846332c72f @LewisJEllis has signed the CLA in windmill-labs/windmill#4210 2024-08-08 01:08:52 +00:00
github-actions[bot]
a01f78cb4c @freimer has signed the CLA in windmill-labs/windmill#4192 2024-08-05 17:48:47 +00:00
github-actions[bot]
456f64bd8c @dommyrock has signed the CLA in windmill-labs/windmill#3955 2024-06-22 21:34:30 +00:00
github-actions[bot]
9601e210da @klees has signed the CLA in windmill-labs/windmill#3901 2024-06-12 09:39:53 +00:00
github-actions[bot]
2d295df413 @hamirmahal has signed the CLA in windmill-labs/windmill#3872 2024-06-04 22:21:56 +00:00
github-actions[bot]
1ca5ab4a63 @marcelklehr has signed the CLA in windmill-labs/windmill#3867 2024-06-04 08:17:56 +00:00
github-actions[bot]
26fe4732fb @sieteunoseis has signed the CLA in windmill-labs/windmill#3795 2024-05-22 23:57:51 +00:00
github-actions[bot]
c57adaff70 @d10sfan has signed the CLA in windmill-labs/windmill#3759 2024-05-17 02:12:42 +00:00
github-actions[bot]
4c95552569 @wendrul has signed the CLA in windmill-labs/windmill#3566 2024-04-17 14:26:13 +00:00
github-actions[bot]
91629fda39 @fatonramadani has signed the CLA in windmill-labs/windmill#3536 2024-04-12 11:08:13 +00:00
github-actions[bot]
9ff198d221 @Git-on-my-level has signed the CLA in windmill-labs/windmill#3448 2024-03-21 09:40:03 +00:00
github-actions[bot]
b22b75b506 @c0nfleis has signed the CLA in windmill-labs/windmill#3442 2024-03-19 13:19:14 +00:00
github-actions[bot]
39c880b615 @wanglf has signed the CLA in windmill-labs/windmill#3393 2024-03-12 10:41:54 +00:00
github-actions[bot]
7b77aaa570 @JacobReynolds has signed the CLA in windmill-labs/windmill#3385 2024-03-09 22:28:00 +00:00
github-actions[bot]
8eaf03eb8b @soumitdas has signed the CLA in windmill-labs/windmill#3332 2024-03-03 04:23:33 +00:00
github-actions[bot]
320546177c @JDWNL has signed the CLA in windmill-labs/windmill#3317 2024-02-29 12:46:05 +00:00
github-actions[bot]
4c3491ca8c @lfanew has signed the CLA in windmill-labs/windmill#3299 2024-02-27 14:06:04 +00:00
github-actions[bot]
b85d16629f @mikhailzagurskiy has signed the CLA in windmill-labs/windmill#3235 2024-02-16 20:48:00 +00:00
github-actions[bot]
7624d5f05f @jacopobonomi has signed the CLA in windmill-labs/windmill#3225 2024-02-14 18:27:06 +00:00
github-actions[bot]
5a5f5b2d4f @doyleish has signed the CLA in windmill-labs/windmill#3192 2024-02-09 23:28:36 +00:00
github-actions[bot]
5e95b2ce40 @istarkov has signed the CLA in windmill-labs/windmill#3132 2024-02-01 22:24:15 +00:00
github-actions[bot]
f096046a09 @louisabraham has signed the CLA in windmill-labs/windmill#3087 2024-01-26 14:48:55 +00:00
github-actions[bot]
535030d400 @mogul has signed the CLA in windmill-labs/windmill#3056 2024-01-21 19:47:03 +00:00
github-actions[bot]
e8b74dd980 @anthonyangel has signed the CLA in windmill-labs/windmill#3040 2024-01-18 09:50:03 +00:00
github-actions[bot]
636dee5f4b @AudriusButkevicius has signed the CLA in windmill-labs/windmill#3019 2024-01-17 00:33:16 +00:00
github-actions[bot]
5feacbaec4 @rorylogue has signed the CLA in windmill-labs/windmill#2986 2024-01-11 10:29:34 +00:00
github-actions[bot]
3e1d22bafd @legalgig has signed the CLA in windmill-labs/windmill#2888 2023-12-19 20:18:47 +00:00
github-actions[bot]
d6655fa871 @notjosh has signed the CLA in windmill-labs/windmill#2871 2023-12-17 00:27:19 +00:00
github-actions[bot]
e52f078555 @nightah has signed the CLA in windmill-labs/windmill#2824 2023-12-09 13:08:03 +00:00
github-actions[bot]
fab1089a86 @Hoodoo has signed the CLA in windmill-labs/windmill#2736 2023-11-30 13:59:52 +00:00
github-actions[bot]
fa92648991 @eltociear has signed the CLA in windmill-labs/windmill#2706 2023-11-26 17:59:08 +00:00
github-actions[bot]
89bdbd32f8 @jacobm001 has signed the CLA in windmill-labs/windmill#2661 2023-11-20 18:40:49 +00:00
github-actions[bot]
d9028de9b9 @knowsuchagency has signed the CLA in windmill-labs/windmill#2650 2023-11-19 00:34:30 +00:00
github-actions[bot]
d5a0b3ff83 @betterthanever2 has signed the CLA in windmill-labs/windmill#2643 2023-11-17 14:21:37 +00:00
github-actions[bot]
421f9f113c @samip5 has signed the CLA in windmill-labs/windmill#2614 2023-11-11 03:46:32 +00:00
github-actions[bot]
c06cd34c4a @invakid404 has signed the CLA in windmill-labs/windmill#2556 2023-11-03 23:46:27 +00:00
github-actions[bot]
1e2c8ef9b1 @Bonniellhwhite has signed the CLA in windmill-labs/windmill#2496 2023-10-23 20:19:17 +00:00
github-actions[bot]
651ed0e735 @happysalada has signed the CLA in windmill-labs/windmill#2477 2023-10-21 09:19:23 +00:00
github-actions[bot]
632a25a2b6 @denglertai has signed the CLA from Pull Request #2353 2023-09-28 11:06:35 +00:00
github-actions[bot]
df8f8ea022 @AndreTeixeira1998 has signed the CLA from Pull Request #2267 2023-09-11 18:16:05 +00:00
github-actions[bot]
1b0836051b @bgoosmanviz has signed the CLA from Pull Request #2145 2023-08-21 21:46:51 +00:00
github-actions[bot]
76a83e08cb @SindreSvendby has signed the CLA from Pull Request #2095 2023-08-16 10:25:40 +00:00
github-actions[bot]
53ee8ec8eb @ImSingee has signed the CLA from Pull Request #2046 2023-08-13 07:49:47 +00:00
github-actions[bot]
33db63fcb5 @hmacr has signed the CLA from Pull Request #2024 2023-08-10 12:44:15 +00:00
github-actions[bot]
ec2f33b70b @antrix has signed the CLA from Pull Request #2023 2023-08-10 09:36:52 +00:00
github-actions[bot]
be6b933ccb @dm-canteen has signed the CLA from Pull Request #2007 2023-08-07 23:15:13 +00:00
github-actions[bot]
ea694f7d22 @dnicolson has signed the CLA from Pull Request #2001 2023-08-07 16:45:37 +00:00
github-actions[bot]
31b70d5c80 @gbouv has signed the CLA from Pull Request #1810 2023-07-07 15:27:22 +00:00
github-actions[bot]
6b5d2e914f @HugoCasa has signed the CLA from Pull Request #1799 2023-07-05 15:24:51 +00:00
github-actions[bot]
e53453e236 @Anton-Shutik has signed the CLA from Pull Request #1726 2023-06-13 13:16:00 +00:00
github-actions[bot]
b8863ac021 @mingfang has signed the CLA from Pull Request #1694 2023-06-09 01:48:22 +00:00
github-actions[bot]
51582f4c6e @junland has signed the CLA from Pull Request #1618 2023-05-21 20:33:00 +00:00
github-actions[bot]
981d41f534 @Pakome has signed the CLA from Pull Request #1568 2023-05-14 16:22:33 +00:00
github-actions[bot]
42bfc99373 @Jberlinsky has signed the CLA from Pull Request #1560 2023-05-12 13:58:35 +00:00
github-actions[bot]
c74da93ae7 @hcourdent has signed the CLA from Pull Request #1502 2023-05-02 06:58:31 +00:00
github-actions[bot]
2497e26b46 @ELLIOTTCABLE has signed the CLA from Pull Request #1450 2023-04-22 07:53:21 +00:00
github-actions[bot]
12a272c162 @mohsinmalik324 has signed the CLA from Pull Request #1437 2023-04-19 20:31:10 +00:00
github-actions[bot]
f2c59985b1 @axelbdt has signed the CLA from Pull Request #1395 2023-04-11 17:00:52 +00:00
github-actions[bot]
b69f23fca9 @jneeee has signed the CLA from Pull Request #1365 2023-04-05 05:59:20 +00:00
github-actions[bot]
c5882fde5d @oliver-veal has signed the CLA from Pull Request #1362 2023-04-04 16:05:02 +00:00
github-actions[bot]
26b4761ff8 @rutviklhase has signed the CLA from Pull Request #1354 2023-04-03 17:55:01 +00:00
github-actions[bot]
13bbdb065d @conscioustahoe has signed the CLA from Pull Request #1256 2023-03-03 08:03:13 +00:00
github-actions[bot]
12c1f32d24 @ryanrich has signed the CLA from Pull Request #1253 2023-03-02 04:37:18 +00:00
github-actions[bot]
ca997bbdb7 @felipealbertao has signed the CLA from Pull Request #1141 2023-01-21 00:20:35 +00:00
github-actions[bot]
fd8af8ad9e @axisofentropy has signed the CLA from Pull Request #1114 2023-01-16 19:05:21 +00:00
github-actions[bot]
fa771d4678 @waveywaves has signed the CLA from Pull Request #1106 2023-01-16 10:18:49 +00:00
github-actions[bot]
2572ca61e8 @kylegalbraith has signed the CLA from Pull Request #1104 2023-01-15 01:32:04 +00:00
github-actions[bot]
7667405678 @martysohio has signed the CLA from Pull Request #845 2022-11-01 18:11:36 +00:00
github-actions[bot]
6b6938c546 @HurricanKai has signed the CLA from Pull Request #731 2022-10-13 14:53:14 +00:00
github-actions[bot]
00c267ac5d @mrl5 has signed the CLA from Pull Request #650 2022-09-30 20:46:51 +00:00
github-actions[bot]
3107b5ff03 @adam-kov has signed the CLA from Pull Request #617 2022-09-22 12:16:22 +00:00
github-actions[bot]
6e04f24990 @ex0ns has signed the CLA from Pull Request #549 2022-09-09 20:10:05 +00:00
github-actions[bot]
d194f4633b @LucasLemanowicz has signed the CLA from Pull Request #472 2022-08-22 21:07:40 +00:00
github-actions[bot]
f2218b3b18 @skurfuerst has signed the CLA from Pull Request #453 2022-08-19 18:09:37 +00:00
github-actions[bot]
8d59c29695 @jaller94 has signed the CLA from Pull Request #386 2022-08-11 09:04:22 +00:00
github-actions[bot]
3a4efa8bd1 @lplit has signed the CLA from Pull Request #301 2022-08-01 15:09:11 +00:00
github-actions[bot]
36147a785b @sqwishy has signed the CLA from Pull Request #199 2022-07-14 15:36:56 +00:00
github-actions[bot]
ec3ec8f0fa @rubenfiszel has signed the CLA from Pull Request #116 2022-06-20 17:55:10 +00:00
github-actions[bot]
ae8451b04a @fatonramadani has signed the CLA from Pull Request #65 2022-05-21 11:20:47 +00:00
github-actions[bot]
e4aca965d6 @nickers has signed the CLA from Pull Request #46 2022-05-14 12:06:42 +00:00
github-actions[bot]
7f03ec7db6 @gaby has signed the CLA from Pull Request #9 2022-05-10 11:37:06 +00:00
github-actions[bot]
ce42a91595 Creating file for storing CLA Signatures 2022-05-10 07:14:39 +00:00
Ruben Fiszel
d03266b0a4 ci: add CLA 2022-05-10 09:12:24 +02:00
Ruben Fiszel
4a4eaa90e2 ci: add CLA 2022-05-10 09:02:28 +02:00
Ruben Fiszel
5e7c14b722 ci: add CLA 2022-05-10 08:52:11 +02:00
Ruben Fiszel
55b5695673 fix: display more than default 30 workspaces as superadmin 2022-05-09 15:18:28 +02:00
Ruben Fiszel
8596ac50b9 delete starter script without lock files 2022-05-08 17:56:16 +02:00
Ruben Fiszel
13fb52117b feat: self host minimal 2 2022-05-08 17:51:33 +02:00
Ruben Fiszel
2c70a15594 feat: self host minimal 2022-05-08 17:26:51 +02:00
Ruben Fiszel
7a51f842f0 feat: superadmin settings 2022-05-08 17:03:13 +02:00
Ruben Fiszel
a130806e19 feat: user settings is now at workspace level 2022-05-08 12:58:58 +02:00
Ruben Fiszel
fd1f05dd16 ci: refactor + dockerhub 2022-05-08 11:57:37 +02:00
Ruben Fiszel
48e51733e0 docs: add main ci badge 2022-05-06 14:59:42 +02:00
Ruben Fiszel
e7817e6c9f alpha.windmill -> app.windmill 2022-05-06 13:55:14 +02:00
Ruben Fiszel
51ad6edfcb docs: typos 2022-05-05 15:59:59 +02:00
Ruben Fiszel
315f7edd64 docs: windmill imgs 2022-05-05 15:53:40 +02:00
Ruben Fiszel
a2c3deab74 docs: README general idea 2022-05-05 15:24:35 +02:00
Ruben Fiszel
891b7eb93a docs: architecture diagram 2022-05-05 13:22:13 +02:00
Ruben Fiszel
7efd87be79 docs: architecture diagram 2022-05-05 13:20:42 +02:00
Ruben Fiszel
5acbc8b48c Create FUNDING.yml 2022-05-05 10:50:54 +02:00
53 changed files with 1987 additions and 620 deletions

1
.env
View File

@@ -1,3 +1,2 @@
SITE_URL=localhost
DB_PASSWORD=changeme
POSTGRES_VERSION=13.3.0

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [rubenfiszel]

View File

@@ -3,6 +3,8 @@ name: Deploy to windmill.dev
on:
push:
branches: [main]
paths:
- "community/*"
jobs:
deploy:

View File

@@ -1,7 +1,13 @@
name: Docker Image CI
env:
LOCAL_REGISTRY: registry.wimill.xyz
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
name: Build and push docker image
on:
push:
branches: [main]
tags: ["*"]
pull_request:
types: [opened, synchronize, reopened]
@@ -12,28 +18,51 @@ concurrency:
jobs:
build:
runs-on: [self-hosted, new]
env:
DOCKER_BUILDKIT: 1
steps:
- name: Wait for release to succeed
if: github.ref == 'refs/heads/main'
uses: lewagon/wait-on-check-action@v1.0.0
with:
ref: ${{ github.ref }}
check-name: "Release please"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: deploy staging stack
run: |
docker build . --cache-from "registry.wimill.xyz/windmill:staging" -t "registry.wimill.xyz/windmill:staging" --build-arg BUILDKIT_INLINE_CACHE=1
docker push "registry.wimill.xyz/windmill:staging"
- name: deploy demo stack
if: github.ref == 'refs/heads/main'
run: |
docker tag registry.wimill.xyz/windmill:staging registry.wimill.xyz/windmill:main
docker push registry.wimill.xyz/windmill:main
# - name: pruning unused images
# run: sudo docker image prune -a
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Docker meta local
id: metalocal
uses: docker/metadata-action@v4
with:
images: ${{ env.LOCAL_REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Login to registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ steps.metalocal.outputs.tags }}
${{ steps.meta.outputs.tags }}
labels: ${{ steps.metalocal.outputs.labels }}
cache-from: type=registry,ref=registry.wimill.xyz/windmilllabs/windmill:buildcache
cache-to: type=registry,ref=registry.wimill.xyz/windmilllabs/windmill:buildcache,mode=max

37
.github/workflows/lsp_on_release.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Publish LSP Server
on:
push:
branches: [main]
paths:
- "python-client/*W"
- "lsp/*"
tags:
- "*"
jobs:
build:
runs-on: [self-hosted, new]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Docker meta local
id: metalocal
uses: docker/metadata-action@v4
with:
images: registry.wimill.xyz/windmilllabs/lsp
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: "{{defaultContext}}:lsp"
push: true
tags: ${{ steps.metalocal.outputs.tags }}
labels: ${{ steps.metalocal.outputs.labels }}
cache-from: type=registry,ref=registry.wimill.xyz/windmilllabs/lsp:buildcache
cache-to: type=registry,ref=registry.wimill.xyz/windmilllabs/lsp:buildcache,mode=max

View File

@@ -1,38 +0,0 @@
name: Build LSP Docker
on:
push:
branches: [main]
paths:
- "python-client/**"
- "Pipfile"
- ".github/workflows/on-release.yml"
jobs:
build_lsp:
runs-on: [self-hosted, new]
steps:
- name: Wait for release to succeed
if: github.ref == 'refs/heads/main'
uses: lewagon/wait-on-check-action@v1.0.0
with:
ref: ${{ github.ref }}
check-name: "Release please"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
- uses: actions/checkout@v2
- name: Upload python client
env:
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
cd python-client
export PATH=$PATH:/usr/local/bin
export PATH=$PATH:/root/.local/bin
./publish.sh
- name: Build the Docker image
run: |
cd lsp
sudo docker pull "registry.wimill.xyz/lsp:main" || true
sudo docker build . --cache-from "registry.wimill.xyz/lsp:main" -t "registry.wimill.xyz/lsp:main" --build-arg BUILDKIT_INLINE_CACHE=1
- name: push to registry
run: |
sudo docker push "registry.wimill.xyz/lsp:main"

19
.github/workflows/pypi_on_release.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Publish python-client
on:
push:
tags:
- "v*"
jobs:
build_lsp:
runs-on: [self-hosted, new]
steps:
- uses: actions/checkout@v2
- name: Upload python client
env:
PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
cd python-client
export PATH=$PATH:/usr/local/bin
export PATH=$PATH:/root/.local/bin
./publish.sh

View File

@@ -1,7 +1,7 @@
on:
push:
branches:
- main
branches: [main]
name: release-please
jobs:
release-please:

34
.github/workflows/sign-cla.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize]
jobs:
CLAssistant:
runs-on: ubuntu-latest
steps:
- name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
# Beta Release
uses: cla-assistant/github-action@v2.1.3-beta
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_PAT }}
with:
path-to-signatures: "signatures/cla.json"
path-to-document: "https://github.com/windmill-labs/windmill/blob/master/CLA.md"
branch: "signatures"
allowlist: rubenfiszel,bot*
#below are the optional inputs - If the optional inputs are not given, then default values will be taken
#remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository)
#remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository)
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA

145
CLA.md Normal file
View File

@@ -0,0 +1,145 @@
## Contributor Agreement
## Individual Contributor Non-Exclusive License Agreement
Thank you for your interest in contributing to Ruben Fiszel's Windmill ("We" or
"Us").
The purpose of this contributor agreement ("Agreement") is to clarify and
document the rights granted by contributors to Us.
### 1\. Definitions
**"You"** means the individual Copyright owner who Submits a Contribution to Us.
**"Legal Entity"** means an entity that is not a natural person.
**"Affiliate"** means any other Legal Entity that controls, is controlled by, or
under common control with that Legal Entity. For the purposes of this
definition, "control" means (i) the power, direct or indirect, to cause the
direction or management of such Legal Entity, whether by contract or otherwise,
(ii) ownership of fifty percent (50%) or more of the outstanding shares or
securities that vote to elect the management or other persons who direct such
Legal Entity or (iii) beneficial ownership of such entity.
**"Contribution"** means any original work of authorship, including any original
modifications or additions to an existing work of authorship, Submitted by You
to Us, in which You own the Copyright.
**"Copyright"** means all rights protecting works of authorship, including
copyright, moral and neighboring rights, as appropriate, for the full term of
their existence.
**"Material"** means the software or documentation made available by Us to third
parties.
**"Submit"** means any act by which a Contribution is transferred to Us by You
by means of tangible or intangible media, including but not limited to
electronic mailing lists, source code control systems, and issue tracking
systems that are managed by, or on behalf of, Us, but excluding any transfer
that is conspicuously marked or otherwise designated in writing by You as "Not a
Contribution."
**"Documentation"** means any non-software portion of a Contribution.
### 2\. License grant
#### 2.1 Copyright license to Us
Subject to the terms and conditions of this Agreement, You hereby grant to Us a
worldwide, royalty-free, NON-exclusive, perpetual and irrevocable (except as
stated in Section 8.2) license, with the right to transfer an unlimited number
of non-exclusive licenses or to grant sublicenses to third parties, under the
Copyright covering the Contribution to use the Contribution by all means,
including, but not limited to:
- publish the Contribution,
- modify the Contribution,
- prepare derivative works based upon or containing the Contribution and/or to
combine the Contribution with other Materials,
- reproduce the Contribution in original or modified form,
- distribute, to make the Contribution available to the public, display and
publicly perform the Contribution in original or modified form.
#### 2.2 Moral rights
Moral Rights remain unaffected to the extent they are recognized and not
waivable by applicable law. Notwithstanding, You may add your name to the
attribution mechanism customary used in the Materials you Contribute to, such as
the header of the source code files of Your Contribution, and We will respect
this attribution when using Your Contribution.
### 3\. Patents
#### 3.1 Patent license
Subject to the terms and conditions of this Agreement You hereby grant to Us and
to recipients of Materials distributed by Us a worldwide, royalty-free,
non-exclusive, perpetual and irrevocable (except as stated in Section 3.2)
patent license, with the right to transfer an unlimited number of non-exclusive
licenses or to grant sublicenses to third parties, to make, have made, use,
sell, offer for sale, import and otherwise transfer the Contribution and the
Contribution in combination with any Material (and portions of such
combination). This license applies to all patents owned or controlled by You,
whether already acquired or hereafter acquired, that would be infringed by
making, having made, using, selling, offering for sale, importing or otherwise
transferring of Your Contribution(s) alone or by combination of Your
Contribution(s) with any Material.
### 4. Disclaimer
THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED
WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF SATISFACTORY
QUALITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY
DISCLAIMED BY YOU TO US AND BY US TO YOU. TO THE EXTENT THAT ANY SUCH WARRANTIES
CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION AND EXTENT TO THE
MINIMUM PERIOD AND EXTENT PERMITTED BY LAW.
### 5. Consequential damage waiver
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE
LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA,
INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT
OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR
OTHERWISE) UPON WHICH THE CLAIM IS BASED.
### 6. Approximation of disclaimer and damage waiver
IF THE DISCLAIMER AND DAMAGE WAIVER MENTIONED IN SECTION 4. AND SECTION 5.
CANNOT BE GIVEN LEGAL EFFECT UNDER APPLICABLE LOCAL LAW, REVIEWING COURTS SHALL
APPLY LOCAL LAW THAT MOST CLOSELY APPROXIMATES AN ABSOLUTE WAIVER OF ALL CIVIL
OR CONTRACTUAL LIABILITY IN CONNECTION WITH THE CONTRIBUTION.
### 7. Term
7.1 This Agreement shall come into effect upon Your acceptance of the terms and
conditions.
7.3 In the event of a termination of this Agreement Sections 4, 5, 6, 7 and 8
shall survive such termination and shall remain in full force thereafter. For
the avoidance of doubt, Free and Open Source Software (sub)licenses that have
already been granted for Contributions at the date of the termination shall
remain in full force after the termination of this Agreement.
### 8 Miscellaneous
8.1 This Agreement and all disputes, claims, actions, suits or other proceedings
arising out of this agreement or relating in any way to it shall be governed by
the laws of France excluding its private international law provisions.
8.2 This Agreement sets out the entire agreement between You and Us for Your
Contributions to Us and overrides all other agreements or understandings.
8.3 In case of Your death, this agreement shall continue with Your heirs. In
case of more than one heir, all heirs must exercise their rights through a
commonly authorized person.
8.4 If any provision of this Agreement is found void and unenforceable, such
provision will be replaced to the extent possible with a provision that comes
closest to the meaning of the original provision and that is enforceable. The
terms and conditions set forth in this Agreement shall apply notwithstanding any
failure of essential purpose of this Agreement or any limited remedy to the
maximum extent possible under law.
8.5 You agree to notify Us of any facts or circumstances of which you become
aware that would make this Agreement inaccurate in any respect.

View File

@@ -1,4 +1,4 @@
{$SITE_URL} {
bind {$ADDRESS}
reverse_proxy /* server:8000
reverse_proxy /* windmill:8000
}

View File

@@ -1,10 +1,13 @@
<p align="center">
<a href="https://alpha.windmill.dev"><img src="./windmill.svg" alt="windmill.dev"></a>
<a href="https://app.windmill.dev"><img src="./imgs/windmill.svg" alt="windmill.dev"></a>
</p>
<p align="center">
<em>Windmill.dev is an OSS developer platform to quickly build production-grade multi-steps automations and internal apps from minimal Python and Typescript scripts.</em>
</p>
<p align="center">
<a href="https://github.com/windmill-labs/windmill/actions/workflows/docker-image.yml" target="_blank">
<img src="https://github.com/windmill-labs/windmill/actions/workflows/docker-image.yml/badge.svg" alt="Docker Image CI">
</a>
<a href="https://pypi.org/project/wmill" target="_blank">
<img src="https://img.shields.io/pypi/v/wmill?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
@@ -16,7 +19,7 @@
---
**Join the alpha (personal workspaces are free forever)**:
<https://alpha.windmill.dev>
<https://app.windmill.dev>
**Documentation**: <https://docs.windmill.dev>
@@ -36,17 +39,36 @@ You can show your support for the project by starring this repo.
especially concerning flows.
</p>
![Windmill](./windmill.webp)
![Windmill Screenshot](./imgs/windmill.webp)
Windmill is <b>fully open-sourced</b>:
- `community/` and `python-client/` are Apache 2.0
- backend, frontend and everything else under AGPLv3.
## What is the general idea behind Windmill
1. Define a minimal and generic script in Python or Typescript that solve a
specific task. Here sending an email with SMTP. The code can be defined in
the provided Web IDE or synchronized with your own github repo:
![Step 1](./imgs/step1.png)
2. Your scripts parameters are automatically parsed and generate a frontend. You
can narrow down the types during task definition to specify regex for string,
an enum or a specific format for objects. Each script correspond to an app by
itself: ![Step 2](./imgs/step2.png)
3. Make it flow! You can chain your scripts or scripts made by the community
inside flow by piping output to input using "Dynamic" fields that are just
plain Javascript. You can also refer to external variables, output from any
steps or inputs of the flow itself. The flow parameters then generate
automatically an intuitive forms that can be triggered by anyone, like for
scripts. ![Step 3](./imgs/step3.png)
## Layout
- `backend/`: The whole Rust backend
- `frontend`: The whole Svelte fronten
- `frontend`: The whole Svelte frontend
- `community/`: Scripts and resource types created and curated by the community,
included in every workspace
- `lsp/`: The lsp asssistant for the monaco editor
@@ -69,18 +91,28 @@ Windmill is <b>fully open-sourced</b>:
- typescript runtime is deno
- python runtime is python3
## Architecture
A detailed section about Windmill architecture is coming soon
### Development stack
- caddy is the reverse proxy used for local development, see frontend's
Caddyfile and CaddyfileRemote
## Architecture
![Architecture](./imgs/architecture.svg)
## How to self-host
Complete instructions coming soon
`docker volume create caddy_data && docker-compose up` with the following
docker-compose is sufficient:
<https://github.com/windmill-labs/windmill-server/blob/main/docker-compose.yml>
The default super-admin user is: admin@windmill.dev / changeme
From there, you can create other users (do not forget to change the password!)
Detailed instructions for more complex deployments will come soon. For simpler
docker based ones, the docker-compose.yml file contains all the necessary
informations.
## Copyright

View File

@@ -1 +1,2 @@
-- Add down migration script here

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,4 @@
-- Add up migration script here
UPDATE password
SET password_hash = '$argon2id$v=19$m=4096,t=3,p=1$oLJo/lPn/gezXCuFOEyaNw$i0T2tCkw3xUFsrBIKZwr8jVNHlIfoxQe+HfDnLtd12I'
WHERE password_hash = '$argon2id$v=19$m=4096,t=3,p=1$z0Kg3qyaS14e+YHeihkJLQ$N69flI6yQ/U98pjAHtbNxbdz2f4PrJEi9Tx1VoYk1as';

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,24 @@
-- Add up migration script here
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'app') THEN
CREATE ROLE app LOGIN PASSWORD 'changeme';
END IF;
END
$do$;
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'admin') THEN
CREATE ROLE admin LOGIN PASSWORD 'changeme';
END IF;
END
$do$;

View File

@@ -0,0 +1 @@
-- Add down migration script here

View File

@@ -0,0 +1,3 @@
-- Add up migration script here
DELETE FROM script WHERE lock IS NULL;

View File

@@ -207,6 +207,72 @@ paths:
schema:
type: string
/users/create:
post:
summary: create user
operationId: createUserGlobally
tags:
- user
requestBody:
description: user info
required: true
content:
application/json:
schema:
type: object
properties:
email:
type: string
password:
type: string
super_admin:
type: boolean
name:
type: string
company:
type: string
required:
- email
- password
- super_admin
responses:
"201":
description: user created
content:
text/plain:
schema:
type: string
/users/update/{email}:
post:
summary: global update user (require super admin)
operationId: globalUserUpdate
tags:
- user
parameters:
- name: email
in: path
required: true
schema:
type: string
requestBody:
description: new user info
required: true
content:
application/json:
schema:
type: object
properties:
is_super_admin:
type: boolean
responses:
"200":
description: user updated
content:
text/plain:
schema:
type: string
/w/{workspace}/users/delete/{username}:
delete:
summary: delete user (require admin privilege)
@@ -393,7 +459,7 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/GlobalWhoami"
$ref: "#/components/schemas/GlobalUserInfo"
/users/list_invites:
get:
@@ -614,7 +680,7 @@ paths:
schema:
type: array
items:
$ref: "#/components/schemas/User"
$ref: "#/components/schemas/GlobalUserInfo"
/w/{workspace}/workspaces/list_pending_invites:
get:
@@ -3243,7 +3309,7 @@ components:
- email
- is_admin
GlobalWhoami:
GlobalUserInfo:
type: object
properties:
email:

View File

@@ -170,6 +170,23 @@
]
}
},
"11b1586acdfc180c5a077861ee1f7201fcbcec9d0ebada464f9d952c9c3e400d": {
"query": "INSERT INTO password(email, verified, password_hash, login_type, super_admin, name, company)\n VALUES ($1, $2, $3, 'password', $4, $5, $6)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Bool",
"Varchar",
"Bool",
"Varchar",
"Varchar"
]
},
"nullable": []
}
},
"11eb4dd4a2c9b0b759294dde5e8b505c5a4391aa0d8cb629c665711ee0fc04a0": {
"query": "SELECT substr(logs, $1) as logs FROM queue WHERE workspace_id = $2 AND id = $3",
"describe": {
@@ -1249,6 +1266,57 @@
"nullable": []
}
},
"77ba7207c8f5fd7156542cfd9943aa9a9fa87a652131c261f5020bab9ba6b5a3": {
"query": "SELECT email, login_type::text, verified, super_admin, name, company from password LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "login_type",
"type_info": "Text"
},
{
"ordinal": 2,
"name": "verified",
"type_info": "Bool"
},
{
"ordinal": 3,
"name": "super_admin",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "company",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
null,
false,
false,
true,
true
]
}
},
"7b1239ad6460e8f5fb41bfe12f662a779528784ec8cf3f6dcce5545ab90bf234": {
"query": "SELECT * FROM resource_type WHERE workspace_id = $1",
"describe": {
@@ -2713,69 +2781,6 @@
"nullable": []
}
},
"ef8b14d0feb4bda6a1f9834712ab47bd21e07f0a743f74a8d1f84cb3d54bfdae": {
"query": "SELECT * from usr LIMIT $1 OFFSET $2",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "workspace_id",
"type_info": "Varchar"
},
{
"ordinal": 1,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 2,
"name": "email",
"type_info": "Varchar"
},
{
"ordinal": 3,
"name": "is_admin",
"type_info": "Bool"
},
{
"ordinal": 4,
"name": "created_at",
"type_info": "Timestamptz"
},
{
"ordinal": 5,
"name": "operator",
"type_info": "Bool"
},
{
"ordinal": 6,
"name": "disabled",
"type_info": "Bool"
},
{
"ordinal": 7,
"name": "role",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
false,
false,
false,
false,
false,
false,
true
]
}
},
"f056b5f3e66a764748925f1bfd3180923fde8c7fdf69088d0e4a5555cc049545": {
"query": "SELECT result FROM completed_job WHERE id = $1 AND workspace_id = $2",
"describe": {

View File

@@ -59,6 +59,7 @@ pub fn global_service() -> Router {
.route("/accept_invite", post(accept_invite))
.route("/list_as_super_admin", get(list_users_as_super_admin))
.route("/setpassword", post(set_password))
.route("/create", post(create_user))
.route("/update/:user", post(update_user))
.route("/logout", post(logout))
.route("/tokens/create", post(create_token))
@@ -316,6 +317,18 @@ pub struct User {
pub role: Option<String>
}
#[derive(FromRow, Serialize)]
pub struct GlobalUserInfo {
email: String,
login_type: Option<String>,
super_admin: bool,
verified: bool,
name: Option<String>,
company: Option<String>,
}
#[derive(Serialize)]
pub struct UserInfo {
pub workspace_id: String,
@@ -368,10 +381,10 @@ pub struct NewUser {
pub email: String,
pub password: String,
pub super_admin: bool,
pub name: Option<String>,
pub company: Option<String>
}
#[derive(Deserialize)]
pub struct AcceptInvite {
pub workspace_id: String,
@@ -385,7 +398,6 @@ pub struct DeclineInvite {
#[derive(Deserialize)]
pub struct EditUser {
pub email: String,
pub is_super_admin: Option<bool>,
}
@@ -482,12 +494,12 @@ async fn list_users_as_super_admin(
authed: Authed,
Extension(db): Extension<DB>,
Query(pagination): Query<Pagination>
) -> JsonResult<Vec<User>> {
) -> JsonResult<Vec<GlobalUserInfo>> {
let mut tx = db.begin().await?;
require_super_admin(&mut tx, authed.email).await?;
let (per_page, offset) = crate::utils::paginate(pagination);
let rows = sqlx::query_as!(User, "SELECT * from usr LIMIT $1 OFFSET $2", per_page as i32, offset as i32)
let rows = sqlx::query_as!(GlobalUserInfo, "SELECT email, login_type::text, verified, super_admin, name, company from password LIMIT $1 OFFSET $2", per_page as i32, offset as i32)
.fetch_all(&mut tx)
.await?;
tx.commit().await?;
@@ -588,16 +600,6 @@ async fn whoami(
}
}
#[derive(FromRow, Serialize)]
pub struct GlobalUserInfo {
email: String,
login_type: Option<String>,
super_admin: bool,
verified: bool,
name: Option<String>,
company: Option<String>,
}
async fn global_whoami(
Extension(db): Extension<DB>,
Authed { email, .. }: Authed,
@@ -837,6 +839,7 @@ async fn update_workspace_user(
async fn update_user(
Authed { email, .. }: Authed,
Path(email_to_update): Path<String>,
Extension(db): Extension<DB>,
Json(eu): Json<EditUser>,
) -> Result<String> {
@@ -848,7 +851,7 @@ async fn update_user(
sqlx::query_scalar!(
"UPDATE password SET super_admin = $1 WHERE email = $2",
sa,
&eu.email
&email_to_update
)
.execute(&mut tx)
.await?;
@@ -860,14 +863,53 @@ async fn update_user(
"users.update",
ActionKind::Update,
"global",
Some(&eu.email),
Some(&email_to_update),
None,
)
.await?;
tx.commit().await?;
Ok(format!("email {} updated", eu.email))
Ok(format!("email {} updated", &email_to_update))
}
async fn create_user(
Authed { email, .. }: Authed,
Extension(db): Extension<DB>,
Extension(argon2): Extension<Arc<Argon2<'_>>>,
Json(nu): Json<NewUser>,
) -> Result<(StatusCode, String)> {
let mut tx = db.begin().await?;
require_super_admin(&mut tx, email.clone()).await?;
sqlx::query!(
"INSERT INTO password(email, verified, password_hash, login_type, super_admin, name, company)
VALUES ($1, $2, $3, 'password', $4, $5, $6)",
&nu.email,
true,
&hash_password(argon2, nu.password)?,
&nu.super_admin,
nu.name,
nu.company
)
.execute(&mut tx)
.await?;
audit_log(
&mut tx,
&email.unwrap(),
"users.update",
ActionKind::Update,
"global",
Some(&nu.email),
None,
)
.await?;
tx.commit().await?;
Ok((StatusCode::CREATED, format!("email {} created", nu.email)))
}
pub fn owner_to_token_owner(user: &str, is_group: bool) -> String {
let prefix = if is_group { 'g' } else { 'u' };
format!("{}/{}", prefix, user)
@@ -909,7 +951,6 @@ async fn delete_user(
)
.await?;
tx.commit().await?;
Ok(format!("username {} deleted", username_to_delete))
}

View File

@@ -7,6 +7,7 @@ services:
restart: always
volumes:
- db_data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/create_tables.sql
ports:
- 5432:5432
environment:
@@ -18,13 +19,20 @@ services:
timeout: 5s
retries: 5
windmill:
image: windmill:main
image: ghcr.io/windmill-labs/windmill:main
privileged: true
restart: unless-stopped
ports:
- 8000:8000
environment:
- DATABASE_URL=postgres://postgres:${DB_PASSWORD}@db/windmill?sslmode=disable
- VARIABLES_KEY=changeme
- APP_USER_PASSWORD=changeme
- BASE_URL=https://localhost"
- BASE_INTERNAL_URL=http://localhost:8000"
- RUST_LOG=info
- NUM_WORKERS=3
- RUST_BACKTRACE=1
depends_on:
db:
condition: service_healthy

View File

@@ -1,16 +0,0 @@
http://localhost {
bind {$ADDRESS}
reverse_proxy /api/* https://demo.windmill.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
reverse_proxy /* http://localhost:3000
}
https://localhost {
bind {$ADDRESS}
reverse_proxy /ws/* https://demo.windmill.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
}

View File

@@ -1,6 +1,6 @@
http://localhost {
bind {$ADDRESS}
reverse_proxy /api/* https://alpha.windmill.dev {
reverse_proxy /api/* https://app.windmill.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
reverse_proxy /* http://localhost:3000
@@ -9,7 +9,7 @@ http://localhost {
https://localhost {
bind {$ADDRESS}
reverse_proxy /ws/* https://alpha.windmill.dev {
reverse_proxy /ws/* https://app.windmill.dev {
header_up Host {http.reverse_proxy.upstream.hostport}
}
}

View File

@@ -1,24 +0,0 @@
export function pathToMeta(path) {
const splitted = path.split('/');
let ownerKind;
if (splitted[0] == 'g') {
ownerKind = 'group';
}
else if (splitted[0] == 'u') {
ownerKind = 'user';
}
else {
console.error('Not recognized owner:' + splitted[0]);
return {
ownerKind: 'user',
owner: '',
name: ''
};
}
return {
ownerKind,
owner: splitted[1],
name: splitted.slice(2).join('/')
};
}
//# sourceMappingURL=common.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"common.js","sourceRoot":"","sources":["common.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAChC,IAAI,SAAoB,CAAA;IACxB,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE;QACvB,SAAS,GAAG,OAAO,CAAA;KACnB;SAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE;QAC9B,SAAS,GAAG,MAAM,CAAA;KAClB;SAAM;QACN,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;QACpD,OAAO;YACN,SAAS,EAAE,MAAM;YACjB,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,EAAE;SACR,CAAA;KACD;IACD,OAAO;QACN,SAAS;QACT,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;QAClB,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;KACjC,CAAA;AACF,CAAC"}

View File

@@ -1,8 +0,0 @@
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
const response = await resolve(event, {
ssr: false
});
return response;
}
//# sourceMappingURL=hooks.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["hooks.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE;IAC3C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE;QAClC,GAAG,EAAE,KAAK;KACb,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACnB,CAAC"}

View File

@@ -1,66 +0,0 @@
import { ScriptService } from "./gen";
import { sendUserToast } from "./utils";
export async function inferArgs(code, schema) {
try {
const inferedSchema = await ScriptService.toJsonschema({
requestBody: code
});
schema.required = [];
const oldProperties = Object.assign({}, schema.properties);
schema.properties = {};
for (const arg of inferedSchema.args) {
if (!(arg.name in oldProperties)) {
schema.properties[arg.name] = { description: '', type: '' };
}
else {
schema.properties[arg.name] = oldProperties[arg.name];
}
pythonToJsonSchemaType(arg.typ, schema.properties[arg.name]);
schema.properties[arg.name].default = arg.default;
if (!arg.has_default) {
schema.required.push(arg.name);
}
}
}
catch (err) {
console.error(err);
sendUserToast(`Could not infer schema: ${err.body ?? err}`, true);
}
}
function array_move(arr, fromIndex, toIndex) {
var element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
function pythonToJsonSchemaType(t, s) {
if (t === 'int') {
s.type = 'integer';
}
else if (t === 'float') {
s.type = 'number';
}
else if (t === 'bool') {
s.type = 'boolean';
}
else if (t === 'str') {
s.type = 'string';
}
else if (t === 'dict') {
s.type = 'object';
}
else if (t === 'list') {
s.type = 'array';
}
else if (t === 'bytes') {
s.type = 'string';
s.contentEncoding = 'base64';
}
else if (t === 'datetime') {
s.type = 'string';
s.format = 'date-time';
}
else {
s.type = undefined;
}
}
//# sourceMappingURL=infer.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"infer.js","sourceRoot":"","sources":["infer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEvC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,MAAc;IACxD,IAAI;QACA,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC;YACnD,WAAW,EAAE,IAAI;SACpB,CAAC,CAAA;QACF,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAA;QACpB,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;QAC1D,MAAM,CAAC,UAAU,GAAG,EAAE,CAAA;QAEtB,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,IAAI,EAAE;YAClC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE;gBAC9B,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;aAC9D;iBAAM;gBACH,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;aACxD;YACD,sBAAsB,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;YAC5D,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;YAEjD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE;gBAClB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;aACjC;SACJ;KACJ;IAAC,OAAO,GAAG,EAAE;QACV,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAClB,aAAa,CAAC,2BAA2B,GAAG,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAA;KACpE;AACL,CAAC;AAED,SAAS,UAAU,CAAI,GAAQ,EAAE,SAAiB,EAAE,OAAe;IAC/D,IAAI,OAAO,GAAG,GAAG,CAAC,SAAS,CAAC,CAAA;IAC5B,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;IACxB,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;AACnC,CAAC;AAED,SAAS,sBAAsB,CAAC,CAAS,EAAE,CAAiB;IACxD,IAAI,CAAC,KAAK,KAAK,EAAE;QACb,CAAC,CAAC,IAAI,GAAG,SAAS,CAAA;KACrB;SAAM,IAAI,CAAC,KAAK,OAAO,EAAE;QACtB,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAA;KACpB;SAAM,IAAI,CAAC,KAAK,MAAM,EAAE;QACrB,CAAC,CAAC,IAAI,GAAG,SAAS,CAAA;KACrB;SAAM,IAAI,CAAC,KAAK,KAAK,EAAE;QACpB,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAA;KACpB;SAAM,IAAI,CAAC,KAAK,MAAM,EAAE;QACrB,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAA;KACpB;SAAM,IAAI,CAAC,KAAK,MAAM,EAAE;QACrB,CAAC,CAAC,IAAI,GAAG,OAAO,CAAA;KACnB;SAAM,IAAI,CAAC,KAAK,OAAO,EAAE;QACtB,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAA;QACjB,CAAC,CAAC,eAAe,GAAG,QAAQ,CAAA;KAC/B;SAAM,IAAI,CAAC,KAAK,UAAU,EAAE;QACzB,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAA;QACjB,CAAC,CAAC,MAAM,GAAG,WAAW,CAAA;KACzB;SAAM;QACH,CAAC,CAAC,IAAI,GAAG,SAAS,CAAA;KACrB;AACL,CAAC"}

View File

@@ -2,7 +2,14 @@
import '../app.css';
import { OpenAPI, UserService, WorkspaceService } from '../gen';
import { logout, clickOutside, sendUserToast, logoutWithRedirect, getUser } from '../utils';
import {
logout,
clickOutside,
sendUserToast,
logoutWithRedirect,
getUser,
refreshSuperadmin
} from '../utils';
import { onDestroy, onMount } from 'svelte';
import Icon from 'svelte-awesome';
import {
@@ -73,15 +80,7 @@
}
async function loadUserInfo() {
if ($superadmin == undefined) {
UserService.globalWhoami().then((x) => {
if (x.super_admin) {
superadmin.set(x.email);
} else {
superadmin.set(false);
}
});
}
refreshSuperadmin();
if (!$usersWorkspaceStore) {
try {
@@ -278,7 +277,7 @@
>
<span class="block px-4 py-2 text-sm text-gray-500">{$usersWorkspaceStore?.email}</span>
<a
href="/settings"
href="/user/settings"
class="block px-4 py-2 text-sm text-gray-700"
role="menuitem"
tabindex="-1"

View File

@@ -0,0 +1,66 @@
<script lang="ts">
import { sendUserToast } from '../../utils';
import Switch from '../components/Switch.svelte';
import type Modal from './Modal.svelte';
import { createEventDispatcher } from 'svelte';
import { UserService } from '../../gen';
const dispatch = createEventDispatcher();
let valid = true;
let modal: Modal;
export function openModal(): void {
modal.openModal();
}
let email: string;
let is_super_admin = false;
let password: string;
let name: string | undefined;
let company: string | undefined;
function handleKeyUp(event: KeyboardEvent) {
const key = event.key || event.keyCode;
if (key === 13 || key === 'Enter') {
event.preventDefault();
addUser();
}
}
async function addUser() {
await UserService.createUserGlobally({
requestBody: {
email,
password,
super_admin: is_super_admin,
name,
company
}
});
sendUserToast(`Successfully added ${email}. Welcome to them!`);
dispatch('new');
}
</script>
<div class="flex flex-row">
<input on:keyup={handleKeyUp} placeholder="email" bind:value={email} />
<Switch class="ml-2" bind:checked={is_super_admin} horizontal={true} label={'admin: '} />
<input on:keyup={handleKeyUp} type="password" placeholder="" bind:value={password} />
<input on:keyup={handleKeyUp} placeholder="name" bind:value={name} />
<input on:keyup={handleKeyUp} placeholder="company" bind:value={company} />
<button
class="ml-4 w-40 {valid ? 'default-button' : 'default-button-disabled'}"
type="button"
on:click={() => {
addUser();
}}
disabled={email == undefined || password == undefined}
>
Add
</button>
</div>

View File

@@ -2,7 +2,7 @@
import { sendUserToast } from '../../utils';
import Switch from '../components/Switch.svelte';
import Modal from './Modal.svelte';
import type Modal from './Modal.svelte';
import { createEventDispatcher } from 'svelte';
import { workspaceStore } from '../../stores';
import { WorkspaceService } from '../../gen';

View File

@@ -2,7 +2,7 @@
import { SettingsService } from '../../gen';
export let subtitle: string | undefined = undefined;
export let title = 'Welcome to windmill.dev (alpha)';
export let title = 'Welcome to Windmill (beta)';
let version = '';
SettingsService.backendVersion().then((x) => {
@@ -10,8 +10,8 @@
});
</script>
<div class="flex justify-center h-screen bg-gray-50">
<div class="w-10/12 md:w-7/12 lg:w-4/12 xl:3/12 m-auto">
<div class="flex justify-center min-h-screen bg-gray-50 pt-10">
<div class="w-10/12 md:w-7/12 lg:w-6/12 xl:4/12 m-auto">
<h1 class="justify-center text-center font-medium pb-2 text-gray-700">
{title}
</h1>
@@ -27,7 +27,7 @@
<!-- empty row to make the form a little bit above vertical centering-->
<div class="py-12" />
</div>
<div class="absolute bottom-0 right-0 text-2xs text-gray-500 italic px-3 py-1">
<div class="absolute top-0 right-0 text-2xs text-gray-500 italic px-3 py-1">
windmill.dev backend v. <span class="font-mono">{version}</span>
</div>
</div>

View File

@@ -1,10 +1,9 @@
<script lang="ts">
import { SvelteToast } from '@zerodevx/svelte-toast';
import { logout, sendUserToast } from '../../utils';
import { logout, refreshSuperadmin, sendUserToast } from '../../utils';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { superadmin, userStore, workspaceStore } from '../../stores';
import { UserService } from '../../gen';
// Default options
const toastOptions = {
@@ -51,15 +50,7 @@
};
workspaceStore.set(undefined);
userStore.set(undefined);
if ($superadmin == undefined) {
UserService.globalWhoami().then((x) => {
if (x.super_admin) {
superadmin.set(x.email);
} else {
superadmin.set(false);
}
});
}
refreshSuperadmin();
});
</script>

View File

@@ -2,7 +2,7 @@
import { goto } from '$app/navigation';
import { UserService } from '../../gen';
import { sendUserToast } from '../../utils';
import { refreshSuperadmin, sendUserToast } from '../../utils';
import { page } from '$app/stores';
import { userStore, usersWorkspaceStore, workspaceStore } from '../../stores';
import CenteredModal from './CenteredModal.svelte';
@@ -26,6 +26,7 @@
password
}
});
refreshSuperadmin();
if (rd) {
goto(decodeURI(rd));
} else {
@@ -62,10 +63,10 @@
<!-- Enable submit form on enter -->
<CenteredModal subtitle={error}>
<div class="justify-center text-center">
<div class="justify-center text-center flex flex-col">
<span class="text-xs text-gray-600">Currently only signup through Github is supported</span>
<a rel="external" href="/api/oauth/login/github"
><button class="w-full default-button bg-black mt-2 py-2"
><button class="m-auto default-button bg-black mt-2 py-2 w-96"
>Signup or login with Github &nbsp;
<Icon class="text-white pb-1" data={faGithub} scale={1.4} />
</button></a
@@ -73,7 +74,7 @@
</div>
<div class="flex flex-row-reverse w-full">
<button
class="my-6 text-xs text-blue-400"
class="my-6 text-xs text-blue-400 m-auto"
id="showPassword"
on:click={() => {
showPassword = !showPassword;

View File

@@ -1,15 +1,14 @@
<script lang="ts">
import { userStore, workspaceStore } from '../stores';
import { usersWorkspaceStore } from '../../stores';
import { onMount } from 'svelte';
import type { TruncatedToken, NewToken } from '../gen';
import { UserService, SettingsService } from '../gen';
import { displayDate, sendUserToast, getToday } from '../utils';
import PageHeader from './components/PageHeader.svelte';
import CenteredPage from './components/CenteredPage.svelte';
import type { TruncatedToken, NewToken } from '../../gen';
import { UserService, SettingsService } from '../../gen';
import { displayDate, sendUserToast, getToday } from '../../utils';
import PageHeader from './../components/PageHeader.svelte';
import Icon from 'svelte-awesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import TableCustom from './components/TableCustom.svelte';
import TableCustom from '../components/TableCustom.svelte';
import CenteredModal from './CenteredModal.svelte';
let newPassword: string | undefined;
let passwordError: string | undefined;
@@ -21,14 +20,6 @@
let displayCreateToken = false;
let login_type = 'none';
function handleKeyUp(event: KeyboardEvent) {
const key = event.key || event.keyCode;
if (key === 13 || key === 'Enter') {
event.preventDefault();
setPassword();
}
}
async function setPassword(): Promise<void> {
try {
if (newPassword) {
@@ -90,19 +81,17 @@
loadVersion();
loadLoginType();
$: {
if ($workspaceStore) {
listTokens();
}
}
listTokens();
</script>
<CenteredPage>
<PageHeader title="User settings" />
<CenteredModal title="User settings">
<div class="flex flex-row justify-between">
<a href="/user/workspaces">&leftarrow; Back to workspaces</a>
</div>
<div class="text-2xs text-gray-500 italic pb-6">
Running windmill version (backend) {version}
</div>
<h2 class="border-b">Change password</h2>
<h2 class="border-b">User info</h2>
<div class="">
{#if passwordError}
<div class="text-purple-500 text-2xs grow">{passwordError}</div>
@@ -110,10 +99,11 @@
<div class="flex flex-col gap-2 w-full ">
<div class="mt-4">
<label class="block w-60 mb-2 text-gray-500">
<input disabled value={$userStore?.email ?? ''} class="input mt-1" />
<div class="text-gray-700">email</div>
<input disabled value={$usersWorkspaceStore?.email} class="input mt-1" />
</label>
{#if login_type == 'password'}
<label class="block w-12/12">
<label class="block w-120">
<div class="text-gray-700">password</div>
<input
type="password"
@@ -131,6 +121,7 @@
text-sm
"
/>
<button on:click={() => setPassword()} class="mt-4 default-button">Set password</button>
</label>
{:else if login_type == 'github'}
<span>Authentified through Github OAuth2. Cannot set a password.</span>
@@ -243,7 +234,8 @@
</tbody>
</TableCustom>
</div>
</CenteredPage>
<style>
</style>
<div class="flex flex-row justify-between pt-4">
<a href="/user/workspaces">&leftarrow; Back to workspaces</a>
</div>
</CenteredModal>

View File

@@ -0,0 +1,96 @@
<script lang="ts">
import Fuse from 'fuse.js';
import { superadmin, usersWorkspaceStore } from '../../stores';
import { UserService, SettingsService, GlobalUserInfo } from '../../gen';
import { displayDate, sendUserToast, getToday } from '../../utils';
import Icon from 'svelte-awesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons';
import TableCustom from '../components/TableCustom.svelte';
import CenteredModal from './CenteredModal.svelte';
import PageHeader from '../components/PageHeader.svelte';
import InviteGlobalUser from '../components/InviteGlobalUser.svelte';
let version: string | undefined;
let users: GlobalUserInfo[] = [];
let filteredUsers: GlobalUserInfo[] | undefined;
let userFilter = '';
const fuseOptions = {
includeScore: false,
keys: ['email', 'name', 'company']
};
const fuse: Fuse<GlobalUserInfo> = new Fuse(users, fuseOptions);
$: filteredUsers = fuse?.search(userFilter).map((value) => value.item);
async function loadVersion(): Promise<void> {
version = await SettingsService.backendVersion();
}
async function listUsers(): Promise<void> {
users = await UserService.listUsersAsSuperAdmin({});
fuse?.setCollection(users);
}
loadVersion();
listUsers();
</script>
<CenteredModal title="Global Users Settings">
<div class="flex flex-row justify-between">
<a href="/user/workspaces">&leftarrow; Back to workspaces</a>
</div>
<div class="text-2xs text-gray-500 italic pb-6">
Running windmill version (backend) {version}
</div>
<PageHeader title="All users" primary={false} />
<div class="pb-1" />
<InviteGlobalUser on:new={listUsers} />
<div class="pb-1" />
<input placeholder="Search users" bind:value={userFilter} class="input mt-1" />
<TableCustom>
<tr slot="header-row">
<th>email</th>
<th>superadmin</th>
<th>login_type</th>
<th>name</th>
<th>company</th>
<th />
</tr>
<tbody slot="body">
{#if filteredUsers && users}
{#each userFilter === '' ? users : filteredUsers as { email, super_admin, login_type, name, company }}
<tr class="border">
<td>{email}</td>
<td>{super_admin}</td>
<td>{login_type}</td>
<td>{name}</td>
<td>{company}</td>
<td>
<button
class="text-blue-500"
on:click={async () => {
await UserService.globalUserUpdate({
email,
requestBody: {
is_super_admin: !super_admin
}
});
listUsers();
}}>{super_admin ? 'demote' : 'promote'}</button
></td
>
</tr>
{/each}
{/if}
</tbody>
</TableCustom>
<div class="flex flex-row justify-between pt-4">
<a href="/user/workspaces">&leftarrow; Back to workspaces</a>
</div>
</CenteredModal>

View File

@@ -6,6 +6,8 @@
import { superadmin, usersWorkspaceStore, workspaceStore } from '../../stores';
import CenteredModal from './CenteredModal.svelte';
import Switch from '../components/Switch.svelte';
import { faCrown, faUser, faUserAlt, faUserCog } from '@fortawesome/free-solid-svg-icons';
import Icon from 'svelte-awesome';
let invites: WorkspaceInvite[] = [];
let list_all_as_super_admin: boolean = false;
@@ -31,7 +33,7 @@
}
async function loadWorkspacesAsAdmin() {
workspaces = (await WorkspaceService.listWorkspacesAsSuperAdmin({})).map((x) => {
workspaces = (await WorkspaceService.listWorkspacesAsSuperAdmin({ perPage: 1000 })).map((x) => {
return { ...x, username: 'superadmin' };
});
}
@@ -68,7 +70,8 @@
<button
class="
block
w-full
w-96
mx-auto
py-1
px-2
rounded-md
@@ -102,7 +105,8 @@
<div
class="
block
w-full
w-96
mx-auto
py-1
px-2
rounded-md
@@ -142,7 +146,15 @@
</span>
</div>
{/each}
<div class="flex flex-row-reverse mt-10">
<div class="flex justify-between mt-10">
{#if $superadmin}
<a class="mr-10" href="/user/superadmin_settings">
<Icon data={faCrown} class="mr-1" scale={1} />Superadmin settings</a
>
{/if}
<a class="mr-10" href="/user/settings">
<Icon data={faUserCog} class="mr-1" scale={1} />User settings</a
>
<button
class="default-button-secondary"
on:click={async () => {

View File

@@ -1,9 +0,0 @@
import { writable, derived, readable } from 'svelte/store';
export const userStore = writable(undefined);
export const workspaceStore = writable(undefined);
export const usersWorkspaceStore = writable(undefined);
export const usernameStore = derived([usersWorkspaceStore, workspaceStore], ($values, set) => {
set($values[0]?.workspaces.find(x => x.id == $values[1])?.username);
});
export const superadmin = writable(undefined);
//# sourceMappingURL=stores.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"stores.js","sourceRoot":"","sources":["stores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAa1D,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAsB,SAAS,CAAC,CAAA;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAA;AACrE,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAgC,SAAS,CAAC,CAAA;AACrF,MAAM,CAAC,MAAM,aAAa,GAAiC,OAAO,CAAC,CAAC,mBAAmB,EAAE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE;IACvH,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;AACvE,CAAC,CAAC,CAAA;AACF,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAA6B,SAAS,CAAC,CAAA"}

View File

@@ -1,266 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { toast } from '@zerodevx/svelte-toast';
import { CancelablePromise, UserService } from './gen';
import { superadmin, userStore, workspaceStore } from './stores';
import { get } from 'svelte/store';
import { goto } from '$app/navigation';
export function isToday(someDate) {
const today = new Date();
return (someDate.getDate() == today.getDate() &&
someDate.getMonth() == today.getMonth() &&
someDate.getFullYear() == today.getFullYear());
}
export function daysAgo(someDate) {
const today = new Date();
return Math.floor((today.getTime() - someDate.getTime()) / 86400000);
}
export function secondsAgo(date) {
return Math.floor((new Date().getTime() - date.getTime()) / 1000);
}
export function displayDaysAgo(dateString) {
const date = new Date(dateString);
const nbSecondsAgo = secondsAgo(date);
if (nbSecondsAgo < 600) {
return `${nbSecondsAgo}s ago`;
}
else if (isToday(date)) {
return `today at ${date.toLocaleTimeString()}`;
}
else if (daysAgo(date) === 0) {
return `${daysAgo(date) + 1} day ago`;
}
else {
return `${daysAgo(date) + 1} day ago`;
}
}
export function displayDate(dateString) {
const date = new Date(dateString ?? '');
if (date.toString() === 'Invalid Date') {
return '';
}
else {
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} at ${date.toLocaleTimeString()}`;
}
}
export function getToday() {
var today = new Date();
return today;
}
export function sendUserToast(message, error = false) {
if (error) {
toast.push(message, {
theme: {
'--toastBackground': '#FEE2E2',
'--toastBarBackground': '#FEE2E2'
}
});
}
else
toast.push(message);
}
export function truncateHash(hash) {
if (hash.length >= 6) {
return hash.substr(hash.length - 6);
}
else {
return hash;
}
}
async function loadStore(workspace) {
try {
const user = await UserService.whoami({ workspace });
const nuser = {
username: user.username,
email: user.email,
created_at: user.created_at,
is_admin: user.is_admin,
groups: user.groups,
pgroups: user.groups.map((x) => `g/${x}`)
};
userStore.set(nuser);
return nuser;
}
catch (error) {
userStore.set(undefined);
return undefined;
}
}
export async function getUser(workspace) {
const user = get(userStore);
if (user === undefined) {
return loadStore(workspace);
}
else {
return user;
}
}
export function logoutWithRedirect(rd) {
const error = encodeURIComponent('You have been logged out because your session has expired.');
goto(`/user/login?error=${error}${rd ? '&rd=' + encodeURIComponent(rd) : ''}`);
}
export async function handle401(promise, rd) {
// Redirects to login if the `promise` returns a 401 due to lack of authentication
// Optionnally provide `rd`, to which the user will be redirected after logging back in
return promise.catch(async (error) => {
if (error.status === 401) {
if (getUser(get(workspaceStore)) === undefined) {
logoutWithRedirect(rd);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return null;
}
else {
throw Error('You do not have enough privilege to access this');
}
}
else {
throw error;
}
});
}
export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function validatePassword(password) {
const re = /^(?=.*[\d])(?=.*[!@#$%^&*])[\w!@#$%^&*]{8,30}$/;
return re.test(password);
}
export async function logout(logoutMessage) {
try {
superadmin.set(undefined);
goto(`/user/login${logoutMessage ? '?error=' + encodeURIComponent(logoutMessage) : ''}`);
await UserService.logout();
sendUserToast('you have been logged out');
}
catch (error) {
goto(`/user/login?error=${encodeURIComponent('There was a problem logging you out, check the logs')}`);
console.error(error);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function clickOutside(node) {
const handleClick = (event) => {
if (node && !node.contains(event.target) && !event.defaultPrevented) {
node.dispatchEvent(new CustomEvent('click_outside', node));
}
};
document.addEventListener('click', handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
};
}
export function emptySchema() {
return {
$schema: 'https://json-schema.org/draft/2020-12/schema',
properties: {},
required: [],
type: 'object'
};
}
export function simpleSchema() {
return {
$schema: 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
properties: {
name: {
description: 'The name to hello world to',
type: 'string'
}
},
required: []
};
}
export function removeItemAll(arr, value) {
var i = 0;
while (i < arr.length) {
if (arr[i] === value) {
arr.splice(i, 1);
}
else {
++i;
}
}
return arr;
}
export function canWrite(path, extra_perms, user) {
let keys = Object.keys(extra_perms);
if (!user) {
return false;
}
if (user.is_admin) {
return true;
}
let userOwner = `u/${user.username}`;
if (path.startsWith(userOwner)) {
return true;
}
if (keys.includes(userOwner) && extra_perms[userOwner]) {
return true;
}
if (user.pgroups.findIndex((x) => path.startsWith(x) || (keys.includes(x) && extra_perms[x])) != -1) {
return true;
}
return false;
}
export function removeKeysWithEmptyValues(obj) {
Object.keys(obj).forEach((key) => (obj[key] === undefined ? delete obj[key] : {}));
}
export function allTrue(dict) {
for (let v of Object.values(dict)) {
if (!v)
return false;
}
return true;
}
export function forLater(scheduledString) {
return new Date() < new Date(scheduledString);
}
export function elapsedSinceSecs(date) {
return Math.round((new Date().getTime() - new Date(date).getTime()) / 1000);
}
export function groupBy(scripts, toGroup, dflts = []) {
let r = {};
for (const dflt of dflts) {
r[dflt] = [];
}
scripts.forEach((sc) => {
let section = toGroup(sc);
if (section in r) {
r[section].push(sc);
}
else {
r[section] = [sc];
}
});
return Object.entries(r).sort((s1, s2) => {
let n1 = s1[0];
let n2 = s2[0];
if (n1 > n2) {
return 1;
}
else if (n1 < n2) {
return -1;
}
else {
return 0;
}
});
}
export function truncate(s, n, suffix = '...') {
if (s.length <= n) {
return s;
}
else {
return s.substring(0, n) + suffix;
}
}
export function truncateRev(s, n, prefix = '...') {
if (s.length <= n) {
return s;
}
else {
return prefix + s.substring(s.length - n, s.length);
}
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -136,6 +136,18 @@ export function validatePassword(password: string): boolean {
return re.test(password)
}
export async function refreshSuperadmin(): Promise<void> {
if (get(superadmin) == undefined) {
UserService.globalWhoami().then((x) => {
if (x.super_admin) {
superadmin.set(x.email)
} else {
superadmin.set(false)
}
})
}
}
export async function logout(logoutMessage?: string): Promise<void> {
try {
superadmin.set(undefined)

4
imgs/architecture.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 31 KiB

BIN
imgs/step1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
imgs/step2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
imgs/step3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

23
init-db.sql Normal file
View File

@@ -0,0 +1,23 @@
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'app') THEN
CREATE ROLE app LOGIN PASSWORD 'changeme';
END IF;
END
$do$;
DO
$do$
BEGIN
IF NOT EXISTS (
SELECT
FROM pg_catalog.pg_roles
WHERE rolname = 'admin') THEN
CREATE ROLE admin LOGIN PASSWORD 'changeme';
END IF;
END
$do$;

1148
signatures/cla.json Normal file

File diff suppressed because it is too large Load Diff