diff --git a/src/main/java/com/microsoft/azure/springcloudplayground/controller/MainController.java b/src/main/java/com/microsoft/azure/springcloudplayground/controller/MainController.java index 7c3f00d..c0a60c8 100644 --- a/src/main/java/com/microsoft/azure/springcloudplayground/controller/MainController.java +++ b/src/main/java/com/microsoft/azure/springcloudplayground/controller/MainController.java @@ -11,10 +11,8 @@ import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Zip; import org.apache.tools.ant.types.ZipFileSet; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.lang.NonNull; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -23,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.resource.ResourceUrlProvider; +import javax.annotation.Nonnull; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -124,6 +123,12 @@ public String home(Map model, OAuth2AuthenticationToken token) { return "home"; } + + @PostMapping("/push-to-github") + public String pushToGithub(@RequestBody @Nonnull ProjectRequest request) { + return "push to github not implemented"; + } + @ResponseBody @PostMapping("/project.zip") public ResponseEntity getZipProject(@RequestBody @NonNull ProjectRequest request) throws IOException { diff --git a/src/main/java/com/microsoft/azure/springcloudplayground/security/WebSecurityConfig.java b/src/main/java/com/microsoft/azure/springcloudplayground/security/WebSecurityConfig.java index 35e4dc5..11326ed 100644 --- a/src/main/java/com/microsoft/azure/springcloudplayground/security/WebSecurityConfig.java +++ b/src/main/java/com/microsoft/azure/springcloudplayground/security/WebSecurityConfig.java @@ -10,10 +10,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().anyRequest().permitAll().and().oauth2Login().loginPage("/"); + http.authorizeRequests().antMatchers("/push-to-github").authenticated() + .anyRequest().permitAll() + .and().oauth2Login().loginPage("/oauth2/authorization/github"); - http.formLogin().disable() - .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/").deleteCookies("JSESSIONID").invalidateHttpSession(true); http.httpBasic().disable(); diff --git a/src/main/resources/static/css/customize.css b/src/main/resources/static/css/customize.css index 9c6670c..df9798e 100644 --- a/src/main/resources/static/css/customize.css +++ b/src/main/resources/static/css/customize.css @@ -41,7 +41,7 @@ } .large-bottom-padding { - padding-bottom: 4rem; + padding-bottom: 6rem; } .module-checkbox { @@ -187,4 +187,15 @@ body, button, input, p, select, textarea, label, h1, h2, h3, h4 { .playground-footer .content ul { margin-top: 0; +} + +.loading-status { + position: absolute; + height: 100%; +} + +.loading-status div { + display: flex; + align-items: center; + padding: 0 0.5rem; } \ No newline at end of file diff --git a/src/main/resources/static/js/auth.js b/src/main/resources/static/js/auth.js index 05b07bc..9c26ea1 100644 --- a/src/main/resources/static/js/auth.js +++ b/src/main/resources/static/js/auth.js @@ -1,8 +1,9 @@ -(function() { +$(function() { var $signInButton = $("#login_link"); var $signOutButton = $("#logout_link"); var $userDropdown = $("#user_dropdown"); var $loggedUser = $("#logged_user"); + var _hasLoggedIn = false; $signOutButton.on("click", function() { logout(); @@ -29,6 +30,15 @@ function loggedOutSuccess() { $signInButton.removeClass("hidden"); $userDropdown.addClass("hidden"); - $loggedUser.text(undefined); + $loggedUser.text(""); + _hasLoggedIn = false; + // Force reload to refresh to csrf token, or 403 error will raise up for csrf required post request + location.reload(true); } -}()) \ No newline at end of file + + _hasLoggedIn = $("#logged_user").text().trim().length > 0 ? true : false; + + window.hasLoggedIn = function () { + return _hasLoggedIn; + } +}); \ No newline at end of file diff --git a/src/main/resources/static/js/start.js b/src/main/resources/static/js/start.js index da3cf45..67c3db6 100644 --- a/src/main/resources/static/js/start.js +++ b/src/main/resources/static/js/start.js @@ -77,6 +77,10 @@ $(function () { var serviceNameHelp = $("#service-name-help"); var servicePortHelp = $("#port-help"); + azureCheckbox.on("change", addServiceBtnChecker); + azureServiceNameInput.on("input", addServiceBtnChecker); + azureServicePortInput.on("input", addServiceBtnChecker); + configPort.on("click", function () { if (infraPort.hasClass("hidden")) { infraPort.addClass("is-active"); @@ -153,24 +157,14 @@ $(function () { } }); - $("#form").submit(function(event) { - var csrfToken = $("input[name='_csrf']").val(); - var csrfTokenHeader = $("input[name='_csrf_header']").val(); - var groupId = $("#groupId").val(); - var artifactId = $("#artifactId").val(); - var projectName = $("#project-name").val(); - var description = $("#description").val(); + $("#generate-project").on("click", function(event) { + event.preventDefault(); + }); - var data = { - name: projectName, - groupId: groupId, - artifactId: artifactId, - baseDir: artifactId, - description: description, - packageName: groupId + "." + artifactId, - microServices: allServiceList.serviceList - }; + $("#download-project").on("click", function(event) { + generateInProgress(); + var data = getProjectData(); var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { var a; @@ -188,12 +182,16 @@ $(function () { a.click(); a.remove(); } + + generateSucceed(); + } else if (xhttp.readyState === 4 && xhttp.status !== 200) { + generateFailed(); } }; xhttp.open("POST", '/project.zip'); xhttp.setRequestHeader("Content-Type", "application/json; charset=utf-8"); - xhttp.setRequestHeader(csrfTokenHeader, csrfToken); + setCsrfHeader(xhttp); // Set responseType as blob for binary responses xhttp.responseType = 'blob'; xhttp.send(JSON.stringify(data)); @@ -201,9 +199,54 @@ $(function () { event.preventDefault(); }); - azureCheckbox.on("change", addServiceBtnChecker); - azureServiceNameInput.on("input", addServiceBtnChecker); - azureServicePortInput.on("input", addServiceBtnChecker); + $("#push-to-github").on("click", function() { + if (!hasLoggedIn()) { + showGithubModal(); + event.preventDefault(); + return; + } + + generateInProgress(); + var data = getProjectData(); + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (xhttp.readyState === 4 && xhttp.status === 200) { + generateSucceed(); + } else { + generateFailed(); + } + }; + + xhttp.open("POST", '/push-to-github'); + xhttp.setRequestHeader("Content-Type", "application/json; charset=utf-8"); + + setCsrfHeader(xhttp); + xhttp.send(JSON.stringify(data)); + }); + + function setCsrfHeader(xhttp) { + var csrfToken = $("input[name='_csrf']").val(); + var csrfTokenHeader = $("input[name='_csrf_header']").val(); + + xhttp.setRequestHeader(csrfTokenHeader, csrfToken); + }; + + function getProjectData() { + var groupId = $("#groupId").val(); + var artifactId = $("#artifactId").val(); + var projectName = $("#project-name").val(); + var description = $("#description").val(); + + return { + name: projectName, + groupId: groupId, + artifactId: artifactId, + baseDir: artifactId, + description: description, + packageName: groupId + "." + artifactId, + microServices: allServiceList.serviceList + }; + } function isValidServiceName(serviceName) { return serviceName && /^([a-zA-Z0-9\-]*)$/.test(serviceName); @@ -220,8 +263,7 @@ $(function () { } function showMetaDataConfig() { - showElements([metaDataConfig]); - hideElements([infraModulesSelector, azureModulesSelector]); + toggleElements([metaDataConfig], [infraModulesSelector, azureModulesSelector]); activateStep(metaDataStep); disActivateStep(infraStep); @@ -231,8 +273,7 @@ $(function () { } function showInfraModulesConfig() { - hideElements([metaDataConfig, azureModulesSelector]); - showElements([infraModulesSelector]); + toggleElements([infraModulesSelector], [metaDataConfig, azureModulesSelector]); completeStep(metaDataStep); activateStep(infraStep); @@ -242,8 +283,7 @@ $(function () { } function showAzureModulesConfig() { - hideElements([metaDataConfig, infraModulesSelector]); - showElements([azureModulesSelector]); + toggleElements([azureModulesSelector], [metaDataConfig, infraModulesSelector]); completeStep(metaDataStep); completeStep(infraStep); @@ -256,18 +296,6 @@ $(function () { $("#form div")[0].scrollIntoView(false); } - function showElements(elements) { - elements.forEach(function(element) { - element.removeClass("hidden"); - }) - } - - function hideElements(elements) { - elements.forEach(function(element) { - element.addClass("hidden"); - }) - } - function activateStep(stepElement) { stepElement.className = "step-item is-active"; } @@ -372,6 +400,34 @@ $(function () { } } + var inProgressLabel = $("#in-progress"); + var generateSucceedLabel = $("#generate-succeed"); + var generateFailedLabel = $("#generate-failed"); + + function generateInProgress() { + toggleElements([inProgressLabel], [generateSucceedLabel, generateFailedLabel]); + } + + function generateSucceed() { + toggleElements([generateSucceedLabel], [inProgressLabel, generateFailedLabel]); + } + + function generateFailed() { + toggleElements([generateFailedLabel], [inProgressLabel, generateSucceedLabel]); + } + + var githubModal = $("#github-login-modal"); + var githubModalClose = $("#github-login-modal .delete"); + + function showGithubModal() { + githubModal.addClass("is-active"); + } + + githubModalClose.on("click", function(event) { + event.preventDefault(); + githubModal.removeClass("is-active"); + }); + // Initialize already selected infra services infraCheckbox.each(function(){ updateInfraService($(this), false); diff --git a/src/main/resources/static/js/utils.js b/src/main/resources/static/js/utils.js new file mode 100644 index 0000000..9c40988 --- /dev/null +++ b/src/main/resources/static/js/utils.js @@ -0,0 +1,16 @@ +function showElements(elements) { + elements.forEach(function(element) { + element.removeClass("hidden"); + }) +} + +function hideElements(elements) { + elements.forEach(function(element) { + element.addClass("hidden"); + }) +} + +function toggleElements(elementsToShow, elementsToHide) { + showElements(elementsToShow); + hideElements(elementsToHide); +} \ No newline at end of file diff --git a/src/main/resources/templates/config-azure-service.mustache b/src/main/resources/templates/config-azure-service.mustache index 48bafb5..15add89 100644 --- a/src/main/resources/templates/config-azure-service.mustache +++ b/src/main/resources/templates/config-azure-service.mustache @@ -41,15 +41,70 @@
-

+

- -

+ + + +
+ + + +
+ +
+
+ + diff --git a/src/main/resources/templates/home.mustache b/src/main/resources/templates/home.mustache index b1c670b..e780aef 100644 --- a/src/main/resources/templates/home.mustache +++ b/src/main/resources/templates/home.mustache @@ -99,6 +99,7 @@ +