├── .gitignore ├── LICENSE.TXT ├── README.md ├── pom.xml ├── screenshot.png └── src ├── main ├── java │ └── de │ │ └── codecentric │ │ └── boot │ │ └── admin │ │ ├── SpringBootAdmin.java │ │ ├── config │ │ └── WebappConfig.java │ │ ├── controller │ │ └── RegistryController.java │ │ └── service │ │ └── ApplicationRegistry.java ├── resources │ └── application.properties └── webapp │ └── public │ ├── img │ ├── favicon.png │ ├── platform-spring-boot.png │ ├── spring-logo.png │ └── yeoman.png │ ├── index.html │ ├── scripts │ ├── app.js │ ├── controllers │ │ └── controllers.js │ ├── filters │ │ └── filters.js │ └── services │ │ └── services.js │ ├── styles │ ├── application.css │ ├── base.css │ ├── buttons.css │ ├── fontcustom.css │ ├── highlight.css │ ├── icons.css │ ├── main.css │ └── typography.css │ └── views │ ├── about.html │ ├── apps.html │ └── apps │ ├── details.html │ ├── details │ ├── env.html │ ├── infos.html │ ├── metrics.html │ └── props.html │ ├── logging.html │ ├── logging │ ├── read.html │ └── write.html │ └── overview.html └── test ├── java └── de │ └── codecentric │ └── boot │ └── admin │ └── service │ └── ApplicationRegistryTest.java └── resources └── logback-test.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.settings 3 | /.classpath 4 | /.project 5 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spring-boot-admin 2 | ================= 3 | 4 | This is a simple admin interface for [Spring Boot](http://projects.spring.io/spring-boot/ "Official Spring-Boot website") applications. 5 | 6 | This application provides a simple GUI to administrate Spring Boot applications in some ways. At the moment it provides the following features for every registered application. 7 | 8 | 20 | 21 | #### Client applications 22 | 23 | Each application that want to register itself to the admin application has to include the [spring-boot-starter-admin-client](https://github.com/dickerpulli/spring-boot-starter-admin-client "GitHub project") as dependency. This starter JAR includes some [AutoConfiguration](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-auto-configuration "Spring Boot docu") features that includes registering tasks, controller, etc. 24 | 25 | #### Screenshot: 26 | 27 | [](url "title") 28 | 29 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | de.codecentric 5 | spring-boot-admin 6 | 1.0.1.RELEASE 7 | 8 | 9 | Apache License, Version 2.0 10 | http://opensource.org/licenses/Apache-2.0 11 | repo 12 | 13 | 14 | 15 | 1.1.4.RELEASE 16 | 4.0.6.RELEASE 17 | 2.3.2 18 | 1.11.0 19 | 1.2.12 20 | 0.2.10-1 21 | 22 | 23 | 24 | de.codecentric 25 | spring-boot-starter-admin-client 26 | 1.0.1.RELEASE 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | ${spring-boot.version} 32 | 33 | 34 | com.fasterxml.jackson.core 35 | jackson-databind 36 | 2.3.3 37 | 38 | 39 | org.apache.commons 40 | commons-lang3 41 | 3.3.2 42 | 43 | 44 | org.webjars 45 | bootstrap 46 | ${bootstrap.version} 47 | 48 | 49 | org.webjars 50 | jquery 51 | ${jquery.version} 52 | 53 | 54 | org.webjars 55 | angularjs 56 | ${angularjs.version} 57 | 58 | 59 | org.webjars 60 | angular-ui-router 61 | ${angular-ui-router.version} 62 | 63 | 64 | 65 | 66 | junit 67 | junit 68 | 4.10 69 | test 70 | 71 | 72 | org.springframework 73 | spring-test 74 | ${spring.version} 75 | test 76 | 77 | 78 | 79 | 80 | repo 81 | 82 | true 83 | ignore 84 | 85 | 86 | false 87 | 88 | https://raw.githubusercontent.com/dickerpulli/maven-repo/master 89 | 90 | 91 | 92 | ${project.artifactId} 93 | 94 | 95 | src/main/resources 96 | 97 | 98 | src/main/resources 99 | true 100 | 101 | application.properties 102 | 103 | 104 | 105 | src/main/webapp 106 | 107 | 108 | src/main/webapp 109 | true 110 | 111 | **/*.html 112 | 113 | 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-compiler-plugin 119 | 3.1 120 | 121 | 1.7 122 | 1.7 123 | utf8 124 | 125 | 126 | 127 | org.springframework.boot 128 | spring-boot-maven-plugin 129 | ${spring-boot.version} 130 | 131 | 132 | 133 | repackage 134 | 135 | 136 | 137 | 138 | de.codecentric.SpringBootAdmin 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dickerpulli/spring-boot-admin/28200fe6a466156b35ff5d08aaff232b1ade3032/screenshot.png -------------------------------------------------------------------------------- /src/main/java/de/codecentric/boot/admin/SpringBootAdmin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.codecentric.boot.admin; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 20 | import org.springframework.context.annotation.Configuration; 21 | import org.springframework.context.annotation.Import; 22 | 23 | import de.codecentric.boot.admin.config.WebappConfig; 24 | 25 | @Configuration 26 | @EnableAutoConfiguration 27 | @Import(WebappConfig.class) 28 | public class SpringBootAdmin { 29 | 30 | /** 31 | * Starting point for application to boot. 32 | * 33 | * @param args 34 | * Passed arguments. 35 | */ 36 | public static void main(String[] args) { 37 | SpringApplication.run(SpringBootAdmin.class, args); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/boot/admin/config/WebappConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.codecentric.boot.admin.config; 17 | 18 | import java.util.List; 19 | 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import org.springframework.http.converter.HttpMessageConverter; 23 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 24 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 25 | 26 | import de.codecentric.boot.admin.controller.RegistryController; 27 | import de.codecentric.boot.admin.service.ApplicationRegistry; 28 | 29 | @Configuration 30 | public class WebappConfig extends WebMvcConfigurerAdapter { 31 | 32 | /** 33 | * Add JSON MessageConverter to send JSON objects to web clients. 34 | */ 35 | @Override 36 | public void configureMessageConverters(List> converters) { 37 | converters.add(new MappingJackson2HttpMessageConverter()); 38 | } 39 | 40 | /** 41 | * Controller with REST-API for spring-boot applications to register itself. 42 | */ 43 | @Bean 44 | public RegistryController registryController() { 45 | return new RegistryController(); 46 | } 47 | 48 | /** 49 | * Registry for all registered application. 50 | */ 51 | @Bean 52 | public ApplicationRegistry applicationRegistry() { 53 | return new ApplicationRegistry(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/boot/admin/controller/RegistryController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.codecentric.boot.admin.controller; 17 | 18 | import java.util.List; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.stereotype.Controller; 24 | import org.springframework.web.bind.annotation.PathVariable; 25 | import org.springframework.web.bind.annotation.RequestBody; 26 | import org.springframework.web.bind.annotation.RequestMapping; 27 | import org.springframework.web.bind.annotation.RequestMethod; 28 | import org.springframework.web.bind.annotation.ResponseBody; 29 | 30 | import de.codecentric.boot.admin.model.Application; 31 | import de.codecentric.boot.admin.service.ApplicationRegistry; 32 | 33 | /** 34 | * REST controller for controlling registration of managed applications. 35 | */ 36 | @Controller 37 | public class RegistryController { 38 | 39 | private static final Logger LOGGER = LoggerFactory.getLogger(RegistryController.class); 40 | 41 | @Autowired 42 | private ApplicationRegistry registry; 43 | 44 | /** 45 | * Register an application within this admin application. 46 | * 47 | * @param app 48 | * The application infos. 49 | * @return The registered application. 50 | */ 51 | @RequestMapping(value = "/api/applications", method = RequestMethod.POST) 52 | @ResponseBody 53 | public Application register(@RequestBody Application app) { 54 | LOGGER.info("Register application with ID '{}' and URL '{}'", app.getId(), app.getUrl()); 55 | return registry.register(app); 56 | } 57 | 58 | /** 59 | * Get a single application out of the registry. 60 | * 61 | * @param id 62 | * The application identifier. 63 | * @return The registered application. 64 | */ 65 | @RequestMapping(value = "/api/application/{id}", method = RequestMethod.GET) 66 | @ResponseBody 67 | public Application get(@PathVariable String id) { 68 | LOGGER.debug("Deliver registered application with ID '{}'", id); 69 | return registry.getApplication(id); 70 | } 71 | 72 | /** 73 | * List all registered applications. 74 | * 75 | * @return List. 76 | */ 77 | @RequestMapping(value = "/api/applications", method = RequestMethod.GET) 78 | @ResponseBody 79 | public List applications() { 80 | LOGGER.debug("Deliver all registered applications"); 81 | return registry.getApplications(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/de/codecentric/boot/admin/service/ApplicationRegistry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package de.codecentric.boot.admin.service; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import org.apache.commons.lang3.Validate; 25 | import org.apache.tomcat.util.net.URL; 26 | import org.springframework.stereotype.Service; 27 | 28 | import de.codecentric.boot.admin.model.Application; 29 | 30 | /** 31 | * Registry for all applications that should be managed/administrated by the spring-boot-admin application. This 32 | * registry is just "in-memory", so that after a restart all applications have to be registered again. 33 | */ 34 | @Service 35 | public class ApplicationRegistry { 36 | 37 | private final Map registry = new HashMap<>(); 38 | 39 | /** 40 | * Register application. 41 | * 42 | * @param app 43 | * The Application. 44 | */ 45 | public Application register(Application app) { 46 | Validate.notNull(app, "Application must not be null"); 47 | Validate.notNull(app.getId(), "ID must not be null"); 48 | Validate.notNull(app.getUrl(), "URL must not be null"); 49 | Validate.isTrue(checkUrl(app.getUrl()), "URL is not valid"); 50 | return registry.put(app.getId(), app); 51 | } 52 | 53 | /** 54 | * Checks the syntax of the given URL. 55 | * 56 | * @param url 57 | * The URL. 58 | * @return true, if valid. 59 | */ 60 | private boolean checkUrl(String url) { 61 | try { 62 | new URL(url); 63 | } catch (MalformedURLException e) { 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | /** 70 | * Checks, if an application is already registerd. 71 | * 72 | * @param id 73 | * The application ID. 74 | * @return exists? 75 | */ 76 | public boolean isRegistered(String id) { 77 | return registry.containsKey(id); 78 | } 79 | 80 | /** 81 | * Get a list of all registered applications. 82 | * 83 | * @return List. 84 | */ 85 | public List getApplications() { 86 | return new ArrayList<>(registry.values()); 87 | } 88 | 89 | /** 90 | * Get a specific application inside the registry. 91 | * 92 | * @param id 93 | * Id. 94 | * @return Application. 95 | */ 96 | public Application getApplication(String id) { 97 | if (!isRegistered(id)) { 98 | throw new IllegalArgumentException("Application with ID " + id + " is not registered"); 99 | } 100 | return registry.get(id); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.resources.cachePeriod=3600 2 | server.port=8080 3 | info.id=@pom.artifactId@ 4 | info.version=@pom.version@ 5 | spring.boot.admin.url=http://localhost:8080 6 | logging.file=/tmp/log.log -------------------------------------------------------------------------------- /src/main/webapp/public/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dickerpulli/spring-boot-admin/28200fe6a466156b35ff5d08aaff232b1ade3032/src/main/webapp/public/img/favicon.png -------------------------------------------------------------------------------- /src/main/webapp/public/img/platform-spring-boot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dickerpulli/spring-boot-admin/28200fe6a466156b35ff5d08aaff232b1ade3032/src/main/webapp/public/img/platform-spring-boot.png -------------------------------------------------------------------------------- /src/main/webapp/public/img/spring-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dickerpulli/spring-boot-admin/28200fe6a466156b35ff5d08aaff232b1ade3032/src/main/webapp/public/img/spring-logo.png -------------------------------------------------------------------------------- /src/main/webapp/public/img/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dickerpulli/spring-boot-admin/28200fe6a466156b35ff5d08aaff232b1ade3032/src/main/webapp/public/img/yeoman.png -------------------------------------------------------------------------------- /src/main/webapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 |
22 | 44 |
45 |
46 |
47 |
48 | 49 |
50 | 55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/webapp/public/scripts/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | 18 | angular.module('springBootAdmin', [ 19 | 'ngResource', 20 | 'ngRoute', 21 | 'ui.router', 22 | 'springBootAdmin.services' 23 | ]) 24 | .config(function ($stateProvider, $urlRouterProvider) { 25 | $urlRouterProvider 26 | .when('/', '/apps/overview') 27 | .otherwise('/') 28 | $stateProvider 29 | .state('apps', { 30 | url: '/apps', 31 | abstract: true, 32 | templateUrl: 'views/apps.html', 33 | }) 34 | .state('about', { 35 | url: '/about', 36 | templateUrl: 'views/about.html' 37 | }) 38 | .state('apps.overview', { 39 | url: '/overview', 40 | templateUrl: 'views/apps/overview.html', 41 | controller: 'overviewCtrl' 42 | }) 43 | .state('apps.details', { 44 | url: '/details/:id', 45 | abstract: true, 46 | templateUrl: 'views/apps/details.html', 47 | controller: 'detailsCtrl' 48 | }) 49 | .state('apps.details.infos', { 50 | url: '/infos', 51 | templateUrl: 'views/apps/details/infos.html', 52 | controller: 'detailsInfosCtrl' 53 | }) 54 | .state('apps.details.metrics', { 55 | url: '/metrics', 56 | templateUrl: 'views/apps/details/metrics.html', 57 | controller: 'detailsMetricsCtrl' 58 | }) 59 | .state('apps.details.env', { 60 | url: '/env', 61 | templateUrl: 'views/apps/details/env.html', 62 | controller: 'detailsEnvCtrl' 63 | }) 64 | .state('apps.details.props', { 65 | url: '/props', 66 | templateUrl: 'views/apps/details/props.html', 67 | controller: 'detailsPropsCtrl' 68 | }) 69 | .state('apps.logging', { 70 | url: '/logging/:id', 71 | abstract: true, 72 | templateUrl: 'views/apps/logging.html', 73 | controller: 'detailsCtrl' 74 | }) 75 | .state('apps.logging.read', { 76 | url: '/read', 77 | templateUrl: 'views/apps/logging/read.html', 78 | controller: 'loggingReadCtrl' 79 | }) 80 | .state('apps.logging.write', { 81 | url: '/write', 82 | templateUrl: 'views/apps/logging/write.html', 83 | controller: 'loggingWriteCtrl' 84 | }); 85 | }) 86 | .run(function ($rootScope, $state, $stateParams, $log) { 87 | $rootScope.$state = $state; 88 | $rootScope.$stateParams = $stateParams; 89 | $rootScope.springBootAdminServerUrl = window.location.protocol + '//' + window.location.host; 90 | }); 91 | -------------------------------------------------------------------------------- /src/main/webapp/public/scripts/controllers/controllers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | 18 | angular.module('springBootAdmin') 19 | .controller('overviewCtrl', function ($scope, Applications, Application, ApplicationOverview, $location, $interval) { 20 | $scope.loadData = function() { 21 | $scope.applications = Applications.query({}, function(applications) { 22 | for (var i = 0; i < applications.length; i++) { 23 | var app = applications[i]; 24 | ApplicationOverview.getVersion(app); 25 | ApplicationOverview.getHealth(app); 26 | ApplicationOverview.getLogfile(app); 27 | } 28 | }); 29 | } 30 | $scope.loadData(); 31 | // callback for ng-click 'showDetails': 32 | $scope.showDetails = function(id) { 33 | $location.path('/apps/details/' + id + '/infos'); 34 | }; 35 | // callback for ng-click 'showLogging': 36 | $scope.showLogging = function(id) { 37 | $location.path('/apps/logging/' + id + '/read'); 38 | }; 39 | // callback for ng-click 'refresh': 40 | $scope.refresh = function(id) { 41 | $scope.application = Application.query({id: id}, function(application) { 42 | ApplicationOverview.refresh(application); 43 | }); 44 | }; 45 | // reload site every 30 seconds 46 | var task = $interval(function() { 47 | $scope.loadData(); 48 | }, 30000); 49 | }) 50 | .controller('navCtrl', function ($scope, $location) { 51 | $scope.navClass = function(page) { 52 | var currentRoute = $location.path().substring(1) || 'main'; 53 | return page == currentRoute ? 'active' : ''; 54 | }; 55 | }) 56 | .controller('detailsCtrl', function ($scope, $stateParams, Application) { 57 | $scope.application = Application.query({id: $stateParams.id}); 58 | }) 59 | .controller('detailsInfosCtrl', function ($scope, $stateParams, Application, ApplicationDetails) { 60 | $scope.application = Application.query({id: $stateParams.id}, function(application) { 61 | ApplicationDetails.getInfo(application); 62 | }); 63 | }) 64 | .controller('detailsMetricsCtrl', function ($scope, $stateParams, Application, ApplicationDetails) { 65 | $scope.application = Application.query({id: $stateParams.id}, function(application) { 66 | ApplicationDetails.getMetrics(application); 67 | }); 68 | }) 69 | .controller('detailsEnvCtrl', function ($scope, $stateParams, Application, ApplicationDetails) { 70 | $scope.application = Application.query({id: $stateParams.id}, function(application) { 71 | ApplicationDetails.getEnv(application); 72 | }); 73 | }) 74 | .controller('detailsPropsCtrl', function ($scope, $stateParams, Application, ApplicationDetails) { 75 | $scope.application = Application.query({id: $stateParams.id}, function(application) { 76 | ApplicationDetails.getProps(application); 77 | }); 78 | }) 79 | .controller('loggingCtrl', function ($scope, $stateParams, Application) { 80 | $scope.application = Application.query({id: $stateParams.id}); 81 | }) 82 | .controller('loggingReadCtrl', function ($scope, $stateParams, Application, ApplicationLogging) { 83 | $scope.$parent.application.logger = new Object(); 84 | $scope.readLoglevel = function(application) { 85 | ApplicationLogging.getLoglevel(application); 86 | }; 87 | }) 88 | .controller('loggingWriteCtrl', function ($scope, $stateParams, Application, ApplicationLogging) { 89 | $scope.$parent.application.logger = new Object(); 90 | $scope.writeLoglevel = function(application) { 91 | ApplicationLogging.setLoglevel(application); 92 | }; 93 | }); 94 | -------------------------------------------------------------------------------- /src/main/webapp/public/scripts/filters/filters.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | 18 | angular.module('springBootAdmin') 19 | .filter('ceil', function() { 20 | return function(input) { 21 | return Math.ceil(input); 22 | } 23 | }) 24 | .filter('floor', function() { 25 | return function(input) { 26 | return Math.floor(input); 27 | } 28 | }) 29 | .filter('padZero', function() { 30 | return function(input) { 31 | var s = input + ""; 32 | while (s.length < 2) s = "0" + s; 33 | return s; 34 | } 35 | }); -------------------------------------------------------------------------------- /src/main/webapp/public/scripts/services/services.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | 18 | angular.module('springBootAdmin.services', ['ngResource']) 19 | .factory('Applications', ['$resource', function($resource) { 20 | return $resource( 21 | '/api/applications', {}, { 22 | query: { method:'GET', isArray:true } 23 | }); 24 | } 25 | ]) 26 | .factory('Application', ['$resource', function($resource) { 27 | return $resource( 28 | '/api/application/:id', {}, { 29 | query: { method:'GET'} 30 | }); 31 | } 32 | ]) 33 | .service('ApplicationOverview', ['$http', function($http) { 34 | this.getVersion = function(app) { 35 | return $http.get(app.url + '/info').success(function(response) { 36 | app.version = response.version; 37 | }).error(function() { 38 | app.version = '---'; 39 | }); 40 | } 41 | this.getHealth = function(app) { 42 | return $http.get(app.url + '/health').success(function(response) { 43 | if (typeof(response) === 'string' && response.indexOf('ok') != -1 44 | || typeof(response.status) === 'string' && 45 | (response.status.indexOf('ok') != -1 || response.status.indexOf('UP') != -1)) { 46 | app.up = true; 47 | } else if (typeof(response.status) === 'string' && response.status.indexOf('DOWN') != -1) { 48 | app.down = true; 49 | } else if (typeof(response.status) === 'string' && response.status.indexOf('OUT-OF-SERVICE') != -1) { 50 | app.outofservice = true; 51 | } else { 52 | app.unknown = true; 53 | } 54 | }).error(function() { 55 | app.offline = true; 56 | }); 57 | } 58 | this.getLogfile = function(app) { 59 | return $http.get(app.url + '/logfile').success(function(response) { 60 | app.providesLogfile = true; 61 | app.urlLogfile = app.url + '/logfile'; 62 | }).error(function() { 63 | app.providesLogfile = false; 64 | app.urlLogfile = null; 65 | }); 66 | } 67 | this.refresh = function(app) { 68 | return $http.post(app.url + '/refresh'); 69 | } 70 | }]) 71 | .service('ApplicationDetails', ['$http', function($http) { 72 | this.getInfo = function(app) { 73 | return $http.get(app.url + '/info').success(function(response) { 74 | app.info = response; 75 | }); 76 | } 77 | this.getMetrics = function(app) { 78 | return $http.get(app.url + '/metrics').success(function(response) { 79 | app.metrics = response; 80 | }); 81 | } 82 | this.getEnv = function(app) { 83 | return $http.get(app.url + '/env').success(function(response) { 84 | app.env = response; 85 | }); 86 | } 87 | this.getProps = function(app) { 88 | return $http.get(app.url + '/env').success(function(response) { 89 | app.props = []; 90 | for (var attr in response) { 91 | if (attr.indexOf('[') != -1 && attr.indexOf('.properties]') != -1) { 92 | var prop = new Object(); 93 | prop.key = attr; 94 | prop.value = response[attr]; 95 | app.props.push(prop); 96 | } 97 | } 98 | }); 99 | } 100 | }]) 101 | .service('ApplicationLogging', ['$http', function($http) { 102 | this.getLoglevel = function(app) { 103 | return $http.get(app.url + 104 | '/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/getLoggerLevel/' + 105 | app.logger.name) 106 | .success(function(response) { 107 | if (response['value'].length > 0) { 108 | app.logger.loglevel = response['value']; 109 | } else { 110 | app.logger.loglevel = ''; 111 | } 112 | }); 113 | } 114 | this.setLoglevel = function(app) { 115 | return $http.get(app.url + 116 | '/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/setLoggerLevel/' + 117 | app.logger.name + '/' + app.logger.loglevel) 118 | .success(function(response) { 119 | if (response['status'] == 200) { 120 | app.logger.success = true; 121 | } else { 122 | app.logger.success = false; 123 | } 124 | }); 125 | } 126 | }]); 127 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/application.css: -------------------------------------------------------------------------------- 1 | #authentication { 2 | line-height: 20px; 3 | padding: 10px 0; 4 | } 5 | 6 | ul.download-links { 7 | list-style-type: none; 8 | margin-left: 0; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f1f1f1; 3 | } 4 | 5 | #scrim { 6 | display: none; 7 | } 8 | #scrim.js-show { 9 | display: block; 10 | position: absolute; 11 | top: 0; 12 | bottom: 0; 13 | left: 0; 14 | right: 0; 15 | z-index: 700; 16 | } 17 | 18 | .container-fluid { 19 | max-width: 1024px; 20 | margin: 0 auto; 21 | } 22 | 23 | .title-divider { 24 | width: 75%; 25 | height: 5px; 26 | background-color: #8CC63F; 27 | margin: 21px 0; 28 | } 29 | .title-divider.full-width { 30 | width: 100%; 31 | } 32 | 33 | .spaced--container { 34 | margin: 60px 0; 35 | } 36 | 37 | .code-snippet { 38 | border-radius: 2px; 39 | border: 1px solid #cccccc; 40 | background-color: #F9F9F9; 41 | padding: 12px 18px; 42 | margin: 15px 0; 43 | font-size: 13px; 44 | line-height: 13px; 45 | } 46 | 47 | .header--navbar { 48 | margin: 0; 49 | } 50 | .header--navbar .navbar-inner { 51 | position: absolute; 52 | z-index: 999; 53 | background-image: none; 54 | filter: none; 55 | background-color: #34302d; 56 | border: none; 57 | border-top: 4px solid #6db33f; 58 | box-shadow: none; 59 | position: relative; 60 | border-radius: 0; 61 | padding: 0; 62 | } 63 | .header--navbar .navbar-inner .spring-logo--container { 64 | display: inline-block; 65 | } 66 | .header--navbar .navbar-inner .spring-logo--container .spring-logo { 67 | margin: 12px 0 6px; 68 | width: 160px; 69 | height: 46px; 70 | display: inline-block; 71 | text-decoration: none; 72 | } 73 | .header--navbar .navbar-inner .navbar-link.active a { 74 | background-color: #6db33f; 75 | box-shadow: none; 76 | } 77 | .header--navbar .navbar-inner .navbar-link a { 78 | color: #eeeeee; 79 | font-family: "Montserrat", sans-serif; 80 | text-transform: uppercase; 81 | text-shadow: none; 82 | font-size: 14px; 83 | line-height: 14px; 84 | padding: 28px 20px; 85 | transition: all 0.15s; 86 | -webkit-transition: all 0.15s; 87 | -moz-transition: all 0.15s; 88 | -o-transition: all 0.15s; 89 | -ms-transition: all 0.15s; 90 | } 91 | .header--navbar .navbar-inner .navbar-link:hover a { 92 | color: #eeeeee; 93 | background-color: #6db33f; 94 | } 95 | .header--navbar .navbar-inner .navbar-link.nav-search { 96 | padding: 20px 0 23px; 97 | } 98 | .header--navbar .navbar-inner .navbar-link.nav-search .navbar-search--icon { 99 | color: #eeeeee; 100 | font-size: 24px; 101 | padding: 3px 16px 3px 18px; 102 | cursor: pointer; 103 | } 104 | .header--navbar .navbar-inner .navbar-link.nav-search:hover .navbar-search--icon { 105 | text-shadow: 0 0 10px #6db33f; 106 | } 107 | .header--navbar .navbar-inner .navbar-link.nav-search .search-input-close { 108 | display: none; 109 | } 110 | .header--navbar .navbar-inner .navbar-link.nav-search.js-highlight { 111 | background-color: #6db33f; 112 | } 113 | .header--navbar .navbar-inner .navbar-link.nav-search.js-highlight .navbar-search--icon { 114 | display: none; 115 | } 116 | .header--navbar .navbar-inner .navbar-link.nav-search.js-highlight .search-input-close { 117 | display: inline-block; 118 | color: #eeeeee; 119 | padding: 4px 24px 3px; 120 | cursor: pointer; 121 | } 122 | .header--navbar .search-dropdown--container { 123 | position: absolute; 124 | z-index: 998; 125 | margin-top: -90px; 126 | background-color: white; 127 | width: 100%; 128 | border-bottom: 1px solid #dddddd; 129 | transition: margin 0.25s; 130 | -webkit-transition: margin 0.25s; 131 | -moz-transition: margin 0.25s; 132 | -o-transition: margin 0.25s; 133 | -ms-transition: margin 0.25s; 134 | } 135 | .header--navbar .search-dropdown--container.no-animation { 136 | transition: none; 137 | -webkit-transition: none; 138 | -moz-transition: none; 139 | -o-transition: none; 140 | -ms-transition: none; 141 | } 142 | .header--navbar .search-dropdown--container .form-search { 143 | margin: 0; 144 | } 145 | .header--navbar .search-dropdown--container .form-search .search-form--form { 146 | background: rgba(0, 0, 0, 0); 147 | border: none; 148 | box-shadow: none; 149 | color: #34302D; 150 | font-size: 21px; 151 | line-height: 29px; 152 | height: 30px; 153 | padding: 25px 0; 154 | width: 93%; 155 | } 156 | .header--navbar .search-dropdown--container .form-search .search-form--form::-webkit-input-placeholder { 157 | font-style: italic; 158 | } 159 | .header--navbar .search-dropdown--container .form-search .search-form--form:-moz-placeholder { 160 | font-style: italic; 161 | } 162 | .header--navbar .search-dropdown--container .form-search .search-form--form::-moz-placeholder { 163 | font-style: italic; 164 | } 165 | .header--navbar .search-dropdown--container .form-search .search-form--form:-ms-input-placeholder { 166 | font-style: italic; 167 | } 168 | .header--navbar .search-dropdown--container .form-search .search-form--submit { 169 | background-color: transparent; 170 | border: none; 171 | float: right; 172 | padding: 28px 16px 24px 0; 173 | } 174 | .header--navbar .search-dropdown--container .form-search .icon-search { 175 | font-size: 22px; 176 | color: #34302d; 177 | } 178 | .header--navbar .search-dropdown--container.js-show { 179 | margin-top: 0; 180 | } 181 | 182 | a.spring-logo { 183 | background: url("../img/spring-logo.png") -1px -1px no-repeat; 184 | } 185 | 186 | a.spring-logo span { 187 | display: block; 188 | width: 160px; 189 | height: 46px; 190 | background: url("../img/spring-logo.png") -1px -48px no-repeat; 191 | opacity: 0; 192 | -moz-transition: opacity 0.12s ease-in-out; 193 | -webkit-transition: opacity 0.12s ease-in-out; 194 | -o-transition: opacity 0.12s ease-in-out; 195 | } 196 | 197 | a:hover.spring-logo span { 198 | opacity: 1; 199 | } 200 | 201 | .right-pane-widget--container { 202 | border: 1px solid #cccccc; 203 | box-shadow: 0 -4px 0 #34302d; 204 | margin: 4px 0 40px 0; 205 | } 206 | .right-pane-widget--container li { 207 | display: block; 208 | padding: 12px 6.2%; 209 | line-height: 14px; 210 | border-top: 1px solid white; 211 | } 212 | .right-pane-widget--container li [class^=icon-] { 213 | font-size: 16px; 214 | } 215 | .right-pane-widget--container li a { 216 | font-size: 14px; 217 | line-height: 14px; 218 | color: #5fa134; 219 | } 220 | .right-pane-widget--container li:first-child { 221 | border-bottom: none; 222 | } 223 | .right-pane-widget--container.secondary-nav li a { 224 | text-transform: uppercase; 225 | color: #888; 226 | font-family: "Montserrat", sans-serif; 227 | font-weight: 400; 228 | text-decoration: none; 229 | } 230 | .right-pane-widget--container.secondary-nav li a:hover { 231 | color: #5fa134; 232 | } 233 | .right-pane-widget--container.secondary-nav li.active a { 234 | font-weight: bold; 235 | color: #34302d; 236 | } 237 | .right-pane-widget--container.with-icon li { 238 | padding: 12px 6.2% 12px 3%; 239 | } 240 | .right-pane-widget--container.with-icon li a { 241 | margin-left: 2%; 242 | } 243 | .right-pane-widget--container.no-top-border { 244 | box-shadow: none; 245 | } 246 | 247 | .header--content { 248 | background-color: #dedede; 249 | padding: 60px 0; 250 | } 251 | .header--content .header--content-subtitle--link { 252 | color: #8cc63f; 253 | } 254 | .header--content .header--content-subtitle--link:hover { 255 | text-decoration: none; 256 | color: #4e681e; 257 | } 258 | 259 | .main-body--wrapper { 260 | margin-top: 100px; 261 | margin-bottom: 100px; 262 | } 263 | 264 | .row--container { 265 | margin-bottom: 60px; 266 | } 267 | 268 | .body--container--row { 269 | margin-bottom: 30px; 270 | } 271 | 272 | .clear { 273 | clear: both; 274 | } 275 | 276 | .body--container--link { 277 | color: #44474c; 278 | } 279 | .body--container--link:hover { 280 | color: #638e2a; 281 | text-decoration: none; 282 | } 283 | .body--container--link.bold { 284 | font-weight: bold; 285 | } 286 | 287 | .list-link--container { 288 | margin: 0; 289 | } 290 | .list-link--container .list-link { 291 | display: block; 292 | } 293 | .list-link--container .list-link a, .list-link--container .list-link div { 294 | color: #333; 295 | cursor: pointer; 296 | } 297 | .list-link--container .list-link a:hover, .list-link--container .list-link div:hover { 298 | color: #8cc63f; 299 | text-decoration: none; 300 | } 301 | 302 | .popover { 303 | border-radius: 1px; 304 | padding: 0; 305 | } 306 | .popover.right { 307 | margin-top: 46px; 308 | } 309 | .popover.right .arrow { 310 | top: 14px; 311 | border-right-color: #8cc63f; 312 | left: -8px; 313 | } 314 | .popover.right .arrow:after { 315 | border-right-color: #8cc63f; 316 | } 317 | .popover.bottom { 318 | margin-top: 5px; 319 | } 320 | .popover.bottom .arrow { 321 | border-bottom-color: #8cc63f; 322 | top: -9px; 323 | left: 15%; 324 | } 325 | .popover.bottom .arrow:after { 326 | border-bottom-color: #8cc63f; 327 | } 328 | .popover .popover-title { 329 | border-radius: 1px 1px 0 0; 330 | background-color: #8cc63f; 331 | color: white; 332 | } 333 | .popover .popover-content { 334 | border-radius: 0 0 1px 1px; 335 | } 336 | 337 | input.floating-input { 338 | background-color: transparent; 339 | border: none; 340 | border-bottom: 1px solid #dedede; 341 | box-shadow: none; 342 | border-radius: 0; 343 | padding: 0 0 0 0; 344 | } 345 | input.floating-input:focus { 346 | border: none; 347 | border-bottom: 1px solid #dedede; 348 | box-shadow: none; 349 | background: #ecebeb; 350 | } 351 | 352 | .floating-input--icon { 353 | font-size: 16px; 354 | color: #c5c5c5; 355 | } 356 | 357 | .bottom-slide--container { 358 | height: 190px; 359 | overflow: hidden; 360 | border-radius: 3px; 361 | background-color: #cccccc; 362 | border: 1px solid #c5c5c5; 363 | box-shadow: 1px 2px 3px rgba(204, 204, 204, 0.5); 364 | } 365 | .bottom-slide--container .bottom-slider--image { 366 | background-image: url("http://imgs.mi9.com/uploads/landscape/2101/beautiful-leaf-wallpapers_1280x960_28083.jpg"); 367 | background-size: cover; 368 | height: 137px; 369 | transition: all 0.33s; 370 | -wekit-transition: all 0.33s; 371 | -moz-transition: all 0.33s; 372 | -o-transition: all 0.33s; 373 | -ms-transition: all 0.33s; 374 | } 375 | .bottom-slide--container .bottom-slider { 376 | background-color: #ecebeb; 377 | padding: 15px 10px; 378 | border-top: 1px solid #9e9e9e; 379 | transition: all 0.33s; 380 | -wekit-transition: all 0.33s; 381 | -moz-transition: all 0.33s; 382 | -o-transition: all 0.33s; 383 | -ms-transition: all 0.33s; 384 | } 385 | .bottom-slide--container .bottom-slider .bottom-slider--title { 386 | display: inline-block; 387 | max-width: 360px; 388 | line-height: 14px; 389 | } 390 | .bottom-slide--container .bottom-slider .bottom-slider--icon { 391 | color: #333; 392 | font-size: 27px; 393 | line-height: 22px; 394 | font-size: 27px; 395 | } 396 | .bottom-slide--container .bottom-slider .bottom-slider--more-info { 397 | opacity: 0; 398 | } 399 | .bottom-slide--container:hover .bottom-slider--image { 400 | transform: translateY(-26px); 401 | -webkit-transform: translateY(-26px); 402 | -moz-transform: translateY(-26px); 403 | -o-transform: translateY(-26px); 404 | -ms-transform: translateY(-26px); 405 | } 406 | .bottom-slide--container:hover .bottom-slider { 407 | transform: translateY(-53px); 408 | -webkit-transform: translateY(-53px); 409 | -moz-transform: translateY(-53px); 410 | -o-transform: translateY(-53px); 411 | -ms-transform: translateY(-53px); 412 | } 413 | .bottom-slide--container:hover .bottom-slider--more-info { 414 | opacity: 1; 415 | } 416 | 417 | .dashboard--posts td { 418 | min-width: 130px; 419 | } 420 | .dashboard--posts td .actions { 421 | min-width: 200px; 422 | } 423 | .dashboard--posts td .actions form { 424 | display: inline; 425 | } 426 | 427 | .blog-post--form input, .blog-post--form textarea { 428 | width: 90%; 429 | } 430 | .blog-post--form textarea { 431 | min-height: 300px; 432 | } 433 | 434 | .blog-post-form--right-pane .control-group.inline label { 435 | display: inline-block; 436 | } 437 | .blog-post-form--right-pane .control-group.inline .controls { 438 | display: inline-block; 439 | vertical-align: middle; 440 | margin-bottom: 5px; 441 | margin-left: 10px; 442 | } 443 | 444 | .gh-project-url--container { 445 | background-color: #FFF; 446 | border: 1px solid #cccccc; 447 | height: 20px; 448 | padding: 4px 6px; 449 | border-radius: 4px; 450 | margin: 12px 0; 451 | } 452 | .gh-project-url--container input { 453 | width: 87%; 454 | background-color: transparent; 455 | border: none; 456 | box-shadow: none; 457 | } 458 | .gh-project-url--container input:focus { 459 | outline: none; 460 | } 461 | .gh-project-url--container .icon--pull-right { 462 | float: right; 463 | margin-top: 2px; 464 | cursor: pointer; 465 | } 466 | 467 | .btn--full-width { 468 | display: block; 469 | } 470 | 471 | .content--title { 472 | display: inline-block; 473 | border-top: 3px solid #6db33f; 474 | font-size: 12px; 475 | line-height: 12px; 476 | text-transform: uppercase; 477 | padding-top: 8px; 478 | position: absolute; 479 | margin-top: -30px; 480 | font-family: "Montserrat", sans-serif; 481 | } 482 | .content--title a { 483 | color: #34302d; 484 | } 485 | .content--title a:hover { 486 | color: #5fa134; 487 | } 488 | .content--title.search-title { 489 | position: relative; 490 | } 491 | .content--title.blog-category { 492 | display: none; 493 | } 494 | .content--title.blog-category.active { 495 | display: block; 496 | } 497 | 498 | .index-page--title { 499 | line-height: 24px; 500 | } 501 | .index-page--title.top-margin { 502 | margin-top: 60px; 503 | } 504 | 505 | .index-page--subtitle { 506 | font-size: 16px; 507 | line-height: 24px; 508 | margin: 30px 0 60px; 509 | } 510 | .index-page--subtitle a { 511 | color: #5fa134; 512 | } 513 | .index-page--subtitle.jobs-subtitle { 514 | margin-bottom: 60px; 515 | padding-bottom: 60px; 516 | border-bottom: 1px solid #cccccc; 517 | } 518 | .index-page--subtitle.with-subtext { 519 | margin-bottom: -30px; 520 | } 521 | .index-page--subtitle.with-subtext .subtext { 522 | display: inline-block; 523 | font-size: 14px; 524 | margin: 21px 0 0; 525 | color: rgba(52, 48, 45, 0.9); 526 | } 527 | 528 | .footer { 529 | background-color: #34302d; 530 | color: #eeeeee; 531 | padding: 30px 0; 532 | } 533 | .footer a { 534 | color: #6db33f; 535 | } 536 | .footer .nav { 537 | margin-bottom: 12px; 538 | } 539 | .footer .nav li a { 540 | color: #eeeeee; 541 | text-shadow: none; 542 | text-transform: uppercase; 543 | font-size: 12px; 544 | line-height: 12px; 545 | padding: 0 17px; 546 | } 547 | .footer .nav li a:hover { 548 | color: #6db33f; 549 | } 550 | .footer .nav li:first-child a { 551 | padding-left: 0; 552 | } 553 | .footer .footer-newsletter--wrapper { 554 | width: auto; 555 | margin: 0; 556 | } 557 | .footer .footer-newsletter--container form { 558 | margin: 0; 559 | } 560 | .footer .footer-newsletter--container label { 561 | text-transform: uppercase; 562 | margin-bottom: 15px; 563 | line-height: 12px; 564 | font-weight: bold; 565 | font-size: 12px; 566 | } 567 | .footer .footer-newsletter--container .footer-subscribe--input--container { 568 | background-color: white; 569 | border: 1px solid #f1f1f1; 570 | } 571 | .footer .footer-newsletter--container .footer-subscribe--input--container .footer-subscribe--input { 572 | background: transparent; 573 | border: none; 574 | box-shadow: none; 575 | font-size: 12px; 576 | padding-left: 10px; 577 | } 578 | .footer .footer-newsletter--container .footer-subscribe--input--container .footer-subscribe--input:focus { 579 | outline: none; 580 | } 581 | .footer .footer-newsletter--container .footer-subscribe--input--container .footer-subscibe--btn { 582 | text-transform: uppercase; 583 | background-color: #6db33f; 584 | background-image: none; 585 | border-radius: 0; 586 | border: none; 587 | box-shadow: none; 588 | color: #eeeeee; 589 | text-shadow: none; 590 | font-size: 12px; 591 | padding: 9px 16px; 592 | } 593 | 594 | .item-dropdown-widget { 595 | text-align: left; 596 | z-index: 800; 597 | position: relative; 598 | } 599 | .item-dropdown-widget .item-dropdown--link { 600 | cursor: pointer; 601 | } 602 | .item-dropdown-widget .item-dropdown--link .item-dropdown--title { 603 | padding: 14px 0 14px 20px; 604 | font-size: 14px; 605 | line-height: 14px; 606 | } 607 | .item-dropdown-widget .item-dropdown--link .item-dropdown--icon { 608 | color: #cbcaca; 609 | padding: 14px; 610 | margin: 0; 611 | } 612 | .item-dropdown-widget .item-dropdown--link:hover .item-dropdown--title, .item-dropdown-widget .item-dropdown--link:hover .item-dropdown--icon { 613 | color: #5fa134; 614 | } 615 | .item-dropdown-widget .item--dropdown { 616 | display: none; 617 | } 618 | .item-dropdown-widget.js-open { 619 | z-index: 801; 620 | } 621 | .item-dropdown-widget.js-open .item-dropdown--title { 622 | color: #f1f1f1; 623 | background-color: #6db33f; 624 | position: relative; 625 | z-index: 802; 626 | } 627 | .item-dropdown-widget.js-open .item-dropdown--icon { 628 | color: #dddddd; 629 | background-color: #34302d; 630 | position: relative; 631 | z-index: 803; 632 | } 633 | .item-dropdown-widget.js-open .item--dropdown { 634 | display: block; 635 | } 636 | .item-dropdown-widget.js-open .item-dropdown--link:hover .item-dropdown--title { 637 | color: #f1f1f1; 638 | } 639 | .item-dropdown-widget.js-open .item-dropdown--link:hover .item-dropdown--icon { 640 | color: #ddd; 641 | } 642 | 643 | .item--dropdown { 644 | position: absolute; 645 | background-color: #34302d; 646 | padding: 20px; 647 | width: 255px; 648 | z-index: 999; 649 | border: 1px solid #eeeeee; 650 | border-top: none; 651 | margin-left: -1px; 652 | } 653 | .item--dropdown .item--header { 654 | padding-bottom: 20px; 655 | border-bottom: 1px solid #cccbca; 656 | } 657 | .item--dropdown .item--header .item--header-link { 658 | color: #6db33f; 659 | } 660 | .item--dropdown .item--header .item--header-link [class^=icon-] { 661 | color: #93918f; 662 | font-size: 20px; 663 | vertical-align: middle; 664 | padding-right: 10px; 665 | } 666 | .item--dropdown .item--header .item--header-link:hover { 667 | text-decoration: none; 668 | color: #7ac04c; 669 | } 670 | .item--dropdown .item--header .item--header-link:hover [class^=icon-] { 671 | color: #f1f1f1; 672 | } 673 | .item--dropdown .item--header .item--header-link.disabled { 674 | opacity: 0.5; 675 | cursor: not-allowed; 676 | } 677 | .item--dropdown .item--header .item--header-link.disabled:hover { 678 | color: #6db33f; 679 | } 680 | .item--dropdown .item--header .item--header-link.disabled:hover [class^=icon-] { 681 | color: #93918f; 682 | } 683 | .item--dropdown .item--left-column, .item--dropdown .item--right-column { 684 | width: 45%; 685 | display: inline-block; 686 | vertical-align: top; 687 | } 688 | .item--dropdown .item--half-column { 689 | display: inline-block; 690 | vertical-align: top; 691 | width: 48%; 692 | } 693 | .item--dropdown .item--half-column:first-child .item--body-title, .item--dropdown .item--half-column:first-child .item--body--version { 694 | padding-right: 15px; 695 | } 696 | .item--dropdown .item--half-column:nth-child(2) .item--body-title, .item--dropdown .item--half-column:nth-child(2) .item--body--version { 697 | padding-left: 15px; 698 | } 699 | .item--dropdown .item--half-column:nth-child(3) { 700 | margin-top: 20px; 701 | } 702 | .item--dropdown .item--body-title { 703 | padding: 30px 0 15px 0; 704 | color: #f1f1f1; 705 | border-bottom: 1px solid #4a4440; 706 | font-size: 12px; 707 | line-height: 12px; 708 | text-transform: uppercase; 709 | } 710 | .item--dropdown .item--body .item--body--version { 711 | border-bottom: 1px solid #4a4540; 712 | padding: 7px 0; 713 | } 714 | .item--dropdown .item--body .item--body--version p { 715 | color: #f1f1f1; 716 | font-size: 14px; 717 | line-height: 19px; 718 | } 719 | .item--dropdown .item--body .item--body--version .docs-link { 720 | display: inline-block; 721 | color: #6db33f; 722 | } 723 | .item--dropdown .item--body .item--body--version .docs-link:hover { 724 | color: #7ac04c; 725 | } 726 | .item--dropdown .item--body .item--body--version .docs-link.reference-link { 727 | border-right: 1px solid #4a4540; 728 | padding-right: 10px; 729 | margin-right: 5px; 730 | } 731 | .item--dropdown.no-item--header .item--body-title { 732 | padding-top: 0; 733 | border-bottom: 1px solid #cccbca; 734 | } 735 | .item--dropdown.no-item--header .tool-download-link .file-type { 736 | color: #5fa134; 737 | line-height: 14px; 738 | font-size: 14px; 739 | display: inline-block; 740 | } 741 | .item--dropdown.no-item--header .tool-download-link .file-size { 742 | color: #6c635d; 743 | font-size: 10px; 744 | line-height: 23px; 745 | float: right; 746 | } 747 | 748 | .item-slider-widget { 749 | display: inline-block; 750 | margin-left: 9px; 751 | } 752 | .item-slider-widget .item--slider { 753 | height: 32px; 754 | position: absolute; 755 | width: 62px; 756 | border-radius: 2px; 757 | margin-top: -4px; 758 | z-index: 800; 759 | background: #68605a; 760 | background: -moz-linear-gradient(top, #68605a 0%, #34302d 100%, #020202 100%); 761 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #68605a), color-stop(100%, #34302d), color-stop(100%, #020202)); 762 | background: -webkit-linear-gradient(top, #68605a 0%, #34302d 100%, #020202 100%); 763 | background: -o-linear-gradient(top, #68605a 0%, #34302d 100%, #020202 100%); 764 | background: -ms-linear-gradient(top, #68605a 0%, #34302d 100%, #020202 100%); 765 | background: linear-gradient(to bottom, #68605a 0%, #34302d 100%, #020202 100%); 766 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#68605a', endColorstr='#020202',GradientType=0 ); 767 | cursor: pointer; 768 | transition: all 0.25s; 769 | -webkit-transition: all 0.25s; 770 | -moz-webkit-transition: all 0.25s; 771 | -ms--webkit-transition: all 0.25s; 772 | -o-webkit-transition: all 0.25s; 773 | } 774 | .item-slider-widget .item-slider--container { 775 | background-color: white; 776 | border: 1px solid #999999; 777 | padding: 0 3px; 778 | } 779 | .item-slider-widget .item-slider--container .item { 780 | display: inline-block; 781 | color: #c3c3c3; 782 | font-family: "Montserrat", sans-serif; 783 | font-size: 12px; 784 | line-height: 12px; 785 | text-transform: uppercase; 786 | padding: 7px 6px 5px; 787 | cursor: pointer; 788 | transition: color 0.25s; 789 | -webkit-transition: color 0.25s; 790 | -moz-webkit-transition: color 0.25s; 791 | -ms--webkit-transition: color 0.25s; 792 | -o-webkit-transition: color 0.25s; 793 | } 794 | .item-slider-widget .item-slider--container .item:hover { 795 | color: black; 796 | } 797 | .item-slider-widget .item-slider--container .item.js-active { 798 | z-index: 801; 799 | position: relative; 800 | color: #f1f1f1; 801 | } 802 | 803 | .content-container--wrapper { 804 | border: 1px solid #34302d; 805 | margin-top: -1px; 806 | } 807 | .content-container--wrapper:first-child { 808 | margin: 0; 809 | } 810 | .content-container--wrapper .content-container--title { 811 | font-size: 14px; 812 | line-height: 14px; 813 | text-transform: uppercase; 814 | background-color: #34302D; 815 | color: #FFF; 816 | padding: 18px 0; 817 | width: 327px; 818 | text-align: center; 819 | margin: 0; 820 | } 821 | .content-container--wrapper .content-container--header { 822 | background-color: #34302D; 823 | } 824 | .content-container--wrapper .content-container--header .content-container--filter { 825 | border-radius: 0; 826 | box-shadow: none; 827 | margin: 9px 20px; 828 | font-size: 18px; 829 | line-height: 22px; 830 | padding: 8px; 831 | width: 291px; 832 | } 833 | .content-container--wrapper .content-container--header .content-container--filter::-webkit-input-placeholder { 834 | color: #999; 835 | } 836 | .content-container--wrapper .content-container--header .content-container--filter:-moz-placeholder { 837 | color: #999; 838 | } 839 | .content-container--wrapper .content-container--header .content-container--filter::-moz-placeholder { 840 | color: #999; 841 | } 842 | .content-container--wrapper .content-container--header .content-container--filter:-ms-input-placeholder { 843 | color: #999; 844 | } 845 | .content-container--wrapper .content-container--header .header-title { 846 | color: #eeeeee; 847 | display: inline-block; 848 | margin: 10px; 849 | } 850 | .content-container--wrapper .content-container--header .btn { 851 | margin: 6px; 852 | } 853 | .content-container--wrapper .content-container--body { 854 | padding: 20px; 855 | border-top: 1px solid white; 856 | } 857 | .content-container--wrapper .content-container--body:first-child { 858 | border-top: none; 859 | } 860 | .content-container--wrapper .content-container--body .content-section-title--container { 861 | margin: 20px 0 40px; 862 | } 863 | .content-container--wrapper .content-container--body .icon-gs-guides, .content-container--wrapper .content-container--body .icon-tutorial { 864 | margin-right: 20px; 865 | } 866 | .content-container--wrapper .content-container--body .content-section-title { 867 | font-family: "Montserrat", sans-serif; 868 | font-size: 16px; 869 | line-height: 16px; 870 | margin-bottom: 20px; 871 | padding-top: 10px; 872 | font-weight: 400; 873 | } 874 | .content-container--wrapper .content-container--body .content-section-sub-title { 875 | padding-right: 50px; 876 | } 877 | .content-container--wrapper .content-items--container { 878 | padding: 20px; 879 | } 880 | 881 | .billboard--wrapper .billboard--container { 882 | padding: 80px 0; 883 | } 884 | .billboard--wrapper .billboard--container .billboard--title, .billboard--wrapper .billboard--container .billboard--sub-title { 885 | background: rgba(52, 48, 45, 0.8); 886 | color: #eeeeee; 887 | display: inline-block; 888 | } 889 | .billboard--wrapper .billboard--container .billboard--title { 890 | font-size: 48px; 891 | line-height: 68px; 892 | font-family: "Montserrat", sans-serif; 893 | padding: 0 15px; 894 | letter-spacing: -2px; 895 | } 896 | .billboard--wrapper .billboard--container .billboard--sub-title--container .billboard--sub-title { 897 | font-size: 18px; 898 | line-height: 18px; 899 | padding: 2px 19px 12px; 900 | } 901 | .billboard--wrapper .billboard--container .billboard--sub-title--container .billboard--sub-title:first-child { 902 | padding-top: 12px; 903 | } 904 | .billboard--wrapper .billboard--container.center-text { 905 | position: absolute; 906 | z-index: 600; 907 | width: 100%; 908 | margin: 0 auto; 909 | margin-top: 80px; 910 | text-align: center; 911 | } 912 | .billboard--wrapper .billboard-bg { 913 | height: 425px; 914 | border-bottom: 1px solid #34302d; 915 | background-repeat: no-repeat; 916 | background-size: cover; 917 | } 918 | .billboard--wrapper .billboard-bg.about-us--bg { 919 | background-image: url("../img/about-us-bg.png"); 920 | } 921 | .billboard--wrapper .billboard-bg.jobs--bg { 922 | background-image: url("../img/jobs-bg.png"); 923 | } 924 | 925 | .billboard-body--wrapper { 926 | padding: 0 0 100px; 927 | } 928 | 929 | .admin-profile--form .admin-profile--field { 930 | padding: 7px 0; 931 | } 932 | .admin-profile--form .admin-profile--field label { 933 | display: inline-block; 934 | width: 250px; 935 | font-family: "Montserrat", sans-serif; 936 | vertical-align: middle; 937 | } 938 | .admin-profile--form .admin-profile--field input, .admin-profile--form .admin-profile--field textarea { 939 | width: 30%; 940 | margin: 0; 941 | } 942 | .admin-profile--form .admin-profile--field .push-right { 943 | margin-left: 254px; 944 | } 945 | .admin-profile--form .admin-profile--field .image-note { 946 | display: inline-block; 947 | width: 10%; 948 | vertical-align: bottom; 949 | margin-left: 1%; 950 | font-size: .9em; 951 | line-height: 1.2em; 952 | } 953 | .admin-profile--form .admin-profile--field .note { 954 | display: inline-block; 955 | vertical-align: middle; 956 | color: #666; 957 | margin-left: 15px; 958 | font-size: 12px; 959 | } 960 | .admin-profile--form .admin-profile--field.bio label { 961 | vertical-align: top; 962 | } 963 | .admin-profile--form .admin-profile--field.bio textarea { 964 | height: 150px; 965 | } 966 | .admin-profile--form .admin-profile--field.submit-form { 967 | margin-top: 30px; 968 | } 969 | 970 | .placeholder-img { 971 | background-color: #D5D5D5; 972 | border: 3px solid white; 973 | padding: 77px 0; 974 | margin-bottom: 30px; 975 | text-align: center; 976 | color: #999; 977 | text-shadow: 0 1px 2px white; 978 | font-size: 18px; 979 | font-weight: bold; 980 | font-family: "Montserrat", sans-serif; 981 | } 982 | 983 | form.lpeRegForm { 984 | border: 1px solid #f1f1f1; 985 | background-color: white; 986 | } 987 | 988 | form.lpeRegForm li { 989 | padding: 0; 990 | list-style-type: none; 991 | } 992 | 993 | form.lpeRegForm ul input[type='text'] { 994 | background: transparent; 995 | border: none; 996 | box-shadow: none; 997 | font-size: 12px; 998 | padding: 12px 0 15px 10px; 999 | font-family: "Varela Round", sans-serif; 1000 | color: #666; 1001 | font-weight: 400; 1002 | } 1003 | 1004 | #mktFrmButtons { 1005 | margin: -78px -3px 0 222px; 1006 | } 1007 | 1008 | #mktFrmSubmit { 1009 | text-transform: uppercase; 1010 | background-color: #6db33f; 1011 | background-image: none; 1012 | border-radius: 0; 1013 | border: none; 1014 | box-shadow: none; 1015 | color: #eeeeee; 1016 | text-shadow: none; 1017 | font-size: 12px; 1018 | padding: 9px 16px !important; 1019 | height: 40px; 1020 | cursor: pointer; 1021 | } 1022 | 1023 | form.lpeRegForm label { 1024 | display: none; 1025 | } 1026 | 1027 | .lpeCElement { 1028 | left: 0 !important; 1029 | top: 0 !important; 1030 | } 1031 | 1032 | form.lpeRegForm ul { 1033 | font-size: 12px; 1034 | color: black; 1035 | font-family: Arial, Helvetica, sans-serif; 1036 | } 1037 | 1038 | form.lpeRegForm ul input, form.lpeRegForm ul input[type='text'], form.lpeRegForm ul textarea, form.lpeRegForm ul select { 1039 | font-size: 12px; 1040 | color: black; 1041 | font-family: Arial, Helvetica, sans-serif; 1042 | } 1043 | 1044 | form.lpeRegForm li { 1045 | margin-bottom: 10px; 1046 | } 1047 | 1048 | form.lpeRegForm .mktInput { 1049 | padding-left: 0px; 1050 | } 1051 | 1052 | .about-feature--container { 1053 | background-color: #34302d; 1054 | text-align: center; 1055 | padding: 40px 20px; 1056 | height: 409px; 1057 | transition: border, box-shadow 0.2s; 1058 | -webkit-transition: border, box-shadow 0.2s; 1059 | -moz-transition: border, box-shadow 0.2s; 1060 | -ms-transition: border, box-shadow 0.2s; 1061 | -o-transition: border, box-shadow 0.2s; 1062 | border: 1px solid transparent; 1063 | } 1064 | .about-feature--container .icon--container { 1065 | height: 200px; 1066 | display: inline-block; 1067 | } 1068 | .about-feature--container .icon--container .icon { 1069 | vertical-align: middle; 1070 | position: relative; 1071 | top: 50%; 1072 | } 1073 | .about-feature--container .icon--container .icon.icon-spring-logo-big { 1074 | margin-top: -77px; 1075 | } 1076 | .about-feature--container .icon--container .icon.icon-spring-logo { 1077 | margin-top: -20px; 1078 | } 1079 | .about-feature--container .title, .about-feature--container .text, .about-feature--container .link { 1080 | color: #f1f1f1; 1081 | } 1082 | .about-feature--container .title { 1083 | font-size: 18px; 1084 | font-family: "Montserrat", sans-serif; 1085 | line-height: 24px; 1086 | margin: 30px 0 20px; 1087 | text-transform: uppercase; 1088 | } 1089 | .about-feature--container .text { 1090 | font-size: 14px; 1091 | line-height: 20px; 1092 | margin-bottom: 20px; 1093 | } 1094 | .about-feature--container .link { 1095 | font-family: "Montserrat", sans-serif; 1096 | text-transform: uppercase; 1097 | font-size: 14px; 1098 | line-height: 20px; 1099 | } 1100 | .about-feature--container:hover { 1101 | text-decoration: none; 1102 | box-shadow: 0 0 9px #6db33f; 1103 | border-color: #6db33f; 1104 | } 1105 | .about-feature--container:active { 1106 | box-shadow: inset 0 6px 10px #191715; 1107 | } 1108 | .about-feature--container.no-link { 1109 | box-shadow: none; 1110 | border: none; 1111 | } 1112 | 1113 | .job-category--container { 1114 | padding-right: 95px; 1115 | } 1116 | .job-category--container .job-category--title { 1117 | font-family: "Montserrat", sans-serif; 1118 | text-transform: uppercase; 1119 | font-size: 16px; 1120 | line-height: 20px; 1121 | margin-bottom: 10px; 1122 | } 1123 | .job-category--container .job--container { 1124 | margin-bottom: 20px; 1125 | } 1126 | .job-category--container .job--container .title { 1127 | color: #5fa134; 1128 | font-size: 16px; 1129 | line-height: 20px; 1130 | } 1131 | 1132 | .job-post--container { 1133 | padding-bottom: 60px; 1134 | margin-bottom: 55px; 1135 | border-bottom: 1px solid white; 1136 | } 1137 | .job-post--container a { 1138 | color: #5fa134; 1139 | } 1140 | .job-post--container .job-post--title { 1141 | margin-bottom: 5px; 1142 | } 1143 | .job-post--container .job-post--location { 1144 | font-size: 16px; 1145 | line-height: 20px; 1146 | margin-bottom: 20px; 1147 | } 1148 | .job-post--container .job-post--details p { 1149 | font-size: 14px; 1150 | line-height: 20px; 1151 | margin-bottom: 20px; 1152 | } 1153 | .job-post--container .job-post--details ul { 1154 | margin-bottom: 30px; 1155 | } 1156 | .job-post--container .job-post--details ul li { 1157 | line-height: 22px; 1158 | margin-bottom: 5px; 1159 | } 1160 | .job-post--container .apply-now-link { 1161 | text-decoration: uppercase; 1162 | } 1163 | .job-post--container:last-child { 1164 | padding-bottom: 0; 1165 | margin-bottom: 0; 1166 | border-bottom: none; 1167 | } 1168 | 1169 | .admin--wrapper { 1170 | text-align: center; 1171 | } 1172 | .admin--wrapper .alert, .admin--wrapper .admin-login--container { 1173 | text-align: left; 1174 | } 1175 | .admin--wrapper .admin-login--container { 1176 | background-color: white; 1177 | padding: 50px; 1178 | display: inline-block; 1179 | border-bottom: 1px solid #cccccc; 1180 | box-shadow: 0 0 3px #cccccc; 1181 | margin-top: 100px; 1182 | } 1183 | .admin--wrapper .admin-login--container .admin-login--title { 1184 | font-size: 24px; 1185 | line-height: 24px; 1186 | margin-bottom: 40px; 1187 | } 1188 | .admin--wrapper .admin-login--container form { 1189 | margin: 0; 1190 | text-align: center; 1191 | } 1192 | .admin--wrapper .admin-login--container form button { 1193 | font-size: 20px; 1194 | font-family: "Varela Round", sans-serif; 1195 | } 1196 | .admin--wrapper .admin-login--container form button i { 1197 | font-size: 30px; 1198 | float: left; 1199 | margin: -10px 15px -10px 0; 1200 | } 1201 | 1202 | .admin-index--title { 1203 | margin-bottom: 30px; 1204 | } 1205 | .admin-index--title.back-btn { 1206 | margin-top: -60px; 1207 | } 1208 | 1209 | .admin-index--link { 1210 | background-color: white; 1211 | padding: 50px 0; 1212 | display: inline-block; 1213 | border-bottom: 1px solid #cccccc; 1214 | box-shadow: 0 0 3px #cccccc; 1215 | text-align: center; 1216 | width: 95%; 1217 | transition: all 0.25s; 1218 | -webkit-transition: all 0.25s; 1219 | -moz-transition: all 0.25s; 1220 | -o-transition: all 0.25s; 1221 | -ms-transition: all 0.25s; 1222 | } 1223 | .admin-index--link .icon--container { 1224 | width: 190px; 1225 | height: 190px; 1226 | border-radius: 190px; 1227 | background-color: #34302d; 1228 | display: inline-block; 1229 | text-align: center; 1230 | margin-bottom: 30px; 1231 | } 1232 | .admin-index--link .icon--container .icon { 1233 | margin: 22px 6px; 1234 | } 1235 | .admin-index--link .icon--container i { 1236 | color: #FFF; 1237 | font-size: 150px; 1238 | margin-top: 11px; 1239 | display: inline-block; 1240 | } 1241 | .admin-index--link p { 1242 | font-size: 20px; 1243 | } 1244 | .admin-index--link:hover { 1245 | box-shadow: 0 0 3px #6db33f; 1246 | text-decoration: none; 1247 | } 1248 | .admin-index--link:active { 1249 | box-shadow: none; 1250 | } 1251 | 1252 | .white-btn--container { 1253 | background-color: white; 1254 | padding: 50px 0; 1255 | display: inline-block; 1256 | border-bottom: 1px solid #cccccc; 1257 | box-shadow: 0 0 3px #cccccc; 1258 | text-align: center; 1259 | width: 95%; 1260 | transition: all 0.25s; 1261 | -webkit-transition: all 0.25s; 1262 | -moz-transition: all 0.25s; 1263 | -o-transition: all 0.25s; 1264 | -ms-transition: all 0.25s; 1265 | } 1266 | .white-btn--container .icon--container { 1267 | width: 190px; 1268 | height: 190px; 1269 | border-radius: 190px; 1270 | background-color: #34302d; 1271 | display: inline-block; 1272 | text-align: center; 1273 | margin-bottom: 30px; 1274 | } 1275 | .white-btn--container .icon--container .icon { 1276 | margin: 22px 6px; 1277 | } 1278 | .white-btn--container .icon--container i { 1279 | color: #FFF; 1280 | font-size: 150px; 1281 | margin-top: 11px; 1282 | display: inline-block; 1283 | } 1284 | .white-btn--container .icon--container.white-bg { 1285 | background-color: white; 1286 | } 1287 | .white-btn--container p { 1288 | font-size: 20px; 1289 | } 1290 | .white-btn--container:hover { 1291 | box-shadow: 0 0 3px #6db33f; 1292 | text-decoration: none; 1293 | } 1294 | .white-btn--container:active { 1295 | box-shadow: none; 1296 | } 1297 | 1298 | .pagination { 1299 | margin: -1px 0 0 0; 1300 | } 1301 | .pagination div, .pagination a { 1302 | display: inline-block; 1303 | color: #888; 1304 | text-decoration: none; 1305 | vertical-align: middle; 1306 | font-size: 16px; 1307 | } 1308 | .pagination div.next, .pagination div.previous, .pagination a.next, .pagination a.previous { 1309 | padding: 10px; 1310 | font-size: 14px; 1311 | line-height: 14px; 1312 | } 1313 | .pagination div div, .pagination div a, .pagination a div, .pagination a a { 1314 | padding: 10px; 1315 | color: #888; 1316 | } 1317 | .pagination div div.active, .pagination div a.active, .pagination a div.active, .pagination a a.active { 1318 | color: #34302d; 1319 | border-top: 2px solid #34302d; 1320 | padding-top: 8px; 1321 | } 1322 | .pagination div:hover, .pagination a:hover { 1323 | color: #34302d; 1324 | } 1325 | 1326 | .usage-lists { 1327 | margin: 0; 1328 | list-style: none; 1329 | } 1330 | .usage-lists li { 1331 | margin-bottom: 5px; 1332 | } 1333 | .usage-lists [class^="icon-"] { 1334 | margin-right: 10px; 1335 | } 1336 | .usage-lists.okay [class^="icon-"] { 1337 | color: #6db33f; 1338 | } 1339 | .usage-lists.not-okay [class^="icon-"] { 1340 | color: #BA4160; 1341 | } 1342 | 1343 | .min-height { 1344 | min-height: 900px; 1345 | } 1346 | 1347 | .platform-index--header { 1348 | padding: 100px 0; 1349 | position: relative; 1350 | } 1351 | .platform-index--header:after { 1352 | content: ""; 1353 | opacity: 0.1; 1354 | background: url("../img/platform-bg.png") 100% 50% no-repeat; 1355 | position: absolute; 1356 | top: 0; 1357 | left: 0; 1358 | right: 0; 1359 | bottom: 0; 1360 | background-size: 55%; 1361 | z-index: -1; 1362 | } 1363 | .platform-index--header .index-page--subtitle { 1364 | margin-bottom: 0; 1365 | padding-right: 20px; 1366 | } 1367 | .platform-index--header .platform-watermark--large { 1368 | background: url("../img/spring-platform-watermark-large.png") 50% no-repeat; 1369 | width: 100%; 1370 | height: 156px; 1371 | background-size: 100%; 1372 | } 1373 | 1374 | .platform-stack--wrapper { 1375 | background: url("../img/platform-diagram-noise.png") #34302d; 1376 | box-shadow: inset 0 15px 30px #191715, inset 0 -15px 30px #191715; 1377 | text-align: center; 1378 | padding: 60px 0; 1379 | } 1380 | 1381 | .platform-project { 1382 | display: inline-block; 1383 | clear: both; 1384 | } 1385 | .platform-project .project--container { 1386 | float: left; 1387 | padding: 20px 10px; 1388 | height: 300px; 1389 | box-sizing: border-box; 1390 | width: 24.153%; 1391 | margin: 40px 1% 40px 0; 1392 | } 1393 | @-moz-document url-prefix() { 1394 | .platform-project .project--container { 1395 | width: 22%; 1396 | } 1397 | } 1398 | .platform-project .project--container:last-child { 1399 | margin-right: 0; 1400 | } 1401 | .platform-project .project-icon { 1402 | font-size: 98px; 1403 | margin-bottom: 20px; 1404 | } 1405 | .platform-project .project--title { 1406 | margin-bottom: 10px; 1407 | text-transform: none; 1408 | } 1409 | .platform-project .description { 1410 | width: 48.306%; 1411 | float: left; 1412 | margin-top: 20px; 1413 | margin-right: 1%; 1414 | padding-right: 1%; 1415 | } 1416 | 1417 | .error-page { 1418 | text-align: center; 1419 | } 1420 | .error-page img { 1421 | display: inline-block; 1422 | } 1423 | .error-page .error-title { 1424 | font-size: 24px; 1425 | line-height: 24px; 1426 | margin: 30px 0 0; 1427 | } 1428 | .error-page .big-search--container { 1429 | display: inline-block; 1430 | width: 64%; 1431 | background-color: white; 1432 | text-align: left; 1433 | margin: 50px 0 40px; 1434 | padding: 0 40px; 1435 | position: relative; 1436 | } 1437 | .error-page .big-search--container input { 1438 | background-color: transparent; 1439 | border: none; 1440 | box-shadow: none; 1441 | font-size: 24px; 1442 | line-height: 30px; 1443 | padding: 26px 0; 1444 | width: 95%; 1445 | display: inline-block; 1446 | } 1447 | .error-page .big-search--container button { 1448 | position: absolute; 1449 | right: 0; 1450 | top: 0; 1451 | background-color: transparent; 1452 | border: none; 1453 | padding: 25px 28px; 1454 | font-size: 20px; 1455 | color: #666; 1456 | } 1457 | .error-page .error-bottom-link { 1458 | font-size: 14px; 1459 | color: #5fa134; 1460 | text-decoration: none; 1461 | } 1462 | .error-page .error-bottom-link .social-icon.twitter { 1463 | background-image: url("../img/iconsprite.png"); 1464 | background-repeat: no-repeat; 1465 | display: inline-block; 1466 | width: 36px; 1467 | height: 36px; 1468 | background-position: -1px -143px; 1469 | vertical-align: middle; 1470 | margin-right: 15px; 1471 | } 1472 | .error-page .error-bottom-link:hover { 1473 | color: #6ab43a; 1474 | } 1475 | .error-page .error-bottom-link:hover .twitter { 1476 | background-position: -84px -143px; 1477 | } 1478 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/buttons.css: -------------------------------------------------------------------------------- 1 | .btn.btn-black { 2 | background-color: #34302d; 3 | background-image: none; 4 | border-radius: 0; 5 | color: #f1f1f1; 6 | font-size: 14px; 7 | line-height: 14px; 8 | font-family: "Montserrat", sans-serif; 9 | border: 2px solid #6db33f; 10 | padding: 21px 60px; 11 | text-shadow: none; 12 | transition: border 0.15s; 13 | -webkit-transition: border 0.15s; 14 | -moz-transition: border 0.15s; 15 | -o-transition: border 0.15s; 16 | -ms-transition: border 0.15s; 17 | } 18 | .btn.btn-black.compact { 19 | padding: 6px 12px; 20 | font-size: 12px; 21 | } 22 | .btn.btn-black.sub-text { 23 | padding: 12px 0; 24 | } 25 | .btn.btn-black.sub-text p { 26 | margin-top: 6px; 27 | color: #eeeeee; 28 | font-size: 14px; 29 | line-height: 14px; 30 | font-family: "Montserrat", sans-serif; 31 | text-transform: none; 32 | } 33 | .btn.btn-black.on-black:hover { 34 | border-color: #6db33f; 35 | box-shadow: 0 1px 3px #0b0a0a; 36 | } 37 | .btn.btn-black.on-black:active { 38 | box-shadow: 0 1px 3px #0b0a0a, inset 0 3px 6px #0b0a0a; 39 | border-color: #6db33f; 40 | } 41 | .btn.btn-black.with-icon [class^="icon-"] { 42 | margin-right: 21px; 43 | font-size: 20px; 44 | vertical-align: top; 45 | line-height: 10px; 46 | } 47 | .btn.btn-black:hover { 48 | border-color: #34302d; 49 | box-shadow: none; 50 | text-decoration: none; 51 | } 52 | .btn.btn-black:active { 53 | box-shadow: inset 0 3px 6px #0b0a0a; 54 | border-color: #34302d; 55 | } 56 | .btn.uppercase { 57 | text-transform: uppercase; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/fontcustom.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Font Custom: bare CSS 3 | */ 4 | 5 | @font-face { 6 | font-family: "fontcustom"; 7 | src: url("../font-custom/fontcustom.eot"); 8 | src: url("../font-custom/fontcustom.eot?#iefix") format("embedded-opentype"), 9 | url("../font-custom/fontcustom.woff") format("woff"), 10 | url("../font-custom/fontcustom.ttf") format("truetype"), 11 | url("../font-custom/fontcustom.svg#fontcustom") format("svg"); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | .icon-community-support, 17 | .icon-gs-guides-black, 18 | .icon-professional-support, 19 | .icon-spring-amqp, 20 | .icon-spring-android, 21 | .icon-spring-batch, 22 | .icon-spring-data, 23 | .icon-spring-framework, 24 | .icon-spring-hateos, 25 | .icon-spring-integration, 26 | .icon-spring-leaf, 27 | .icon-spring-logo, 28 | .icon-spring-mobile, 29 | .icon-spring-security, 30 | .icon-spring-social, 31 | .icon-spring-web-flow, 32 | .icon-spring-web-services, 33 | .icon-tutorials-black { 34 | font-family: "fontcustom" !important; 35 | font-style: normal; 36 | font-weight: normal; 37 | font-variant: normal; 38 | text-transform: none; 39 | line-height: 1; 40 | -webkit-font-smoothing: antialiased; 41 | display: inline-block !important; 42 | text-decoration: inherit; 43 | } 44 | 45 | .icon-community-support:before { content: "\f100"; } 46 | .icon-gs-guides-black:before { content: "\f101"; } 47 | .icon-professional-support:before { content: "\f102"; } 48 | .icon-spring-amqp:before { content: "\f103"; } 49 | .icon-spring-android:before { content: "\f104"; } 50 | .icon-spring-batch:before { content: "\f105"; } 51 | .icon-spring-data:before { content: "\f106"; } 52 | .icon-spring-framework:before { content: "\f107"; } 53 | .icon-spring-hateos:before { content: "\f108"; } 54 | .icon-spring-integration:before { content: "\f109"; } 55 | .icon-spring-leaf:before { content: "\f10a"; } 56 | .icon-spring-logo:before { content: "\f10b"; } 57 | .icon-spring-mobile:before { content: "\f10c"; } 58 | .icon-spring-security:before { content: "\f10d"; } 59 | .icon-spring-social:before { content: "\f10e"; } 60 | .icon-spring-web-flow:before { content: "\f10f"; } 61 | .icon-spring-web-services:before { content: "\f110"; } 62 | .icon-tutorials-black:before { content: "\f111"; } 63 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/highlight.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { 2 | background-color: #ffffcc; 3 | } 4 | 5 | .highlight .c { 6 | color: #999988; 7 | font-style: italic; 8 | } 9 | 10 | /* Comment */ 11 | .highlight .err { 12 | color: #a61717; 13 | background-color: #e3d2d2; 14 | } 15 | 16 | /* Error */ 17 | .highlight .k { 18 | color: #000000; 19 | font-weight: bold; 20 | } 21 | 22 | /* Keyword */ 23 | .highlight .o { 24 | color: #000000; 25 | font-weight: bold; 26 | } 27 | 28 | /* Operator */ 29 | .highlight .cm { 30 | color: #999988; 31 | font-style: italic; 32 | } 33 | 34 | /* Comment.Multiline */ 35 | .highlight .cp { 36 | color: #999999; 37 | font-weight: bold; 38 | font-style: italic; 39 | } 40 | 41 | /* Comment.Preproc */ 42 | .highlight .c1 { 43 | color: #999988; 44 | font-style: italic; 45 | } 46 | 47 | /* Comment.Single */ 48 | .highlight .cs { 49 | color: #999999; 50 | font-weight: bold; 51 | font-style: italic; 52 | } 53 | 54 | /* Comment.Special */ 55 | .highlight .gd { 56 | color: #000000; 57 | background-color: #ffdddd; 58 | } 59 | 60 | /* Generic.Deleted */ 61 | .highlight .ge { 62 | color: #000000; 63 | font-style: italic; 64 | } 65 | 66 | /* Generic.Emph */ 67 | .highlight .gr { 68 | color: #aa0000; 69 | } 70 | 71 | /* Generic.Error */ 72 | .highlight .gh { 73 | color: #999999; 74 | } 75 | 76 | /* Generic.Heading */ 77 | .highlight .gi { 78 | color: #000000; 79 | background-color: #ddffdd; 80 | } 81 | 82 | /* Generic.Inserted */ 83 | .highlight .go { 84 | color: #888888; 85 | } 86 | 87 | /* Generic.Output */ 88 | .highlight .gp { 89 | color: #555555; 90 | } 91 | 92 | /* Generic.Prompt */ 93 | .highlight .gs { 94 | font-weight: bold; 95 | } 96 | 97 | /* Generic.Strong */ 98 | .highlight .gu { 99 | color: #aaaaaa; 100 | } 101 | 102 | /* Generic.Subheading */ 103 | .highlight .gt { 104 | color: #aa0000; 105 | } 106 | 107 | /* Generic.Traceback */ 108 | .highlight .kc { 109 | color: #000000; 110 | font-weight: bold; 111 | } 112 | 113 | /* Keyword.Constant */ 114 | .highlight .kd { 115 | color: #000000; 116 | font-weight: bold; 117 | } 118 | 119 | /* Keyword.Declaration */ 120 | .highlight .kn { 121 | color: #000000; 122 | font-weight: bold; 123 | } 124 | 125 | /* Keyword.Namespace */ 126 | .highlight .kp { 127 | color: #000000; 128 | font-weight: bold; 129 | } 130 | 131 | /* Keyword.Pseudo */ 132 | .highlight .kr { 133 | color: #000000; 134 | font-weight: bold; 135 | } 136 | 137 | /* Keyword.Reserved */ 138 | .highlight .kt { 139 | color: #445588; 140 | font-weight: bold; 141 | } 142 | 143 | /* Keyword.Type */ 144 | .highlight .m { 145 | color: #009999; 146 | } 147 | 148 | /* Literal.Number */ 149 | .highlight .s { 150 | color: #d01040; 151 | } 152 | 153 | /* Literal.String */ 154 | .highlight .na { 155 | color: teal; 156 | } 157 | 158 | /* Name.Attribute */ 159 | .highlight .nb { 160 | color: #0086b3; 161 | } 162 | 163 | /* Name.Builtin */ 164 | .highlight .nc { 165 | color: #445588; 166 | font-weight: bold; 167 | } 168 | 169 | /* Name.Class */ 170 | .highlight .no { 171 | color: teal; 172 | } 173 | 174 | /* Name.Constant */ 175 | .highlight .nd { 176 | color: #3c5d5d; 177 | font-weight: bold; 178 | } 179 | 180 | /* Name.Decorator */ 181 | .highlight .ni { 182 | color: purple; 183 | } 184 | 185 | /* Name.Entity */ 186 | .highlight .ne { 187 | color: #990000; 188 | font-weight: bold; 189 | } 190 | 191 | /* Name.Exception */ 192 | .highlight .nf { 193 | color: #990000; 194 | font-weight: bold; 195 | } 196 | 197 | /* Name.Function */ 198 | .highlight .nl { 199 | color: #990000; 200 | font-weight: bold; 201 | } 202 | 203 | /* Name.Label */ 204 | .highlight .nn { 205 | color: #555555; 206 | } 207 | 208 | /* Name.Namespace */ 209 | .highlight .nt { 210 | color: navy; 211 | } 212 | 213 | /* Name.Tag */ 214 | .highlight .nv { 215 | color: teal; 216 | } 217 | 218 | /* Name.Variable */ 219 | .highlight .ow { 220 | color: #000000; 221 | font-weight: bold; 222 | } 223 | 224 | /* Operator.Word */ 225 | .highlight .w { 226 | color: #bbbbbb; 227 | } 228 | 229 | /* Text.Whitespace */ 230 | .highlight .mf { 231 | color: #009999; 232 | } 233 | 234 | /* Literal.Number.Float */ 235 | .highlight .mh { 236 | color: #009999; 237 | } 238 | 239 | /* Literal.Number.Hex */ 240 | .highlight .mi { 241 | color: #009999; 242 | } 243 | 244 | /* Literal.Number.Integer */ 245 | .highlight .mo { 246 | color: #009999; 247 | } 248 | 249 | /* Literal.Number.Oct */ 250 | .highlight .sb { 251 | color: #d01040; 252 | } 253 | 254 | /* Literal.String.Backtick */ 255 | .highlight .sc { 256 | color: #d01040; 257 | } 258 | 259 | /* Literal.String.Char */ 260 | .highlight .sd { 261 | color: #d01040; 262 | } 263 | 264 | /* Literal.String.Doc */ 265 | .highlight .s2 { 266 | color: #d01040; 267 | } 268 | 269 | /* Literal.String.Double */ 270 | .highlight .se { 271 | color: #d01040; 272 | } 273 | 274 | /* Literal.String.Escape */ 275 | .highlight .sh { 276 | color: #d01040; 277 | } 278 | 279 | /* Literal.String.Heredoc */ 280 | .highlight .si { 281 | color: #d01040; 282 | } 283 | 284 | /* Literal.String.Interpol */ 285 | .highlight .sx { 286 | color: #d01040; 287 | } 288 | 289 | /* Literal.String.Other */ 290 | .highlight .sr { 291 | color: #009926; 292 | } 293 | 294 | /* Literal.String.Regex */ 295 | .highlight .s1 { 296 | color: #d01040; 297 | } 298 | 299 | /* Literal.String.Single */ 300 | .highlight .ss { 301 | color: #990073; 302 | } 303 | 304 | /* Literal.String.Symbol */ 305 | .highlight .bp { 306 | color: #999999; 307 | } 308 | 309 | /* Name.Builtin.Pseudo */ 310 | .highlight .vc { 311 | color: teal; 312 | } 313 | 314 | /* Name.Variable.Class */ 315 | .highlight .vg { 316 | color: teal; 317 | } 318 | 319 | /* Name.Variable.Global */ 320 | .highlight .vi { 321 | color: teal; 322 | } 323 | 324 | /* Name.Variable.Instance */ 325 | .highlight .il { 326 | color: #009999; 327 | } 328 | 329 | /* Literal.Number.Integer.Long */ 330 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/icons.css: -------------------------------------------------------------------------------- 1 | .icon { 2 | background-image: url("../img/iconsprite.png"); 3 | display: inline-block !important; 4 | } 5 | .icon.icon-spring-logo-mobile { 6 | width: 204px; 7 | height: 60px; 8 | background-position: 904px 302px; 9 | margin-top: -13px; 10 | transform: scale(0.5); 11 | -moz-transform: scale(0.5); 12 | -webkit-transform: scale(0.5); 13 | -o-transform: scale(0.5); 14 | -ms-transform: scale(0.5); 15 | } 16 | .icon.icon-spring-logo-big { 17 | width: 154px; 18 | height: 154px; 19 | background-position: 154px 191px; 20 | } 21 | .icon.project--logo.logo-spring-framework { 22 | width: 135px; 23 | height: 133px; 24 | background-position: 518px 837px; 25 | margin-top: 2px; 26 | } 27 | .icon.project--logo.logo-spring-security { 28 | width: 99px; 29 | height: 137px; 30 | background-position: 519px 677px; 31 | } 32 | .icon.project--logo.logo-spring-data { 33 | width: 111px; 34 | height: 111px; 35 | background-position: 519px 512px; 36 | margin-top: 13px; 37 | } 38 | .icon.project--logo.logo-spring-batch { 39 | width: 104px; 40 | height: 108px; 41 | background-position: 360px 837px; 42 | margin-top: 14px; 43 | } 44 | .icon.project--logo.logo-spring-integration { 45 | width: 145px; 46 | height: 104px; 47 | background-position: 361px 677px; 48 | margin-top: 16px; 49 | } 50 | .icon.project--logo.logo-spring-amqp { 51 | width: 109px; 52 | height: 98px; 53 | background-position: 361px 513px; 54 | margin: 19px; 55 | } 56 | .icon.project--logo.logo-spring-default { 57 | width: 100px; 58 | height: 100px; 59 | background-position: -805px 0; 60 | margin-top: 18px; 61 | } 62 | .icon.project--logo.logo-spring-mobile { 63 | width: 104px; 64 | height: 137px; 65 | background-position: 196px 677px; 66 | } 67 | .icon.project--logo.logo-spring-android { 68 | width: 104px; 69 | height: 126px; 70 | background-position: 196px 513px; 71 | margin-top: 10px; 72 | } 73 | .icon.project--logo.logo-spring-social { 74 | width: 130px; 75 | height: 100px; 76 | background-position: 518px 357px; 77 | margin-top: 18px; 78 | } 79 | .icon.project--logo.logo-spring-hateoas { 80 | width: 126px; 81 | height: 99px; 82 | background-position: 518px 256px; 83 | margin-top: 19px; 84 | } 85 | .icon.project--logo.logo-spring-webflow { 86 | width: 99px; 87 | height: 99px; 88 | background-position: 619px 256px; 89 | margin-top: 19px; 90 | } 91 | .icon.project--logo.logo-spring-web-services { 92 | width: 100px; 93 | height: 100px; 94 | background-position: 532px 155px; 95 | margin-top: 19px; 96 | } 97 | .icon.project--logo.logo-spring-xd { 98 | width: 104px; 99 | height: 113px; 100 | background-position: 247px 837px; 101 | margin-top: 11px; 102 | } 103 | .icon.project--logo.logo-spring-boot { 104 | width: 102px; 105 | height: 90px; 106 | background-position: 143px 837px; 107 | margin-top: 23px; 108 | } 109 | .icon.project--logo.logo-spring-ldap { 110 | width: 93px; 111 | height: 105px; 112 | background-position: 247px 944px; 113 | margin-top: 19px; 114 | } 115 | .icon.project--logo.logo-grails { 116 | width: 107px; 117 | height: 100px; 118 | background-position: 361px 944px; 119 | margin-top: 23px; 120 | } 121 | .icon.project--logo.logo-groovy { 122 | width: 183px; 123 | height: 92px; 124 | background-position: 546px 940px; 125 | margin-top: 23px; 126 | } 127 | .icon.project--logo.logo-spring-cloud { 128 | width: 150px; 129 | height: 111px; 130 | background-position: 698px 955px; 131 | margin-top: 13px; 132 | } 133 | .icon.icon-projects { 134 | width: 28px; 135 | height: 28px; 136 | background-position: -641px -481px; 137 | } 138 | .icon.icon-projects:hover { 139 | background-position: -641px -521px; 140 | } 141 | .icon.icon-forum { 142 | width: 32px; 143 | height: 32px; 144 | background-position: -409px -450px; 145 | } 146 | .icon.icon-forum:hover { 147 | background-position: -449px -450px; 148 | } 149 | .icon.icon-current-version { 150 | height: 13px; 151 | width: 42px; 152 | background-position: 718px 317px; 153 | } 154 | .icon.icon-pre-release { 155 | height: 13px; 156 | width: 23px; 157 | background-position: 657px 317px; 158 | } 159 | .icon.icon-ga-release { 160 | margin-left: 0 !important; 161 | height: 13px; 162 | width: 9px; 163 | background-position: 626px 317px; 164 | background-color: #6db33f; 165 | padding: 0 2px; 166 | border-radius: 10px; 167 | } 168 | .icon.icon-snapshot-release { 169 | height: 13px; 170 | width: 46px; 171 | background-position: 603px 317px; 172 | } 173 | .icon.logo-eclipse-text { 174 | width: 159px; 175 | height: 38px; 176 | background-position: 603px 38px; 177 | } 178 | .icon.icon-social-twitter { 179 | width: 36px; 180 | height: 36px; 181 | background-position: -1px -143px; 182 | } 183 | .icon.icon-social-twitter:hover { 184 | background-position: -84px -143px; 185 | } 186 | .icon.icon-build-anything { 187 | width: 107px; 188 | background-position: 107px 279px; 189 | height: 88px; 190 | } 191 | .icon.icon-run-anywhere { 192 | background-position: 120px 344px; 193 | width: 120px; 194 | height: 65px; 195 | margin-top: 11px; 196 | } 197 | .icon.icon-rest-assured { 198 | background-position: 253px 86px; 199 | width: 86px; 200 | height: 86px; 201 | margin-top: 1px; 202 | } 203 | .icon.icon-community-support { 204 | width: 97px; 205 | height: 97px; 206 | background-position: 257px 185px; 207 | font-size: 0; 208 | } 209 | .icon.icon-professional-support { 210 | width: 126px; 211 | height: 97px; 212 | background-position: 272px 282px; 213 | font-size: 0; 214 | } 215 | .icon.icon-gs-guides { 216 | width: 73px; 217 | height: 101px; 218 | background-position: 356px 101px; 219 | } 220 | .icon.icon-tutorial { 221 | width: 73px; 222 | height: 101px; 223 | background-position: 430px 101px; 224 | } 225 | .icon.blog-icon { 226 | width: 20px; 227 | height: 20px; 228 | vertical-align: middle; 229 | } 230 | .icon.blog-icon.comments { 231 | background-position: 20px 636px; 232 | } 233 | .icon.blog-icon.calendar { 234 | background-position: 20px 594px; 235 | } 236 | .icon.blog-icon.all-posts { 237 | background-position: 20px 561px; 238 | } 239 | .icon.blog-icon.broadcasts { 240 | background-position: 20px 519px; 241 | } 242 | .icon.blog-icon.engineering { 243 | background-position: 20px 477px; 244 | } 245 | .icon.blog-icon.releases { 246 | background-position: 20px 433px; 247 | } 248 | .icon.blog-icon.news-and-events { 249 | background-position: 20px 387px; 250 | } 251 | .icon.about-icon.team { 252 | width: 183px; 253 | height: 127px; 254 | background-position: 902px 202px; 255 | } 256 | .icon.about-icon.jobs { 257 | width: 112px; 258 | height: 85px; 259 | background-position: 387px 415px; 260 | margin: 21px 0; 261 | } 262 | .icon.about-icon.pivotal { 263 | width: 93px; 264 | height: 114px; 265 | background-position: 387px 329px; 266 | margin: 6px 0 7px; 267 | } 268 | 269 | i.icon { 270 | background-image: none; 271 | } 272 | -------------------------------------------------------------------------------- /src/main/webapp/public/styles/main.css: -------------------------------------------------------------------------------- 1 | @import "fontcustom.css"; 2 | @import "icons.css"; 3 | @import "buttons.css"; 4 | @import "typography.css"; 5 | @import "base.css"; 6 | @import "application.css"; 7 | @import "highlight.css"; 8 | 9 | .table > thead > tr > th { 10 | background-color: #34302D; /* lighten 3% */ 11 | color: #f1f1f1; 12 | } 13 | 14 | .table tr.highlight > td { 15 | background-color: #6db33f !important; 16 | font-weight: bold; 17 | } 18 | 19 | .table-filter { 20 | background-color: #34302D; 21 | padding: 9px 12px; 22 | } 23 | 24 | .nav > li > a { 25 | color: #838789; 26 | } 27 | 28 | .nav { 29 | margin-bottom: 0; 30 | } 31 | 32 | .nav-tabs > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus { 33 | background-color: #34302D; 34 | border-color: #34302D; 35 | color: #f1f1f1; 36 | } 37 | 38 | a.spring-boot-logo { 39 | background: url("../img/platform-spring-boot.png") -1px -1px no-repeat; 40 | } 41 | 42 | a.spring-boot-logo span { 43 | display: block; 44 | width: 160px; 45 | height: 50px; 46 | background: url("../img/platform-spring-boot.png") 20px -6px no-repeat; 47 | } 48 | 49 | a:hover.spring-boot-logo span { 50 | opacity: 1; 51 | } 52 | 53 | span.up { 54 | color: #00AA00; 55 | } 56 | 57 | span.offline { 58 | color: #DD0000; 59 | font-weight: bold; 60 | } 61 | 62 | span.down, span.outofservice { 63 | color: #FF8800; 64 | font-weight: bold; 65 | } 66 | 67 | span.unknown { 68 | color: #DDDDDD; 69 | } -------------------------------------------------------------------------------- /src/main/webapp/public/styles/typography.css: -------------------------------------------------------------------------------- 1 | body, h1, h2, h3, p, input { 2 | margin: 0; 3 | font-weight: 400; 4 | font-family: "Varela Round", sans-serif; 5 | color: #34302d; 6 | } 7 | 8 | h1 { 9 | font-size: 24px; 10 | line-height: 30px; 11 | font-family: "Montserrat", sans-serif; 12 | } 13 | 14 | h2 { 15 | font-size: 18px; 16 | font-weight: 700; 17 | line-height: 24px; 18 | margin-bottom: 10px; 19 | font-family: "Montserrat", sans-serif; 20 | } 21 | 22 | h3 { 23 | font-size: 16px; 24 | line-height: 24px; 25 | margin-bottom: 10px; 26 | font-weight: 700; 27 | } 28 | 29 | p { 30 | font-size: 15px; 31 | line-height: 24px; 32 | } 33 | p a { 34 | color: #5fa134; 35 | } 36 | p a:hover { 37 | color: #5fa134; 38 | } 39 | 40 | code { 41 | font-size: 14px; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/about.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | This is an administration GUI for Spring-Boot applications. All applications has to register itself at this application. 4 | This is done by including spring-boot-starters-admin-client as dependency. This will 5 | auto-configure a registrator that registers the application. 6 |
7 |
-------------------------------------------------------------------------------- /src/main/webapp/public/views/apps.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/details.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ application.id }}

4 |

Show information provided by the Spring-Boot Actuators.

5 |
6 |
7 |
8 |
9 |
10 | 16 |
17 |
18 |
19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/details/env.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
PropertyValue
{{ envkey }}
{{ key }}{{ value }}
20 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/details/infos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
KeyValue
{{ key }}{{ value }}
-------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/details/metrics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Used MemoryFree MemoryUsed HeapMax HeapProcessorsUptime
{{ application.metrics['mem'] / 1024 | number:2 }} MB{{ application.metrics['mem.free'] / 1024 | number:2 }} MB{{ application.metrics['heap.used'] / 1024 | number:2 }} MB{{ application.metrics['heap'] / 1024 | number:2 }} MB{{ application.metrics['processors'] }}{{ application.metrics['uptime'] / 86400000 | floor | padZero }}:{{ application.metrics['uptime'] % 86400000 / 3600000 | floor | padZero }}:{{ application.metrics['uptime'] % 3600000 / 60000 | floor | padZero }}:{{ application.metrics['uptime'] % 60000 / 1000 | floor | padZero }} [d:h:m:s]
23 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/details/props.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
PropertyValue
{{ prop.key }}
{{ key }}{{ value }}
20 | 21 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/logging.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ application.id }}

4 |

Read and write log levels of specified loggers.

5 |
6 |
7 |
8 |
9 |
10 | 14 |
15 |
16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/logging/read.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 25 | 26 | 27 |
Read log level
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
Logger name
Log level{{ application.logger.loglevel }}
24 |
28 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/logging/write.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 43 | 44 | 45 |
Write log level
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 34 | 35 | 36 | 37 | 40 | 41 |
Logger name
Log level
18 | 20 |
21 |
22 | 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 | 33 |
Success!Error!
42 |
46 | -------------------------------------------------------------------------------- /src/main/webapp/public/views/apps/overview.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Spring-Boot applications

4 |

Here you'll find all Spring-Boot applications that registered itself at this admin 5 | application.

6 |
7 |
8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 38 | 39 | 40 |
ApplicationVersionStatus
{{ application.id }}{{ application.version }} 24 | UP 25 | DOWN 26 | OUT OF SERVICE 27 | UNKNOWN 28 | OFFLINE 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /src/test/java/de/codecentric/boot/admin/service/ApplicationRegistryTest.java: -------------------------------------------------------------------------------- 1 | package de.codecentric.boot.admin.service; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.test.context.ContextConfiguration; 11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 12 | 13 | import de.codecentric.boot.admin.config.WebappConfig; 14 | import de.codecentric.boot.admin.model.Application; 15 | 16 | @ContextConfiguration(classes = { WebappConfig.class }) 17 | @RunWith(SpringJUnit4ClassRunner.class) 18 | public class ApplicationRegistryTest { 19 | 20 | @Autowired 21 | private ApplicationRegistry registry; 22 | 23 | @Test(expected = NullPointerException.class) 24 | public void registerFailed1() throws Exception { 25 | registry.register(new Application()); 26 | } 27 | 28 | @Test(expected = NullPointerException.class) 29 | public void registerFailed2() throws Exception { 30 | Application app = new Application(); 31 | app.setId("abc"); 32 | registry.register(app); 33 | } 34 | 35 | @Test(expected = IllegalArgumentException.class) 36 | public void registerFailed3() throws Exception { 37 | Application app = new Application(); 38 | app.setId("abc"); 39 | app.setUrl("not-an-url"); 40 | registry.register(app); 41 | } 42 | 43 | @Test 44 | public void register() throws Exception { 45 | Application app = new Application(); 46 | app.setId("abc"); 47 | app.setUrl("http://localhost:8080"); 48 | registry.register(app); 49 | } 50 | 51 | @Test 52 | public void isRegistered() throws Exception { 53 | Application app = new Application(); 54 | app.setId("abc"); 55 | app.setUrl("http://localhost:8080"); 56 | registry.register(app); 57 | 58 | assertFalse(registry.isRegistered("xyz")); 59 | assertTrue(registry.isRegistered("abc")); 60 | } 61 | 62 | @Test 63 | public void getApplication() throws Exception { 64 | Application app = new Application(); 65 | app.setId("abc"); 66 | app.setUrl("http://localhost:8080"); 67 | registry.register(app); 68 | 69 | assertEquals(app, registry.getApplication("abc")); 70 | } 71 | 72 | @Test 73 | public void getApplications() throws Exception { 74 | Application app = new Application(); 75 | app.setId("abc"); 76 | app.setUrl("http://localhost:8080"); 77 | registry.register(app); 78 | 79 | assertEquals(1, registry.getApplications().size()); 80 | assertEquals(app, registry.getApplications().get(0)); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------