wildfire chat server source initial submit

This commit is contained in:
heavyrain2012
2019-03-16 17:04:23 +08:00
parent e59217bd10
commit cab3ca0916
1466 changed files with 528862 additions and 15919 deletions
@@ -0,0 +1,105 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.admin.GetIMTokenAction;
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
import io.moquette.spi.ISessionsStore;
import io.moquette.spi.impl.Utils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.DBUtil;
import win.liyufan.im.ErrorCode;
import static win.liyufan.im.ErrorCode.ERROR_CODE_SUCCESS;
/**
* 请求处理接口<br>
* 当用户请求某个Path,则调用相应Action的doAction方法
* @author Looly
*
*/
abstract public class Action {
public static IMessagesStore messagesStore = null;
public static ISessionsStore sessionsStore = null;
public ChannelHandlerContext ctx;
protected boolean isSync = true;
public ErrorCode preAction(Request request, Response response) {
if (getClass().getAnnotation(RequireAuthentication.class) != null) {
//do authentication
}
return ERROR_CODE_SUCCESS;
}
public boolean doAction(Request request, Response response) {
ErrorCode errorCode = preAction(request, response);
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
//事务逻辑有缺陷,先注释掉
// if (isTransactionAction() && !(this instanceof IMAction)) {
// DBUtil.beginTransaction();
// try {
// action(request, response);
// DBUtil.commit();
// } catch (Exception e) {
// e.printStackTrace();
// DBUtil.roolback();
// throw e;
// }
// } else {
action(request, response);
// }
afterAction(request, response);
} else {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), RestResult.resultOf(errorCode));
response.setContent(new Gson().toJson(result));
response.send();
}
if(this instanceof IMAction || this instanceof RouteAction || this instanceof GetIMTokenAction) {
isSync = false;
}
return isSync;
}
public boolean isTransactionAction() {
return false;
}
abstract public void action(Request request, Response response);
public void afterAction(Request request, Response response) {
}
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
Gson gson = new Gson();
T t = gson.fromJson(content, cls);
return t;
}
return null;
}
}
@@ -0,0 +1,234 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassUtil {
/**
* 获取同一路径下所有子类或接口实现类
*
* @param cls
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List<Class<?>> getAllAssignedClass(Class<?> cls) throws IOException,
ClassNotFoundException {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (Class<?> c : getClasses(cls)) {
if (cls.isAssignableFrom(c) && !cls.equals(c)) {
classes.add(c);
}
}
return classes;
}
/**
* 取得当前类路径下的所有类
*
* @param cls
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List<Class<?>> getClasses(Class<?> cls) throws IOException,
ClassNotFoundException {
String pk = cls.getPackage().getName();
// String path = pk.replace('.', '/');
// ClassLoader classloader = Thread.currentThread().getContextClassLoader();
// URL url = classloader.getResource(path);
// String type = url.getProtocol();
//
// System.out.println("the type is " + type);
// System.out.println(url.getPath());
//
// return getClasses(new File(url.getFile()), pk);
List<String> classNames = getClassName(pk, true);
List<Class<?>> classes = new ArrayList<>();
for (String className :
classNames) {
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
className = className.substring(className.lastIndexOf("/")+1, className.length());
}
classes.add(Class.forName(className));
}
return classes;
}
/**
* 迭代查找类
*
* @param dir
* @param pk
* @return
* @throws ClassNotFoundException
*/
private static List<Class<?>> getClasses(File dir, String pk) throws ClassNotFoundException {
System.out.println(dir.getAbsolutePath());
List<Class<?>> classes = new ArrayList<Class<?>>();
if (!dir.exists()) {
return classes;
}
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
classes.addAll(getClasses(f, pk + "." + f.getName()));
}
String name = f.getName();
if (name.endsWith(".class")) {
classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
}
}
return classes;
}
/**
* 获取某包下所有类
* @param packageName 包名
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
public static List<String> getClassName(String packageName, boolean childPackage) {
List<String> fileNames = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String packagePath = packageName.replace(".", "/");
URL url = loader.getResource(packagePath);
if (url != null) {
String type = url.getProtocol();
if (type.equals("file")) {
fileNames = getClassNameByFile(url.getPath(), null, childPackage);
} else if (type.equals("jar")) {
fileNames = getClassNameByJar(url.getPath(), childPackage);
}
} else {
fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);
}
return fileNames;
}
/**
* 从项目文件获取某包下所有类
* @param filePath 文件路径
* @param className 类名集合
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
File file = new File(filePath);
File[] childFiles = file.listFiles();
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
if (childPackage) {
myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
}
} else {
String childFilePath = childFile.getPath();
if (childFilePath.endsWith(".class")) {
if (childFilePath.indexOf("\\classes") >= 0 || childFilePath.indexOf("/classes") >= 0) {
int start = childFilePath.indexOf("\\classes");
if (start == -1) {
start = childFilePath.indexOf("/classes");
}
childFilePath = childFilePath.substring(start + 9, childFilePath.lastIndexOf("."));
}
childFilePath = childFilePath.replace("\\", ".");
childFilePath = childFilePath.replace("/", ".");
myClassName.add(childFilePath);
}
}
}
return myClassName;
}
/**
* 从jar获取某包下所有类
* @param jarPath jar文件路径
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByJar(String jarPath, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
String[] jarInfo = jarPath.split("!");
String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
String packagePath = jarInfo[1].substring(1);
try {
JarFile jarFile = new JarFile(jarFilePath);
Enumeration<JarEntry> entrys = jarFile.entries();
while (entrys.hasMoreElements()) {
JarEntry jarEntry = entrys.nextElement();
String entryName = jarEntry.getName();
if (entryName.endsWith(".class")) {
if (childPackage) {
if (entryName.startsWith(packagePath)) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
myClassName.add(entryName);
}
} else {
int index = entryName.lastIndexOf("/");
String myPackagePath;
if (index != -1) {
myPackagePath = entryName.substring(0, index);
} else {
myPackagePath = entryName;
}
if (myPackagePath.equals(packagePath)) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
myClassName.add(entryName);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return myClassName;
}
/**
* 从所有jar中搜索该包,并获取该包下所有类
* @param urls URL集合
* @param packagePath 包路径
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
if (urls != null) {
for (int i = 0; i < urls.length; i++) {
URL url = urls[i];
String urlPath = url.getPath();
// 不必搜索classes文件夹
if (urlPath.endsWith("classes/")) {
continue;
}
String jarPath = urlPath + "!/" + packagePath;
myClassName.addAll(getClassNameByJar(jarPath, childPackage));
}
}
return myClassName;
}
}
@@ -0,0 +1,18 @@
package com.xiaoleilu.loServer.action;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
/**
* 默认的主页Action,当访问主页且没有定义主页Action时,调用此Action
* @author Looly
*
*/
public class DefaultIndexAction extends Action{
@Override
public void action(Request request, Response response) {
response.setContent("Welcome to LoServer.");
}
}
@@ -0,0 +1,117 @@
package com.xiaoleilu.loServer.action;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Pattern;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.DateUtil;
import com.xiaoleilu.hutool.util.FileUtil;
import com.xiaoleilu.hutool.util.ReUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.LoggerFactory;
/**
* 默认的主页Action,当访问主页且没有定义主页Action时,调用此Action
*
* @author Looly
*
*/
public class FileAction extends Action {
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(FileAction.class);
@Override
public void action(Request request, Response response) {
if (false == Request.METHOD_GET.equalsIgnoreCase(request.getMethod())) {
response.sendError(HttpResponseStatus.METHOD_NOT_ALLOWED, "Please use GET method to request file!");
return;
}
if(ServerSetting.isRootAvailable() == false){
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Root dir not avaliable!");
return;
}
final File file = getFileByPath(request.getPath());
Logger.debug("Client [{}] get file [{}]", request.getIp(), file.getPath());
// 隐藏文件,跳过
if (file.isHidden() || !file.exists()) {
response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
return;
}
// 非文件,跳过
if (false == file.isFile()) {
response.sendError(HttpResponseStatus.FORBIDDEN, "403 Forbidden!");
return;
}
// Cache Validation
String ifModifiedSince = request.getHeader(HttpHeaderNames.IF_MODIFIED_SINCE.toString());
if (StrUtil.isNotBlank(ifModifiedSince)) {
Date ifModifiedSinceDate = null;
try {
ifModifiedSinceDate = DateUtil.parse(ifModifiedSince, HTTP_DATE_FORMATER);
} catch (Exception e) {
Logger.warn("If-Modified-Since header parse error: {}", e.getMessage());
}
if(ifModifiedSinceDate != null) {
// 只对比到秒一级别
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
Logger.debug("File {} not modified.", file.getPath());
response.sendNotModified();
return;
}
}
}
response.setContent(file);
}
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private static final SimpleDateFormat HTTP_DATE_FORMATER = new SimpleDateFormat(DateUtil.HTTP_DATETIME_PATTERN, Locale.US);
/**
* 通过URL中的path获得文件的绝对路径
*
* @param httpPath Http请求的Path
* @return 文件绝对路径
*/
public static File getFileByPath(String httpPath) {
// Decode the path.
try {
httpPath = URLDecoder.decode(httpPath, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
if (httpPath.isEmpty() || httpPath.charAt(0) != '/') {
return null;
}
// 路径安全检查
if (httpPath.contains("/.") || httpPath.contains("./") || httpPath.charAt(0) == '.' || httpPath.charAt(httpPath.length() - 1) == '.' || ReUtil.isMatch(INSECURE_URI, httpPath)) {
return null;
}
// 转换为绝对路径
return FileUtil.file(ServerSetting.getRoot(), httpPath);
}
}
@@ -0,0 +1,124 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.persistence.TargetEntry;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.security.AES;
import io.moquette.spi.security.Tokenor;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import java.util.Base64;
import java.util.concurrent.Executor;
@Route("/im")
@HttpMethod("POST")
public class IMAction extends Action {
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String str = new String(bytes);
try {
bytes = Base64.getDecoder().decode(str);
} catch (IllegalArgumentException e) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return;
}
String cid = fullHttpRequest.headers().get("cid");
byte[] cbytes = Base64.getDecoder().decode(cid);
cbytes = AES.AESDecrypt(cbytes, null, true);
if (cbytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return;
}
cid = new String(cbytes);
MemorySessionStore.Session session = sessionsStore.getSession(cid);
if (session != null) {
bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
} else {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return;
}
if (bytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return;
}
try {
WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
String token = wrapper.getToken();
String userId = Tokenor.getUserId(token.getBytes());
if (userId == null) {
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null);
} else {
RPCCenter.getInstance().sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
sendResponse(response, null, result);
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
}
} catch (InvalidProtocolBufferException e) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
}
}
}
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents) {
response.setStatus(HttpResponseStatus.OK);
if(contents == null) {
ByteBuf ackPayload = Unpooled.buffer();
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
response.setContent(ackPayload);
} else {
response.setContent(contents);
}
response.send();
}
}
@@ -0,0 +1,33 @@
package com.xiaoleilu.loServer.action;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 错误堆栈Action类
* @author Looly
*
*/
public class NotFoundAction extends Action{
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(NotFoundAction.class);
public final static String ERROR_PARAM_NAME = "_e";
private final static String TEMPLATE_ERROR = "<!DOCTYPE html><html><head><title>LoServer - Error report</title><style>h1,h3 {color:white; background-color: gray;}</style></head><body><h1>HTTP Status {} - {}</h1><hr size=\"1\" noshade=\"noshade\" /><p>{}</p><hr size=\"1\" noshade=\"noshade\" /><h3>LoServer</h3></body></html>";
@Override
public void action(Request request, Response response) {
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Not found!");
}
}
@@ -0,0 +1,154 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.security.AES;
import io.moquette.spi.security.Tokenor;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.ErrorCode;
import java.util.Base64;
import java.util.concurrent.Executor;
@Route("/route")
@HttpMethod("POST")
public class RouteAction extends Action {
private static final Logger LOG = LoggerFactory.getLogger(RouteAction.class);
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setContentType("application/octet-stream");
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
boolean b = false;
if ("web".equalsIgnoreCase(fullHttpRequest.headers().get("p"))) {
b = true;
}
final boolean base64Response = b;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String str = new String(bytes);
try {
bytes = Base64.getDecoder().decode(str);
} catch (IllegalArgumentException e) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
return;
}
String cid = fullHttpRequest.headers().get("cid");
byte[] cbytes = Base64.getDecoder().decode(cid);
cbytes = AES.AESDecrypt(cbytes, null, true);
if (cbytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
return;
}
cid = new String(cbytes);
String uid = fullHttpRequest.headers().get("uid");
byte[] ubytes = Base64.getDecoder().decode(uid);
ubytes = AES.AESDecrypt(ubytes, null, true);
if (ubytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
return;
}
uid = new String(ubytes);
MemorySessionStore.Session session = sessionsStore.sessionForClientAndUser(uid, cid);
if (session == null) {
ErrorCode errorCode = sessionsStore.createNewSession(uid, cid, true);
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
sendResponse(response, errorCode, null, base64Response);
return;
}
session = sessionsStore.sessionForClientAndUser(uid, cid);
}
if (session != null) {
bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
} else {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
return;
}
if (bytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
return;
}
try {
WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
String token = wrapper.getToken();
String userId = Tokenor.getUserId(token.getBytes());
LOG.info("RouteAction token={}, userId={}", token, userId);
if (userId == null) {
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null, base64Response);
} else {
RPCCenter.getInstance().sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
sendResponse(response, null, result, base64Response);
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null, base64Response);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null, base64Response);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
}
} catch (InvalidProtocolBufferException e) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, false);
}
}
}
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents, boolean base64Response) {
response.setStatus(HttpResponseStatus.OK);
if(contents == null) {
ByteBuf ackPayload = Unpooled.buffer();
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
response.setContent(ackPayload);
} else {
response.setContent(contents);
}
response.send();
}
}
@@ -0,0 +1,33 @@
package com.xiaoleilu.loServer.action;
import java.io.PrintWriter;
import java.io.StringWriter;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.LoggerFactory;
/**
* 错误堆栈Action类
* @author Looly
*
*/
public class UnknownErrorAction extends Action{
private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(UnknownErrorAction.class);
public final static String ERROR_PARAM_NAME = "_e";
private final static String TEMPLATE_ERROR = "<!DOCTYPE html><html><head><title>LoServer - Error report</title><style>h1,h3 {color:white; background-color: gray;}</style></head><body><h1>HTTP Status {} - {}</h1><hr size=\"1\" noshade=\"noshade\" /><p>{}</p><hr size=\"1\" noshade=\"noshade\" /><h3>LoServer</h3></body></html>";
@Override
public void action(Request request, Response response) {
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
}
}
@@ -0,0 +1,344 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.*;
import io.moquette.server.config.MediaServerConfig;
import io.moquette.spi.impl.security.AES;
import io.moquette.spi.security.DES;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import static com.xiaoleilu.loServer.handler.HttpResponseHelper.getFileExt;
@Route("/fs")
@HttpMethod("POST")
@RequireAuthentication
public class UploadFileAction extends Action {
private static final String KEY = "imfile";
private static final Logger logger = LoggerFactory.getLogger(UploadFileAction.class);
private static final HttpDataFactory factory = new DefaultHttpDataFactory(false);
public static class InvalidateTokenExecption extends Exception {
}
public static String getToken(int type) {
String signKey = KEY + "|" + (System.currentTimeMillis()) + "|" + type;
try {
return DES.encryptDES(signKey);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static int validateToken(String token) throws InvalidateTokenExecption {
try {
String signKey = DES.decryptDES(token);
String[] parts = signKey.split("\\|");
if(parts.length == 3) {
if(parts[0].equals(KEY)) {
long timestamp = Long.parseLong(parts[1]);
if(Math.abs(System.currentTimeMillis() - timestamp) < 2 * 60 * 60 * 1000) {
return Integer.parseInt(parts[2]);
}
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
throw new InvalidateTokenExecption();
}
@Override
public void action(Request r, Response response) {
if (r.getNettyRequest() instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) r.getNettyRequest();
String requestId = UUID.randomUUID().toString().replace("-", "");
logger.info("HttpFileServerHandler received a request: method=" + request.getMethod() + ", uri=" + request.getUri() + ", requestId=" + requestId);
if (!request.getDecoderResult().isSuccess()) {
logger.warn("http decode failed!");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("http decode failed");
return;
}
multipartUpload(request, requestId, response);
}
}
/**
* multipart上传
*/
private void multipartUpload(FullHttpRequest request, String requestId, Response response) {
HttpPostRequestDecoder decoder = null;
try {
decoder = new HttpPostRequestDecoder(factory, request);
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
logger.error("Failed to decode file data!", e1);
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("Failed to decode file data!");
return;
}
if (decoder != null) {
if (request instanceof HttpContent) {
HttpContent chunk = (HttpContent) request;
try {
decoder.offer(chunk);
} catch (HttpPostRequestDecoder.ErrorDataDecoderException e1) {
logger.warn("BAD_REQUEST, Failed to decode file data");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("Failed to decode file data!");
return;
}
long fileTotalSize = 0;
if (request.headers().contains("X-File-Total-Size")) {
try {
fileTotalSize = Integer.parseInt(request.headers().get("X-File-Total-Size"));
} catch (Exception e) {
logger.warn("invalid X-File-Total-Size value!");
}
}
readHttpDataChunkByChunk(response, decoder, requestId, HttpHeaders.isKeepAlive(request));
if (chunk instanceof LastHttpContent) {
}
} else {
logger.warn("BAD_REQUEST, Not a http request");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("Not a http request");
}
}
}
/**
* readHttpDataChunkByChunk
*/
private void readHttpDataChunkByChunk(Response response, HttpPostRequestDecoder decoder, String requestId, boolean isKeepAlive) {
try {
int[] bucket = new int[1];
bucket[0] = -1;
while (decoder.hasNext()) {
InterfaceHttpData data = decoder.next();
if (data != null) {
try {
if(!writeFileUploadData(data, response, requestId, isKeepAlive, bucket)) {
break;
}
} finally {
data.release();
}
}
}
} catch (Exception e) {
logger.info("chunk end");
}
}
/**
* writeFileUploadData
*/
private boolean writeFileUploadData(InterfaceHttpData data, Response response, String requestId, boolean isKeepAlive, int[] bucket) {
try {
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
FileUpload fileUpload = (FileUpload) data;
String remoteFileName = fileUpload.getFilename();
long remoteFileSize = fileUpload.length();
if(bucket[0] == -1) {
logger.warn("Not authenticated!");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("Not authenticated!");
return false;
}
if (StringUtil.isNullOrEmpty(remoteFileName)) {
logger.warn("remoteFileName is empty!");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("file name is empty");
return false;
}
if (StringUtil.isNullOrEmpty(requestId)) {
logger.warn("requestId is empty!");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("requestId is empty!");
return false;
}
if (remoteFileSize > 50 * 1024 * 1024) {
logger.warn("file over limite!(" + remoteFileSize + ")");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("file over limite!");
return false;
}
String remoteFileExt = "";
if (remoteFileName.lastIndexOf(".") == -1) {
remoteFileExt = "octetstream";
remoteFileName = remoteFileName + "." + remoteFileExt;
} else {
remoteFileExt = getFileExt(remoteFileName);
}
if (StringUtil.isNullOrEmpty(remoteFileExt) || remoteFileExt.equals("ing")) {
logger.warn("Invalid file extention name");
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("Invalid file extention name");
return false;
}
int remoteFileTotalSize = (int) remoteFileSize;
ByteBuf byteBuf = null;
int savedThunkSize = 0; // 分片接收保存的大小
int offset = 0; // 断点续传开始位置
Date nowTime=new Date();
SimpleDateFormat time=new SimpleDateFormat("yyyy/MM/dd/HH");
String datePath = time.format(nowTime);
datePath = "fs/" + bucket[0] + "/" + datePath; //add bucket
String dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
File dirFile = new File(dir);
boolean bFile = dirFile.exists();
if(!bFile) {
bFile = dirFile.mkdirs();
if (!bFile) {
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
response.setContent("服务器错误:无法创建文件");
return false;
}
}
String filePath = dir + "/" + requestId;
logger.info("the file path is " + filePath);
File tmpFile = new File(filePath);
logger.info("before write the file");
boolean isError = false;
while (true) {
byte[] thunkData;
try {
byteBuf = fileUpload.getChunk(128 * 1024);
int readableBytesSize = byteBuf.readableBytes();
thunkData = new byte[readableBytesSize];
byteBuf.readBytes(thunkData);
put(tmpFile, offset, thunkData);
savedThunkSize += readableBytesSize;
offset += readableBytesSize;
if (savedThunkSize >= remoteFileSize) {
byteBuf.release();
fileUpload.release();
response.setStatus(HttpResponseStatus.OK);
String relativePath = datePath + "/" + requestId;
response.setContent("{\"key\":\"" + relativePath + "\"}");
break;
}
} catch (Exception e) {
logger.error("save thunckData error!", e);
if (fileUpload != null)
fileUpload.release();
if (byteBuf != null)
byteBuf.release();
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
response.setContent("服务器错误:" + e.getMessage());
isError = true;
return false;
} finally {
thunkData = null;
if (isError) {
tmpFile.delete();
}
}
}
} else if(data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
Attribute attribute = (Attribute)data;
if(attribute.getName().equals("token")) {
String token = attribute.getValue();
try {
bucket[0] = validateToken(token);
} catch (InvalidateTokenExecption e) {
logger.error("无效的token!", e);
response.setStatus(HttpResponseStatus.BAD_REQUEST);
response.setContent("无效的token:" + e.getMessage());
return false;
}
}
}
} catch (Exception e) {
logger.error("writeHttpData error!", e);
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
response.setContent("服务器错误:" + e.getMessage());
return false;
}
return true;
}
public static void put(File file, long pos, byte[] data) throws Exception {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(file, "rwd");
raf.seek(pos);
raf.write(data);
} finally {
try {
if (raf != null)
raf.close();
} catch (Exception e) {
logger.warn("release error!", e);
}
}
}
}
@@ -0,0 +1,48 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputUserLogin;
import com.xiaoleilu.loServer.pojos.OutputLoginData;
import io.moquette.spi.impl.Utils;
import io.moquette.spi.impl.security.TokenAuthenticator;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.GitRepositoryState;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Route("/api/version")
@HttpMethod("GET")
public class VersionAction extends Action {
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setStatus(HttpResponseStatus.OK);
try {
response.setContent(new Gson().toJson(GitRepositoryState.getGitRepositoryState()));
} catch (IOException e) {
e.printStackTrace();
response.setContent("No version info!");
}
}
}
}
@@ -0,0 +1,73 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateUser;
import io.moquette.persistence.RPCCenter;
import io.moquette.spi.impl.Utils;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import org.apache.commons.codec.digest.DigestUtils;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.RateLimiter;
abstract public class AdminAction extends Action {
private static String SECRET_KEY = "123456";
private final RateLimiter mLimitCounter = new RateLimiter(10, 500);
public static void setSecretKey(String secretKey) {
SECRET_KEY = secretKey;
}
@Override
public ErrorCode preAction(Request request, Response response) {
if (!mLimitCounter.isGranted("admin")) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
String nonce = request.getHeader("nonce");
String timestamp = request.getHeader("timestamp");
String sign = request.getHeader("sign");
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign)) {
return ErrorCode.INVALID_PARAMETER;
}
Long ts;
try {
ts = Long.parseLong(timestamp);
} catch (NumberFormatException e) {
e.printStackTrace();
return ErrorCode.INVALID_PARAMETER;
}
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
// return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
}
String str = nonce + "|" + SECRET_KEY + "|" + timestamp;
String localSign = DigestUtils.sha1Hex(str);
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
}
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
response.setContent(new Gson().toJson(result));
response.send();
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputOutputUserBlockStatus;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("admin/user/status")
@HttpMethod("POST")
public class BlockUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputOutputUserBlockStatus inputUserBlock = getRequestBody(request.getNettyRequest(), InputOutputUserBlockStatus.class);
if (inputUserBlock != null
&& !StringUtil.isNullOrEmpty(inputUserBlock.getUserId())) {
ErrorCode errorCode = messagesStore.modifyUserStatus(inputUserBlock.getUserId(), inputUserBlock.getStatus());
response.setStatus(HttpResponseStatus.OK);
RestResult result;
result = RestResult.resultOf(errorCode);
response.setContent(new Gson().toJson(result));
RPCCenter.getInstance().sendRequest(null, null, RPCCenter.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), inputUserBlock.getUserId(), TargetEntry.Type.TARGET_TYPE_USER, null);
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, null);
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,45 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("/admin/user/onlinestatus")
@HttpMethod("POST")
public class CheckUserOnlineAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return false;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
if (inputUserId == null || !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
RPCCenter.getInstance().sendRequest(null, null, RPCCenter.CHECK_USER_ONLINE_REQUEST, inputUserId.getUserId().getBytes(), inputUserId.getUserId(), TargetEntry.Type.TARGET_TYPE_USER, null);
return;
} else {
sendResponse(response, ErrorCode.INVALID_PARAMETER, null);
}
}
}
}
@@ -0,0 +1,71 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateChatroom;
import com.xiaoleilu.loServer.pojos.OutputCreateChatroom;
import io.moquette.spi.impl.Utils;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.UUIDGenerator;
@Route("/admin/chatroom")
@HttpMethod("PUT")
public class CreateChatroomAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
Gson gson = new Gson();
InputCreateChatroom inputCreateChatroom = gson.fromJson(content, InputCreateChatroom.class);
if (inputCreateChatroom != null
&& !StringUtil.isNullOrEmpty(inputCreateChatroom.getTitle())) {
if (StringUtil.isNullOrEmpty(inputCreateChatroom.getChatroomId())) {
inputCreateChatroom.setChatroomId(messagesStore.getShortUUID());
}
if (inputCreateChatroom.getPortrait() == null || inputCreateChatroom.getPortrait().length() == 0) {
inputCreateChatroom.setPortrait("https://avatars.io/gravatar/" + inputCreateChatroom.getChatroomId());
}
WFCMessage.ChatroomInfo info = inputCreateChatroom.toChatroomInfo();
messagesStore.createChatroom(inputCreateChatroom.getChatroomId(), info);
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(new OutputCreateChatroom(inputCreateChatroom.getChatroomId()));
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,85 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateGroup;
import com.xiaoleilu.loServer.pojos.OutputCreateGroupResult;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.concurrent.Executor;
@Route("admin/group")
@HttpMethod("PUT")
public class CreateGroupAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputCreateGroup inputCreateGroup = getRequestBody(request.getNettyRequest(), InputCreateGroup.class);
if (inputCreateGroup.isValide()) {
RPCCenter.getInstance().sendRequest(inputCreateGroup.getOperator(), null, IMTopic.CreateGroupTopic, inputCreateGroup.toProtoGroupRequest().toByteArray(), inputCreateGroup.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
String groupId = new String(data);
sendResponse(response, null, new OutputCreateGroupResult(groupId));
} else {
sendResponse(response, errorCode, null);
}
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,80 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateRobot;
import com.xiaoleilu.loServer.pojos.OutputCreateRobot;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.UUIDGenerator;
@Route("admin/robot/create")
@HttpMethod("POST")
public class CreateRobotAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputCreateRobot inputCreateRobot = getRequestBody(request.getNettyRequest(), InputCreateRobot.class);
if (inputCreateRobot != null
&& !StringUtil.isNullOrEmpty(inputCreateRobot.getName())) {
if(StringUtil.isNullOrEmpty(inputCreateRobot.getPassword())) {
inputCreateRobot.setPassword(UUIDGenerator.getUUID());
}
if(StringUtil.isNullOrEmpty(inputCreateRobot.getUserId())) {
inputCreateRobot.setUserId(messagesStore.getShortUUID());
}
if (inputCreateRobot.getPortrait() == null || inputCreateRobot.getPortrait().length() == 0) {
inputCreateRobot.setPortrait("https://avatars.io/gravatar/" + inputCreateRobot.getUserId());
}
WFCMessage.User newUser = inputCreateRobot.toUser();
messagesStore.addUserInfo(newUser, inputCreateRobot.getPassword());
if (StringUtil.isNullOrEmpty(inputCreateRobot.getOwner())) {
inputCreateRobot.setOwner(inputCreateRobot.getUserId());
}
if (StringUtil.isNullOrEmpty(inputCreateRobot.getSecret())) {
inputCreateRobot.setSecret(UUIDGenerator.getUUID());
}
messagesStore.addRobot(inputCreateRobot.toRobot());
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(new OutputCreateRobot(inputCreateRobot.getUserId(), inputCreateRobot.getSecret()));
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,94 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateUser;
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.UUIDGenerator;
@Route("admin/user/create")
@HttpMethod("POST")
public class CreateUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputCreateUser inputCreateUser = getRequestBody(request.getNettyRequest(), InputCreateUser.class);
if (inputCreateUser != null
&& !StringUtil.isNullOrEmpty(inputCreateUser.getName())) {
if(StringUtil.isNullOrEmpty(inputCreateUser.getPassword())) {
inputCreateUser.setPassword(UUIDGenerator.getUUID());
}
if(StringUtil.isNullOrEmpty(inputCreateUser.getUserId())) {
inputCreateUser.setUserId(messagesStore.getShortUUID());
}
if (inputCreateUser.getPortrait() == null || inputCreateUser.getPortrait().length() == 0) {
inputCreateUser.setPortrait("https://avatars.io/gravatar/" + inputCreateUser.getUserId());
}
WFCMessage.User.Builder newUserBuilder = WFCMessage.User.newBuilder()
.setUid(StringUtil.isNullOrEmpty(inputCreateUser.getUserId()) ? "" : inputCreateUser.getUserId());
if (inputCreateUser.getName() != null)
newUserBuilder.setName(inputCreateUser.getName());
if (inputCreateUser.getDisplayName() != null)
newUserBuilder.setDisplayName(StringUtil.isNullOrEmpty(inputCreateUser.getDisplayName()) ? inputCreateUser.getName() : inputCreateUser.getDisplayName());
if (inputCreateUser.getPortrait() != null)
newUserBuilder.setPortrait(inputCreateUser.getPortrait());
if (inputCreateUser.getEmail() != null)
newUserBuilder.setEmail(inputCreateUser.getEmail());
if (inputCreateUser.getAddress() != null)
newUserBuilder.setAddress(inputCreateUser.getAddress());
if (inputCreateUser.getCompany() != null)
newUserBuilder.setCompany(inputCreateUser.getCompany());
if (inputCreateUser.getSocial() != null)
newUserBuilder.setSocial(inputCreateUser.getSocial());
if (inputCreateUser.getMobile() != null)
newUserBuilder.setMobile(inputCreateUser.getMobile());
newUserBuilder.setGender(inputCreateUser.getGender());
if (inputCreateUser.getExtra() != null)
newUserBuilder.setExtra(inputCreateUser.getExtra());
newUserBuilder.setUpdateDt(System.currentTimeMillis());
messagesStore.addUserInfo(newUserBuilder.build(), inputCreateUser.getPassword());
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(new OutputCreateUser(inputCreateUser.getUserId(), inputCreateUser.getName()));
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputDestoryChatroom;
import io.moquette.spi.impl.Utils;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("/admin/chatroom")
@HttpMethod("DELETE")
public class DestoryChatroomAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest)request.getNettyRequest();
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
Gson gson = new Gson();
InputDestoryChatroom inputDestoryChatroom = gson.fromJson(content, InputDestoryChatroom.class);
if (inputDestoryChatroom != null
&& !StringUtil.isNullOrEmpty(inputDestoryChatroom.getChatroomId())) {
messagesStore.destoryChatroom(inputDestoryChatroom.getChatroomId());
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok();
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.OutputGetChatroomInfo;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("/admin/chatroom")
@HttpMethod("GET")
public class GetChatroomInfoAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
String chatroomid = request.getParam("id");
if (!StringUtil.isNullOrEmpty(chatroomid)) {
WFCMessage.ChatroomInfo info = messagesStore.getChatroomInfo(chatroomid);
RestResult result;
if (info == null) {
result = RestResult.ok(new OutputGetChatroomInfo(chatroomid, messagesStore.getChatroomMemberCount(chatroomid), info));
} else {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_SUCCESS);
}
response.setStatus(HttpResponseStatus.OK);
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,81 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputGetToken;
import com.xiaoleilu.loServer.pojos.OutputGetIMTokenData;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.handler.codec.http.FullHttpRequest;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.Base64;
import java.util.concurrent.Executor;
@Route("/admin/user/token")
@HttpMethod("POST")
public class GetIMTokenAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return false;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetToken input = getRequestBody(request.getNettyRequest(), InputGetToken.class);
String userId = input.getUserId();
WFCMessage.GetTokenRequest getTokenRequest = WFCMessage.GetTokenRequest.newBuilder().setUserId(userId).setClientId(input.getClientId()).build();
RPCCenter.getInstance().sendRequest(userId, input.getClientId(), IMTopic.GetTokenTopic, getTokenRequest.toByteArray(), userId, TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ErrorCode errorCode1 = ErrorCode.fromCode(result[0]);
if (errorCode1 == ErrorCode.ERROR_CODE_SUCCESS) {
//ba errorcode qudiao
byte[] data = new byte[result.length -1];
for (int i = 0; i < data.length; i++) {
data[i] = result[i+1];
}
String token = Base64.getEncoder().encodeToString(data);
sendResponse(response, null, new OutputGetIMTokenData(userId, token));
} else {
sendResponse(response, errorCode1, null);
}
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
}
}
}
@@ -0,0 +1,68 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateUser;
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("admin/user/info")
@HttpMethod("POST")
public class GetUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
if (inputUserId != null
&& (!StringUtil.isNullOrEmpty(inputUserId.getUserId()) || !StringUtil.isNullOrEmpty(inputUserId.getName()))) {
WFCMessage.User user = null;
if(!StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
user = messagesStore.getUserInfo(inputUserId.getUserId());
} else if(!StringUtil.isNullOrEmpty(inputUserId.getName())) {
user = messagesStore.getUserInfoByName(inputUserId.getName());
} else if(!StringUtil.isNullOrEmpty(inputUserId.getMobile())) {
user = messagesStore.getUserInfoByMobile(inputUserId.getMobile());
}
response.setStatus(HttpResponseStatus.OK);
RestResult result;
if (user == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputCreateUser.fromPbUser(user));
}
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,37 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
@Route("admin/user/statuslist")
@HttpMethod("POST")
public class GetUserBlockListAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(messagesStore.getUserStatusList());
response.setContent(new Gson().toJson(result));
}
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
import com.xiaoleilu.loServer.pojos.OutputUserStatus;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("admin/user/checkstatus")
@HttpMethod("POST")
public class GetUserBlockStatusAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
if (inputUserId != null
&& !StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
int status = messagesStore.getUserStatus(inputUserId.getUserId());
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.ok(new OutputUserStatus(status));
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,80 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.RecallMessageData;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.concurrent.Executor;
@Route("admin/message/recall")
@HttpMethod("POST")
public class RecallMessageAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
RecallMessageData recallMessageData = getRequestBody(request.getNettyRequest(), RecallMessageData.class);
if (recallMessageData != null && !StringUtil.isNullOrEmpty(recallMessageData.getOperator())) {
WFCMessage.INT64Buf idBuf = WFCMessage.INT64Buf.newBuilder().setId(recallMessageData.getMessageUid()).build();
RPCCenter.getInstance().sendRequest(recallMessageData.getOperator(), null, IMTopic.RecallMessageTopic, idBuf.toByteArray(), recallMessageData.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
sendResponse(response, errorCode, null);
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,84 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.SendMessageData;
import com.xiaoleilu.loServer.pojos.SendMessageResult;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.concurrent.Executor;
@Route("admin/message/send")
@HttpMethod("POST")
public class SendMessageAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
if (SendMessageData.isValide(sendMessageData)) {
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
long messageId = byteBuf.readLong();
long timestamp = byteBuf.readLong();
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
} else {
sendResponse(response, errorCode, null);
}
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
import com.xiaoleilu.loServer.pojos.InputOutputUserBlockStatus;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("admin/sensitive/add")
@HttpMethod("POST")
public class SensitiveWordAddAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputOutputSensitiveWords input = getRequestBody(request.getNettyRequest(), InputOutputSensitiveWords.class);
if (input != null && input.getWords() != null && !input.getWords().isEmpty()) {
ErrorCode errorCode = messagesStore.addSensitiveWords(input.getWords()) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_SERVER_ERROR;
response.setStatus(HttpResponseStatus.OK);
sendResponse(response, errorCode, null);
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,48 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
@Route("admin/sensitive/del")
@HttpMethod("POST")
public class SensitiveWordDeleteAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputOutputSensitiveWords input = getRequestBody(request.getNettyRequest(), InputOutputSensitiveWords.class);
if (input != null && input.getWords() != null && !input.getWords().isEmpty()) {
ErrorCode errorCode = messagesStore.removeSensitiveWords(input.getWords()) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_SERVER_ERROR;
response.setStatus(HttpResponseStatus.OK);
sendResponse(response, errorCode, null);
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,42 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.admin;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputOutputSensitiveWords;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import java.util.List;
@Route("admin/sensitive/query")
@HttpMethod("POST")
public class SensitiveWordQueryAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
List<String> words = messagesStore.getAllSensitiveWords();
InputOutputSensitiveWords out = new InputOutputSensitiveWords();
out.setWords(words);
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, out);
}
}
}
@@ -0,0 +1,90 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.channel;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.RPCCenter;
import io.moquette.spi.impl.Utils;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import org.apache.commons.codec.digest.DigestUtils;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.RateLimiter;
abstract public class ChannelAction extends Action {
private final RateLimiter mLimitCounter = new RateLimiter(10, 200);
protected WFCMessage.ChannelInfo channelInfo;
@Override
public ErrorCode preAction(Request request, Response response) {
String nonce = request.getHeader("nonce");
String timestamp = request.getHeader("timestamp");
String sign = request.getHeader("sign");
String cid = request.getHeader("cid");
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(cid)) {
return ErrorCode.INVALID_PARAMETER;
}
if (!mLimitCounter.isGranted(cid)) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
Long ts;
try {
ts = Long.parseLong(timestamp);
} catch (NumberFormatException e) {
e.printStackTrace();
return ErrorCode.INVALID_PARAMETER;
}
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
}
channelInfo = messagesStore.getChannelInfo(cid);
if (channelInfo == null) {
return ErrorCode.INVALID_PARAMETER;
}
String str = nonce + "|" + channelInfo.getSecret() + "|" + timestamp;
String localSign = DigestUtils.sha1Hex(str);
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
}
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
response.setContent(new Gson().toJson(result));
response.send();
}
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
Gson gson = new Gson();
T t = gson.fromJson(content, cls);
return t;
}
return null;
}
}
@@ -0,0 +1,92 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.channel;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.admin.AdminAction;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.Conversation;
import com.xiaoleilu.loServer.pojos.SendMessageData;
import com.xiaoleilu.loServer.pojos.SendMessageResult;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.concurrent.Executor;
import static cn.wildfirechat.proto.ProtoConstants.ConversationType.ConversationType_Channel;
@Route("channel/message/send")
@HttpMethod("POST")
public class SendMessageAction extends ChannelAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
sendMessageData.setConv(new Conversation());
sendMessageData.getConv().setType(ConversationType_Channel);
sendMessageData.getConv().setTarget(channelInfo.getTargetId());
sendMessageData.setSender(channelInfo.getOwner());
if (SendMessageData.isValide(sendMessageData)) {
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
long messageId = byteBuf.readLong();
long timestamp = byteBuf.readLong();
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
} else {
sendResponse(response, errorCode, null);
}
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,63 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.robot;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.InputCreateUser;
import com.xiaoleilu.loServer.pojos.InputGetUserInfo;
import com.xiaoleilu.loServer.pojos.OutputCreateUser;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import win.liyufan.im.ErrorCode;
@Route("admin/robot/user_info")
@HttpMethod("POST")
public class GetUserAction extends RobotAction {
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserInfo inputUserId = getRequestBody(request.getNettyRequest(), InputGetUserInfo.class);
if (inputUserId != null
&& (!StringUtil.isNullOrEmpty(inputUserId.getUserId()) || !StringUtil.isNullOrEmpty(inputUserId.getName()))) {
WFCMessage.User user = null;
if(!StringUtil.isNullOrEmpty(inputUserId.getUserId())) {
user = messagesStore.getUserInfo(inputUserId.getUserId());
} else if(!StringUtil.isNullOrEmpty(inputUserId.getName())) {
user = messagesStore.getUserInfoByName(inputUserId.getName());
} else if(!StringUtil.isNullOrEmpty(inputUserId.getMobile())) {
user = messagesStore.getUserInfoByMobile(inputUserId.getMobile());
}
response.setStatus(HttpResponseStatus.OK);
RestResult result;
if (user == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputCreateUser.fromPbUser(user));
}
response.setContent(new Gson().toJson(result));
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}
@@ -0,0 +1,90 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.robot;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.RPCCenter;
import io.moquette.spi.impl.Utils;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import org.apache.commons.codec.digest.DigestUtils;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.RateLimiter;
abstract public class RobotAction extends Action {
private final RateLimiter mLimitCounter = new RateLimiter(10, 1000);
protected WFCMessage.Robot robot;
@Override
public ErrorCode preAction(Request request, Response response) {
String nonce = request.getHeader("nonce");
String timestamp = request.getHeader("timestamp");
String sign = request.getHeader("sign");
String rid = request.getHeader("rid");
if (StringUtil.isNullOrEmpty(nonce) || StringUtil.isNullOrEmpty(timestamp) || StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(rid)) {
return ErrorCode.INVALID_PARAMETER;
}
if (mLimitCounter.isGranted(rid)) {
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
Long ts;
try {
ts = Long.parseLong(timestamp);
} catch (NumberFormatException e) {
e.printStackTrace();
return ErrorCode.INVALID_PARAMETER;
}
if (System.currentTimeMillis() - ts > 2 * 60 * 60 * 1000) {
return ErrorCode.ERROR_CODE_SIGN_EXPIRED;
}
robot = messagesStore.getRobot(rid);
if (robot == null) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
String str = nonce + "|" + robot.getSecret() + "|" + timestamp;
String localSign = DigestUtils.sha1Hex(str);
return localSign.equals(sign) ? ErrorCode.ERROR_CODE_SUCCESS : ErrorCode.ERROR_CODE_AUTH_FAILURE;
}
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
response.setStatus(HttpResponseStatus.OK);
if (errorCode == null) {
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
}
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
response.setContent(new Gson().toJson(result));
response.send();
}
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes);
Gson gson = new Gson();
T t = gson.fromJson(content, cls);
return t;
}
return null;
}
}
@@ -0,0 +1,87 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package com.xiaoleilu.loServer.action.robot;
import cn.wildfirechat.proto.ProtoConstants;
import com.google.gson.Gson;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.admin.AdminAction;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import com.xiaoleilu.loServer.pojos.SendMessageData;
import com.xiaoleilu.loServer.pojos.SendMessageResult;
import io.moquette.persistence.RPCCenter;
import io.moquette.persistence.TargetEntry;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import win.liyufan.im.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.concurrent.Executor;
@Route("admin/robot/send")
@HttpMethod("POST")
public class SendMessageAction extends RobotAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public void action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
SendMessageData sendMessageData = getRequestBody(request.getNettyRequest(), SendMessageData.class);
sendMessageData.setSender(robot.getUid());
if (SendMessageData.isValide(sendMessageData)) {
RPCCenter.getInstance().sendRequest(sendMessageData.getSender(), null, IMTopic.SendMessageTopic, sendMessageData.toProtoMessage().toByteArray(), sendMessageData.getSender(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() {
@Override
public void onSuccess(byte[] result) {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
long messageId = byteBuf.readLong();
long timestamp = byteBuf.readLong();
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
} else {
sendResponse(response, errorCode, null);
}
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
public Executor getResponseExecutor() {
return command -> {
ctx.executor().execute(command);
};
}
});
} else {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER);
response.setContent(new Gson().toJson(result));
}
}
}
}