├── .gitmodules ├── .travis.yml ├── README.md ├── client └── chrome-plugin │ ├── icons │ └── icon.png │ ├── manifest.json │ └── popup.html ├── release-notes.md └── server ├── README.md ├── pom.xml ├── sqlschema ├── mysql.sql └── sqlite.sql └── src ├── main ├── java │ └── us │ │ └── codecraft │ │ └── blackhole │ │ └── suite │ │ ├── connector │ │ ├── BlackholeConnector.java │ │ └── ZonesFileApplyer.java │ │ ├── constant │ │ └── LoginConstant.java │ │ ├── dao │ │ ├── UserPassportDAO.java │ │ ├── UserZonesDAO.java │ │ └── ZonesFileDAO.java │ │ ├── exception │ │ ├── LoginException.java │ │ └── RegisterException.java │ │ ├── filter │ │ └── UserPassportFilter.java │ │ ├── init │ │ ├── InitData.java │ │ └── SqliteDao.java │ │ ├── model │ │ ├── CustomZone.java │ │ ├── CustomZoneGroup.java │ │ ├── JsonResult.java │ │ ├── UserPassport.java │ │ └── ZonesFile.java │ │ ├── service │ │ ├── UserPassportSerivce.java │ │ ├── UserZonesService.java │ │ ├── ZonesFileService.java │ │ └── impl │ │ │ ├── UserPassportServiceImpl.java │ │ │ ├── UserZonesServiceImpl.java │ │ │ └── ZonesFileServiceImpl.java │ │ ├── util │ │ ├── CookieUtils.java │ │ ├── DoubleKeyMap.java │ │ ├── IPUtils.java │ │ ├── MultiKeyMapBase.java │ │ ├── RequestThreadUtils.java │ │ ├── SpringLocator.java │ │ ├── UserPassportUtil.java │ │ └── UserZonesUtils.java │ │ └── web │ │ ├── HelpController.java │ │ ├── LoginController.java │ │ ├── RegisterController.java │ │ ├── ShareController.java │ │ ├── ZonesApplyController.java │ │ ├── ZonesEditController.java │ │ ├── ZonesFileController.java │ │ ├── ZonesListController.java │ │ └── ZonesPickController.java ├── resources │ ├── log4j.xml │ ├── spring │ │ ├── applicationContext-freemarker.xml │ │ ├── applicationContext-myBatis.xml │ │ └── applicationContext.xml │ └── web_messages_zh_CN.properties └── webapp │ ├── WEB-INF │ ├── jsp │ │ ├── 404.jsp │ │ ├── 500.jsp │ │ └── count.jsp │ ├── log4j.xml │ ├── pages │ │ ├── edit.ftl │ │ ├── help-dns.ftl │ │ ├── login.ftl │ │ ├── nav.ftl │ │ ├── register.ftl │ │ ├── shareboard.ftl │ │ └── zonespick.ftl │ ├── spring-servlet.xml │ └── web.xml │ ├── css │ ├── bootstrap-mobile.css │ ├── bootstrap-responsive.css │ ├── bootstrap-responsive.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── codemirror.css │ ├── font-awesome.css │ ├── hostd.css │ ├── jqm-icon-pack-1.1.1-fa.css │ ├── jquery.mobile.structure-1.2.0.css │ ├── jquery.mobile.structure-1.3.1.css │ ├── shareboard.css │ └── solarized.css │ ├── font │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff │ ├── img │ ├── ajax-loader.gif │ ├── ajax-loader.png │ ├── favicon.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── icons-18-black-pack.png │ ├── icons-18-black.png │ ├── icons-18-white-pack.png │ ├── icons-18-white.png │ ├── icons-36-black-pack.png │ ├── icons-36-black.png │ ├── icons-36-white-pack.png │ └── icons-36-white.png │ └── js │ ├── baiduTemplate.js │ ├── bootstrap.js │ ├── codemirror.js │ ├── common-action.js │ ├── edit.js │ ├── javascript.js │ ├── jquery-1.8.2.js │ ├── jquery-1.9.1.js │ ├── jquery.mobile-1.2.0.js │ ├── json.js │ ├── login.js │ ├── register.js │ ├── shareboard.js │ └── zonespick.js └── test └── java └── us └── codecraft └── blackhole └── suite ├── dao ├── SqliteDaoTest.java ├── UserPassportDaoTest.java └── ZonesDaoTest.java └── utils └── UserZonesUtilsTest.java /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wifesays"] 2 | path = wifesays 3 | url = git@github.com:code4craft/wifesays.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: 3 | - git submodule update --init --recursive 4 | - cd wifesays 5 | - mvn install 6 | script: 7 | - mvn clean package 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hostd 2 | ----------- 3 | Hostd是面向DNS客户端的一个管理BlackHole的DNS配置的工具。 4 | 5 | 它提供Web页面和http接口,用户可以在hostd里修改DNS配置(仅对自己生效),是一个跨平台的域名绑定工具,用于开发和生产环境的域名切换。它解决了hosts配置管理不方便,以及移动设备难以修改hosts文件的问题。 6 | 7 | 其他部分可以看hostd的[pages](http://code4craft.github.io/hostd/) -------------------------------------------------------------------------------- /client/chrome-plugin/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/client/chrome-plugin/icons/icon.png -------------------------------------------------------------------------------- /client/chrome-plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Hostd", 3 | "icons": { 4 | "16": "icons/icon.png", 5 | "24": "icons/icon.png", 6 | "32": "icons/icon.png", 7 | "128": "icons/icon.png" 8 | }, 9 | "version": "1.0.0", 10 | "manifest_version": 2, 11 | "permissions": ["tabs", 12 | "http://setdns.diors.it/", 13 | "unlimitedStorage" 14 | ], 15 | "description": "Saving your time when you switch domain-ip mapping (Hosts file) between different environment. Awesome host file editor !", 16 | 17 | "browser_action": { 18 | "default_icon": "icons/icon.png", 19 | "default_popup": "popup.html" 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /client/chrome-plugin/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 30 | 31 | 32 | 33 | 34 | 41 |
42 | Write hosts file failed check permissions, Learn more 43 |
44 | 45 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /release-notes.md: -------------------------------------------------------------------------------- 1 | RELEASE-NOTES 2 | ------- 3 | *2013-6-5* 4 | 5 | version 0.1.1-alpha 6 | 7 | * 为配置增加注释 8 | * 增加配置的分享功能 9 | * 修复一个删除的bug 10 | 11 | 12 | *2013-5-31* 13 | 14 | version 0.1.0 15 | 16 | 第一版 -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | Hostd-server 2 | --------- 3 | Hostd-server是blackhole的一个控制套件,它提供一个Web页面,让最终用户可以管理和修改自己的DNS设置。 4 | 5 | 基于Spring MVC、myBatis、sqlite进行开发。 6 | 7 | 计划内置jetty作为web容器,sqlite作为存储,是个开箱可用的服务器。但是也可以用其他容易进行部署,并提供mysql连接的支持。 8 | 9 | 前端部分比较烂,属于赶鸭子上架那种,使用bootstrap做的,希望能做到自适应。js部分使用jQuery和codeMirror。主要配置的选取和编辑功能参考了[HostsAdmin](https://github.com/tg123/chrome-hostadmin),非常优秀的思路,在此感谢@tg123。 10 | 11 | 其他部分可以看hostd的[pages](http://code4craft.github.io/hostd/) -------------------------------------------------------------------------------- /server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | war 6 | us.codecraft 7 | hostd-server 8 | 0.1.0 9 | 10 | 11 | UTF-8 12 | 13 | / 14 | 3.1.1.RELEASE 15 | 3.1.0.RELEASE 16 | 17 | 18 | 19 | org.xerial 20 | sqlite-jdbc 21 | 3.7.2 22 | 23 | 24 | dnsjava 25 | dnsjava 26 | 2.1.1 27 | 28 | 29 | 30 | org.springframework 31 | spring-webmvc 32 | ${spring-version} 33 | 34 | 35 | org.springframework 36 | spring-jdbc 37 | ${spring-version} 38 | 39 | 40 | org.apache.commons 41 | commons-lang3 42 | 3.1 43 | 44 | 45 | javax.servlet 46 | servlet-api 47 | 2.5 48 | 49 | 50 | us.codecraft 51 | wifesays 52 | 1.0.0-alpha 53 | 54 | 55 | mysql 56 | mysql-connector-java 57 | 5.1.18 58 | 59 | 60 | commons-dbcp 61 | commons-dbcp 62 | 1.3 63 | 64 | 65 | org.slf4j 66 | slf4j-api 67 | 1.6.4 68 | 69 | 70 | org.slf4j 71 | slf4j-log4j12 72 | 1.6.4 73 | 74 | 75 | junit 76 | junit 77 | 4.7 78 | test 79 | 80 | 81 | 82 | org.codehaus.jackson 83 | jackson-core-lgpl 84 | 1.9.8 85 | 86 | 87 | org.mybatis 88 | mybatis 89 | 3.1.1 90 | 91 | 92 | 93 | org.mybatis 94 | mybatis-spring 95 | 1.1.1 96 | 97 | 98 | 99 | 100 | org.codehaus.jackson 101 | jackson-mapper-lgpl 102 | 1.9.7 103 | 104 | 105 | 106 | org.freemarker 107 | freemarker 108 | 2.3.19 109 | 110 | 111 | org.springframework 112 | spring-test 113 | ${spring-version} 114 | test 115 | 116 | 117 | 118 | org.springframework 119 | spring-aop 120 | ${spring-version} 121 | 122 | 123 | 124 | org.aspectj 125 | aspectjrt 126 | 1.7.2 127 | 128 | 129 | org.aspectj 130 | aspectjweaver 131 | 1.7.2 132 | 133 | 134 | 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-compiler-plugin 140 | 141 | 1.6 142 | 1.6 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /server/sqlschema/mysql.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE ZonesFile ( 2 | `id` INTEGER PRIMARY KEY AUTO_INCREMENT, 3 | `type` INTEGER, 4 | `name` VARCHAR(100), 5 | `text` VARCHAR(10000), 6 | `user` VARCHAR(100) 7 | ) 8 | ENGINE ="innodb" 9 | DEFAULT CHARSET utf8; 10 | 11 | CREATE TABLE User_Passport ( 12 | `id` INTEGER PRIMARY KEY AUTO_INCREMENT, 13 | `username` VARCHAR(100) UNIQUE, 14 | `passwordSalt` VARCHAR(100), 15 | `salt` VARCHAR(100), 16 | `ticket` VARCHAR(100) UNIQUE, 17 | `zones` VARCHAR(10000) DEFAULT "", 18 | UNIQUE KEY `uk_username` (`username`), 19 | UNIQUE KEY `uk_ticket` (`ticket`) 20 | ) 21 | ENGINE ="innodb" 22 | DEFAULT CHARSET utf8; -------------------------------------------------------------------------------- /server/sqlschema/sqlite.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE ZonesFile ( 2 | `id` INTEGER primary key AUTOINCREMENT, 3 | `type` INTEGER, 4 | `name` string, 5 | `text` string, 6 | `user` string 7 | ); 8 | 9 | CREATE TABLE User_Passport ( 10 | `id` INTEGER primary key AUTOINCREMENT, 11 | `username` text UNIQUE, 12 | `passwordSalt` string, 13 | `salt` string, 14 | `ticket` text UNIQUE, 15 | `zones` string default "" 16 | ); -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/connector/BlackholeConnector.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.connector; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.apache.log4j.Logger; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.stereotype.Service; 7 | import us.codecraft.wifesays.wife.WifeSays; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-13 14 | * Time: 上午7:44 15 | */ 16 | @Service 17 | public class BlackholeConnector implements InitializingBean { 18 | 19 | //delete_zones_ip_192.168.0.1 20 | private static final String DELETE_ZONES_IP = "delete_zones_ip_"; 21 | //add_zones_ip_192.168.0.1:127.0.0.1_*.dianping.com 22 | private static final String ADD_ZONES_IP = "add_zones_ip_"; 23 | 24 | private WifeSays wifeSays; 25 | 26 | private Logger logger = Logger.getLogger(getClass()); 27 | 28 | public void deleteAllByIp(String ip) { 29 | wifeSays.say(DELETE_ZONES_IP + ip); 30 | } 31 | 32 | public void addByIp(String ip, String line) { 33 | if (line.startsWith("#")) { 34 | return; 35 | } 36 | if (line.contains(":")) { 37 | line = StringUtils.substringAfterLast(line, ":"); 38 | } 39 | line = StringUtils.trim(line); 40 | line = line.replaceAll("\\s+#[^\\s]+", ""); 41 | line = line.replaceAll("\\s+", "_"); 42 | wifeSays.say(ADD_ZONES_IP + ip + ":" + line); 43 | } 44 | 45 | public boolean isConnected() { 46 | return wifeSays.isConnected(); 47 | } 48 | 49 | public void setConnected(boolean connected) { 50 | wifeSays.setConnected(connected); 51 | } 52 | 53 | 54 | @Override 55 | public void afterPropertiesSet() throws Exception { 56 | wifeSays = new WifeSays(); 57 | try { 58 | wifeSays.connect(); 59 | } catch (IOException e) { 60 | logger.warn("connnect error", e); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/connector/ZonesFileApplyer.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.connector; 2 | 3 | import org.apache.log4j.Logger; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.StringReader; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-13 14 | * Time: 上午7:51 15 | */ 16 | @Service 17 | public class ZonesFileApplyer { 18 | 19 | @Autowired 20 | private BlackholeConnector blackholeConnector; 21 | 22 | private Logger logger = Logger.getLogger(getClass()); 23 | 24 | public void apply(String ip, String text) { 25 | 26 | BufferedReader bufferedReader = new BufferedReader(new StringReader( 27 | text)); 28 | String line = null; 29 | try { 30 | blackholeConnector.deleteAllByIp(ip); 31 | while ((line = bufferedReader.readLine()) != null) { 32 | blackholeConnector.addByIp(ip, line); 33 | } 34 | } catch (IOException e) { 35 | logger.info("wtf!??", e); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/constant/LoginConstant.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.constant; 2 | 3 | /** 4 | * User: cairne 5 | * Date: 13-5-15 6 | * Time: 上午6:41 7 | */ 8 | public interface LoginConstant { 9 | 10 | public static final String LOGIN_ERROR_USER_NOTEXIST = "login.error.user.notexist"; 11 | 12 | public static final String REGISTER_ERROR = "register.error"; 13 | 14 | public static final String REGISTER_ERROR_USERNAME_EXIST = "register.error.username.exist"; 15 | 16 | public static final String LOGIN_ERROR_PASSWORD_INCORRECT = "login.error.password.incorrect"; 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/dao/UserPassportDAO.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.apache.ibatis.annotations.Update; 7 | import us.codecraft.blackhole.suite.model.UserPassport; 8 | 9 | /** 10 | * @author 11 | * @date 2012-3-29 12 | */ 13 | public interface UserPassportDAO { 14 | 15 | @Select("select * from User_Passport where username=#{username}") 16 | public UserPassport getByUsername(@Param("username") String username); 17 | 18 | @Select("select * from User_Passport where ticket=#{ticket}") 19 | public UserPassport getByTicket(@Param("ticket") String ticket); 20 | 21 | @Insert("insert into User_Passport (`username`,`passwordSalt`,`salt`,`ticket`) values"+ 22 | " (#{username},#{passwordSalt},#{salt},#{ticket})") 23 | public int insert(UserPassport userPassport); 24 | 25 | @Update("update User_Passport set `username`=#{username},`passwordSalt`=#{passwordSalt},"+ 26 | "`salt`=#{salt},`ticket`=#{ticket} where `id`=#{id}") 27 | public int update(UserPassport userPassport); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/dao/UserZonesDAO.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.apache.ibatis.annotations.Param; 4 | import org.apache.ibatis.annotations.Select; 5 | import org.apache.ibatis.annotations.Update; 6 | 7 | /** 8 | * @author 9 | * @date 2012-3-29 10 | */ 11 | public interface UserZonesDAO { 12 | 13 | @Update("update User_Passport set `zones`=#{zones} where `id`=#{userId}") 14 | public int updateZones(@Param("userId") int userId, @Param("zones") String zones); 15 | 16 | @Select("select zones from User_Passport where `id`=#{userId}") 17 | public String getZones(@Param("userId") int userId); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/dao/ZonesFileDAO.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.apache.ibatis.annotations.Update; 7 | import us.codecraft.blackhole.suite.model.ZonesFile; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-13 14 | * Time: 下午9:44 15 | */ 16 | public interface ZonesFileDAO { 17 | 18 | @Select("SELECT * FROM ZonesFile where type=1") 19 | public List findPublic(); 20 | 21 | @Select("SELECT * FROM ZonesFile where user=#{user}") 22 | public List findByUser(@Param("user") String user); 23 | 24 | @Select("SELECT * FROM ZonesFile where id = #{id}") 25 | public ZonesFile load(@Param("id") int id); 26 | 27 | @Insert("insert into ZonesFile (`type`,`user`,`name`,`text`) values (#{type},#{user},#{name},#{text})") 28 | public int add(ZonesFile zonesFile); 29 | 30 | @Update("update ZonesFile set `name`=#{name},`text`=#{text} where `id`=#{id}") 31 | public int update(ZonesFile zonesFile); 32 | 33 | @Update("delete from ZonesFile where `id`=#{id}") 34 | public int deleteById(@Param("id") int id); 35 | } 36 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/exception/LoginException.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.exception; 2 | 3 | /** 4 | * @author cairne 5 | * @date 2012-3-31 6 | */ 7 | public class LoginException extends Exception { 8 | 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | private final String code; 15 | 16 | public LoginException(String code) { 17 | super(code); 18 | this.code = code; 19 | } 20 | 21 | public String getCode() { 22 | return code; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/exception/RegisterException.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.exception; 2 | 3 | /** 4 | * 5 | */ 6 | public class RegisterException extends Exception { 7 | 8 | /** 9 | * 10 | */ 11 | private static final long serialVersionUID = 1L; 12 | 13 | private final String code; 14 | 15 | public RegisterException(String code) { 16 | super(code); 17 | this.code = code; 18 | } 19 | 20 | public String getCode() { 21 | return code; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/filter/UserPassportFilter.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.filter; 2 | 3 | import us.codecraft.blackhole.suite.model.UserPassport; 4 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 5 | import us.codecraft.blackhole.suite.util.SpringLocator; 6 | 7 | import javax.servlet.*; 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.io.IOException; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-14 14 | * Time: 下午1:54 15 | */ 16 | public class UserPassportFilter implements Filter { 17 | 18 | @Override 19 | public void init(FilterConfig filterConfig) throws ServletException { 20 | } 21 | 22 | @Override 23 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 24 | UserPassportSerivce userPassportSerivce = SpringLocator.getBean(UserPassportSerivce.class); 25 | UserPassport userPassport = userPassportSerivce.getUserPassport((HttpServletRequest) request); 26 | request.setAttribute("userPassport", userPassport); 27 | chain.doFilter(request,response); 28 | } 29 | 30 | @Override 31 | public void destroy() { 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/init/InitData.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.init; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.context.support.ClassPathXmlApplicationContext; 5 | import us.codecraft.blackhole.suite.dao.ZonesFileDAO; 6 | import us.codecraft.blackhole.suite.exception.RegisterException; 7 | import us.codecraft.blackhole.suite.model.ZonesFile; 8 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.SQLException; 12 | 13 | /** 14 | * User: cairne 15 | * Date: 13-5-19 16 | * Time: 下午6:02 17 | */ 18 | public class InitData { 19 | 20 | public static void main(String[] args) throws SQLException { 21 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/spring/applicationContext*.xml"); 22 | DataSource dataSource = applicationContext.getBean(DataSource.class); 23 | SqliteDao sqliteDao = new SqliteDao(dataSource); 24 | sqliteDao.excute("CREATE TABLE User_Passport (\n" + 25 | " `id` INTEGER primary key AUTOINCREMENT,\n" + 26 | " `username` text UNIQUE,\n" + 27 | " `passwordSalt` string,\n" + 28 | " `salt` string,\n" + 29 | " `ticket` text UNIQUE,\n" + 30 | " `zones` string default \"\"\n" + 31 | ");"); 32 | sqliteDao.excute("CREATE TABLE ZonesFile (\n" + 33 | " `id` INTEGER primary key AUTOINCREMENT,\n" + 34 | " `type` INTEGER,\n" + 35 | " `name` string,\n" + 36 | " `text` string,\n" + 37 | " `user` string\n" + 38 | ");"); 39 | UserPassportSerivce userPassportSerivce = applicationContext.getBean(UserPassportSerivce.class); 40 | try { 41 | userPassportSerivce.addUserPassport("admin","admin123"); 42 | } catch (RegisterException e) { 43 | } 44 | ZonesFileDAO zonesFileDAO = applicationContext.getBean(ZonesFileDAO.class); 45 | zonesFileDAO.add(new ZonesFile().setName("sample").setText("127.0.0.1 localhost\n" + 46 | "69.171.229.25 *.facebook.com").setType(1).setUser("admin")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/init/SqliteDao.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.init; 2 | 3 | import javax.sql.DataSource; 4 | import java.sql.Connection; 5 | import java.sql.SQLException; 6 | import java.sql.Statement; 7 | 8 | /** 9 | * User: cairne 10 | * Date: 13-5-11 11 | * Time: 上午7:32 12 | */ 13 | public class SqliteDao { 14 | 15 | private DataSource dataSource; 16 | 17 | public SqliteDao(DataSource dataSource) { 18 | this.dataSource = dataSource; 19 | } 20 | 21 | public void excute(String query) throws SQLException { 22 | Connection connection = dataSource.getConnection(); 23 | try { 24 | // create a database connection 25 | Statement statement = connection.createStatement(); 26 | statement.setQueryTimeout(30); // set timeout to 30 sec. 27 | 28 | statement.executeUpdate(query); 29 | } catch (SQLException e) { 30 | // if the error message is "out of memory", 31 | // it probably means no database file is found 32 | e.printStackTrace(); 33 | } finally { 34 | try { 35 | if (connection != null) 36 | connection.close(); 37 | } catch (SQLException e) { 38 | // connection close failed. 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/model/CustomZone.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.model; 2 | 3 | /** 4 | * User: cairne 5 | * Date: 13-5-11 6 | * Time: 上午9:02 7 | */ 8 | public class CustomZone { 9 | 10 | private int id; 11 | 12 | private int groupId; 13 | 14 | private String pattern; 15 | 16 | public int getGroupId() { 17 | return groupId; 18 | } 19 | 20 | public void setGroupId(int groupId) { 21 | this.groupId = groupId; 22 | } 23 | 24 | public String getPattern() { 25 | return pattern; 26 | } 27 | 28 | public void setPattern(String pattern) { 29 | this.pattern = pattern; 30 | } 31 | 32 | public int getId() { 33 | return id; 34 | } 35 | 36 | public void setId(int id) { 37 | this.id = id; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/model/CustomZoneGroup.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.model; 2 | 3 | /** 4 | * User: cairne 5 | * Date: 13-5-11 6 | * Time: 上午9:02 7 | */ 8 | public class CustomZoneGroup { 9 | 10 | /** 11 | * 12 | */ 13 | private String userName; 14 | 15 | private int id; 16 | 17 | private String name; 18 | 19 | public int getId() { 20 | return id; 21 | } 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public String getUserName() { 36 | return userName; 37 | } 38 | 39 | public void setUserName(String userName) { 40 | this.userName = userName; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/model/JsonResult.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.model; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * User: cairne 8 | * Date: 13-5-15 9 | * Time: 上午6:34 10 | */ 11 | public class JsonResult { 12 | 13 | private final int code; 14 | 15 | private final String msg; 16 | 17 | private JsonResult(int code, String msg) { 18 | this.code = code; 19 | this.msg = msg; 20 | } 21 | 22 | public static JsonResult success(String msg) { 23 | return new JsonResult(200, msg); 24 | } 25 | 26 | public static JsonResult error(String msg) { 27 | return new JsonResult(500, msg); 28 | } 29 | 30 | public static JsonResult result(int code, String msg) { 31 | return new JsonResult(code, msg); 32 | } 33 | 34 | public int getCode() { 35 | return code; 36 | } 37 | 38 | public String getMsg() { 39 | return msg; 40 | } 41 | 42 | public Map toMap(){ 43 | Map resultMap = new LinkedHashMap(); 44 | resultMap.put("code",code); 45 | resultMap.put("msg",msg); 46 | return resultMap; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/model/UserPassport.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.model; 2 | 3 | /** 4 | * @author cairne huangyihua@diandian.com 5 | * @date 2012-3-29 6 | */ 7 | public class UserPassport { 8 | 9 | private int id; 10 | 11 | private String username; 12 | 13 | private String passwordSalt; 14 | 15 | private String salt; 16 | 17 | private String ticket; 18 | 19 | private String zones; 20 | 21 | public String getTicket() { 22 | return ticket; 23 | } 24 | 25 | public String getZones() { 26 | return zones; 27 | } 28 | 29 | public void setZones(String zones) { 30 | this.zones = zones; 31 | } 32 | 33 | public void setPasswordSalt(String passwordSalt) { 34 | this.passwordSalt = passwordSalt; 35 | } 36 | 37 | public void setSalt(String salt) { 38 | this.salt = salt; 39 | } 40 | 41 | public void setUsername(String username) { 42 | this.username = username; 43 | } 44 | 45 | public void setTicket(String ticket) { 46 | this.ticket = ticket; 47 | } 48 | 49 | public int getId() { 50 | return id; 51 | } 52 | 53 | public void setId(int id) { 54 | this.id = id; 55 | } 56 | 57 | public UserPassport() { 58 | 59 | } 60 | 61 | /** 62 | * @param id 63 | * @param username 64 | * @param passwordSalt 65 | * @param salt 66 | * @param ticket 67 | */ 68 | public UserPassport(int id, String username, String passwordSalt, String salt, String ticket) { 69 | super(); 70 | this.id = id; 71 | this.username = username; 72 | this.passwordSalt = passwordSalt; 73 | this.salt = salt; 74 | this.ticket = ticket; 75 | } 76 | 77 | /** 78 | * @param username 79 | * @param passwordSalt 80 | * @param salt 81 | * @param ticket 82 | */ 83 | public UserPassport(String username, String passwordSalt, String salt, String ticket) { 84 | super(); 85 | this.username = username; 86 | this.passwordSalt = passwordSalt; 87 | this.salt = salt; 88 | this.ticket = ticket; 89 | } 90 | 91 | /** 92 | * @param username 93 | * @param passwordSalt 94 | * @param salt 95 | */ 96 | public UserPassport(String username, String passwordSalt, String salt) { 97 | super(); 98 | this.username = username; 99 | this.passwordSalt = passwordSalt; 100 | this.salt = salt; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "UserPassport [id=" + id + ", email=" + username + ", passwordSalt=" + passwordSalt 106 | + ", salt=" + salt + ", ticket=" + ticket + "]"; 107 | } 108 | 109 | public String getUsername() { 110 | return username; 111 | } 112 | 113 | public String getSalt() { 114 | return salt; 115 | } 116 | 117 | public String getPasswordSalt() { 118 | return passwordSalt; 119 | } 120 | 121 | public boolean isAdmin(){ 122 | return "admin".equalsIgnoreCase(username); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/model/ZonesFile.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.model; 2 | 3 | import org.apache.commons.lang3.builder.ToStringBuilder; 4 | 5 | /** 6 | * User: cairne 7 | * Date: 13-5-12 8 | * Time: 下午6:59 9 | */ 10 | public class ZonesFile { 11 | 12 | private String text; 13 | 14 | private String name; 15 | 16 | private String user; 17 | 18 | /** 19 | * 1: public 2:personal 20 | */ 21 | private int type; 22 | 23 | private int id; 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public ZonesFile setName(String name) { 30 | this.name = name; 31 | return this; 32 | } 33 | 34 | public String getText() { 35 | return text; 36 | } 37 | 38 | public ZonesFile setText(String text) { 39 | this.text = text; 40 | return this; 41 | } 42 | 43 | public String getUser() { 44 | return user; 45 | } 46 | 47 | public ZonesFile setUser(String user) { 48 | this.user = user; 49 | return this; 50 | } 51 | 52 | public int getId() { 53 | return id; 54 | } 55 | 56 | public ZonesFile setId(int id) { 57 | this.id = id; 58 | return this; 59 | } 60 | 61 | public int getType() { 62 | return type; 63 | } 64 | 65 | public ZonesFile setType(int type) { 66 | this.type = type; 67 | return this; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return ToStringBuilder.reflectionToString(this); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/UserPassportSerivce.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service; 2 | 3 | import us.codecraft.blackhole.suite.exception.LoginException; 4 | import us.codecraft.blackhole.suite.exception.RegisterException; 5 | import us.codecraft.blackhole.suite.model.UserPassport; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | public interface UserPassportSerivce { 10 | 11 | public UserPassport addUserPassport(String username, String password) throws RegisterException; 12 | 13 | public UserPassport doLogin(String username, String password) throws LoginException; 14 | 15 | public UserPassport getUserPassport(HttpServletRequest request); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/UserZonesService.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service; 2 | 3 | import us.codecraft.blackhole.suite.model.UserPassport; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | /** 9 | * User: cairne 10 | * Date: 13-5-14 11 | * Time: 下午1:22 12 | */ 13 | public interface UserZonesService { 14 | 15 | public String getZones(UserPassport userPassport); 16 | 17 | public void updateZones(UserPassport userPassport,String zones); 18 | 19 | /** 20 | * merge zones when not login to userZones 21 | * @param request 22 | * @param response 23 | * @param userPassport 24 | */ 25 | public void mergeUserZones(HttpServletRequest request, HttpServletResponse response,UserPassport userPassport); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/ZonesFileService.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service; 2 | 3 | import us.codecraft.blackhole.suite.model.UserPassport; 4 | import us.codecraft.blackhole.suite.model.ZonesFile; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * User: cairne 11 | * Date: 13-5-14 12 | * Time: 下午1:22 13 | */ 14 | public interface ZonesFileService { 15 | 16 | public Map> getZonesFileList(UserPassport userPassport); 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/impl/UserPassportServiceImpl.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import us.codecraft.blackhole.suite.constant.LoginConstant; 5 | import us.codecraft.blackhole.suite.dao.UserPassportDAO; 6 | import us.codecraft.blackhole.suite.exception.LoginException; 7 | import us.codecraft.blackhole.suite.exception.RegisterException; 8 | import us.codecraft.blackhole.suite.model.UserPassport; 9 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 10 | import us.codecraft.blackhole.suite.util.UserPassportUtil; 11 | 12 | import javax.annotation.Resource; 13 | import javax.servlet.http.HttpServletRequest; 14 | 15 | @Service 16 | public class UserPassportServiceImpl implements UserPassportSerivce { 17 | 18 | @Resource 19 | private UserPassportDAO userPassportDAO; 20 | 21 | 22 | /** 23 | * @param username 24 | * @param password 25 | * @return 26 | */ 27 | @Override 28 | public UserPassport addUserPassport(String username, String password) throws RegisterException { 29 | if (userPassportDAO.getByUsername(username) != null) { 30 | throw new RegisterException(LoginConstant.REGISTER_ERROR_USERNAME_EXIST); 31 | } 32 | String salt = UserPassportUtil.generateSalt(); 33 | String ticket = UserPassportUtil.generateTicket(username); 34 | while (userPassportDAO.getByTicket(ticket) != null) { 35 | ticket = UserPassportUtil.generateTicket(username); 36 | } 37 | String passwordSalt = UserPassportUtil.salt(password, salt); 38 | UserPassport userPassport = new UserPassport(username, passwordSalt, salt, ticket); 39 | try { 40 | userPassportDAO.insert(userPassport); 41 | } catch (Exception e) { 42 | throw new RegisterException(LoginConstant.REGISTER_ERROR); 43 | } 44 | UserPassport byUsername = userPassportDAO.getByUsername(username); 45 | userPassport.setId(byUsername.getId()); 46 | return userPassport; 47 | } 48 | 49 | /** 50 | * @param username 51 | * @param password 52 | * @return 53 | */ 54 | @Override 55 | public UserPassport doLogin(String username, String password) throws LoginException { 56 | UserPassport userPassport = userPassportDAO.getByUsername(username); 57 | if (userPassport == null) { 58 | throw new LoginException(LoginConstant.LOGIN_ERROR_USER_NOTEXIST); 59 | } 60 | String passwordSalt = UserPassportUtil.salt(password, userPassport.getSalt()); 61 | if (passwordSalt.equals(userPassport.getPasswordSalt())) { 62 | // //登录时重新生成票 63 | // final String generateTicket = UserPassportUtil.generateTicket(userPassport.getUsername()); 64 | // userPassport.setTicket(generateTicket); 65 | // userPassportDAO.update(userPassport); 66 | return userPassport; 67 | } else { 68 | throw new LoginException(LoginConstant.LOGIN_ERROR_PASSWORD_INCORRECT); 69 | } 70 | } 71 | 72 | @Override 73 | public UserPassport getUserPassport(HttpServletRequest request) { 74 | final String ticketFromCookie = UserPassportUtil.getTicketFromCookie(request); 75 | if (ticketFromCookie == null) { 76 | return null; 77 | } 78 | return userPassportDAO.getByTicket(ticketFromCookie); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/impl/UserZonesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import org.springframework.web.context.request.ServletRequestAttributes; 6 | import us.codecraft.blackhole.suite.dao.UserZonesDAO; 7 | import us.codecraft.blackhole.suite.model.UserPassport; 8 | import us.codecraft.blackhole.suite.service.UserZonesService; 9 | import us.codecraft.blackhole.suite.util.CookieUtils; 10 | import us.codecraft.blackhole.suite.util.UserZonesUtils; 11 | 12 | import javax.annotation.Resource; 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | 16 | /** 17 | * User: cairne 18 | * Date: 13-5-26 19 | * Time: 上午10:58 20 | */ 21 | @Service 22 | public class UserZonesServiceImpl implements UserZonesService { 23 | 24 | 25 | @Resource 26 | private UserZonesDAO userZonesDAO; 27 | 28 | @Override 29 | public String getZones(UserPassport userPassport) { 30 | if (userPassport != null) { 31 | return userZonesDAO.getZones(userPassport.getId()); 32 | } 33 | return null; 34 | } 35 | 36 | @Override 37 | public void updateZones(UserPassport userPassport, String zones) { 38 | if (userPassport != null) { 39 | userZonesDAO.updateZones(userPassport.getId(), zones); 40 | } 41 | } 42 | 43 | @Override 44 | public void mergeUserZones(HttpServletRequest request, HttpServletResponse response,UserPassport userPassport) { 45 | String userZones = getZones(userPassport); 46 | String cookieZones = CookieUtils.getZones(request); 47 | if (cookieZones != null) { 48 | userZones = UserZonesUtils.merge(userZones, cookieZones); 49 | updateZones(userPassport, userZones); 50 | CookieUtils.saveZones(response,cookieZones); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/service/impl/ZonesFileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.service.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import us.codecraft.blackhole.suite.dao.ZonesFileDAO; 5 | import us.codecraft.blackhole.suite.model.UserPassport; 6 | import us.codecraft.blackhole.suite.model.ZonesFile; 7 | import us.codecraft.blackhole.suite.service.ZonesFileService; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.Collections; 11 | import java.util.LinkedHashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * User: cairne 17 | * Date: 13-5-14 18 | * Time: 下午1:23 19 | */ 20 | @Service 21 | public class ZonesFileServiceImpl implements ZonesFileService{ 22 | 23 | @Resource 24 | private ZonesFileDAO zonesFileDAO; 25 | 26 | @Override 27 | public Map> getZonesFileList(UserPassport userPassport) { 28 | Map> result = new LinkedHashMap>(); 29 | List zonesFiles = zonesFileDAO.findPublic(); 30 | result.put("public", zonesFiles); 31 | if (userPassport != null) { 32 | List byUser = zonesFileDAO.findByUser(userPassport.getUsername()); 33 | result.put("personal", byUser); 34 | } else { 35 | result.put("personal", Collections.emptyList()); 36 | } 37 | return result; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/CookieUtils.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import sun.misc.BASE64Decoder; 5 | import sun.misc.BASE64Encoder; 6 | 7 | import javax.servlet.http.Cookie; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | import java.io.UnsupportedEncodingException; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * User: cairne 16 | * Date: 13-5-14 17 | * Time: 上午7:18 18 | */ 19 | public class CookieUtils { 20 | 21 | public static final String TICKET_KEY = "_t"; 22 | 23 | public final static String ZONES_KEY = "_zones"; 24 | 25 | public static String getCookie(HttpServletRequest request, String key) { 26 | final Cookie[] cookies = request.getCookies(); 27 | return getCookie(cookies, key); 28 | } 29 | 30 | public static String getZones(HttpServletRequest request) { 31 | String zones = CookieUtils.getCookie(request, CookieUtils.ZONES_KEY); 32 | if (zones != null) { 33 | BASE64Decoder base64Decoder = new BASE64Decoder(); 34 | try { 35 | zones = new String(base64Decoder.decodeBuffer(zones), "utf-8"); 36 | } catch (IOException e) { 37 | zones = null; 38 | } 39 | } 40 | return zones; 41 | } 42 | 43 | public static void saveZones(HttpServletResponse response,String text){ 44 | BASE64Encoder base64Encoder = new BASE64Encoder(); 45 | //cookie only support single line,so encode text to base64. 46 | String encode = null; 47 | try { 48 | encode = base64Encoder.encode(text.getBytes("utf-8")); 49 | } catch (UnsupportedEncodingException e) { 50 | } 51 | CookieUtils.saveCookie(response, CookieUtils.ZONES_KEY, encode, 52 | (int) TimeUnit.DAYS.toSeconds(30), "/"); 53 | } 54 | 55 | public static String getCookie(Cookie[] cookies, String key) { 56 | if (cookies == null || cookies.length == 0) { 57 | return null; 58 | } 59 | 60 | for (int i = 0; i < cookies.length; i++) { 61 | if (cookies[i].getName().equals(key)) { 62 | return cookies[i].getValue(); 63 | } 64 | } 65 | 66 | return null; 67 | } 68 | 69 | public static void saveCookie(HttpServletResponse response, String key, String value, 70 | int second, String path) { 71 | value = StringUtils.remove(value, '\n'); 72 | value = StringUtils.remove(value, '\r'); 73 | Cookie cookie = new Cookie(key, value); 74 | cookie.setPath(path); 75 | cookie.setMaxAge(second); 76 | response.addCookie(cookie); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/DoubleKeyMap.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author yihua.huang@dianping.com 8 | * @date Dec 14, 2012 9 | */ 10 | public class DoubleKeyMap extends MultiKeyMapBase { 11 | private Map> map; 12 | 13 | public DoubleKeyMap() { 14 | init(); 15 | } 16 | 17 | public DoubleKeyMap(Map> map) { 18 | this(map,DEFAULT_CLAZZ); 19 | } 20 | 21 | public DoubleKeyMap(Class protoMapClass) { 22 | super(protoMapClass); 23 | init(); 24 | } 25 | 26 | private void init() { 27 | if (map == null) { 28 | map = this.>newMap(); 29 | } 30 | } 31 | 32 | /** 33 | * init map with protoMapClass 34 | * 35 | * @param protoMapClass 36 | */ 37 | @SuppressWarnings("rawtypes") 38 | public DoubleKeyMap(Map> map, Class protoMapClass) { 39 | super(protoMapClass); 40 | this.map = map; 41 | init(); 42 | } 43 | 44 | /** 45 | * @param key 46 | * @return 47 | */ 48 | public Map get(K1 key) { 49 | return map.get(key); 50 | } 51 | 52 | public Set>> entrySet() { 53 | return map.entrySet(); 54 | } 55 | 56 | /** 57 | * @param key1 58 | * @param key2 59 | * @return 60 | */ 61 | public V get(K1 key1, K2 key2) { 62 | if (get(key1) == null) { 63 | return null; 64 | } 65 | return get(key1).get(key2); 66 | } 67 | 68 | 69 | /** 70 | * @param key1 71 | * @param submap 72 | * @return 73 | */ 74 | public V put(K1 key1, Map submap) { 75 | return put(key1, submap); 76 | } 77 | 78 | /** 79 | * @param key1 80 | * @param key2 81 | * @param value 82 | * @return 83 | */ 84 | public V put(K1 key1, K2 key2, V value) { 85 | if (map.get(key1) == null) { 86 | map.put(key1, this.newMap()); 87 | } 88 | return get(key1).put(key2, value); 89 | } 90 | 91 | /** 92 | * @param key1 93 | * @param key2 94 | * @return 95 | */ 96 | public V remove(K1 key1, K2 key2) { 97 | if (get(key1) == null) { 98 | return null; 99 | } 100 | V remove = get(key1).remove(key2); 101 | // 如果上一级map为空,把它也回收掉 102 | if (get(key1).size() == 0) { 103 | remove(key1); 104 | } 105 | return remove; 106 | } 107 | 108 | /** 109 | * @param key1 110 | * @return 111 | */ 112 | public Map remove(K1 key1) { 113 | Map remove = map.remove(key1); 114 | return remove; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/IPUtils.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.net.Inet6Address; 8 | import java.net.InetAddress; 9 | import java.net.NetworkInterface; 10 | import java.net.SocketException; 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.Enumeration; 14 | 15 | /** 16 | * User: cairne 17 | * Date: 13-5-13 18 | * Time: 上午8:09 19 | */ 20 | public class IPUtils { 21 | 22 | public static String getClientIp(HttpServletRequest request) { 23 | return request.getRemoteAddr(); 24 | } 25 | 26 | private static Logger logger = LoggerFactory.getLogger(IPUtils.class); 27 | 28 | public static final String LOCAL_LOOP_ADDRESS = "127.0.0.1"; 29 | 30 | public static final String LOCAL_ADDRESS_START = "192.168."; 31 | 32 | public static final String LOCAL = "本地"; 33 | 34 | public static Collection getAllHostAddress() { 35 | try { 36 | Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); 37 | Collection addresses = new ArrayList(); 38 | 39 | while (networkInterfaces.hasMoreElements()) { 40 | NetworkInterface networkInterface = networkInterfaces.nextElement(); 41 | Enumeration inetAddresses = networkInterface.getInetAddresses(); 42 | while (inetAddresses.hasMoreElements()) { 43 | InetAddress inetAddress = inetAddresses.nextElement(); 44 | addresses.add(inetAddress); 45 | } 46 | } 47 | 48 | return addresses; 49 | } catch (SocketException e) { 50 | throw new RuntimeException(e.getMessage(), e); 51 | } 52 | } 53 | 54 | public static Collection getNoLoopbackIP4Addresses() { 55 | Collection noLoopbackIP4Addresses = new ArrayList(); 56 | Collection allInetAddresses = getAllHostAddress(); 57 | 58 | for (InetAddress address : allInetAddresses) { 59 | if (!address.isLoopbackAddress() && !address.isSiteLocalAddress() 60 | && !Inet6Address.class.isInstance(address)) { 61 | noLoopbackIP4Addresses.add(address.getHostAddress()); 62 | } 63 | } 64 | if (noLoopbackIP4Addresses.isEmpty()) { 65 | // 降低过滤标准,将site local address纳入结果 66 | for (InetAddress address : allInetAddresses) { 67 | if (!address.isLoopbackAddress() && !Inet6Address.class.isInstance(address)) { 68 | noLoopbackIP4Addresses.add(address.getHostAddress()); 69 | } 70 | } 71 | } 72 | return noLoopbackIP4Addresses; 73 | } 74 | 75 | /** 76 | * 获取第一个no loop address 77 | * 78 | * @return first no loop address, or null if not exists 79 | */ 80 | public static String getFirstNoLoopbackIP4Address() { 81 | Collection allNoLoopbackIP4Addresses = getNoLoopbackIP4Addresses(); 82 | if (allNoLoopbackIP4Addresses.isEmpty()) { 83 | if (logger.isInfoEnabled()) { 84 | logger.info("Cannot get ip address, seems you don't have a network card!"); 85 | } 86 | return null; 87 | } 88 | return allNoLoopbackIP4Addresses.iterator().next(); 89 | } 90 | 91 | /** 92 | * 直接获取客户端IP地址 93 | * 94 | * @param request 95 | * @return if request is null or ip from request is null return "0.0.0.0" 96 | * @deprecated 97 | */ 98 | public static String directUserIP(final HttpServletRequest request) { 99 | if (request == null) { 100 | return "0.0.0.0"; 101 | } 102 | String ip = request.getRemoteAddr(); 103 | return (ip == null || "".equals(ip)) ? "0.0.0.0" : ip; 104 | } 105 | 106 | /** 107 | * 获得客户端IP地址,如果客户端使用代理服务器,并且代理服务器在HTTP Header中包含相关信息, 则将返回用户真实IP地址 108 | * 109 | * @param request 110 | * @return 111 | * @deprecated use LoginUtils in avatar-biz instead 112 | */ 113 | public static String getUserIP(final HttpServletRequest request) { 114 | if (request == null) { 115 | return "0.0.0.0"; 116 | } 117 | // 获取cdn-src-ip中的源IP 118 | String ip = request.getHeader("Cdn-Src-Ip"); 119 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 120 | ip = request.getHeader("X-Forwarded-For"); 121 | } 122 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 123 | ip = request.getHeader("Proxy-Client-IP"); 124 | } 125 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 126 | ip = request.getHeader("WL-Proxy-Client-IP"); 127 | } 128 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 129 | ip = request.getRemoteAddr(); 130 | } 131 | return (ip == null || "".equals(ip)) ? "0.0.0.0" : ip; 132 | } 133 | 134 | /** 135 | * Retrive mac address for current machine 136 | */ 137 | public static String getMacAddr() { 138 | String macAddr = ""; 139 | String str = ""; 140 | try { 141 | NetworkInterface nic = NetworkInterface.getByName("eth0"); 142 | if (nic != null) { 143 | byte[] buf = nic.getHardwareAddress(); 144 | if (buf != null) { 145 | for (int i = 0; i < buf.length; i++) { 146 | str = str + byteHEX(buf[i]); 147 | } 148 | } 149 | macAddr = str.toUpperCase(); 150 | } else { 151 | Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); 152 | NetworkInterface networkInterface = null; 153 | while (networkInterfaces.hasMoreElements()) { 154 | networkInterface = networkInterfaces.nextElement(); 155 | Enumeration inetAddresses = networkInterface.getInetAddresses(); 156 | while (inetAddresses.hasMoreElements()) { 157 | InetAddress inetAddress = inetAddresses.nextElement(); 158 | if (!inetAddress.isLoopbackAddress() && !Inet6Address.class.isInstance(inetAddress)) { 159 | break; 160 | } 161 | } 162 | } 163 | if (networkInterface != null) { 164 | byte[] buf = networkInterface.getHardwareAddress(); 165 | if (buf != null) { 166 | for (int i = 0; i < buf.length; i++) { 167 | str = str + byteHEX(buf[i]); 168 | } 169 | } 170 | } 171 | macAddr = str.toUpperCase(); 172 | } 173 | } catch (SocketException e) { 174 | e.printStackTrace(); 175 | System.exit(-1); 176 | } 177 | return macAddr; 178 | } 179 | 180 | /** 181 | * Retrive ip address for current machine 182 | */ 183 | public static String getLocalIP() { 184 | String ip = ""; 185 | try { 186 | Enumeration e1 = NetworkInterface.getNetworkInterfaces(); 187 | while (e1.hasMoreElements()) { 188 | NetworkInterface ni = (NetworkInterface) e1.nextElement(); 189 | if (ni != null && !ni.getName().equals("eth1")) { 190 | continue; 191 | } else { 192 | Enumeration e2 = ni.getInetAddresses(); 193 | while (e2.hasMoreElements()) { 194 | InetAddress ia = (InetAddress) e2.nextElement(); 195 | if (ia instanceof Inet6Address) 196 | continue; 197 | ip = ia.getHostAddress(); 198 | } 199 | break; 200 | } 201 | } 202 | if (!"".equals(ip)) { 203 | return ip; 204 | } 205 | e1 = NetworkInterface.getNetworkInterfaces(); 206 | while (e1.hasMoreElements()) { 207 | NetworkInterface ni = (NetworkInterface) e1.nextElement(); 208 | if (ni != null && !ni.getName().equals("en1")) { 209 | continue; 210 | } else { 211 | Enumeration e2 = ni.getInetAddresses(); 212 | while (e2.hasMoreElements()) { 213 | InetAddress ia = (InetAddress) e2.nextElement(); 214 | if (ia instanceof Inet6Address) 215 | continue; 216 | ip = ia.getHostAddress(); 217 | } 218 | break; 219 | } 220 | } 221 | } catch (SocketException e) { 222 | e.printStackTrace(); 223 | System.exit(-1); 224 | } 225 | return ip; 226 | } 227 | 228 | public static String byteHEX(byte ib) { 229 | char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 230 | char[] ob = new char[2]; 231 | ob[0] = Digit[(ib >>> 4) & 0X0F]; 232 | ob[1] = Digit[ib & 0X0F]; 233 | String s = new String(ob); 234 | return s; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/MultiKeyMapBase.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | /** 4 | * @author yihua.huang@dianping.com 5 | * @date Dec 14, 2012 6 | */ 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * multikey map, some basic objects * 13 | * 14 | * @author yihua.huang 15 | */ 16 | public abstract class MultiKeyMapBase { 17 | 18 | protected static final Class DEFAULT_CLAZZ = HashMap.class; 19 | @SuppressWarnings("rawtypes") 20 | private Class protoMapClass = DEFAULT_CLAZZ; 21 | 22 | public MultiKeyMapBase() { 23 | } 24 | 25 | @SuppressWarnings("rawtypes") 26 | public MultiKeyMapBase(Class protoMapClass) { 27 | this.protoMapClass = protoMapClass; 28 | } 29 | 30 | @SuppressWarnings("unchecked") 31 | protected Map newMap() { 32 | try { 33 | return (Map) protoMapClass.newInstance(); 34 | } catch (InstantiationException e) { 35 | throw new IllegalArgumentException("wrong proto type map " 36 | + protoMapClass); 37 | } catch (IllegalAccessException e) { 38 | throw new IllegalArgumentException("wrong proto type map " 39 | + protoMapClass); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/RequestThreadUtils.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.springframework.web.context.request.RequestAttributes; 4 | import org.springframework.web.context.request.RequestContextHolder; 5 | import us.codecraft.blackhole.suite.model.UserPassport; 6 | 7 | /** 8 | * @author cairne huangyihua@diandian.com 9 | * @date 2012-3-31 10 | */ 11 | public class RequestThreadUtils { 12 | 13 | private final static String PASSPORT_KEY = "userPassport"; 14 | 15 | public static UserPassport getUserPassport() { 16 | return getAttribute(PASSPORT_KEY, UserPassport.class); 17 | } 18 | 19 | public static void setUserPassport(UserPassport userPassport) { 20 | setAttribute(PASSPORT_KEY, userPassport); 21 | } 22 | 23 | public static String getAttribute(String name) { 24 | return getAttribute(name, String.class); 25 | } 26 | 27 | public static T getAttribute(String name, Class clazz) { 28 | return clazz.cast(RequestContextHolder.currentRequestAttributes().getAttribute(name, 29 | RequestAttributes.SCOPE_REQUEST)); 30 | } 31 | 32 | public static void setAttribute(String name, T t) { 33 | RequestContextHolder.currentRequestAttributes().setAttribute(name, t, 34 | RequestAttributes.SCOPE_REQUEST); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/SpringLocator.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 5 | import org.springframework.context.ApplicationContext; 6 | import org.springframework.context.ApplicationContextAware; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author yihua.huang@dianping.com 11 | * @date Dec 15, 2012 12 | */ 13 | @Component 14 | public class SpringLocator implements ApplicationContextAware { 15 | 16 | public static ApplicationContext applicationContext; 17 | 18 | public static Object getBean(String name) throws BeansException { 19 | return applicationContext.getBean(name); 20 | } 21 | 22 | public static T getBean(String name, Class requiredType) 23 | throws BeansException { 24 | return applicationContext.getBean(name, requiredType); 25 | } 26 | 27 | public static T getBean(Class requiredType) throws BeansException { 28 | return applicationContext.getBean(requiredType); 29 | } 30 | 31 | public static Object getBean(String name, Object... args) 32 | throws BeansException { 33 | return applicationContext.getBean(name, args); 34 | } 35 | 36 | public static boolean containsBean(String name) { 37 | return applicationContext.containsBean(name); 38 | } 39 | 40 | public static boolean isSingleton(String name) 41 | throws NoSuchBeanDefinitionException { 42 | return applicationContext.isSingleton(name); 43 | } 44 | 45 | public static boolean isPrototype(String name) 46 | throws NoSuchBeanDefinitionException { 47 | return applicationContext.isPrototype(name); 48 | } 49 | 50 | public static boolean isTypeMatch(String name, Class targetType) 51 | throws NoSuchBeanDefinitionException { 52 | return applicationContext.isTypeMatch(name, targetType); 53 | } 54 | 55 | public static Class getType(String name) 56 | throws NoSuchBeanDefinitionException { 57 | return applicationContext.getType(name); 58 | } 59 | 60 | public static String[] getAliases(String name) { 61 | return applicationContext.getAliases(name); 62 | } 63 | 64 | @Override 65 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 66 | SpringLocator.applicationContext = applicationContext; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/UserPassportUtil.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.util.DigestUtils; 5 | import us.codecraft.blackhole.suite.model.UserPassport; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.util.Random; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * 14 | */ 15 | public class UserPassportUtil { 16 | 17 | private static Random random = new Random(); 18 | 19 | public static String salt(String password, String salt) { 20 | return DigestUtils.md5DigestAsHex((password + salt).getBytes()); 21 | } 22 | 23 | public static boolean checkPassword(UserPassport userPassport, String password) { 24 | if (salt(password, userPassport.getSalt()).equals(userPassport.getPasswordSalt())) { 25 | return true; 26 | } else { 27 | return false; 28 | } 29 | } 30 | 31 | public static String generateTicket(String email) { 32 | return DigestUtils.md5DigestAsHex((email + random.nextInt() + System.currentTimeMillis()) 33 | .getBytes()); 34 | } 35 | 36 | public static String generateSalt() { 37 | return StringUtils.substring(DigestUtils.md5DigestAsHex(("" + random.nextInt() + System 38 | .currentTimeMillis()).getBytes()), 20); 39 | } 40 | 41 | public static String getTicketFromCookie(HttpServletRequest request) { 42 | String ticket = CookieUtils.getCookie(request, CookieUtils.TICKET_KEY); 43 | return ticket; 44 | } 45 | 46 | public static void saveUserPassportCookie(HttpServletResponse response, UserPassport userPassport) { 47 | CookieUtils.saveCookie(response, CookieUtils.TICKET_KEY, userPassport.getTicket(), 48 | (int) TimeUnit.DAYS.toSeconds(30), "/"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/util/UserZonesUtils.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.codehaus.jackson.map.ObjectMapper; 5 | import org.codehaus.jackson.map.annotate.JsonSerialize; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.LinkedHashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * User: cairne 15 | * Date: 13-5-26 16 | * Time: 下午4:19 17 | */ 18 | public class UserZonesUtils { 19 | 20 | public static class DomainConfig { 21 | private String domain; 22 | private boolean active; 23 | private String ip; 24 | private String comment; 25 | 26 | public DomainConfig() { 27 | } 28 | 29 | public DomainConfig(String domain, String ip, boolean active) { 30 | this.active = active; 31 | this.domain = domain; 32 | this.ip = ip; 33 | } 34 | 35 | public DomainConfig(String domain, String ip, String comment, boolean active) { 36 | this.active = active; 37 | this.domain = domain; 38 | this.comment = comment; 39 | this.ip = ip; 40 | } 41 | 42 | public String getComment() { 43 | return comment; 44 | } 45 | 46 | public void setComment(String comment) { 47 | this.comment = comment; 48 | } 49 | 50 | public boolean isActive() { 51 | return active; 52 | } 53 | 54 | public void setActive(boolean active) { 55 | this.active = active; 56 | } 57 | 58 | public String getIp() { 59 | return ip; 60 | } 61 | 62 | public void setIp(String ip) { 63 | this.ip = ip; 64 | } 65 | 66 | public String getDomain() { 67 | return domain; 68 | } 69 | 70 | public void setDomain(String domain) { 71 | this.domain = domain; 72 | } 73 | } 74 | 75 | public static String fromJson(String json) throws IOException { 76 | StringBuilder sb = new StringBuilder(); 77 | ObjectMapper objectMapper = new ObjectMapper(); 78 | List> list = (List>) objectMapper.readValue(json, List.class); 79 | for (Map objectMap : list) { 80 | List config = (List) objectMap.get("config"); 81 | for (Map domainConfig : config) { 82 | sb.append(((Boolean) domainConfig.get("active") ? "" : "#") + domainConfig.get("ip") + "\t" + domainConfig.get("domain") + (domainConfig.get("comment") != null && ((String) domainConfig.get("comment")).trim().length() > 0 ? "\t#" + domainConfig.get("comment") : "") + "\n"); 83 | } 84 | } 85 | return sb.toString().trim(); 86 | } 87 | 88 | public static String toJson(String text) throws IOException { 89 | if (StringUtils.isBlank(text)) { 90 | return "[]"; 91 | } 92 | Map> map = new LinkedHashMap>(); 93 | String[] lines = text.split("\n"); 94 | for (String line : lines) { 95 | boolean isActive = true; 96 | line = StringUtils.trim(line); 97 | if (StringUtils.startsWith(line, "#")) { 98 | isActive = false; 99 | line = line.replaceAll("^#+", ""); 100 | } 101 | String[] tokens = line.split("\\s+"); 102 | if (tokens.length > 1) { 103 | String ip = tokens[0]; 104 | String comment = null; 105 | //scan for commet 106 | for (String token : tokens) { 107 | if (token.startsWith("#")) { 108 | comment = token.replaceAll("^#+", ""); 109 | break; 110 | } 111 | } 112 | 113 | for (int i = 1; i < tokens.length; i++) { 114 | String domain = tokens[i]; 115 | if (domain.startsWith("#")) { 116 | continue; 117 | } 118 | List domainConfigs = map.get(domain); 119 | if (domainConfigs == null) { 120 | domainConfigs = new ArrayList(); 121 | map.put(domain, domainConfigs); 122 | } 123 | domainConfigs.add(new DomainConfig(domain, ip, comment, isActive)); 124 | } 125 | } 126 | } 127 | List> list = new ArrayList>(); 128 | for (Map.Entry> listEntry : map.entrySet()) { 129 | Map innerMap = new LinkedHashMap(); 130 | innerMap.put("domain", listEntry.getKey()); 131 | innerMap.put("config", listEntry.getValue()); 132 | list.add(innerMap); 133 | } 134 | 135 | ObjectMapper objectMapper = new ObjectMapper(); 136 | objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL); 137 | return objectMapper.writeValueAsString(list); 138 | } 139 | 140 | public static String merge(String textA, String textB) { 141 | if (StringUtils.isBlank(textA) && StringUtils.isBlank(textB)) { 142 | return ""; 143 | } 144 | if (StringUtils.isBlank(textA)) { 145 | return textB; 146 | } 147 | if (StringUtils.isBlank(textB)) { 148 | return textA; 149 | } 150 | String[] linesA = textA.split("\n"); 151 | String[] linesB = textB.split("\n"); 152 | // domain-ip-active 153 | DoubleKeyMap linesMap = new DoubleKeyMap(new LinkedHashMap>(), LinkedHashMap.class); 154 | for (String line : linesA) { 155 | extractLine(linesMap, line); 156 | } 157 | for (String line : linesB) { 158 | extractLine(linesMap, line); 159 | } 160 | StringBuilder sb = new StringBuilder(); 161 | //only one config can be active for a domain 162 | for (Map.Entry> mapEntry : linesMap.entrySet()) { 163 | boolean hasOneActive = false; 164 | for (Map.Entry entry : mapEntry.getValue().entrySet()) { 165 | if (entry.getValue().isActive()) { 166 | if (!hasOneActive) { 167 | hasOneActive = true; 168 | } else { 169 | entry.getValue().setActive(false); 170 | } 171 | } 172 | } 173 | } 174 | 175 | for (Map.Entry> mapEntry : linesMap.entrySet()) { 176 | for (Map.Entry entry : mapEntry.getValue().entrySet()) { 177 | sb.append((entry.getValue().isActive() ? "" : "#") + entry.getKey() + "\t" + mapEntry.getKey() + 178 | (!StringUtils.isBlank(entry.getValue().getComment()) ? "\t#" + entry.getValue().getComment() : "") + "\n"); 179 | } 180 | } 181 | return sb.toString().trim(); 182 | } 183 | 184 | private static void extractLine(DoubleKeyMap linesMap, String line) { 185 | boolean isActive = true; 186 | String comment = null; 187 | line = StringUtils.trim(line); 188 | if (StringUtils.startsWith(line, "#")) { 189 | isActive = false; 190 | line = line.replaceAll("^#+", ""); 191 | } 192 | String[] tokens = line.split("\\s+"); 193 | if (tokens.length > 1) { 194 | String ip = tokens[0]; 195 | for (String token : tokens) { 196 | if (token.startsWith("#")) { 197 | comment = token.replaceAll("^#+", ""); 198 | break; 199 | } 200 | } 201 | for (int i = 1; i < tokens.length; i++) { 202 | String domain = tokens[i]; 203 | if (domain.startsWith("#")) { 204 | continue; 205 | } 206 | if (linesMap.get(domain, ip) == null) { 207 | linesMap.put(domain, ip, new DomainConfig(domain, ip, comment, isActive)); 208 | } else { 209 | if ((!linesMap.get(domain, ip).isActive() && isActive) || (StringUtils.isBlank(linesMap.get(domain, ip).getComment()) && StringUtils.isNotBlank(comment))) { 210 | linesMap.put(domain, ip, new DomainConfig(domain, ip, comment, isActive)); 211 | } 212 | } 213 | } 214 | } 215 | } 216 | 217 | } 218 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/HelpController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.servlet.ModelAndView; 6 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 7 | import us.codecraft.blackhole.suite.util.IPUtils; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-12 14 | * Time: 下午7:40 15 | */ 16 | @Controller 17 | @RequestMapping("help") 18 | public class HelpController extends MultiActionController { 19 | 20 | @RequestMapping("dns") 21 | public ModelAndView dnsHelp() throws IOException { 22 | String localIp = IPUtils.getLocalIP(); 23 | ModelAndView modelAndView = new ModelAndView("help-dns"); 24 | modelAndView.addObject("localIp", localIp); 25 | return modelAndView; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/LoginController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.ResponseBody; 10 | import org.springframework.web.servlet.ModelAndView; 11 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 12 | import us.codecraft.blackhole.suite.exception.LoginException; 13 | import us.codecraft.blackhole.suite.model.JsonResult; 14 | import us.codecraft.blackhole.suite.model.UserPassport; 15 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 16 | import us.codecraft.blackhole.suite.service.UserZonesService; 17 | import us.codecraft.blackhole.suite.util.UserPassportUtil; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.util.Locale; 22 | import java.util.Map; 23 | 24 | /** 25 | * User: cairne 26 | * Date: 13-5-15 27 | * Time: 上午6:10 28 | */ 29 | @Controller 30 | @RequestMapping("login") 31 | public class LoginController extends MultiActionController { 32 | 33 | @Autowired 34 | private UserPassportSerivce userPassportSerivce; 35 | 36 | @Autowired 37 | private MessageSource messageSource; 38 | 39 | @Autowired 40 | private UserZonesService userZonesService; 41 | 42 | @RequestMapping(value = "", method = RequestMethod.GET) 43 | public ModelAndView showLogin() { 44 | ModelAndView modelAndView = new ModelAndView("login"); 45 | return modelAndView; 46 | } 47 | 48 | @ResponseBody 49 | @RequestMapping(value = "", method = RequestMethod.POST) 50 | public Object doLogin(HttpServletRequest request, HttpServletResponse response, @RequestParam("username") String username, @RequestParam("password") String password) { 51 | UserPassport userPassport = null; 52 | try { 53 | userPassport = userPassportSerivce.doLogin(username, password); 54 | UserPassportUtil.saveUserPassportCookie(response, userPassport); 55 | userZonesService.mergeUserZones(request, response, userPassport); 56 | Map resultMap = JsonResult.success("Success!").toMap(); 57 | resultMap.put("token", userPassport.getTicket()); 58 | return resultMap; 59 | } catch (LoginException e) { 60 | return JsonResult.error(messageSource.getMessage(e.getCode(), null, Locale.CHINA)); 61 | } 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/RegisterController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.ResponseBody; 10 | import org.springframework.web.servlet.ModelAndView; 11 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 12 | import us.codecraft.blackhole.suite.exception.RegisterException; 13 | import us.codecraft.blackhole.suite.model.JsonResult; 14 | import us.codecraft.blackhole.suite.model.UserPassport; 15 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 16 | import us.codecraft.blackhole.suite.service.UserZonesService; 17 | import us.codecraft.blackhole.suite.util.UserPassportUtil; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.util.Locale; 22 | import java.util.Map; 23 | 24 | /** 25 | * User: cairne 26 | * Date: 13-5-15 27 | * Time: 上午6:10 28 | */ 29 | @Controller 30 | @RequestMapping("register") 31 | public class RegisterController extends MultiActionController { 32 | 33 | @Autowired 34 | private UserPassportSerivce userPassportSerivce; 35 | 36 | @Autowired 37 | private UserZonesService userZonesService; 38 | 39 | @Autowired 40 | private MessageSource messageSource; 41 | 42 | @RequestMapping(value = "", method = RequestMethod.GET) 43 | public ModelAndView showRegister() { 44 | ModelAndView modelAndView = new ModelAndView("register"); 45 | return modelAndView; 46 | } 47 | 48 | @ResponseBody 49 | @RequestMapping(value = "", method = RequestMethod.POST) 50 | public Object doRegister(HttpServletRequest request, HttpServletResponse response, @RequestParam("username") String username, @RequestParam("password") String password) { 51 | UserPassport userPassport = null; 52 | try { 53 | userPassport = userPassportSerivce.addUserPassport(username, password); 54 | UserPassportUtil.saveUserPassportCookie(response, userPassport); 55 | Map resultMap = JsonResult.success("Sign up success!").toMap(); 56 | userZonesService.mergeUserZones(request, response, userPassport); 57 | resultMap.put("token", userPassport.getTicket()); 58 | return resultMap; 59 | } catch (RegisterException e) { 60 | return JsonResult.error(messageSource.getMessage(e.getCode(), null, Locale.CHINA)); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ShareController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.codehaus.jackson.map.ObjectMapper; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.servlet.ModelAndView; 8 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 9 | import us.codecraft.blackhole.suite.model.UserPassport; 10 | import us.codecraft.blackhole.suite.model.ZonesFile; 11 | import us.codecraft.blackhole.suite.service.ZonesFileService; 12 | import us.codecraft.blackhole.suite.util.RequestThreadUtils; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import java.io.IOException; 16 | import java.util.LinkedHashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | * User: cairne 22 | * Date: 13-5-12 23 | * Time: 下午7:40 24 | */ 25 | @Controller 26 | @RequestMapping("share") 27 | public class ShareController extends MultiActionController { 28 | 29 | 30 | @Autowired 31 | private ZonesFileService zonesFileService; 32 | 33 | @RequestMapping("") 34 | public ModelAndView dashboard(HttpServletRequest request) throws IOException { 35 | ModelAndView modelAndView = new ModelAndView("shareboard"); 36 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 37 | Map> zonesFileList = zonesFileService.getZonesFileList(userPassport); 38 | modelAndView.addAllObjects(zonesFileList); 39 | 40 | constructJsonData(modelAndView, zonesFileList); 41 | return modelAndView; 42 | } 43 | 44 | private void constructJsonData(ModelAndView modelAndView, Map> zonesFileList) throws IOException { 45 | Map dataMap = new LinkedHashMap(); 46 | for (List zonesFiles : zonesFileList.values()) { 47 | for (ZonesFile zonesFile : zonesFiles) { 48 | dataMap.put(zonesFile.getId(), zonesFile); 49 | } 50 | } 51 | ObjectMapper objectMapper = new ObjectMapper(); 52 | String data = objectMapper.writeValueAsString(dataMap); 53 | modelAndView.addObject("data", data); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ZonesApplyController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 10 | import us.codecraft.blackhole.suite.connector.BlackholeConnector; 11 | import us.codecraft.blackhole.suite.connector.ZonesFileApplyer; 12 | import us.codecraft.blackhole.suite.model.JsonResult; 13 | import us.codecraft.blackhole.suite.util.IPUtils; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | 17 | /** 18 | * User: cairne 19 | * Date: 13-5-12 20 | * Time: 下午8:53 21 | */ 22 | @Controller 23 | @RequestMapping("apply") 24 | public class ZonesApplyController extends MultiActionController { 25 | 26 | @Autowired 27 | private ZonesFileApplyer zonesFileApplyer; 28 | 29 | @Autowired 30 | private BlackholeConnector blackholeConnector; 31 | 32 | @ResponseBody 33 | @RequestMapping(value = "", method = RequestMethod.POST) 34 | public Object save(@RequestParam("text") String text, HttpServletRequest request) { 35 | zonesFileApplyer.apply(IPUtils.getClientIp(request), text); 36 | if (blackholeConnector.isConnected()) { 37 | return JsonResult.success("Aplly success!"); 38 | } else { 39 | return JsonResult.error("Aplly failed,Blackhole is not connected!"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ZonesEditController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.*; 6 | import org.springframework.web.servlet.ModelAndView; 7 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 8 | import sun.misc.BASE64Decoder; 9 | import sun.misc.BASE64Encoder; 10 | import us.codecraft.blackhole.suite.dao.ZonesFileDAO; 11 | import us.codecraft.blackhole.suite.model.JsonResult; 12 | import us.codecraft.blackhole.suite.model.UserPassport; 13 | import us.codecraft.blackhole.suite.model.ZonesFile; 14 | import us.codecraft.blackhole.suite.service.UserZonesService; 15 | import us.codecraft.blackhole.suite.util.CookieUtils; 16 | import us.codecraft.blackhole.suite.util.RequestThreadUtils; 17 | import us.codecraft.blackhole.suite.util.UserZonesUtils; 18 | 19 | import javax.annotation.Resource; 20 | import javax.servlet.http.HttpServletRequest; 21 | import javax.servlet.http.HttpServletResponse; 22 | import java.io.IOException; 23 | import java.io.UnsupportedEncodingException; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * User: cairne 28 | * Date: 13-5-12 29 | * Time: 下午7:40 30 | */ 31 | @Controller 32 | @RequestMapping("edit") 33 | public class ZonesEditController extends MultiActionController { 34 | 35 | @Resource 36 | private ZonesFileDAO zonesFileDAO; 37 | 38 | @Autowired 39 | private UserZonesService userZonesService; 40 | 41 | @Autowired 42 | private ZonesApplyController zonesApplyController; 43 | 44 | @ResponseBody 45 | @RequestMapping("delete/{id}") 46 | public Object delete(@PathVariable("id") int id) { 47 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 48 | if (userPassport == null) { 49 | return JsonResult.result(403, "请先登录!"); 50 | } 51 | zonesFileDAO.deleteById(id); 52 | return JsonResult.success("删除成功!"); 53 | } 54 | 55 | @RequestMapping("new") 56 | public ModelAndView newZones() { 57 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 58 | if (userPassport == null) { 59 | return new ModelAndView("redirect:/login"); 60 | } 61 | ModelAndView modelAndView = new ModelAndView("edit"); 62 | modelAndView.addObject("name", "新的配置"); 63 | modelAndView.addObject("id", 0); 64 | modelAndView.addObject("content", ""); 65 | modelAndView.addObject("user", userPassport.getUsername()); 66 | return modelAndView; 67 | } 68 | 69 | @RequestMapping("{id}") 70 | public ModelAndView edit(@PathVariable(value = "id") int id) { 71 | ModelAndView modelAndView = new ModelAndView("edit"); 72 | ZonesFile zonesFile = zonesFileDAO.load(id); 73 | if (zonesFile != null) { 74 | modelAndView.addObject("id", id); 75 | modelAndView.addObject("name", zonesFile.getName()); 76 | modelAndView.addObject("content", zonesFile.getText()); 77 | modelAndView.addObject("user", zonesFile.getUser()); 78 | } 79 | return modelAndView; 80 | } 81 | 82 | @RequestMapping("") 83 | public ModelAndView editDefault(HttpServletRequest request) throws IOException { 84 | ModelAndView modelAndView = new ModelAndView("edit"); 85 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 86 | String zones = userZonesService.getZones(userPassport); 87 | if (zones == null) { 88 | zones = CookieUtils.getZones(request); 89 | } 90 | modelAndView.addObject("id", 0); 91 | modelAndView.addObject("content", zones); 92 | modelAndView.addObject("type", "userZones"); 93 | return modelAndView; 94 | } 95 | 96 | @ResponseBody 97 | @RequestMapping(value = "save", method = RequestMethod.POST) 98 | public Object saveDefault(HttpServletRequest request,HttpServletResponse response, @RequestParam("text") String text) throws UnsupportedEncodingException { 99 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 100 | userZonesService.updateZones(userPassport, text); 101 | CookieUtils.saveZones(response,text); 102 | return zonesApplyController.save(text, request); 103 | } 104 | 105 | @ResponseBody 106 | @RequestMapping(value = "save/{id}", method = RequestMethod.POST) 107 | public Object save(@PathVariable(value = "id") int id, @RequestParam("text") String text, @RequestParam("name") String name) { 108 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 109 | if (userPassport == null) { 110 | return JsonResult.result(403, "请先登录!"); 111 | } 112 | int type = userPassport.isAdmin() ? 1 : 2; 113 | if (id == 0) { 114 | ZonesFile zonesFile = new ZonesFile(); 115 | zonesFile.setText(text).setUser(userPassport.getUsername()).setName(name).setType(type); 116 | int add = zonesFileDAO.add(zonesFile); 117 | if (add > 0) { 118 | return JsonResult.success("新增成功!"); 119 | } 120 | } else { 121 | ZonesFile zonesFile = new ZonesFile(); 122 | zonesFile.setText(text).setName(name).setType(type).setId(id); 123 | int update = zonesFileDAO.update(zonesFile); 124 | if (update > 0) { 125 | return JsonResult.success("修改成功!"); 126 | } 127 | } 128 | return JsonResult.error("修改失败!"); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ZonesFileController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RequestMethod; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 9 | import us.codecraft.blackhole.suite.dao.ZonesFileDAO; 10 | import us.codecraft.blackhole.suite.model.ZonesFile; 11 | 12 | import javax.annotation.Resource; 13 | import java.io.IOException; 14 | 15 | /** 16 | * User: cairne 17 | * Date: 13-5-12 18 | * Time: 下午4:46 19 | */ 20 | @Controller 21 | @RequestMapping("zones") 22 | public class ZonesFileController extends MultiActionController { 23 | 24 | @Resource 25 | private ZonesFileDAO zonesFileDAO; 26 | 27 | @ResponseBody 28 | @RequestMapping(value = "get", method = RequestMethod.GET) 29 | public Object show(@RequestParam("id") int id) throws IOException { 30 | ZonesFile zonesFile = zonesFileDAO.load(id); 31 | return zonesFile; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ZonesListController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RequestMethod; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 9 | import us.codecraft.blackhole.suite.model.UserPassport; 10 | import us.codecraft.blackhole.suite.model.ZonesFile; 11 | import us.codecraft.blackhole.suite.service.UserPassportSerivce; 12 | import us.codecraft.blackhole.suite.service.ZonesFileService; 13 | import us.codecraft.blackhole.suite.util.RequestThreadUtils; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.io.IOException; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | * User: cairne 22 | * Date: 13-5-12 23 | * Time: 下午4:46 24 | */ 25 | @Controller 26 | @RequestMapping("zones") 27 | public class ZonesListController extends MultiActionController { 28 | 29 | @Autowired 30 | private ZonesFileService zonesFileService; 31 | 32 | @Autowired 33 | private UserPassportSerivce userPassportSerivce; 34 | 35 | @ResponseBody 36 | @RequestMapping(value = "list", method = RequestMethod.GET) 37 | public Object show(HttpServletRequest httpServletRequest) throws IOException { 38 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 39 | Map> zonesFileList = zonesFileService.getZonesFileList(userPassport); 40 | return zonesFileList; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/main/java/us/codecraft/blackhole/suite/web/ZonesPickController.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.web; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | import org.springframework.web.servlet.ModelAndView; 10 | import org.springframework.web.servlet.mvc.multiaction.MultiActionController; 11 | import sun.misc.BASE64Decoder; 12 | import us.codecraft.blackhole.suite.model.UserPassport; 13 | import us.codecraft.blackhole.suite.service.UserZonesService; 14 | import us.codecraft.blackhole.suite.util.CookieUtils; 15 | import us.codecraft.blackhole.suite.util.IPUtils; 16 | import us.codecraft.blackhole.suite.util.RequestThreadUtils; 17 | import us.codecraft.blackhole.suite.util.UserZonesUtils; 18 | 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | 23 | /** 24 | * User: cairne 25 | * Date: 13-5-12 26 | * Time: 下午7:40 27 | */ 28 | @Controller 29 | @RequestMapping("") 30 | public class ZonesPickController extends MultiActionController { 31 | 32 | @Autowired 33 | private UserZonesService userZonesService; 34 | 35 | @Autowired 36 | private ZonesApplyController zonesApplyController; 37 | 38 | @RequestMapping("z") 39 | public ModelAndView dashboardZ(HttpServletRequest request, HttpServletResponse response, @RequestParam(required = false, value = "z") String zonesBase64) throws IOException { 40 | return dashboard(request,response,zonesBase64); 41 | } 42 | 43 | @RequestMapping("") 44 | public ModelAndView dashboard(HttpServletRequest request, HttpServletResponse response, @RequestParam(required = false, value = "z") String zonesBase64) throws IOException { 45 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 46 | String zones = userZonesService.getZones(userPassport); 47 | if (zones == null) { 48 | zones = CookieUtils.getZones(request); 49 | } 50 | if (StringUtils.isBlank(zones) && !StringUtils.isBlank(zonesBase64)) { 51 | zonesBase64 = zonesBase64.replaceAll("^\"","").replaceAll("\"$",""); 52 | BASE64Decoder base64Decoder = new BASE64Decoder(); 53 | zones = new String(base64Decoder.decodeBuffer(zonesBase64), "utf-8"); 54 | CookieUtils.saveZones(response, zones); 55 | } 56 | String localIp = IPUtils.getLocalIP(); 57 | ModelAndView modelAndView = new ModelAndView("zonespick"); 58 | modelAndView.addObject("zones", UserZonesUtils.toJson(zones)); 59 | modelAndView.addObject("localIp", localIp); 60 | return modelAndView; 61 | } 62 | 63 | @ResponseBody 64 | @RequestMapping("pick") 65 | public Object pick(HttpServletRequest request, HttpServletResponse response, @RequestParam("json") String json) throws IOException { 66 | String text = UserZonesUtils.fromJson(json); 67 | UserPassport userPassport = RequestThreadUtils.getUserPassport(); 68 | userZonesService.updateZones(userPassport, text); 69 | CookieUtils.saveZones(response, text); 70 | return zonesApplyController.save(text, request); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /server/src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/src/main/resources/spring/applicationContext-freemarker.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 0 14 | zh_CN 15 | yyyy-MM-dd HH:mm:ss 16 | yyyy-MM-dd 17 | #.## 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /server/src/main/resources/spring/applicationContext-myBatis.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /server/src/main/resources/spring/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | web_messages 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /server/src/main/resources/web_messages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | #Created by JInto - www.guh-software.de 2 | #Mon Aug 02 14:18:44 CST 2010 3 | login.error.user.notexist=\u8BE5\u7528\u6237\u4E0D\u5B58\u5728! 4 | login.error.password.incorrect=\u5BC6\u7801\u4E0D\u6B63\u786E! 5 | register.error=注册失败! 6 | register.error.username.exist=注册失败,该用户名已存在! -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/jsp/404.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf8" 2 | pageEncoding="utf8"%> 3 | 4 | 5 | 6 | 7 | 404 8 | 9 | 10 |

Page not found!

11 | 12 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/jsp/500.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf8" 2 | pageEncoding="utf8" isErrorPage="true" import="java.io.*"%> 3 | 4 | 5 | 6 | 7 | 500 8 | 9 | 10 | 页面出错啦! 11 | <% 12 | 13 | StringWriter stringWriter = new StringWriter(); 14 | exception.printStackTrace(new PrintWriter(stringWriter)); 15 | out.println(stringWriter.toString()); 16 | %> 17 | 18 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/jsp/count.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=utf8" 2 | pageEncoding="utf8"%> 3 | 4 | 5 | 6 | 7 | hey there! 8 | 9 | 10 | <%=application.getAttribute("count")%> 11 | 12 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/edit.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hostd-edit 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | <#assign tab="edit"> 28 | <#include "nav.ftl"> 29 |
30 |
31 | 32 | <#--
--> 33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 | <#if type?exists && type=="userZones"> 41 | Save 42 | <#else> 43 | 应用 44 | <#if userPassport?exists && user==userPassport.username> 45 | 保存 46 | <#if id gt 0>删除 48 | 49 | 50 |
51 |
52 |
53 |

Syntax:

54 | 55 | 56 |
    57 |
  • 58 |

    ip domain

    59 | 60 |

    e.g. 127.0.0.1 www.dianping.com

    61 | 62 |
  • 63 |
  • 64 |

    "*" stand for any length of charactor

    65 | 66 |

    e.g. 127.0.0.1 *.dianping.com

    67 | 68 |
  • 69 |
  • 70 |

    "#" for comment

    71 | 72 |

    e.g. #127.0.0.1 t.dianping.com

    73 | 74 |

    Commented line is a candidate for choice.

    75 |
  • 76 |
  • 77 |

    "#" in line for comment of config

    78 | 79 |

    e.g. #127.0.0.1 t.dianping.com #local test

    80 | 81 |

    'local test' is comment of this line.

    82 |
  • 83 |
84 |
85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/help-dns.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hostd 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 | <#assign tab=""> 22 | <#include "nav.ftl"> 23 |
24 |
25 |

Windows XP/7

26 |

Mac OS X

27 |

Android

28 |

iPhone/iPad

29 |
30 | 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/login.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hostd-login 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | <#assign tab="login"> 27 | <#include "nav.ftl"> 28 |
29 |
30 | <#----> 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/nav.ftl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/register.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hostd-register 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | <#assign tab="register"> 26 | <#include "nav.ftl"> 27 |
28 |
29 | 46 |
47 | 48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/shareboard.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hostd-share 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 |
27 |
28 | <#assign tab="switch"> 29 | <#include "nav.ftl"> 30 |
31 |
32 |

 公用配置

33 |
    34 | <#if public?exists> 35 | <#list public as zones> 36 |
  • ${zones.name}    使用 39 |
  • 40 | 41 | 42 |
43 | <#if userPassport?exists> 44 |

 个人配置

45 | 56 | <#else> 57 |

 个人配置

58 | 61 | 62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/pages/zonespick.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hostd 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 |
27 |
28 | <#assign tab="hosts"> 29 | <#include "nav.ftl"> 30 |
31 | 32 | 103 | 143 |
144 |
145 | 156 |
157 |

158 | 159 |

Share

160 |
161 | 194 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/spring-servlet.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /server/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | diandian-operation 6 | 7 | 8 | 9 | log4jConfigLocation 10 | /WEB-INF/log4j.xml 11 | 12 | 13 | 14 | log4jRefreshInterval 15 | 2000 16 | 17 | 18 | 19 | UserPassportFilter 20 | us.codecraft.blackhole.suite.filter.UserPassportFilter 21 | 22 | 23 | UserPassportFilter 24 | /* 25 | 26 | 27 | 28 | spring 29 | org.springframework.web.servlet.DispatcherServlet 30 | 31 | contextConfigLocation 32 | /WEB-INF/spring-servlet.xml,classpath:/spring/applicationContext*.xml 33 | 34 | 1 35 | 36 | 37 | spring 38 | / 39 | 40 | 41 | 404 42 | /WEB-INF/jsp/404.jsp 43 | 44 | 45 | 500 46 | /WEB-INF/jsp/500.jsp 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /server/src/main/webapp/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.0 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /server/src/main/webapp/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | white-space: nowrap; 32 | } 33 | .CodeMirror-linenumbers {} 34 | .CodeMirror-linenumber { 35 | padding: 0 3px 0 5px; 36 | min-width: 20px; 37 | text-align: right; 38 | color: #999; 39 | } 40 | 41 | /* CURSOR */ 42 | 43 | .CodeMirror div.CodeMirror-cursor { 44 | border-left: 1px solid black; 45 | z-index: 3; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 52 | width: auto; 53 | border: 0; 54 | background: #7e7; 55 | z-index: 1; 56 | } 57 | /* Can style cursor different in overwrite (non-insert) mode */ 58 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 59 | 60 | .cm-tab { display: inline-block; } 61 | 62 | /* DEFAULT THEME */ 63 | 64 | .cm-s-default .cm-keyword {color: #708;} 65 | .cm-s-default .cm-atom {color: #219;} 66 | .cm-s-default .cm-number {color: #164;} 67 | .cm-s-default .cm-def {color: #00f;} 68 | .cm-s-default .cm-variable {color: black;} 69 | .cm-s-default .cm-variable-2 {color: #05a;} 70 | .cm-s-default .cm-variable-3 {color: #085;} 71 | .cm-s-default .cm-property {color: black;} 72 | .cm-s-default .cm-operator {color: black;} 73 | .cm-s-default .cm-comment {color: #a50;} 74 | .cm-s-default .cm-string {color: #a11;} 75 | .cm-s-default .cm-string-2 {color: #f50;} 76 | .cm-s-default .cm-meta {color: #555;} 77 | .cm-s-default .cm-error {color: #f00;} 78 | .cm-s-default .cm-qualifier {color: #555;} 79 | .cm-s-default .cm-builtin {color: #30a;} 80 | .cm-s-default .cm-bracket {color: #997;} 81 | .cm-s-default .cm-tag {color: #170;} 82 | .cm-s-default .cm-attribute {color: #00c;} 83 | .cm-s-default .cm-header {color: blue;} 84 | .cm-s-default .cm-quote {color: #090;} 85 | .cm-s-default .cm-hr {color: #999;} 86 | .cm-s-default .cm-link {color: #00c;} 87 | 88 | .cm-negative {color: #d44;} 89 | .cm-positive {color: #292;} 90 | .cm-header, .cm-strong {font-weight: bold;} 91 | .cm-em {font-style: italic;} 92 | .cm-link {text-decoration: underline;} 93 | 94 | .cm-invalidchar {color: #f00;} 95 | 96 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 97 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 98 | 99 | /* STOP */ 100 | 101 | /* The rest of this file contains styles related to the mechanics of 102 | the editor. You probably shouldn't touch them. */ 103 | 104 | .CodeMirror { 105 | line-height: 1; 106 | position: relative; 107 | overflow: hidden; 108 | background: white; 109 | color: black; 110 | } 111 | 112 | .CodeMirror-scroll { 113 | /* 30px is the magic margin used to hide the element's real scrollbars */ 114 | /* See overflow: hidden in .CodeMirror */ 115 | margin-bottom: -30px; margin-right: -30px; 116 | padding-bottom: 30px; padding-right: 30px; 117 | height: 100%; 118 | outline: none; /* Prevent dragging from highlighting the element */ 119 | position: relative; 120 | } 121 | .CodeMirror-sizer { 122 | position: relative; 123 | } 124 | 125 | /* The fake, visible scrollbars. Used to force redraw during scrolling 126 | before actuall scrolling happens, thus preventing shaking and 127 | flickering artifacts. */ 128 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 129 | position: absolute; 130 | z-index: 6; 131 | display: none; 132 | } 133 | .CodeMirror-vscrollbar { 134 | right: 0; top: 0; 135 | overflow-x: hidden; 136 | overflow-y: scroll; 137 | } 138 | .CodeMirror-hscrollbar { 139 | bottom: 0; left: 0; 140 | overflow-y: hidden; 141 | overflow-x: scroll; 142 | } 143 | .CodeMirror-scrollbar-filler { 144 | right: 0; bottom: 0; 145 | } 146 | .CodeMirror-gutter-filler { 147 | left: 0; bottom: 0; 148 | } 149 | 150 | .CodeMirror-gutters { 151 | position: absolute; left: 0; top: 0; 152 | height: 100%; 153 | padding-bottom: 30px; 154 | z-index: 3; 155 | } 156 | .CodeMirror-gutter { 157 | white-space: normal; 158 | height: 100%; 159 | padding-bottom: 30px; 160 | margin-bottom: -32px; 161 | display: inline-block; 162 | /* Hack to make IE7 behave */ 163 | *zoom:1; 164 | *display:inline; 165 | } 166 | .CodeMirror-gutter-elt { 167 | position: absolute; 168 | cursor: default; 169 | z-index: 4; 170 | } 171 | 172 | .CodeMirror-lines { 173 | cursor: text; 174 | } 175 | .CodeMirror pre { 176 | /* Reset some styles that the rest of the page might have set */ 177 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 178 | border-width: 0; 179 | background: transparent; 180 | font-family: inherit; 181 | font-size: inherit; 182 | margin: 0; 183 | white-space: pre; 184 | word-wrap: normal; 185 | line-height: inherit; 186 | color: inherit; 187 | z-index: 2; 188 | position: relative; 189 | overflow: visible; 190 | } 191 | .CodeMirror-wrap pre { 192 | word-wrap: break-word; 193 | white-space: pre-wrap; 194 | word-break: normal; 195 | } 196 | .CodeMirror-linebackground { 197 | position: absolute; 198 | left: 0; right: 0; top: 0; bottom: 0; 199 | z-index: 0; 200 | } 201 | 202 | .CodeMirror-linewidget { 203 | position: relative; 204 | z-index: 2; 205 | overflow: auto; 206 | } 207 | 208 | .CodeMirror-widget { 209 | display: inline-block; 210 | } 211 | 212 | .CodeMirror-wrap .CodeMirror-scroll { 213 | overflow-x: hidden; 214 | } 215 | 216 | .CodeMirror-measure { 217 | position: absolute; 218 | width: 100%; height: 0px; 219 | overflow: hidden; 220 | visibility: hidden; 221 | } 222 | .CodeMirror-measure pre { position: static; } 223 | 224 | .CodeMirror div.CodeMirror-cursor { 225 | position: absolute; 226 | visibility: hidden; 227 | border-right: none; 228 | width: 0; 229 | } 230 | .CodeMirror-focused div.CodeMirror-cursor { 231 | visibility: visible; 232 | } 233 | 234 | .CodeMirror-selected { background: #d9d9d9; } 235 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 236 | 237 | .cm-searching { 238 | background: #ffa; 239 | background: rgba(255, 255, 0, .4); 240 | } 241 | 242 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 243 | .CodeMirror span { *vertical-align: text-bottom; } 244 | 245 | @media print { 246 | /* Hide the cursor when printing */ 247 | .CodeMirror div.CodeMirror-cursor { 248 | visibility: hidden; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /server/src/main/webapp/css/hostd.css: -------------------------------------------------------------------------------- 1 | .ui-btn-up-a { 2 | border: 1px solid #cccccc /*{a-bup-border}*/; 3 | background: #E6E6E6 /*{a-bup-background-color}*/; 4 | font-weight: bold; 5 | color: #333333 /*{a-bup-color}*/; 6 | text-shadow: undefined /*{a-bup-shadow-x}*/ undefined /*{a-bup-shadow-y}*/ undefined /*{a-bup-shadow-radius}*/ undefined /*{a-bup-shadow-color}*/; 7 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff /*{a-bup-background-start}*/), to(#E6E6E6 /*{a-bup-background-end}*/)); /* Saf4+, Chrome */ 8 | background-image: -webkit-linear-gradient(#ffffff /*{a-bup-background-start}*/, #E6E6E6 /*{a-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ 9 | background-image: -moz-linear-gradient(#ffffff /*{a-bup-background-start}*/, #E6E6E6 /*{a-bup-background-end}*/); /* FF3.6 */ 10 | background-image: -ms-linear-gradient(#ffffff /*{a-bup-background-start}*/, #E6E6E6 /*{a-bup-background-end}*/); /* IE10 */ 11 | background-image: -o-linear-gradient(#ffffff /*{a-bup-background-start}*/, #E6E6E6 /*{a-bup-background-end}*/); /* Opera 11.10+ */ 12 | background-image: linear-gradient(#ffffff /*{a-bup-background-start}*/, #E6E6E6 /*{a-bup-background-end}*/); 13 | } 14 | 15 | .ui-btn-up-b { 16 | border: 1px solid #0056bc /*{b-bup-border}*/; 17 | background: #0044cc /*{b-bup-background-color}*/; 18 | font-weight: bold; 19 | color: #ffffff 20 | /*{b-bup-color}*/; 21 | text-shadow: 0 /*{b-bup-shadow-x}*/ 0 /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #ffffff /*{b-bup-shadow-color}*/; 22 | background-image: -webkit-gradient(linear, left top, left bottom, from( #0088cc /*{b-bup-background-start}*/), to( #0044cc /*{b-bup-background-end}*/)); /* Saf4+, Chrome */ 23 | background-image: -webkit-linear-gradient( #0088cc /*{b-bup-background-start}*/, #0044cc /*{b-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ 24 | background-image: -moz-linear-gradient( #0088cc /*{b-bup-background-start}*/, #0044cc /*{b-bup-background-end}*/); /* FF3.6 */ 25 | background-image: -ms-linear-gradient( #0088cc /*{b-bup-background-start}*/, #0044cc /*{b-bup-background-end}*/); /* IE10 */ 26 | background-image: -o-linear-gradient( #0088cc /*{b-bup-background-start}*/, #0044cc /*{b-bup-background-end}*/); /* Opera 11.10+ */ 27 | background-image: linear-gradient( #0088cc /*{b-bup-background-start}*/, #0044cc /*{b-bup-background-end}*/); 28 | } 29 | 30 | .ui-btn-up-b:visited, 31 | .ui-btn-up-b a.ui-link-inherit { 32 | color: #ffffff /*{a-bup-color}*/; 33 | } 34 | 35 | .ui-btn-up-a:visited, 36 | .ui-btn-up-a a.ui-link-inherit { 37 | color: #333333 /*{a-bup-color}*/; 38 | } 39 | 40 | .ui-btn-hover-a { 41 | border: 1px solid #cccccc /*{a-bhover-border}*/; 42 | background: #e6e6e6 /*{a-bhover-background-color}*/; 43 | font-weight: bold; 44 | color: #333333 /*{a-bhover-color}*/; 45 | text-shadow: undefined /*{a-bhover-shadow-x}*/ undefined /*{a-bhover-shadow-y}*/ undefined /*{a-bhover-shadow-radius}*/ undefined /*{a-bhover-shadow-color}*/; 46 | background-image: -webkit-gradient(linear, left top, left bottom, from(#E6E6E6 /*{a-bhover-background-start}*/), to(#E6E6E6 /*{a-bhover-background-end}*/)); /* Saf4+, Chrome */ 47 | background-image: -webkit-linear-gradient(#E6E6E6 /*{a-bhover-background-start}*/, #E6E6E6 /*{a-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ 48 | background-image: -moz-linear-gradient(#E6E6E6 /*{a-bhover-background-start}*/, #E6E6E6 /*{a-bhover-background-end}*/); /* FF3.6 */ 49 | background-image: -ms-linear-gradient(#E6E6E6 /*{a-bhover-background-start}*/, #E6E6E6 /*{a-bhover-background-end}*/); /* IE10 */ 50 | background-image: -o-linear-gradient(#E6E6E6 /*{a-bhover-background-start}*/, #E6E6E6 /*{a-bhover-background-end}*/); /* Opera 11.10+ */ 51 | background-image: linear-gradient(#E6E6E6 /*{a-bhover-background-start}*/, #E6E6E6 /*{a-bhover-background-end}*/); 52 | } 53 | 54 | .ui-btn-hover-a:visited, 55 | .ui-btn-hover-a:hover, 56 | .ui-btn-hover-a a.ui-link-inherit { 57 | color: #333333 /*{a-bhover-color}*/; 58 | } 59 | 60 | .ui-btn-down-a { 61 | border: 1px solid #cccccc /*{a-bdown-border}*/; 62 | background: #d6d6d6 /*{a-bdown-background-color}*/; 63 | font-weight: bold; 64 | color: #333333 /*{a-bdown-color}*/; 65 | text-shadow: undefined /*{a-bdown-shadow-x}*/ undefined /*{a-bdown-shadow-y}*/ undefined /*{a-bdown-shadow-radius}*/ undefined /*{a-bdown-shadow-color}*/; 66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#D6D6D6 /*{a-bdown-background-start}*/), to(#D6D6D6 /*{a-bdown-background-end}*/)); /* Saf4+, Chrome */ 67 | background-image: -webkit-linear-gradient(#D6D6D6 /*{a-bdown-background-start}*/, #D6D6D6 /*{a-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ 68 | background-image: -moz-linear-gradient(#D6D6D6 /*{a-bdown-background-start}*/, #D6D6D6 /*{a-bdown-background-end}*/); /* FF3.6 */ 69 | background-image: -ms-linear-gradient(#D6D6D6 /*{a-bdown-background-start}*/, #D6D6D6 /*{a-bdown-background-end}*/); /* IE10 */ 70 | background-image: -o-linear-gradient(#D6D6D6 /*{a-bdown-background-start}*/, #D6D6D6 /*{a-bdown-background-end}*/); /* Opera 11.10+ */ 71 | background-image: linear-gradient(#D6D6D6 /*{a-bdown-background-start}*/, #D6D6D6 /*{a-bdown-background-end}*/); 72 | } 73 | 74 | .ui-btn-down-a:visited, 75 | .ui-btn-down-a:hover, 76 | .ui-btn-down-a a.ui-link-inherit { 77 | color: #333333 /*{a-bdown-color}*/; 78 | } 79 | 80 | .ui-btn-up-a, 81 | .ui-btn-hover-a, 82 | .ui-btn-down-a { 83 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif /*{global-font-family}*/; 84 | text-decoration: none; 85 | } 86 | 87 | .ui-btn-inner { 88 | padding: .7em 15px; 89 | display: block; 90 | border-left-width: 1px; 91 | border-right-width: 1px; 92 | } 93 | 94 | li{ 95 | list-style-type:none; 96 | padding: 0px; 97 | } 98 | 99 | ul{ 100 | margin: 0 0 0 0; 101 | } 102 | -------------------------------------------------------------------------------- /server/src/main/webapp/css/jqm-icon-pack-1.1.1-fa.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/css/jqm-icon-pack-1.1.1-fa.css -------------------------------------------------------------------------------- /server/src/main/webapp/css/shareboard.css: -------------------------------------------------------------------------------- 1 | .config-list{ 2 | font-size: 20px; 3 | line-height: 150%; 4 | } 5 | 6 | .dashboard { 7 | height:100%; 8 | } 9 | -------------------------------------------------------------------------------- /server/src/main/webapp/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/font/FontAwesome.otf -------------------------------------------------------------------------------- /server/src/main/webapp/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /server/src/main/webapp/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /server/src/main/webapp/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /server/src/main/webapp/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/ajax-loader.gif -------------------------------------------------------------------------------- /server/src/main/webapp/img/ajax-loader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/ajax-loader.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/favicon.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-18-black-pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-18-black-pack.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-18-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-18-black.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-18-white-pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-18-white-pack.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-18-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-18-white.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-36-black-pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-36-black-pack.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-36-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-36-black.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-36-white-pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-36-white-pack.png -------------------------------------------------------------------------------- /server/src/main/webapp/img/icons-36-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code4craft/hostd/ff57c22098ecfaa6aab4bd2c92743ba1c4562c37/server/src/main/webapp/img/icons-36-white.png -------------------------------------------------------------------------------- /server/src/main/webapp/js/baiduTemplate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * baiduTemplate简单好用的Javascript模板引擎 1.0.6 版本 3 | * http://baidufe.github.com/BaiduTemplate 4 | * 开源协议:BSD License 5 | * 浏览器环境占用命名空间 baidu.template ,nodejs环境直接安装 npm install baidutemplate 6 | * @param str{String} dom结点ID,或者模板string 7 | * @param data{Object} 需要渲染的json对象,可以为空。当data为{}时,仍然返回html。 8 | * @return 如果无data,直接返回编译后的函数;如果有data,返回html。 9 | * @author wangxiao 10 | * @email 1988wangxiao@gmail.com 11 | */ 12 | 13 | ;(function(window){ 14 | 15 | //取得浏览器环境的baidu命名空间,非浏览器环境符合commonjs规范exports出去 16 | //修正在nodejs环境下,采用baidu.template变量名 17 | var baidu = typeof module === 'undefined' ? (window.baidu = window.baidu || {}) : module.exports; 18 | 19 | //模板函数(放置于baidu.template命名空间下) 20 | baidu.template = function(str, data){ 21 | 22 | //检查是否有该id的元素存在,如果有元素则获取元素的innerHTML/value,否则认为字符串为模板 23 | var fn = (function(){ 24 | 25 | //判断如果没有document,则为非浏览器环境 26 | if(!window.document){ 27 | return bt._compile(str); 28 | }; 29 | 30 | //HTML5规定ID可以由任何不包含空格字符的字符串组成 31 | var element = document.getElementById(str); 32 | if (element) { 33 | 34 | //取到对应id的dom,缓存其编译后的HTML模板函数 35 | if (bt.cache[str]) { 36 | return bt.cache[str]; 37 | }; 38 | 39 | //textarea或input则取value,其它情况取innerHTML 40 | var html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML; 41 | return bt._compile(html); 42 | 43 | }else{ 44 | 45 | //是模板字符串,则生成一个函数 46 | //如果直接传入字符串作为模板,则可能变化过多,因此不考虑缓存 47 | return bt._compile(str); 48 | }; 49 | 50 | })(); 51 | 52 | //有数据则返回HTML字符串,没有数据则返回函数 支持data={}的情况 53 | var result = bt._isObject(data) ? fn( data ) : fn; 54 | fn = null; 55 | 56 | return result; 57 | }; 58 | 59 | //取得命名空间 baidu.template 60 | var bt = baidu.template; 61 | 62 | //标记当前版本 63 | bt.versions = bt.versions || []; 64 | bt.versions.push('1.0.6'); 65 | 66 | //缓存 将对应id模板生成的函数缓存下来。 67 | bt.cache = {}; 68 | 69 | //自定义分隔符,可以含有正则中的字符,可以是HTML注释开头 70 | bt.LEFT_DELIMITER = bt.LEFT_DELIMITER||'<%'; 71 | bt.RIGHT_DELIMITER = bt.RIGHT_DELIMITER||'%>'; 72 | 73 | //自定义默认是否转义,默认为默认自动转义 74 | bt.ESCAPE = true; 75 | 76 | //HTML转义 77 | bt._encodeHTML = function (source) { 78 | return String(source) 79 | .replace(/&/g,'&') 80 | .replace(//g,'>') 82 | .replace(/\\/g,'\') 83 | .replace(/"/g,'"') 84 | .replace(/'/g,'''); 85 | }; 86 | 87 | //转义影响正则的字符 88 | bt._encodeReg = function (source) { 89 | return String(source).replace(/([.*+?^=!:${}()|[\]/\\])/g,'\\$1'); 90 | }; 91 | 92 | //转义UI UI变量使用在HTML页面标签onclick等事件函数参数中 93 | bt._encodeEventHTML = function (source) { 94 | return String(source) 95 | .replace(/&/g,'&') 96 | .replace(//g,'>') 98 | .replace(/"/g,'"') 99 | .replace(/'/g,''') 100 | .replace(/\\\\/g,'\\') 101 | .replace(/\\\//g,'\/') 102 | .replace(/\\n/g,'\n') 103 | .replace(/\\r/g,'\r'); 104 | }; 105 | 106 | //将字符串拼接生成函数,即编译过程(compile) 107 | bt._compile = function(str){ 108 | var funBody = "var _template_fun_array=[];\nvar fn=(function(__data__){\nvar _template_varName='';\nfor(name in __data__){\n_template_varName+=('var '+name+'=__data__[\"'+name+'\"];');\n};\neval(_template_varName);\n_template_fun_array.push('"+bt._analysisStr(str)+"');\n_template_varName=null;\n})(_template_object);\nfn = null;\nreturn _template_fun_array.join('');\n"; 109 | return new Function("_template_object",funBody); 110 | }; 111 | 112 | //判断是否是Object类型 113 | bt._isObject = function (source) { 114 | return 'function' === typeof source || !!(source && 'object' === typeof source); 115 | }; 116 | 117 | //解析模板字符串 118 | bt._analysisStr = function(str){ 119 | 120 | //取得分隔符 121 | var _left_ = bt.LEFT_DELIMITER; 122 | var _right_ = bt.RIGHT_DELIMITER; 123 | 124 | //对分隔符进行转义,支持正则中的元字符,可以是HTML注释 125 | var _left = bt._encodeReg(_left_); 126 | var _right = bt._encodeReg(_right_); 127 | 128 | str = String(str) 129 | 130 | //去掉分隔符中js注释 131 | .replace(new RegExp("("+_left+"[^"+_right+"]*)//.*\n","g"), "$1") 132 | 133 | //去掉注释内容 <%* 这里可以任意的注释 *%> 134 | //默认支持HTML注释,将HTML注释匹配掉的原因是用户有可能用 来做分割符 135 | .replace(new RegExp("", "g"),"") 136 | .replace(new RegExp(_left+"\\*.*?\\*"+_right, "g"),"") 137 | 138 | //把所有换行去掉 \r回车符 \t制表符 \n换行符 139 | .replace(new RegExp("[\\r\\t\\n]","g"), "") 140 | 141 | //用来处理非分隔符内部的内容中含有 斜杠 \ 单引号 ‘ ,处理办法为HTML转义 142 | .replace(new RegExp(_left+"(?:(?!"+_right+")[\\s\\S])*"+_right+"|((?:(?!"+_left+")[\\s\\S])+)","g"),function (item, $1) { 143 | var str = ''; 144 | if($1){ 145 | 146 | //将 斜杠 单引 HTML转义 147 | str = $1.replace(/\\/g,"\").replace(/'/g,'''); 148 | while(/<[^<]*?'[^<]*?>/g.test(str)){ 149 | 150 | //将标签内的单引号转义为\r 结合最后一步,替换为\' 151 | str = str.replace(/(<[^<]*?)'([^<]*?>)/g,'$1\r$2') 152 | }; 153 | }else{ 154 | str = item; 155 | } 156 | return str ; 157 | }); 158 | 159 | 160 | str = str 161 | //定义变量,如果没有分号,需要容错 <%var val='test'%> 162 | .replace(new RegExp("("+_left+"[\\s]*?var[\\s]*?.*?[\\s]*?[^;])[\\s]*?"+_right,"g"),"$1;"+_right_) 163 | 164 | //对变量后面的分号做容错(包括转义模式 如<%:h=value%>) <%=value;%> 排除掉函数的情况 <%fun1();%> 排除定义变量情况 <%var val='test';%> 165 | .replace(new RegExp("("+_left+":?[hvu]?[\\s]*?=[\\s]*?[^;|"+_right+"]*?);[\\s]*?"+_right,"g"),"$1"+_right_) 166 | 167 | //按照 <% 分割为一个个数组,再用 \t 和在一起,相当于将 <% 替换为 \t 168 | //将模板按照<%分为一段一段的,再在每段的结尾加入 \t,即用 \t 将每个模板片段前面分隔开 169 | .split(_left_).join("\t"); 170 | 171 | //支持用户配置默认是否自动转义 172 | if(bt.ESCAPE){ 173 | str = str 174 | 175 | //找到 \t=任意一个字符%> 替换为 ‘,任意字符,' 176 | //即替换简单变量 \t=data%> 替换为 ',data,' 177 | //默认HTML转义 也支持HTML转义写法<%:h=value%> 178 | .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'"); 179 | }else{ 180 | str = str 181 | 182 | //默认不转义HTML转义 183 | .replace(new RegExp("\\t=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':$1,'"); 184 | }; 185 | 186 | str = str 187 | 188 | //支持HTML转义写法<%:h=value%> 189 | .replace(new RegExp("\\t:h=(.*?)"+_right,"g"),"',typeof($1) === 'undefined'?'':baidu.template._encodeHTML($1),'") 190 | 191 | //支持不转义写法 <%:=value%>和<%-value%> 192 | .replace(new RegExp("\\t(?::=|-)(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':$1,'") 193 | 194 | //支持url转义 <%:u=value%> 195 | .replace(new RegExp("\\t:u=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':encodeURIComponent($1),'") 196 | 197 | //支持UI 变量使用在HTML页面标签onclick等事件函数参数中 <%:v=value%> 198 | .replace(new RegExp("\\t:v=(.*?)"+_right,"g"),"',typeof($1)==='undefined'?'':baidu.template._encodeEventHTML($1),'") 199 | 200 | //将字符串按照 \t 分成为数组,在用'); 将其合并,即替换掉结尾的 \t 为 '); 201 | //在if,for等语句前面加上 '); ,形成 ');if ');for 的形式 202 | .split("\t").join("');") 203 | 204 | //将 %> 替换为_template_fun_array.push(' 205 | //即去掉结尾符,生成函数中的push方法 206 | //如:if(list.length=5){%>

',list[4],'

');} 207 | //会被替换为 if(list.length=5){_template_fun_array.push('

',list[4],'

');} 208 | .split(_right_).join("_template_fun_array.push('") 209 | 210 | //将 \r 替换为 \ 211 | .split("\r").join("\\'"); 212 | 213 | return str; 214 | }; 215 | 216 | })(window); 217 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/common-action.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午9:14 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | 9 | function apply(text) { 10 | $.ajax({ 11 | url: "/apply/", 12 | data: { 13 | text: text 14 | }, 15 | method: "post" 16 | } 17 | ).done(function (data) { 18 | if (data["code"] == 200) { 19 | alert(data["msg"]) 20 | } else { 21 | alert(data["msg"]) 22 | } 23 | }); 24 | } 25 | 26 | function pick(text) { 27 | $.ajax({ 28 | url: "/pick", 29 | data: { 30 | json: text 31 | }, 32 | method: "post" 33 | } 34 | ).done(function (data) { 35 | if (data["code"] == 200) { 36 | } else { 37 | alert(data["msg"]) 38 | } 39 | }); 40 | } 41 | 42 | function save(id) { 43 | $.ajax({ 44 | url: "/edit/save/" + id, 45 | method: "post", 46 | data: { 47 | text:myCodeMirror.getValue(), 48 | name:$("#zones-name").val() 49 | } 50 | } 51 | ).done(function (data) { 52 | if (data["code"] == 200) { 53 | alert("保存成功!") 54 | } else { 55 | alert("保存失败!") 56 | } 57 | }); 58 | } 59 | 60 | function save(id) { 61 | $.ajax({ 62 | url: "/edit/save/" + id, 63 | method: "post", 64 | data: { 65 | text:myCodeMirror.getValue(), 66 | name:$("#zones-name").val() 67 | } 68 | } 69 | ).done(function (data) { 70 | if (data["code"] == 200) { 71 | alert(data["msg"]) 72 | } else { 73 | alert(data["msg"]) 74 | } 75 | }); 76 | } 77 | 78 | function del(id) { 79 | $.ajax({ 80 | url: "/edit/delete/" + id 81 | } 82 | ).done(function (data) { 83 | if (data["code"] == 200) { 84 | alert(data["msg"]) 85 | } else { 86 | alert(data["msg"]) 87 | } 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午7:52 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | 9 | function bindApply() { 10 | $("a#apply-a").bind("click", function () { 11 | apply(myCodeMirror.getValue()); 12 | }); 13 | } 14 | 15 | function bindEdit() { 16 | $("a#save").bind("click", function () { 17 | save($(this).attr("file-id")) 18 | }); 19 | } 20 | 21 | function bindSaveDefault() { 22 | console.log($("a#save-default")) 23 | $("a#save-default").bind("click", function () { 24 | save('') 25 | }); 26 | } 27 | 28 | function bindDel() { 29 | $("a#del").bind("click", function () { 30 | del($(this).attr("file-id")); 31 | setTimeout("window.location.href=\"/\"", 1000); 32 | }); 33 | } 34 | 35 | $(function () { 36 | myCodeMirror = CodeMirror.fromTextArea($("#code").get(0), { 37 | mode: "javascript", 38 | theme: "solarized" 39 | }); 40 | bindSaveDefault(); 41 | bindApply(); 42 | bindEdit(); 43 | bindDel(); 44 | $("#code-containe").bind("keypress", function (e) { 45 | console.log($(e)); 46 | }) 47 | }); 48 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/javascript.js: -------------------------------------------------------------------------------- 1 | // TODO actually recognize syntax of TypeScript constructs 2 | 3 | CodeMirror.defineMode("javascript", function(config, parserConfig) { 4 | var indentUnit = config.indentUnit; 5 | var jsonMode = parserConfig.json; 6 | var isTS = parserConfig.typescript; 7 | 8 | // Tokenizer 9 | 10 | var keywords = function(){ 11 | function kw(type) {return {type: type, style: "keyword"};} 12 | var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); 13 | var operator = kw("operator"), atom = {type: "atom", style: "atom"}; 14 | 15 | var jsKeywords = { 16 | "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, 17 | "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, 18 | "var": kw("var"), "const": kw("var"), "let": kw("var"), 19 | "function": kw("function"), "catch": kw("catch"), 20 | "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), 21 | "in": operator, "typeof": operator, "instanceof": operator, 22 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, 23 | "this": kw("this") 24 | }; 25 | 26 | // Extend the 'normal' keywords with the TypeScript language extensions 27 | if (isTS) { 28 | var type = {type: "variable", style: "variable-3"}; 29 | var tsKeywords = { 30 | // object-like things 31 | "interface": kw("interface"), 32 | "class": kw("class"), 33 | "extends": kw("extends"), 34 | "constructor": kw("constructor"), 35 | 36 | // scope modifiers 37 | "public": kw("public"), 38 | "private": kw("private"), 39 | "protected": kw("protected"), 40 | "static": kw("static"), 41 | 42 | "super": kw("super"), 43 | 44 | // types 45 | "string": type, "number": type, "bool": type, "any": type 46 | }; 47 | 48 | for (var attr in tsKeywords) { 49 | jsKeywords[attr] = tsKeywords[attr]; 50 | } 51 | } 52 | 53 | return jsKeywords; 54 | }(); 55 | 56 | var isOperatorChar = /[+\-*&%=<>!?|~^]/; 57 | 58 | function chain(stream, state, f) { 59 | state.tokenize = f; 60 | return f(stream, state); 61 | } 62 | 63 | function nextUntilUnescaped(stream, end) { 64 | var escaped = false, next; 65 | while ((next = stream.next()) != null) { 66 | if (next == end && !escaped) 67 | return false; 68 | escaped = !escaped && next == "\\"; 69 | } 70 | return escaped; 71 | } 72 | 73 | // Used as scratch variables to communicate multiple values without 74 | // consing up tons of objects. 75 | var type, content; 76 | function ret(tp, style, cont) { 77 | type = tp; content = cont; 78 | return style; 79 | } 80 | 81 | function jsTokenBase(stream, state) { 82 | var ch = stream.next(); 83 | if (ch == '"' || ch == "'") 84 | return chain(stream, state, jsTokenString(ch)); 85 | else if (/[\[\]{}\(\),;\:\.]/.test(ch)) 86 | return ret(ch); 87 | else if (ch == "0" && stream.eat(/x/i)) { 88 | stream.eatWhile(/[\da-f]/i); 89 | return ret("number", "number"); 90 | } 91 | else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { 92 | stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); 93 | return ret("number", "number"); 94 | } 95 | else if (ch == "/") { 96 | if (stream.eat("*")) { 97 | return chain(stream, state, jsTokenComment); 98 | } 99 | else if (stream.eat("/")) { 100 | stream.skipToEnd(); 101 | return ret("comment", "comment"); 102 | } 103 | else if (state.lastType == "operator" || state.lastType == "keyword c" || 104 | /^[\[{}\(,;:]$/.test(state.lastType)) { 105 | nextUntilUnescaped(stream, "/"); 106 | stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla 107 | return ret("regexp", "string-2"); 108 | } 109 | else { 110 | stream.eatWhile(isOperatorChar); 111 | return ret("operator", null, stream.current()); 112 | } 113 | } 114 | else if (ch == "#") { 115 | stream.skipToEnd(); 116 | return ret("error", "error"); 117 | } 118 | else if (isOperatorChar.test(ch)) { 119 | stream.eatWhile(isOperatorChar); 120 | return ret("operator", null, stream.current()); 121 | } 122 | else { 123 | stream.eatWhile(/[\w\$_]/); 124 | var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; 125 | return (known && state.lastType != ".") ? ret(known.type, known.style, word) : 126 | ret("variable", "variable", word); 127 | } 128 | } 129 | 130 | function jsTokenString(quote) { 131 | return function(stream, state) { 132 | if (!nextUntilUnescaped(stream, quote)) 133 | state.tokenize = jsTokenBase; 134 | return ret("string", "string"); 135 | }; 136 | } 137 | 138 | function jsTokenComment(stream, state) { 139 | var maybeEnd = false, ch; 140 | while (ch = stream.next()) { 141 | if (ch == "/" && maybeEnd) { 142 | state.tokenize = jsTokenBase; 143 | break; 144 | } 145 | maybeEnd = (ch == "*"); 146 | } 147 | return ret("comment", "comment"); 148 | } 149 | 150 | // Parser 151 | 152 | var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true}; 153 | 154 | function JSLexical(indented, column, type, align, prev, info) { 155 | this.indented = indented; 156 | this.column = column; 157 | this.type = type; 158 | this.prev = prev; 159 | this.info = info; 160 | if (align != null) this.align = align; 161 | } 162 | 163 | function inScope(state, varname) { 164 | for (var v = state.localVars; v; v = v.next) 165 | if (v.name == varname) return true; 166 | } 167 | 168 | function parseJS(state, style, type, content, stream) { 169 | var cc = state.cc; 170 | // Communicate our context to the combinators. 171 | // (Less wasteful than consing up a hundred closures on every call.) 172 | cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; 173 | 174 | if (!state.lexical.hasOwnProperty("align")) 175 | state.lexical.align = true; 176 | 177 | while(true) { 178 | var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; 179 | if (combinator(type, content)) { 180 | while(cc.length && cc[cc.length - 1].lex) 181 | cc.pop()(); 182 | if (cx.marked) return cx.marked; 183 | if (type == "variable" && inScope(state, content)) return "variable-2"; 184 | return style; 185 | } 186 | } 187 | } 188 | 189 | // Combinator utils 190 | 191 | var cx = {state: null, column: null, marked: null, cc: null}; 192 | function pass() { 193 | for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); 194 | } 195 | function cont() { 196 | pass.apply(null, arguments); 197 | return true; 198 | } 199 | function register(varname) { 200 | function inList(list) { 201 | for (var v = list; v; v = v.next) 202 | if (v.name == varname) return true; 203 | return false; 204 | } 205 | var state = cx.state; 206 | if (state.context) { 207 | cx.marked = "def"; 208 | if (inList(state.localVars)) return; 209 | state.localVars = {name: varname, next: state.localVars}; 210 | } else { 211 | if (inList(state.globalVars)) return; 212 | state.globalVars = {name: varname, next: state.globalVars}; 213 | } 214 | } 215 | 216 | // Combinators 217 | 218 | var defaultVars = {name: "this", next: {name: "arguments"}}; 219 | function pushcontext() { 220 | cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; 221 | cx.state.localVars = defaultVars; 222 | } 223 | function popcontext() { 224 | cx.state.localVars = cx.state.context.vars; 225 | cx.state.context = cx.state.context.prev; 226 | } 227 | function pushlex(type, info) { 228 | var result = function() { 229 | var state = cx.state; 230 | state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info); 231 | }; 232 | result.lex = true; 233 | return result; 234 | } 235 | function poplex() { 236 | var state = cx.state; 237 | if (state.lexical.prev) { 238 | if (state.lexical.type == ")") 239 | state.indented = state.lexical.indented; 240 | state.lexical = state.lexical.prev; 241 | } 242 | } 243 | poplex.lex = true; 244 | 245 | function expect(wanted) { 246 | return function(type) { 247 | if (type == wanted) return cont(); 248 | else if (wanted == ";") return pass(); 249 | else return cont(arguments.callee); 250 | }; 251 | } 252 | 253 | function statement(type) { 254 | if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); 255 | if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); 256 | if (type == "keyword b") return cont(pushlex("form"), statement, poplex); 257 | if (type == "{") return cont(pushlex("}"), block, poplex); 258 | if (type == ";") return cont(); 259 | if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse(cx.state.indented)); 260 | if (type == "function") return cont(functiondef); 261 | if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), 262 | poplex, statement, poplex); 263 | if (type == "variable") return cont(pushlex("stat"), maybelabel); 264 | if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), 265 | block, poplex, poplex); 266 | if (type == "case") return cont(expression, expect(":")); 267 | if (type == "default") return cont(expect(":")); 268 | if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), 269 | statement, poplex, popcontext); 270 | return pass(pushlex("stat"), expression, expect(";"), poplex); 271 | } 272 | function expression(type) { 273 | return expressionInner(type, maybeoperatorComma); 274 | } 275 | function expressionNoComma(type) { 276 | return expressionInner(type, maybeoperatorNoComma); 277 | } 278 | function expressionInner(type, maybeop) { 279 | if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); 280 | if (type == "function") return cont(functiondef); 281 | if (type == "keyword c") return cont(maybeexpression); 282 | if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); 283 | if (type == "operator") return cont(expression); 284 | if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop); 285 | if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop); 286 | return cont(); 287 | } 288 | function maybeexpression(type) { 289 | if (type.match(/[;\}\)\],]/)) return pass(); 290 | return pass(expression); 291 | } 292 | 293 | function maybeoperatorComma(type, value) { 294 | if (type == ",") return pass(); 295 | return maybeoperatorNoComma(type, value, maybeoperatorComma); 296 | } 297 | function maybeoperatorNoComma(type, value, me) { 298 | if (!me) me = maybeoperatorNoComma; 299 | if (type == "operator") { 300 | if (/\+\+|--/.test(value)) return cont(me); 301 | if (value == "?") return cont(expression, expect(":"), expression); 302 | return cont(expression); 303 | } 304 | if (type == ";") return; 305 | if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me); 306 | if (type == ".") return cont(property, me); 307 | if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me); 308 | } 309 | function maybelabel(type) { 310 | if (type == ":") return cont(poplex, statement); 311 | return pass(maybeoperatorComma, expect(";"), poplex); 312 | } 313 | function property(type) { 314 | if (type == "variable") {cx.marked = "property"; return cont();} 315 | } 316 | function objprop(type, value) { 317 | if (type == "variable") { 318 | cx.marked = "property"; 319 | if (value == "get" || value == "set") return cont(getterSetter); 320 | } else if (type == "number" || type == "string") { 321 | cx.marked = type + " property"; 322 | } 323 | if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma); 324 | } 325 | function getterSetter(type) { 326 | if (type == ":") return cont(expression); 327 | if (type != "variable") return cont(expect(":"), expression); 328 | cx.marked = "property"; 329 | return cont(functiondef); 330 | } 331 | function commasep(what, end) { 332 | function proceed(type) { 333 | if (type == ",") { 334 | var lex = cx.state.lexical; 335 | if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; 336 | return cont(what, proceed); 337 | } 338 | if (type == end) return cont(); 339 | return cont(expect(end)); 340 | } 341 | return function(type) { 342 | if (type == end) return cont(); 343 | else return pass(what, proceed); 344 | }; 345 | } 346 | function block(type) { 347 | if (type == "}") return cont(); 348 | return pass(statement, block); 349 | } 350 | function maybetype(type) { 351 | if (type == ":") return cont(typedef); 352 | return pass(); 353 | } 354 | function typedef(type) { 355 | if (type == "variable"){cx.marked = "variable-3"; return cont();} 356 | return pass(); 357 | } 358 | function vardef1(type, value) { 359 | if (type == "variable") { 360 | register(value); 361 | return isTS ? cont(maybetype, vardef2) : cont(vardef2); 362 | } 363 | return pass(); 364 | } 365 | function vardef2(type, value) { 366 | if (value == "=") return cont(expressionNoComma, vardef2); 367 | if (type == ",") return cont(vardef1); 368 | } 369 | function maybeelse(indent) { 370 | return function(type, value) { 371 | if (type == "keyword b" && value == "else") { 372 | cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical); 373 | return cont(statement, poplex); 374 | } 375 | return pass(); 376 | }; 377 | } 378 | function forspec1(type) { 379 | if (type == "var") return cont(vardef1, expect(";"), forspec2); 380 | if (type == ";") return cont(forspec2); 381 | if (type == "variable") return cont(formaybein); 382 | return pass(expression, expect(";"), forspec2); 383 | } 384 | function formaybein(_type, value) { 385 | if (value == "in") return cont(expression); 386 | return cont(maybeoperatorComma, forspec2); 387 | } 388 | function forspec2(type, value) { 389 | if (type == ";") return cont(forspec3); 390 | if (value == "in") return cont(expression); 391 | return pass(expression, expect(";"), forspec3); 392 | } 393 | function forspec3(type) { 394 | if (type != ")") cont(expression); 395 | } 396 | function functiondef(type, value) { 397 | if (type == "variable") {register(value); return cont(functiondef);} 398 | if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); 399 | } 400 | function funarg(type, value) { 401 | if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} 402 | } 403 | 404 | // Interface 405 | 406 | return { 407 | startState: function(basecolumn) { 408 | return { 409 | tokenize: jsTokenBase, 410 | lastType: null, 411 | cc: [], 412 | lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), 413 | localVars: parserConfig.localVars, 414 | globalVars: parserConfig.globalVars, 415 | context: parserConfig.localVars && {vars: parserConfig.localVars}, 416 | indented: 0 417 | }; 418 | }, 419 | 420 | token: function(stream, state) { 421 | if (stream.sol()) { 422 | if (!state.lexical.hasOwnProperty("align")) 423 | state.lexical.align = false; 424 | state.indented = stream.indentation(); 425 | } 426 | if (state.tokenize != jsTokenComment && stream.eatSpace()) return null; 427 | var style = state.tokenize(stream, state); 428 | if (type == "comment") return style; 429 | state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; 430 | return parseJS(state, style, type, content, stream); 431 | }, 432 | 433 | indent: function(state, textAfter) { 434 | if (state.tokenize == jsTokenComment) return CodeMirror.Pass; 435 | if (state.tokenize != jsTokenBase) return 0; 436 | var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; 437 | if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; 438 | var type = lexical.type, closing = firstChar == type; 439 | if (parserConfig.statementIndent != null) { 440 | if (type == ")" && lexical.prev && lexical.prev.type == "stat") lexical = lexical.prev; 441 | if (lexical.type == "stat") return lexical.indented + parserConfig.statementIndent; 442 | } 443 | 444 | if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); 445 | else if (type == "form" && firstChar == "{") return lexical.indented; 446 | else if (type == "form") return lexical.indented + indentUnit; 447 | else if (type == "stat") 448 | return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); 449 | else if (lexical.info == "switch" && !closing) 450 | return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); 451 | else if (lexical.align) return lexical.column + (closing ? 0 : 1); 452 | else return lexical.indented + (closing ? 0 : indentUnit); 453 | }, 454 | 455 | electricChars: ":{}", 456 | 457 | jsonMode: jsonMode 458 | }; 459 | }); 460 | 461 | CodeMirror.defineMIME("text/javascript", "javascript"); 462 | CodeMirror.defineMIME("text/ecmascript", "javascript"); 463 | CodeMirror.defineMIME("application/javascript", "javascript"); 464 | CodeMirror.defineMIME("application/ecmascript", "javascript"); 465 | CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); 466 | CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); 467 | CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); 468 | CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); 469 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/login.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午7:52 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | function bindLogin() { 9 | $("#login-button").bind("click", function () { 10 | login($("#username").val(), $("#password").val()); 11 | }); 12 | } 13 | 14 | function bindRegister() { 15 | $("#register-button").bind("click", function () { 16 | window.location.href="/register"; 17 | }); 18 | } 19 | 20 | $(function () { 21 | bindLogin(); 22 | bindRegister(); 23 | }); 24 | 25 | function login(username, password) { 26 | $.ajax({ 27 | url: "/login", 28 | method: "post", 29 | data: { 30 | username: username, 31 | password: password 32 | } 33 | } 34 | ). 35 | done(function (data) { 36 | if (data["code"] == 200) { 37 | alert(data["msg"]); 38 | setTimeout("window.location.href=\"/\"", 1000); 39 | } else { 40 | alert(data["msg"]) 41 | } 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午7:52 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | function bindRegister() { 9 | $("#register-button").bind("click", function () { 10 | if ($("#password").val() != $("#repassword").val()) { 11 | alert("两次密码输入不一致,请重新输入!"); 12 | } else { 13 | register($("#username").val(), $("#password").val()); 14 | } 15 | }); 16 | } 17 | 18 | $(function () { 19 | bindRegister(); 20 | }); 21 | 22 | function register(username, password) { 23 | $.ajax({ 24 | url: "/register", 25 | method: "post", 26 | data: { 27 | username: username, 28 | password: password 29 | } 30 | } 31 | ). 32 | done(function (data) { 33 | if (data["code"] == 200) { 34 | alert(data["msg"]); 35 | setTimeout("window.location.href=\"/\"", 1000); 36 | } else { 37 | alert(data["msg"]) 38 | } 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/shareboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午7:52 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | $(function () { 9 | bindDashboardApply(); 10 | }); 11 | 12 | function bindDashboardApply() { 13 | $("a#apply-a").bind("click", function () { 14 | apply(BHzones[$(this).attr("file-id")].text) 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/webapp/js/zonespick.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created with IntelliJ IDEA. 3 | * User: cairne 4 | * Date: 13-5-12 5 | * Time: 下午7:52 6 | * To change this template use File | Settings | File Templates. 7 | */ 8 | $(function () { 9 | bindSlideUp($("a#fold-button")); 10 | bindAcitve($("a#active-button")); 11 | bindNew($("a#button-new")); 12 | bindAdd($("a#button-do-add")); 13 | bindDelete($("a#delete-button")) 14 | bindCandidate(); 15 | $('#addModal').bind("keypress", function (e) { 16 | if ($(e)[0].charCode == 13) { 17 | doAdd(); 18 | } 19 | }) 20 | $("#button-clear-domain").bind("click", function () { 21 | $('#input-domain').val("") 22 | }) 23 | $("#button-clear-ip").bind("click", function () { 24 | $('#input-ip').val("") 25 | }) 26 | $("#button-clear-comment").bind("click", function () { 27 | $('#input-comment').val("") 28 | }) 29 | $("#button-share").bind("click", function () { 30 | var reg = /_zones=([^;]+)/; 31 | var zones = reg.exec(document.cookie) 32 | $("#config-share-url").val(window.location.origin + "/z?z=" + zones[1]) 33 | $('#shareModal').modal('show'); 34 | $("#config-share-url").select() 35 | }); 36 | $("#config-share-url").bind("click",function(){ 37 | $(this).select(); 38 | }) 39 | 40 | }); 41 | 42 | function getConfigHtml(config) { 43 | var innerHTML = ""; 44 | if (config.active) { 45 | innerHTML += ' ' + config.ip; 46 | } 47 | else { 48 | innerHTML += '     ' + config.ip; 49 | } 50 | if (config.comment != undefined && config.comment != "") { 51 | innerHTML += '   (' + config.comment + ')'; 52 | } 53 | return innerHTML; 54 | } 55 | 56 | function bindCandidate() { 57 | domainCandidate = domainCandidateDefault; 58 | ipCandidate = ipCandidateDefault; 59 | buildCandidate() 60 | $('#input-domain').typeahead({ 61 | source: domainCandidate, 62 | items: 4 63 | }) 64 | $('#input-ip').typeahead({ 65 | source: ipCandidate, 66 | items: 4 67 | }) 68 | } 69 | 70 | function buildCandidate() { 71 | BHzones.forEach(function (e) { 72 | var domain = e.domain; 73 | if (domainCandidate.indexOf(domain) == -1) { 74 | domainCandidate.push(domain) 75 | } 76 | e.config.forEach(function (config) { 77 | if (ipCandidate.indexOf(config.ip) == -1) { 78 | ipCandidate.push(config.ip) 79 | } 80 | }) 81 | }); 82 | } 83 | 84 | function bindSlideDown(e) { 85 | e.bind("click", function () { 86 | var ul = $(this).parent().parent(); 87 | var foldUl = ul.children("#configs").children("ul"); 88 | foldUl.slideDown("fast"); 89 | var i = $(this).children("i#fold-icon"); 90 | i.removeClass("icon-double-angle-down"); 91 | i.addClass("icon-double-angle-up") 92 | $(this).unbind("click"); 93 | bindSlideUp($(this)); 94 | 95 | }); 96 | } 97 | 98 | function bindDelete(e) { 99 | e.bind("click", function () { 100 | var li = $(this).parent().parent(); 101 | var domainIndex = li.attr("domain-index"); 102 | var configIndex = li.attr("config-index"); 103 | var aa = BHzones[domainIndex].config.splice(configIndex, 1); 104 | if (BHzones[domainIndex].config.length == 0) { 105 | 106 | var ul = li.parent().parent().parent(); 107 | ul.remove() 108 | } else { 109 | li.remove() 110 | } 111 | pick(JSON.stringify(BHzones)); 112 | }); 113 | } 114 | 115 | function bindNew(e) { 116 | e.bind("click", function () { 117 | var li = $(this).parent().parent(); 118 | $("input#input-domain").val(li.attr("data-domain")); 119 | $('#addModal').modal('show'); 120 | $("#input-error").addClass("hide"); 121 | }); 122 | } 123 | 124 | 125 | function validIp(ip){ 126 | var ipReg=/[1-2]{0,1}[0-9]{1,2}\.[1-2]{0,1}[0-9]{1,2}\.[1-2]{0,1}[0-9]{1,2}\.[1-2]{0,1}[0-9]{1,2}/; 127 | return ipReg.test(ip) 128 | } 129 | 130 | 131 | function doAdd() { 132 | var domain = $("#input-domain").val(); 133 | var ip = $("#input-ip").val(); 134 | var comment = $("#input-comment").val(); 135 | if (domain==""){ 136 | $("#input-error").html("Sorry,domain can't be empty."); 137 | $("#input-error").removeClass("hide"); 138 | $("input#input-domain").select(); 139 | return; 140 | } 141 | if (ip==""){ 142 | $("#input-error").html("Sorry,ip can't be empty."); 143 | $("#input-error").removeClass("hide"); 144 | $("input#input-ip").select(); 145 | return; 146 | } 147 | if (!validIp(ip)){ 148 | $("#input-error").html("Please input a valid ip address."); 149 | $("#input-error").removeClass("hide"); 150 | $("input#input-ip").select(); 151 | return; 152 | } 153 | if (comment.indexOf(" ")>0) { 154 | $("#input-error").html("Sorry, whitespace is not supported for comment yet."); 155 | $("#input-error").removeClass("hide"); 156 | $("input#input-comment").select(); 157 | return; 158 | } 159 | var data = BHzones; 160 | var domainIndex = data.length; 161 | for (var i = 0; i < data.length; i++) { 162 | if (data[i].domain == domain) { 163 | domainIndex = i; 164 | break; 165 | } 166 | } 167 | 168 | var config = { 169 | active: false, 170 | domain: domain, 171 | ip: ip, 172 | comment: comment 173 | }; 174 | 175 | if (domainIndex < data.length) { 176 | var exist = false; 177 | data[i].config.forEach(function (e) { 178 | if (e.ip == ip) { 179 | exist = true; 180 | } 181 | }) 182 | if (!exist) { 183 | var ul = $("ul[data-index=" + domainIndex + "]"); 184 | var configId = ul.children("li").length; 185 | data[i].config.push(config); 186 | var newLi = '
  • '; 187 | newLi += '\n     ' + ip; 188 | if (comment != "") { 189 | newLi += '  (' + comment + ')'; 190 | } 191 | newLi += 'Delete
  • '; 192 | ul.children("li#configs").children("ul").append(newLi); 193 | bindAcitve(ul.find("li[config-index=" + configId + "]").children("a#active-button")) 194 | bindDelete(ul.find("li[config-index=" + configId + "]").find("a#delete-button")) 195 | } 196 | } else { 197 | var zone = { 198 | domain: domain, 199 | config: [config] 200 | } 201 | BHzones.push(zone); 202 | var bt = baidu.template; 203 | var data = { 204 | i: domainIndex, 205 | zone: zone 206 | } 207 | var html = bt("config-template", data); 208 | $("div#configs-container").append(html); 209 | var ul = $("ul[data-index=" + domainIndex + "]"); 210 | bindSlideUp(ul.find("a#fold-button")); 211 | bindAcitve(ul.find("a#active-button")); 212 | bindNew(ul.find("a#button-new")); 213 | bindAdd(ul.find("a#button-do-add")); 214 | bindDelete(ul.find("a#delete-button")) 215 | } 216 | $('#addModal').modal("hide"); 217 | $("#empty-tip").remove(); 218 | buildCandidate() 219 | pick(JSON.stringify(BHzones)); 220 | }; 221 | 222 | function bindAdd(e) { 223 | e.bind("click", doAdd); 224 | } 225 | 226 | function bindSlideUp(e) { 227 | e.bind("click", function () { 228 | var ul = $(this).parent().parent(); 229 | var foldUl = ul.children("#configs").children("ul"); 230 | foldUl.slideUp("fast"); 231 | var i = $(this).children("i#fold-icon"); 232 | i.removeClass("icon-double-angle-up"); 233 | i.addClass("icon-double-angle-down") 234 | $(this).unbind("click"); 235 | bindSlideDown($(this)); 236 | }); 237 | } 238 | 239 | function bindAcitve(e) { 240 | e.bind("click", function () { 241 | var li = $(this).parent("li"); 242 | var i = li.attr("domain-index"); 243 | var j = li.attr("config-index"); 244 | var configs = BHzones[i].config; 245 | var finalStat = !configs[j].active; 246 | //unselect others 247 | if (finalStat) { 248 | for (var k = 0; k < configs.length; k++) { 249 | var config = configs[k]; 250 | if (config.active) { 251 | config.active = false; 252 | li.parent("ul").children("[config-index=" + k + "]").children("a")[0].innerHTML = getConfigHtml(config); 253 | } 254 | } 255 | } 256 | configs[j].active = finalStat; 257 | $(this)[0].innerHTML = getConfigHtml(configs[j]) 258 | pick(JSON.stringify(BHzones)); 259 | }); 260 | } 261 | -------------------------------------------------------------------------------- /server/src/test/java/us/codecraft/blackhole/suite/dao/SqliteDaoTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.Connection; 12 | import java.sql.ResultSet; 13 | import java.sql.SQLException; 14 | import java.sql.Statement; 15 | 16 | /** 17 | * User: cairne 18 | * Date: 13-5-11 19 | * Time: 上午7:32 20 | */ 21 | @RunWith(SpringJUnit4ClassRunner.class) 22 | @ContextConfiguration(locations = {"classpath:/spring/applicationContext-*.xml"}) 23 | public class SqliteDaoTest { 24 | 25 | @Autowired 26 | private DataSource dataSource; 27 | 28 | @Ignore 29 | @Test 30 | public void test() throws SQLException { 31 | Connection connection = dataSource.getConnection(); 32 | try { 33 | // create a database connection 34 | Statement statement = connection.createStatement(); 35 | statement.setQueryTimeout(30); // set timeout to 30 sec. 36 | 37 | statement.executeUpdate("create table person (id integer, name string) if not exists person"); 38 | statement.executeUpdate("insert into person values(1, 'leo')"); 39 | statement.executeUpdate("insert into person values(2, 'yui')"); 40 | ResultSet rs = statement.executeQuery("select * from person"); 41 | while (rs.next()) { 42 | // read the result set 43 | System.out.println("name = " + rs.getString("name")); 44 | System.out.println("id = " + rs.getInt("id")); 45 | } 46 | } catch (SQLException e) { 47 | // if the error message is "out of memory", 48 | // it probably means no database file is found 49 | e.printStackTrace(); 50 | System.err.println(e.getMessage()); 51 | } finally { 52 | try { 53 | if (connection != null) 54 | connection.close(); 55 | } catch (SQLException e) { 56 | // connection close failed. 57 | System.err.println(e); 58 | } 59 | } 60 | } 61 | 62 | @Ignore 63 | @Test 64 | public void createTable() throws SQLException { 65 | excute("drop table if exists User_Passport"); 66 | // excute("CREATE TABLE ZonesFile (\n" + 67 | // " `id` INTEGER primary key AUTOINCREMENT,\n" + 68 | // " `type` INTEGER,\n" + 69 | // " `name` string,\n" + 70 | // " `text` string,\n" + 71 | // " `user` string\n" + 72 | // ");\n"); 73 | excute("CREATE TABLE User_Passport (\n" + 74 | " `id` INTEGER primary key AUTOINCREMENT,\n" + 75 | " `username` text UNIQUE,\n" + 76 | " `passwordSalt` string,\n" + 77 | " `salt` string,\n" + 78 | " `ticket` text UNIQUE\n" + 79 | ");"); 80 | } 81 | 82 | @Ignore 83 | @Test 84 | public void insert() throws SQLException { 85 | excute("insert into ZonesFile (`type`,`name`,`user`,`text`) values(1,'dev','public','127.0.0.1 i*.static.dp\n" + 86 | "127.0.0.1 *.local.dp')"); 87 | excute("insert into ZonesFile (`type`,`name`,`user`,`text`) values(1,'none','public','')"); 88 | } 89 | 90 | private void excute(String query) throws SQLException { 91 | Connection connection = dataSource.getConnection(); 92 | try { 93 | // create a database connection 94 | Statement statement = connection.createStatement(); 95 | statement.setQueryTimeout(30); // set timeout to 30 sec. 96 | 97 | statement.executeUpdate(query); 98 | } catch (SQLException e) { 99 | // if the error message is "out of memory", 100 | // it probably means no database file is found 101 | e.printStackTrace(); 102 | System.err.println(e.getMessage()); 103 | } finally { 104 | try { 105 | if (connection != null) 106 | connection.close(); 107 | } catch (SQLException e) { 108 | // connection close failed. 109 | System.err.println(e); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /server/src/test/java/us/codecraft/blackhole/suite/dao/UserPassportDaoTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | import us.codecraft.blackhole.suite.model.UserPassport; 9 | 10 | import javax.annotation.Resource; 11 | 12 | /** 13 | * User: cairne 14 | * Date: 13-5-13 15 | * Time: 下午8:33 16 | */ 17 | @RunWith(SpringJUnit4ClassRunner.class) 18 | @ContextConfiguration(locations = {"classpath:/spring/applicationContext-*.xml"}) 19 | public class UserPassportDaoTest { 20 | @Resource 21 | private UserPassportDAO userPassportDAO; 22 | 23 | @Ignore 24 | @Test 25 | public void test() { 26 | UserPassport userPassport = new UserPassport("admin", "admin123", "123", "12345161"); 27 | try { 28 | int insert = userPassportDAO.insert(userPassport); 29 | System.out.println(insert); 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | } 33 | UserPassport byTicket = userPassportDAO.getByTicket("123456"); 34 | System.out.println(byTicket); 35 | UserPassport cairne = userPassportDAO.getByUsername("cairne"); 36 | System.out.println(cairne); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/test/java/us/codecraft/blackhole/suite/dao/ZonesDaoTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.dao; 2 | 3 | import org.junit.Ignore; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * User: cairne 13 | * Date: 13-5-13 14 | * Time: 下午10:08 15 | */ 16 | @RunWith(SpringJUnit4ClassRunner.class) 17 | @ContextConfiguration(locations = {"classpath:/spring/applicationContext-*.xml"}) 18 | public class ZonesDaoTest { 19 | 20 | @Resource 21 | private ZonesFileDAO zonesFileDAO; 22 | 23 | @Ignore 24 | @Test 25 | public void test(){ 26 | System.out.println(zonesFileDAO.findPublic()); 27 | System.out.println(zonesFileDAO.load(1)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/test/java/us/codecraft/blackhole/suite/utils/UserZonesUtilsTest.java: -------------------------------------------------------------------------------- 1 | package us.codecraft.blackhole.suite.utils; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import us.codecraft.blackhole.suite.util.UserZonesUtils; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * User: cairne 11 | * Date: 13-5-26 12 | * Time: 下午4:45 13 | */ 14 | public class UserZonesUtilsTest { 15 | 16 | @Test 17 | public void test() throws IOException { 18 | String text = "#192.168.9.109\twww.dianping.com\t#test\n" + 19 | "#127.0.0.1\twww.dianping.com\t#测试服务器\n" + 20 | "#192.168.9.110\twww.dianping.com"; 21 | String json = UserZonesUtils.toJson(text); 22 | String jsonSupposed = "[{\"domain\":\"www.dianping.com\",\"config\":[{\"domain\":\"www.dianping.com\",\"active\":false,\"ip\":\"192.168.9.109\",\"comment\":\"test\"},{\"domain\":\"www.dianping.com\",\"active\":false,\"ip\":\"127.0.0.1\",\"comment\":\"测试服务器\"},{\"domain\":\"www.dianping.com\",\"active\":false,\"ip\":\"192.168.9.110\"}]}]"; 23 | Assert.assertEquals(jsonSupposed,json); 24 | String text2 = UserZonesUtils.fromJson(json); 25 | Assert.assertEquals(text2,text); 26 | } 27 | 28 | @Test 29 | public void testMerge() { 30 | String textA = "#127.0.0.1\twww.dianping.com\t#test\n"; 31 | String textB = "#127.0.0.1\twww.dianping.com\n"; 32 | String merge = UserZonesUtils.merge(textA, textB); 33 | Assert.assertEquals("#127.0.0.1\twww.dianping.com\t#test",merge); 34 | merge = UserZonesUtils.merge(textB, textA); 35 | Assert.assertEquals("#127.0.0.1\twww.dianping.com\t#test",merge); 36 | } 37 | } 38 | --------------------------------------------------------------------------------