diff options
author | Jordan Bracco <href@random.sh> | 2021-08-06 15:55:35 +0200 |
---|---|---|
committer | Jordan Bracco <href@random.sh> | 2021-08-06 15:55:35 +0200 |
commit | fd639a6f74d3a2c4cdcc18f5191c101cd41f289b (patch) | |
tree | 70697bb7c165197eefdeb60f42fbb71045c8ca59 /apps | |
parent | New rebar app (diff) |
Kratos flows
Diffstat (limited to 'apps')
44 files changed, 2330 insertions, 1 deletions
diff --git a/apps/ory/.gitignore b/apps/ory/.gitignore new file mode 100644 index 0000000..f1c4554 --- /dev/null +++ b/apps/ory/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ diff --git a/apps/ory/LICENSE b/apps/ory/LICENSE new file mode 100644 index 0000000..e389eb2 --- /dev/null +++ b/apps/ory/LICENSE @@ -0,0 +1,191 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2021, Jordan Bracco <href@random.sh>. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/apps/ory/README.md b/apps/ory/README.md new file mode 100644 index 0000000..33d9d20 --- /dev/null +++ b/apps/ory/README.md @@ -0,0 +1,9 @@ +ory +===== + +Ory library + +Build +----- + + $ rebar3 compile diff --git a/apps/ory/rebar.config b/apps/ory/rebar.config new file mode 100644 index 0000000..a5ac854 --- /dev/null +++ b/apps/ory/rebar.config @@ -0,0 +1,2 @@ +{erl_opts, [debug_info]}. +{deps, [{hackney, "1.17.4"}, {jsone, "1.6.1"}]}. diff --git a/apps/ory/src/ory.app.src b/apps/ory/src/ory.app.src new file mode 100644 index 0000000..c4098ee --- /dev/null +++ b/apps/ory/src/ory.app.src @@ -0,0 +1,16 @@ +{application, ory, + [{description, "Ory library"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, + [kernel, + stdlib, + hackney, + jsone + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/apps/ory/src/ory.erl b/apps/ory/src/ory.erl new file mode 100644 index 0000000..5c06bc9 --- /dev/null +++ b/apps/ory/src/ory.erl @@ -0,0 +1,3 @@ +-module(ory). + +-export([]). diff --git a/apps/ory/src/ory_kratos.erl b/apps/ory/src/ory_kratos.erl new file mode 100644 index 0000000..14afa82 --- /dev/null +++ b/apps/ory/src/ory_kratos.erl @@ -0,0 +1,76 @@ +-module(ory_kratos). + +-export([login_url/1, registration_url/1, settings_url/1, recovery_url/1, verification_url/1, url/0]). +-export([registration_flow/2, login_flow/2, settings_flow/2, recovery_flow/2, verification_flow/2, logout_flow/1, whoami/1, error/1]). + +login_url(browser) -> + [url(), "/self-service/login/browser"]. + +registration_url(browser) -> + [url(), "/self-service/registration/browser"]. + +settings_url(browser) -> + [url(), "/self-service/settings/browser"]. + +recovery_url(browser) -> + [url(), "/self-service/recovery/browser"]. + +verification_url(browser) -> + [url(), "/self-service/verification/browser"]. + +url() -> + {ok, Value} = application:get_env(ory, kratos_url), + Value. + +registration_flow(Cookie, Id) -> + Url = [url(), "/self-service/registration/flows?id=", Id], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +login_flow(Cookie, Id) -> + Url = [url(), "/self-service/login/flows?id=", Id], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +settings_flow(Cookie, Id) -> + Url = [url(), "/self-service/settings/flows?id=", Id], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +recovery_flow(Cookie, Id) -> + Url = [url(), "/self-service/recovery/flows?id=", Id], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +verification_flow(Cookie, Id) -> + Url = [url(), "/self-service/verification/flows?id=", Id], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +logout_flow(Cookie) -> + Url = [url(), "/self-service/logout/browser"], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +whoami(Cookie) -> + Url = [url(), "/sessions/whoami"], + Headers = [{<<"cookie">>, Cookie}, {"accept", "application/json"}], + api_response(hackney:request(get, Url, Headers, <<>>, [])). + +error(Id) -> + Url = [url(), "/self-service/errors?id=", Id], + {ok, 200, _, Client} = hackney:request(get, Url, [], <<>>, []), + {ok, Body} = hackney:body(Client), + {ok, jsone:decode(Body)}. + +api_response(Error = {error, Error}) -> + logger:error("ory_kratos hackney error: ~p", [Error]), + {error, #{<<"code">> => 503, <<"status">> => "Not Available", <<"message">> => "This service isn't available at the moment."}}; +api_response({ok, 200, _, Client}) -> + {ok, Body} = hackney:body(Client), + {ok, jsone:decode(Body)}; +api_response({ok, Code, _, Client}) -> + {ok, Body} = hackney:body(Client), + JSON = #{<<"error">> := Error} = jsone:decode(Body), + logger:debug("hydra error: ~p", [JSON]), + {error, Error}. diff --git a/apps/styx/src/styx.app.src b/apps/styx/src/styx.app.src index 92a1e55..25f091e 100644 --- a/apps/styx/src/styx.app.src +++ b/apps/styx/src/styx.app.src @@ -5,7 +5,11 @@ {mod, {styx_app, []}}, {applications, [kernel, - stdlib + stdlib, + sasl, + styx_service, + ory, + styx_web ]}, {env,[]}, {modules, []}, diff --git a/apps/styx/src/styx.erl b/apps/styx/src/styx.erl new file mode 100644 index 0000000..1a582b8 --- /dev/null +++ b/apps/styx/src/styx.erl @@ -0,0 +1,9 @@ +-module(styx). + +-export([kratos_url/0, hydra_url/0]). + +kratos_url() -> + application:get_env(styx, kratos_url, undefined). + +hydra_url() -> + application:get_env(styx, hydra_url, undefined). diff --git a/apps/styx_service/.gitignore b/apps/styx_service/.gitignore new file mode 100644 index 0000000..f1c4554 --- /dev/null +++ b/apps/styx_service/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ diff --git a/apps/styx_service/LICENSE b/apps/styx_service/LICENSE new file mode 100644 index 0000000..e389eb2 --- /dev/null +++ b/apps/styx_service/LICENSE @@ -0,0 +1,191 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2021, Jordan Bracco <href@random.sh>. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/apps/styx_service/README.md b/apps/styx_service/README.md new file mode 100644 index 0000000..c73c5ca --- /dev/null +++ b/apps/styx_service/README.md @@ -0,0 +1,9 @@ +styx_service +===== + +Styx: child processes + +Build +----- + + $ rebar3 compile diff --git a/apps/styx_service/rebar.config b/apps/styx_service/rebar.config new file mode 100644 index 0000000..ba889f2 --- /dev/null +++ b/apps/styx_service/rebar.config @@ -0,0 +1,9 @@ +{erl_opts, [debug_info]}. +{deps, [ + {erlexec, "1.18.11"} +]}. + +{shell, [ + % {config, "config/sys.config"}, + {apps, [styx_service]} +]}. diff --git a/apps/styx_service/src/styx_service.app.src b/apps/styx_service/src/styx_service.app.src new file mode 100644 index 0000000..96115e7 --- /dev/null +++ b/apps/styx_service/src/styx_service.app.src @@ -0,0 +1,16 @@ +{application, styx_service, + [{description, "Styx: child processes"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {styx_service_app, []}}, + {applications, + [kernel, + stdlib, + erlexec + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/apps/styx_service/src/styx_service.erl b/apps/styx_service/src/styx_service.erl new file mode 100644 index 0000000..1e7ff88 --- /dev/null +++ b/apps/styx_service/src/styx_service.erl @@ -0,0 +1,25 @@ +-module(styx_service). + +-behaviour(gen_server). + +-export([start_link/2]). + +-export([init/1, handle_info/2]). + +start_link(Name, Cmd) -> + gen_server:start_link({local, Name}, ?MODULE, [Name, Cmd], []). + +init([Name, Cmd]) -> + Opts = [stdout, stderr], + {ok, Pid, OsPid} = exec:run_link(Cmd, Opts), + logger:info("Started system process ~p '~p', system pid = ~p", [Name, Cmd, OsPid]), + {ok, {Name, Pid, OsPid}}. + +handle_info({stdout, OsPid, Binary}, {Name, _Pid, OsPid} = State) -> + logger:info("~p[~p] ~p", [Name, OsPid, Binary]), + {noreply, State}; +handle_info({stderr, OsPid, Binary}, {Name, _Pid, OsPid} = State) -> + logger:warning("~p[~p] ~p", [Name, OsPid, Binary]), + {noreply, State}; +handle_info(Info, {Name, _, _} = State) -> + logger:warning("~p ~p", [Name, Info]). diff --git a/apps/styx_service/src/styx_service_app.erl b/apps/styx_service/src/styx_service_app.erl new file mode 100644 index 0000000..67e3414 --- /dev/null +++ b/apps/styx_service/src/styx_service_app.erl @@ -0,0 +1,18 @@ +%%%------------------------------------------------------------------- +%% @doc styx_service public API +%% @end +%%%------------------------------------------------------------------- + +-module(styx_service_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + styx_service_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions diff --git a/apps/styx_service/src/styx_service_sup.erl b/apps/styx_service/src/styx_service_sup.erl new file mode 100644 index 0000000..5461ab2 --- /dev/null +++ b/apps/styx_service/src/styx_service_sup.erl @@ -0,0 +1,52 @@ +%%%------------------------------------------------------------------- +%% @doc styx_service top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(styx_service_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). +-define(EXEC_TIMEOUT, 5000). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + Env = application:get_all_env(styx_service), + SupFlags = #{strategy => one_for_one, + intensity => 0, + period => 1}, + ChildSpecs = child_specs(Env, []), + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions + +child_specs([{watch_assets, _Options} | Rest], Acc) -> + ExecArgs = [watch_assets, "npm --prefix /Users/href/dev/styx/assets/ run watch"], + Spec = {watch_assets, {styx_service, start_link, ExecArgs}, permanent, 5000, worker, [styx_service]}, + child_specs(Rest, [Spec | Acc]); +child_specs([{kratos, _Options} | Rest], Acc) -> + ExecArgs = [kratos, "kratos serve --watch-courier --config /Users/href/dev/styx/config/kratos.yml"], + Spec = {kratos, {styx_service, start_link, ExecArgs}, permanent, 5000, worker, [styx_service]}, + child_specs(Rest, [Spec | Acc]); +child_specs([{hydra, _Options} | Rest], Acc) -> + ExecArgs = [hydra, "hydra serve all --dangerous-allow-insecure-redirect-urls --dangerous-force-http --config /Users/href/dev/styx/config/hydra.yml"], + Spec = {hydra, {styx_service, start_link, ExecArgs}, permanent, 5000, worker, [styx_service]}, + child_specs(Rest, [Spec | Acc]); +child_specs([], Acc) -> + Acc. diff --git a/apps/styx_web/.gitignore b/apps/styx_web/.gitignore new file mode 100644 index 0000000..f1c4554 --- /dev/null +++ b/apps/styx_web/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +_build +.idea +*.iml +rebar3.crashdump +*~ diff --git a/apps/styx_web/LICENSE b/apps/styx_web/LICENSE new file mode 100644 index 0000000..e389eb2 --- /dev/null +++ b/apps/styx_web/LICENSE @@ -0,0 +1,191 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2021, Jordan Bracco <href@random.sh>. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/apps/styx_web/README.md b/apps/styx_web/README.md new file mode 100644 index 0000000..68d41c2 --- /dev/null +++ b/apps/styx_web/README.md @@ -0,0 +1,9 @@ +styx_web +===== + +Styx: web/html + +Build +----- + + $ rebar3 compile diff --git a/apps/styx_web/priv/assets/app.css b/apps/styx_web/priv/assets/app.css new file mode 100644 index 0000000..32d2d21 --- /dev/null +++ b/apps/styx_web/priv/assets/app.css @@ -0,0 +1,997 @@ +/*! tailwindcss v2.2.7 | MIT License | https://tailwindcss.com */ + +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +/** +Use a more readable tab size (opinionated). +*/ + +html { + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; +} + +/** +1. Correct the line height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +*/ + +html { + line-height: 1.15; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + +/* +Sections +======== +*/ + +/** +Remove the margin in all browsers. +*/ + +body { + margin: 0; +} + +/** +Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +*/ + +body { + font-family: + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; +} + +/* +Grouping content +================ +*/ + +/** +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ +} + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr[title] { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/** +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/** +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +Tabular data +============ +*/ + +/** +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ +} + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + line-height: 1.15; + /* 1 */ + margin: 0; + /* 2 */ +} + +/** +Remove the inheritance of text transform in Edge and Firefox. +1. Remove the inheritance of text transform in Firefox. +*/ + +button, +select { + /* 1 */ + text-transform: none; +} + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** +Remove the inner border and padding in Firefox. +*/ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** +Restore the focus styles unset by the previous rule. +*/ + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** +Remove the additional ':invalid' styles in Firefox. +See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +legend { + padding: 0; +} + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +button { + background-color: transparent; + background-image: none; +} + +fieldset { + margin: 0; + padding: 0; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +html { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 1 */ + line-height: 1.5; + /* 2 */ +} + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +body { + font-family: inherit; + line-height: inherit; +} + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like <details> where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: currentColor; + /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +hr { + border-top-width: 1px; +} + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +img { + border-style: solid; +} + +textarea { + resize: vertical; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + color: #9ca3af; +} + +input:-ms-input-placeholder, textarea:-ms-input-placeholder { + opacity: 1; + color: #9ca3af; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} + +button, +[role="button"] { + cursor: pointer; +} + +table { + border-collapse: collapse; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +a { + color: inherit; + text-decoration: inherit; +} + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +pre, +code, +kbd, +samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +/** + * 1. Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + * + * 2. Add `vertical-align: middle` to align replaced elements more + * sensibly by default when overriding `display` by adding a + * utility like `inline`. + * + * This can trigger a poorly considered linting error in some + * tools but is included by design. + * + * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 + */ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/** + * Constrain images and videos to the parent width and preserve + * their intrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +video { + max-width: 100%; + height: auto; +} + +/** + * Ensure the default browser behavior of the `hidden` attribute. + */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.mt-8 { + margin-top: 2rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.root { + display: flex; + min-height: 100vh; + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); +} + +@media (prefers-color-scheme: dark) { + .root { + --tw-bg-opacity: 1; + background-color: rgba(17, 24, 39, var(--tw-bg-opacity)); + } +} + +.main { + display: flex; + flex: 1 1 0%; + flex-direction: column; + justify-content: center; + padding-top: 3rem; + padding-bottom: 3rem; + padding-left: 1rem; + padding-right: 1rem; +} + +@media (min-width: 640px) { + .main { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 1024px) { + .main { + flex: none; + } + + .main { + padding-left: 5rem; + padding-right: 5rem; + } +} + +@media (min-width: 1280px) { + .main { + padding-left: 6rem; + padding-right: 6rem; + } +} + +.container { + margin-left: auto; + margin-right: auto; + width: 100%; + max-width: 24rem; +} + +@media (min-width: 1024px) { + .container { + width: 24rem; + } +} + +.container > h1 { + margin-bottom: 2rem; + width: auto; + font-size: 1.5rem; + line-height: 2rem; + font-weight: 800; + --tw-text-opacity: 1; + color: rgba(76, 29, 149, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .container > h1 { + --tw-text-opacity: 1; + color: rgba(167, 139, 250, var(--tw-text-opacity)); + } +} + +.container > .header > h2 { + font-size: 1.875rem; + line-height: 2.25rem; + font-weight: 700; + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .container > .header > h2 { + --tw-text-opacity: 1; + color: rgba(156, 163, 175, var(--tw-text-opacity)); + } +} + +.container > .header > p { + margin-top: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + --tw-text-opacity: 1; + color: rgba(75, 85, 99, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .container > .header > p { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); + } +} + +.container > .header > p > a { + font-weight: 500; + --tw-text-opacity: 1; + color: rgba(79, 70, 229, var(--tw-text-opacity)); +} + +.container > .header > p > a:hover { + --tw-text-opacity: 1; + color: rgba(99, 102, 241, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .container > .header > p > a { + --tw-text-opacity: 1; + color: rgba(129, 140, 248, var(--tw-text-opacity)); + } + + .container > .header > p > a:hover { + --tw-text-opacity: 1; + color: rgba(165, 180, 252, var(--tw-text-opacity)); + } +} + +.background { + position: relative; + display: none; + width: 0px; + flex: 1 1 0%; +} + +@media (min-width: 1024px) { + .background { + display: block; + } +} + +.background > img { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + height: 100%; + width: 100%; + -o-object-fit: cover; + object-fit: cover; +} + +.form-message { + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 700; +} + +.text-error, .text-alert { + --tw-text-opacity: 1; + color: rgba(153, 27, 27, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .text-error, .text-alert { + --tw-text-opacity: 1; + color: rgba(252, 165, 165, var(--tw-text-opacity)); + } +} + +.input { + display: block; + width: 100%; + border-radius: 0.375rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgba(209, 213, 219, var(--tw-border-opacity)); + padding-left: 0.75rem; + padding-right: 0.75rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.input::-moz-placeholder { + --tw-placeholder-opacity: 1; + color: rgba(156, 163, 175, var(--tw-placeholder-opacity)); +} + +.input:-ms-input-placeholder { + --tw-placeholder-opacity: 1; + color: rgba(156, 163, 175, var(--tw-placeholder-opacity)); +} + +.input::placeholder { + --tw-placeholder-opacity: 1; + color: rgba(156, 163, 175, var(--tw-placeholder-opacity)); +} + +.input { + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.input:focus { + --tw-border-opacity: 1; + border-color: rgba(99, 102, 241, var(--tw-border-opacity)); + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-opacity: 1; + --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)); +} + +@media (prefers-color-scheme: dark) { + .input { + --tw-border-opacity: 1; + border-color: rgba(55, 65, 81, var(--tw-border-opacity)); + } + + .input { + --tw-border-opacity: 1; + border-color: rgba(107, 114, 128, var(--tw-border-opacity)); + } + + .input { + --tw-bg-opacity: 1; + background-color: rgba(156, 163, 175, var(--tw-bg-opacity)); + } + + .input::-moz-placeholder { + --tw-placeholder-opacity: 1; + color: rgba(31, 41, 55, var(--tw-placeholder-opacity)); + } + + .input:-ms-input-placeholder { + --tw-placeholder-opacity: 1; + color: rgba(31, 41, 55, var(--tw-placeholder-opacity)); + } + + .input::placeholder { + --tw-placeholder-opacity: 1; + color: rgba(31, 41, 55, var(--tw-placeholder-opacity)); + } + + .input:focus { + --tw-border-opacity: 1; + border-color: rgba(99, 102, 241, var(--tw-border-opacity)); + } + + .input:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)); + } +} + +@media (min-width: 640px) { + .input { + font-size: 0.875rem; + line-height: 1.25rem; + } +} + +.btn-submit { + display: flex; + width: 100%; + justify-content: center; + border-radius: 0.375rem; + border-width: 1px; + border-color: transparent; + --tw-bg-opacity: 1; + background-color: rgba(79, 70, 229, var(--tw-bg-opacity)); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + padding-right: 1rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); + --tw-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.btn-submit:hover { + --tw-bg-opacity: 1; + background-color: rgba(67, 56, 202, var(--tw-bg-opacity)); +} + +.btn-submit:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 1; + --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)); + --tw-ring-offset-width: 2px; +} + +@media (prefers-color-scheme: dark) { + .btn-submit:hover { + --tw-bg-opacity: 1; + background-color: rgba(99, 102, 241, var(--tw-bg-opacity)); + } + + .btn-submit:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(165, 180, 252, var(--tw-ring-opacity)); + } +} + +.label { + display: block; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgba(55, 65, 81, var(--tw-text-opacity)); +} + +@media (prefers-color-scheme: dark) { + .label { + --tw-text-opacity: 1; + color: rgba(209, 213, 219, var(--tw-text-opacity)); + } +} diff --git a/apps/styx_web/priv/assets/app.js b/apps/styx_web/priv/assets/app.js new file mode 100644 index 0000000..1627bc2 --- /dev/null +++ b/apps/styx_web/priv/assets/app.js @@ -0,0 +1,3 @@ +(() => { +})(); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFtdLAogICJzb3VyY2VzQ29udGVudCI6IFtdLAogICJtYXBwaW5ncyI6ICIiLAogICJuYW1lcyI6IFtdCn0K diff --git a/apps/styx_web/rebar.config b/apps/styx_web/rebar.config new file mode 100644 index 0000000..42392a1 --- /dev/null +++ b/apps/styx_web/rebar.config @@ -0,0 +1,21 @@ +{erl_opts, [debug_info]}. +{deps, [ + {cowboy, "2.9.0"}, + {trails, "2.3.0"}, + {erlydtl, "0.14.0"} +]}. + +{plugins, [ + {rebar3_erlydtl_plugin, ".*", {git, "https://github.com/tsloughter/rebar3_erlydtl_plugin.git", {branch, "master"}}} +]}. + +{provider_hooks, [ + {pre, [{compile, {erlydtl, compile}}]} + ]}. + +{shell, [ + % {config, "config/sys.config"}, + {apps, [styx_web]} +]}. + +{erlydtl_opts, [{doc_root, "templates"}]}. diff --git a/apps/styx_web/src/styx_web.app.src b/apps/styx_web/src/styx_web.app.src new file mode 100644 index 0000000..9e82554 --- /dev/null +++ b/apps/styx_web/src/styx_web.app.src @@ -0,0 +1,18 @@ +{application, styx_web, + [{description, "Styx: web/html"}, + {vsn, "0.1.0"}, + {registered, []}, + {mod, {styx_web_app, []}}, + {applications, + [kernel, + stdlib, + erlydtl, + cowboy, + trails + ]}, + {env,[]}, + {modules, []}, + + {licenses, ["Apache 2.0"]}, + {links, []} + ]}. diff --git a/apps/styx_web/src/styx_web.erl b/apps/styx_web/src/styx_web.erl new file mode 100644 index 0000000..cf565bd --- /dev/null +++ b/apps/styx_web/src/styx_web.erl @@ -0,0 +1,77 @@ +-module(styx_web). + +-export([render/3, render_form/1, reply_html/3, reply_html/4, temporary_redirect/2, req_param/2, identity_name/1]). + +identity_name(#{<<"identity">> := Identity}) -> + identity_name(Identity); +identity_name(#{<<"traits">> := #{<<"name">> := #{<<"first">> := F, <<"last">> := L}}}) when is_binary(F), is_binary(L) -> + [F, " ", L]; +identity_name(#{<<"traits">> := #{<<"name">> := N}}) when is_binary(N) -> + N; +identity_name(#{<<"traits">> := #{<<"username">> := U}}) when is_binary(U) -> + U; +identity_name(#{<<"traits">> := #{<<"email">> := E}}) when is_binary(E) -> + E; +identity_name(#{<<"id">> := Id}) -> + Id. + +render(Req, InnerModule, Assigns) -> + {ok, InnerHtml} = InnerModule:render(Assigns), + render_layout(Req, InnerHtml, Assigns). + +render_form(UI = #{<<"action">> := Action, <<"nodes">> := Nodes}) -> + Inputs = render_node(Nodes, []), + Msgs = maps:get(<<"messages">>, UI, []), + {ok, Html} = form_dtl:render([{"action", Action}, {"inputs", Inputs}, {"messages", Msgs}]), + Html. + +render_layout(_Req, InnerHtml, Assigns0) -> + Assigns = [{"site_title", application:get_env(?MODULE, site_title, <<"Styx SSO">>)}, + {"background_image_url", application:get_env(?MODULE, background_image_url, <<"https://images.unsplash.com/photo-1505904267569-f02eaeb45a4c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1908&q=80">>)}, + {"inner", InnerHtml} + | Assigns0], + {ok, Html} = layout_dtl:render(Assigns), + Html. + +reply_html(Req, Code, Html) -> + reply_html(Req, Code, Html, #{}). + +reply_html(Req, Code, Html, Headers0) -> + Headers = maps:put(<<"content-type">>, <<"text/html">>, Headers0), + cowboy_req:reply(Code, Headers, Html, Req). + +temporary_redirect(Req0, Url) -> + cowboy_req:reply(307, #{<<"location">> => Url}, Req0). + +req_param(Req, Param) -> + Qs = cowboy_req:parse_qs(Req), + case lists:keyfind(Param, 1, Qs) of + {_, Value} -> + {ok, Value}; + _ -> + {error, {missing_param, Param}} + end. + +render_node([#{<<"attributes">> := Attrs = #{<<"name">> := AttrName, <<"type">> := AttrType}, <<"type">> := <<"input">>, <<"messages">> := Msgs, <<"meta">> := Meta} | Rest], Acc) -> + Assigns0 = [{"input_name", AttrName}, + {"input_type", AttrType}, + {"input_value", maps:get(<<"value">>, Attrs, undefined)}, + {"input_required", maps:get(<<"required">>, Attrs, false)}, + {"input_disabled", maps:get(<<"disabled">>, Attrs, false)}], + Assigns1 = case maps:get(<<"label">>, Meta, false) of + #{<<"text">> := Text, <<"type">> := LType} -> [{"label", Text}, {"label_type", LType} | Assigns0]; + _ -> Assigns0 + end, + Assigns2 = case AttrName of + <<"traits.email">> -> [{"autocomplete", "email"} | Assigns1]; + <<"password">> -> [{"autocomplete", "password"} | Assigns1]; + _ -> Assigns1 + end, + Assigns = Assigns2, + {ok, Html} = case AttrType of + <<"submit">> -> form_submit_dtl:render(Assigns); + _ -> form_input_dtl:render(Assigns) + end, + render_node(Rest, Acc ++ Html); +render_node([], Acc) -> + Acc. diff --git a/apps/styx_web/src/styx_web_app.erl b/apps/styx_web/src/styx_web_app.erl new file mode 100644 index 0000000..1386e71 --- /dev/null +++ b/apps/styx_web/src/styx_web_app.erl @@ -0,0 +1,53 @@ +%%%------------------------------------------------------------------- +%% @doc styx_web public API +%% @end +%%%------------------------------------------------------------------- + +-module(styx_web_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +start(_StartType, _StartArgs) -> + Port = 5000, + CowboyOpts = [{port, Port}], + CowboyEnv = #{env => #{ + dispatch => routes() + }}, + {ok, _} = cowboy:start_clear(styx_web_listener, CowboyOpts, CowboyEnv), + logger:notice("listening on ~p", [Port]), + styx_web_sup:start_link(). + +stop(_State) -> + ok. + +%% internal functions + +routes() -> + Trails = [ + %% App + {"/", styx_web_index, undefined}, + {"/launchpad", styx_web_launchpad, undefined}, + + %% Kratos + {"/login", styx_web_kratos_flow, #{page_title => "Login", template => login_dtl, getflowmf => {ory_kratos, login_flow}, initflowmf => {ory_kratos, login_url}}}, + {"/register", styx_web_kratos_flow, #{page_title => "Register", template => registration_dtl, getflowmf => {ory_kratos, registration_flow}, initflowmf => {ory_kratos, registration_url}}}, + {"/account", styx_web_kratos_flow, #{page_title => "Account", template => account_dtl, getflowmf => {ory_kratos, settings_flow}, initflowmf => {ory_kratos, settings_url}}}, + {"/account/recovery", styx_web_kratos_flow, #{page_title => "Recover Account", template => recovery_dtl, getflowmf => {ory_kratos, recovery_flow}, initflowmf => {ory_kratos, recovery_url}}}, + {"/account/verification", styx_web_kratos_flow, #{page_title => "Verify Account", template => verification_dtl, getflowmf => {ory_kratos, verification_flow}, initflowmf => {ory_kratos, verification_url}}}, + {"/account/error", styx_web_error, undefined}, + {"/account/logout", styx_web_logout, undefined}, + + %% Hydra + {"/account/oauth2/login", styx_web_oauth2_login, undefined}, + {"/account/oauth2/consent", styx_web_oauth2_consent, undefined}, + + %% Static + {"/account/app.css", cowboy_static, {priv_file, styx_web, "assets/app.css"}}, + {"/account/app.js", cowboy_static, {priv_file, styx_web, "assets/app.js"}}, + + %% 404 Catch all + {'_', styx_web_error, #{code => 404, status => <<"Not found">>}} + ], + trails:single_host_compile(Trails). diff --git a/apps/styx_web/src/styx_web_error.erl b/apps/styx_web/src/styx_web_error.erl new file mode 100644 index 0000000..8b40371 --- /dev/null +++ b/apps/styx_web/src/styx_web_error.erl @@ -0,0 +1,17 @@ +-module(styx_web_error). +-behaviour(cowboy_handler). +-export([init/2]). + +init(Req, State = #{code := Code, status := Status}) -> + reply(Req, Code, Status, maps:get(message, State, undefined)); +init(Req = #{method := <<"GET">>}, State) -> + {ok, ErrorId} = styx_web:req_param(Req, <<"id">>), + {ok, Error} = ory_kratos:error(ErrorId), + {ok, #{<<"error">> := #{<<"status">> := Status, <<"code">> := Code, <<"message">> := Msg}}} = ory_kratos:error(ErrorId), + reply(Req, Code, Status, Msg). + +reply(Req0, Code, Status, Msg) -> + Assigns = [{"message", Msg}, {"status", Status}], + Html = styx_web:render(Req0, error_dtl, Assigns), + Req = styx_web:reply_html(Req0, Code, Html), + {ok, Req, undefined}. diff --git a/apps/styx_web/src/styx_web_index.erl b/apps/styx_web/src/styx_web_index.erl new file mode 100644 index 0000000..377a5de --- /dev/null +++ b/apps/styx_web/src/styx_web_index.erl @@ -0,0 +1,14 @@ +-module(styx_web_index). +-behaviour(cowboy_handler). +-export([init/2]). + +init(Req0, State) -> + Cookie = cowboy_req:header(<<"cookie">>, Req0), + Req = case ory_kratos:whoami(Cookie) of + {ok, #{<<"active">> := true}} -> + styx_web:temporary_redirect(Req0, <<"/launchpad">>); + _ -> + Html = styx_web:render(Req0, index_dtl, []), + styx_web:reply_html(Req0, 200, Html) + end, + {ok, Req, State}. diff --git a/apps/styx_web/src/styx_web_kratos_flow.erl b/apps/styx_web/src/styx_web_kratos_flow.erl new file mode 100644 index 0000000..c889794 --- /dev/null +++ b/apps/styx_web/src/styx_web_kratos_flow.erl @@ -0,0 +1,25 @@ +-module(styx_web_kratos_flow). +-behaviour(cowboy_handler). +-export([init/2]). + +init(Req = #{method := <<"GET">>}, State = #{page_title := _, template := _, getflowmf := _, initflowmf := _}) -> + get(Req, State, styx_web:req_param(Req, <<"flow">>)); +init(Req, _) -> + styx_web_error:init(Req, #{code => 404, status => <<"Not Found">>}). + +get(Req, State = #{getflowmf := {Mod, Fun}}, {ok, FlowId}) -> + Cookie = cowboy_req:header(<<"cookie">>, Req), + get_(Req, State, Mod:Fun(Cookie, FlowId)); +get(Req0, State = #{initflowmf := {Mod, Fun}}, {error, {missing_param, _}}) -> + Req = styx_web:temporary_redirect(Req0, Mod:Fun(browser)), + {ok, Req, State}. + +get_(Req0, State = #{page_title := PageTitle, template := Template}, {ok, Flow = #{<<"ui">> := UI}}) -> + logger:debug("Flow = ~p", [Flow]), + FormHtml = styx_web:render_form(UI), + Assigns = [{"page_title", PageTitle}, {"form", FormHtml}], + Html = styx_web:render(Req0, Template, Assigns), + Req = styx_web:reply_html(Req0, 200, Html), + {ok, Req, State}; +get_(Req, State, {error, Error = #{<<"code">> := Code, <<"status">> := Status, <<"message">> := Msg}}) -> + styx_web_error:init(Req, #{code => Code, status => Status, message => maps:get(<<"reason">>, Error, Msg)}). diff --git a/apps/styx_web/src/styx_web_launchpad.erl b/apps/styx_web/src/styx_web_launchpad.erl new file mode 100644 index 0000000..382c1cd --- /dev/null +++ b/apps/styx_web/src/styx_web_launchpad.erl @@ -0,0 +1,16 @@ +-module(styx_web_launchpad). +-behaviour(cowboy_handler). +-export([init/2]). + +init(Req0, State) -> + Cookie = cowboy_req:header(<<"cookie">>, Req0), + case ory_kratos:whoami(Cookie) of + {ok, Session = #{<<"active">> := true}} -> + logger:debug("Session ~p", [Session]), + Html = styx_web:render(Req0, launchpad_dtl, [{"session", Session}, {"name", styx_web:identity_name(Session)}]), + {ok, styx_web:reply_html(Req0, 200, Html), State}; + {error, #{<<"code">> := 401}} -> + {ok, styx_web:temporary_redirect(Req0, <<"/login">>), State}; + {error, Error = #{<<"code">> := Code, <<"status">> := Status, <<"message">> := Msg}} -> + styx_web_error:init(Req0, #{code => Code, status => Status, message => maps:get(<<"reason">>, Error, Msg)}) + end. diff --git a/apps/styx_web/src/styx_web_logout.erl b/apps/styx_web/src/styx_web_logout.erl new file mode 100644 index 0000000..80ef57e --- /dev/null +++ b/apps/styx_web/src/styx_web_logout.erl @@ -0,0 +1,13 @@ +-module(styx_web_logout). +-behaviour(cowboy_handler). +-export([init/2]). + +init(Req0, State) -> + Cookie = cowboy_req:header(<<"cookie">>, Req0), + case ory_kratos:logout_flow(Cookie) of + {ok, #{<<"logout_url">> := URL}} -> + Req = styx_web:temporary_redirect(Req0, URL), + {ok, Req, State}; + _ -> + styx_web_error:init(Req0, #{code => 404, status => <<"Not Found">>}) + end. diff --git a/apps/styx_web/src/styx_web_sup.erl b/apps/styx_web/src/styx_web_sup.erl new file mode 100644 index 0000000..8e861b4 --- /dev/null +++ b/apps/styx_web/src/styx_web_sup.erl @@ -0,0 +1,35 @@ +%%%------------------------------------------------------------------- +%% @doc styx_web top level supervisor. +%% @end +%%%------------------------------------------------------------------- + +-module(styx_web_sup). + +-behaviour(supervisor). + +-export([start_link/0]). + +-export([init/1]). + +-define(SERVER, ?MODULE). + +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%% sup_flags() = #{strategy => strategy(), % optional +%% intensity => non_neg_integer(), % optional +%% period => pos_integer()} % optional +%% child_spec() = #{id => child_id(), % mandatory +%% start => mfargs(), % mandatory +%% restart => restart(), % optional +%% shutdown => shutdown(), % optional +%% type => worker(), % optional +%% modules => modules()} % optional +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 0, + period => 1}, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +%% internal functions diff --git a/apps/styx_web/templates/account.dtl b/apps/styx_web/templates/account.dtl new file mode 100644 index 0000000..c146b32 --- /dev/null +++ b/apps/styx_web/templates/account.dtl @@ -0,0 +1,17 @@ +<div class="header"> + <h2> + Your Account + </h2> + <p> + Or + <a href="/account/logout"> + logout + </a> + </p> +</div> + +<div class="mt-8"> + + <div class="mt-6">{{form | safe}}</div> + +</div> diff --git a/apps/styx_web/templates/error.dtl b/apps/styx_web/templates/error.dtl new file mode 100644 index 0000000..c0177f2 --- /dev/null +++ b/apps/styx_web/templates/error.dtl @@ -0,0 +1,8 @@ +<div class="header"> + <h2> + {{ status }} + </h2> + <p class="text-error"> + {{ message }} + </p> +</div> diff --git a/apps/styx_web/templates/form.dtl b/apps/styx_web/templates/form.dtl new file mode 100644 index 0000000..87135b5 --- /dev/null +++ b/apps/styx_web/templates/form.dtl @@ -0,0 +1,6 @@ +<form action="{{action}}" method="POST" class="space-y-6"> + <div>{% for m in messages %} + <div class="form-message text-{{m.type}}">{{m.text}}</div> + {% endfor %}</div> + {{inputs | safe }} +</form>
\ No newline at end of file diff --git a/apps/styx_web/templates/form_input.dtl b/apps/styx_web/templates/form_input.dtl new file mode 100644 index 0000000..c5b9876 --- /dev/null +++ b/apps/styx_web/templates/form_input.dtl @@ -0,0 +1,18 @@ +<div> + {% if label %} + <label for="{{input_name}}" class="label"> + {{label}} + {% if input_required %}<span class="text-alert">*</span>{% endif %} + </label> + {% endif %} + + <div class="mt-1"> + <input name="{{input_name}}" + type="{{input_type}}" + {% if autocomplete %}autocomplete="{{autocomplete}}"{% endif %} + {% if input_value %}value="{{input_value}}"{% endif %} + {% if input_required %}required{% endif %} + {% if input_disabled %}disabled{% endif %} + class="input"> + </div> +</div>
\ No newline at end of file diff --git a/apps/styx_web/templates/form_submit.dtl b/apps/styx_web/templates/form_submit.dtl new file mode 100644 index 0000000..5e3556e --- /dev/null +++ b/apps/styx_web/templates/form_submit.dtl @@ -0,0 +1,3 @@ +<div> + <button type="{{input_type}}" class="btn-submit" name="{{input_name}}" value="{{input_value}}">{{label}}</button> +</div> diff --git a/apps/styx_web/templates/index.dtl b/apps/styx_web/templates/index.dtl new file mode 100644 index 0000000..ab050ed --- /dev/null +++ b/apps/styx_web/templates/index.dtl @@ -0,0 +1,4 @@ +<div class="mt-8"> + <a href="/login" class="btn-submit">Login</a> + <a href="/register" class="btn-submit mt-2">Register</a> +</div>
\ No newline at end of file diff --git a/apps/styx_web/templates/launchpad.dtl b/apps/styx_web/templates/launchpad.dtl new file mode 100644 index 0000000..588ad48 --- /dev/null +++ b/apps/styx_web/templates/launchpad.dtl @@ -0,0 +1,10 @@ +<div class="header"> + <h2> + {{name}} + </h2> +</div> + +<div class="mt-8"> + <a href="/account" class="btn-submit">Edit account</a> + <a href="/account/logout" class="btn-submit mt-2">Logout</a> +</div> diff --git a/apps/styx_web/templates/layout.dtl b/apps/styx_web/templates/layout.dtl new file mode 100644 index 0000000..f648f09 --- /dev/null +++ b/apps/styx_web/templates/layout.dtl @@ -0,0 +1,21 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>{% if page_title %}{{ page_title }} - {% endif %}{{site_title}}</title> + <link rel="stylesheet" href="/account/app.css"> +</head> +<body> + <div class="root"> + <div class="main"> + <div class="container"> + <h1><a href="/">{{site_title}}</a></h1> + {{ inner | safe }} + </div> + </div> + <div class="background"> + <img src="{{background_image_url}}" alt=""> + </div> + </div> +</body> +</html> diff --git a/apps/styx_web/templates/login.dtl b/apps/styx_web/templates/login.dtl new file mode 100644 index 0000000..9f0f9e9 --- /dev/null +++ b/apps/styx_web/templates/login.dtl @@ -0,0 +1,21 @@ +<div class="header"> + <h2> + Sign in + </h2> + <p> + Or + <a href="/register"> + register an account + </a> + </p> +</div> + +<div class="mt-8"> + + <div class="mt-6">{{form | safe}}</div> + + <p class="mt-4 text-sm"> + <a href="/account/recovery">Forgot your password ?</a> + </p> + +</div> diff --git a/apps/styx_web/templates/recovery.dtl b/apps/styx_web/templates/recovery.dtl new file mode 100644 index 0000000..9287aac --- /dev/null +++ b/apps/styx_web/templates/recovery.dtl @@ -0,0 +1,17 @@ +<div class="header"> + <h2> + Recover Account + </h2> + <p> + Or + <a href="/login"> + login + </a> + </p> +</div> + +<div class="mt-8"> + + <div class="mt-6">{{form | safe}}</div> + +</div> diff --git a/apps/styx_web/templates/registration.dtl b/apps/styx_web/templates/registration.dtl new file mode 100644 index 0000000..df564e3 --- /dev/null +++ b/apps/styx_web/templates/registration.dtl @@ -0,0 +1,17 @@ +<div class="header"> + <h2> + Register an account + </h2> + <p> + Or + <a href="/login"> + sign in + </a> + </p> +</div> + +<div class="mt-8"> + + <div class="mt-6">{{form | safe}}</div> + +</div> diff --git a/apps/styx_web/templates/verification.dtl b/apps/styx_web/templates/verification.dtl new file mode 100644 index 0000000..d63d457 --- /dev/null +++ b/apps/styx_web/templates/verification.dtl @@ -0,0 +1,11 @@ +<div class="header"> + <h2> + Verify Account + </h2> +</div> + +<div class="mt-8"> + + <div class="mt-6">{{form | safe}}</div> + +</div> |