Former-commit-id: 8117d223a3aa66fa9c36c2af712cac6157708310
master
wangbing 5 years ago
parent 984938b7b7
commit afec7ed793

@ -220,22 +220,22 @@ public class SpringBootCallable implements Callable {
ctx.put("modules", project.getModules()); ctx.put("modules", project.getModules());
File control = new File(root.getAbsolutePath(), "control"); File control = new File(root.getAbsolutePath(), "control");
control.mkdirs();
File ajax = new File(root.getAbsolutePath(), "ajax");
ajax.mkdirs();
File system = new File(ajax.getAbsolutePath(), "system");
system.mkdirs();
File screen = new File(root.getAbsolutePath(), "screen"); File screen = new File(root.getAbsolutePath(), "screen");
screen.mkdirs(); screen.mkdirs();
control.mkdirs();
freeMarkerManager.outputTemp(new File(control.getAbsolutePath(), "Header.java"), option + "/java/action/control/Header.java", ctx); freeMarkerManager.outputTemp(new File(control.getAbsolutePath(), "Header.java"), option + "/java/action/control/Header.java", ctx);
{
File ajax = new File(root.getAbsolutePath(), "ajax");
ajax.mkdirs();
File system = new File(ajax.getAbsolutePath(), "system");
system.mkdirs();
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "AuthAjax.java"), option + "/java/action/ajax/system/AuthAjax.java", ctx); freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "AuthAjax.java"), option + "/java/action/ajax/system/AuthAjax.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictAjax.java"), option + "/java/action/ajax/system/DictAjax.java", ctx); freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictAjax.java"), option + "/java/action/ajax/system/DictAjax.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictItemAjax.java"), option + "/java/action/ajax/system/DictItemAjax.java", ctx); freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictItemAjax.java"), option + "/java/action/ajax/system/DictItemAjax.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "FileAjax.java"), option + "/java/action/ajax/system/FileAjax.java", ctx); freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "FileAjax.java"), option + "/java/action/ajax/system/FileAjax.java", ctx);
for (Module module : project.getModules()) { for (Module module : project.getModules()) {
File m = new File(ajax.getAbsolutePath(), module.getModuleName()); File m = new File(ajax.getAbsolutePath(), module.getModuleName());
m.mkdirs(); m.mkdirs();
@ -251,10 +251,34 @@ public class SpringBootCallable implements Callable {
freeMarkerManager.outputTemp(new File(m.getAbsolutePath(), table.getCName() + "Ajax.java"), option + "/java/action/ajax/Ajax.java", ctxss); freeMarkerManager.outputTemp(new File(m.getAbsolutePath(), table.getCName() + "Ajax.java"), option + "/java/action/ajax/Ajax.java", ctxss);
} }
} }
}
{
File api = new File(root.getAbsolutePath(), "api");
api.mkdirs();
File system = new File(api.getAbsolutePath(), "system");
system.mkdirs();
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "AuthApi.java"), option + "/java/action/api/system/AuthApi.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictApi.java"), option + "/java/action/api/system/DictApi.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "DictItemApi.java"), option + "/java/action/api/system/DictItemApi.java", ctx);
freeMarkerManager.outputTemp(new File(system.getAbsolutePath(), "FileApi.java"), option + "/java/action/api/system/FileApi.java", ctx);
for (Module module : project.getModules()) {
File m = new File(api.getAbsolutePath(), module.getModuleName());
m.mkdirs();
for (Table table : module.getTables()) {
HashMap<String, Object> ctxss = new HashMap<String, Object>();
ctxss.put("basePackage", project.getProjectBasePackage());
ctxss.put("module", module.getModuleName());
ctxss.put("author", project.getProjectAuthor());
ctxss.put("date", new Date());
ctxss.put("table", table);
freeMarkerManager.outputTemp(new File(m.getAbsolutePath(), table.getCName() + "Api.java"), option + "/java/action/api/Api.java", ctxss);
}
}
}
freeMarkerManager.outputTemp(new File(screen.getAbsolutePath(), "Index.java"), option + "/java/action/screen/Index.java", ctx); freeMarkerManager.outputTemp(new File(screen.getAbsolutePath(), "Index.java"), option + "/java/action/screen/Index.java", ctx);
freeMarkerManager.outputTemp(new File(root.getAbsolutePath(), "GlobalController.java"), option + "/java/action/GlobalController.java", ctx); freeMarkerManager.outputTemp(new File(root.getAbsolutePath(), "GlobalController.java"), option + "/java/action/GlobalController.java", ctx);
// freeMarkerManager.outputTemp(new File(root.getAbsolutePath(), "AjaxController.java"), option + "/java/action/AjaxController.java", ctx); // freeMarkerManager.outputTemp(new File(root.getAbsolutePath(), "AjaxController.java"), option + "/java/action/AjaxController.java", ctx);

@ -15,7 +15,7 @@ import java.net.URLEncoder;
public class FileAjax { public class FileAjax {
public Object upload(MultipartFile file) { public FileUploadResponse upload(MultipartFile file) {
FileUploadResponse fileUploadResponse = new FileUploadResponse(); FileUploadResponse fileUploadResponse = new FileUploadResponse();
String fileName = file.getOriginalFilename(); String fileName = file.getOriginalFilename();

@ -0,0 +1,58 @@
package ${basePackage}.action.api.${module};
import ${basePackage}.frame.auth.LocalData;
import ${basePackage}.module.${module}.mgr.${table.getCName()}Manager;
import ${basePackage}.module.${module}.req.*;
import ${basePackage}.module.${module}.rsp.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.List;
public class ${table.getCName()}Api{
@Autowired
private ${table.getCName()}Manager ${table.getFName()}Manager;
<#if table.getCreate()>
public ${table.getCName()}CreateResponse create(${table.getCName()}CreateRequest request) {
return ${table.getFName()}Manager.create(request, LocalData.getToken());
}
</#if>
<#if table.getDelete()>
public ${table.getCName()}DeleteResponse delete(${table.getCName()}DeleteRequest request) {
return ${table.getFName()}Manager.delete(request, LocalData.getToken());
}
</#if>
<#if table.getUpdate()>
public ${table.getCName()}UpdateResponse update(${table.getCName()}UpdateRequest request) {
return ${table.getFName()}Manager.update(request, LocalData.getToken());
}
</#if>
<#if table.getFind()>
public ${table.getCName()}FindResponse find(${table.getCName()}FindRequest request) {
return ${table.getFName()}Manager.find(request, LocalData.getToken());
}
</#if>
<#if table.getGet()>
public ${table.getCName()}GetResponse get(${table.getCName()}GetRequest request) {
return ${table.getFName()}Manager.get(request, LocalData.getToken());
}
</#if>
<#if table.getSearch()>
public ${table.getCName()}SearchResponse search(${table.getCName()}SearchRequest request) {
return ${table.getFName()}Manager.search(request, LocalData.getToken());
}
</#if>
<#if table.getGetAll()>
public ${table.getCName()}GetAllResponse getAll(${table.getCName()}GetAllRequest request) {
return ${table.getFName()}Manager.getAll(request, LocalData.getToken());
}
</#if>
}

@ -0,0 +1,23 @@
package ${basePackage}.action.api.system;
import ${basePackage}.module.system.req.AuthLoginRequest;
import ${basePackage}.module.system.rsp.AuthLoginResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AuthApi {
public AuthLoginResponse login(AuthLoginRequest request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
AuthLoginResponse response = new AuthLoginResponse();
// todo 设置cookie
Cookie token = new Cookie("token", "");
token.setDomain(httpServletRequest.getServerName());
token.setPath("/");
httpServletResponse.addCookie(token);
return response;
}
}

@ -0,0 +1,40 @@
package ${basePackage}.action.api.system;
import ${basePackage}.frame.auth.LocalData;
import ${basePackage}.module.system.mgr.DictManager;
import ${basePackage}.module.system.req.*;
import ${basePackage}.module.system.rsp.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.List;
public class DictApi {
@Autowired
private DictManager dictManager;
public DictCreateResponse create(DictCreateRequest request) {
return dictManager.create(request, LocalData.getToken());
}
public DictDeleteResponse delete(DictDeleteRequest request) {
return dictManager.delete(request, LocalData.getToken());
}
public DictUpdateResponse update(DictUpdateRequest request) {
return dictManager.update(request, LocalData.getToken());
}
public DictFindResponse find(DictFindRequest request) {
return dictManager.find(request, LocalData.getToken());
}
public DictGetResponse get(DictGetRequest request) {
return dictManager.get(request, LocalData.getToken());
}
public DictLoadResponse load(DictLoadRequest request) {
return dictManager.load(request, LocalData.getToken());
}
}

@ -0,0 +1,36 @@
package ${basePackage}.action.api.system;
import ${basePackage}.frame.auth.LocalData;
import ${basePackage}.module.system.mgr.DictItemManager;
import ${basePackage}.module.system.req.*;
import ${basePackage}.module.system.rsp.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.List;
public class DictItemApi {
@Autowired
private DictItemManager dictItemManager;
public DictItemCreateResponse create(DictItemCreateRequest request) {
return dictItemManager.create(request, LocalData.getToken());
}
public DictItemDeleteResponse delete(DictItemDeleteRequest request) {
return dictItemManager.delete(request, LocalData.getToken());
}
public DictItemUpdateResponse update(DictItemUpdateRequest request) {
return dictItemManager.update(request, LocalData.getToken());
}
public DictItemFindResponse find(DictItemFindRequest request) {
return dictItemManager.find(request, LocalData.getToken());
}
public DictItemGetResponse get(DictItemGetRequest request) {
return dictItemManager.get(request, LocalData.getToken());
}
}

@ -0,0 +1,54 @@
package ${basePackage}.action.api.system;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import ${basePackage}.frame.base.BaseResponse;
import ${basePackage}.frame.base.ErrorType;
import ${basePackage}.frame.base.FileUploadResponse;
import javax.swing.tree.TreeNode;
import java.io.IOException;
import java.net.URLEncoder;
public class FileApi {
public FileUploadResponse upload(MultipartFile file) {
FileUploadResponse fileUploadResponse = new FileUploadResponse();
String fileName = file.getOriginalFilename();
//========
// todo 处理文件
//========
fileUploadResponse.setId(1L);
fileUploadResponse.setUrl("example.com\\img\\1.jpg");
fileUploadResponse.setDownloadUrl("example.com\\img\\1.jpg");
if (file != null) {
fileUploadResponse.addError(ErrorType.BUSINESS_ERROR, "文件上传成功,但未处理文件[" + fileName + "]!");
} else {
fileUploadResponse.addError(ErrorType.BUSINESS_ERROR, "文件上传失败!");
}
return fileUploadResponse;
}
public Object download(TreeNode jsonParam) {
try {
//========
// todo 下载示例
//========
byte[] bytes = "test".getBytes();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", URLEncoder.encode("example.test", "utf-8"));
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
} catch (IOException e) {
BaseResponse baseResponse = new BaseResponse();
baseResponse.addError(ErrorType.BUSINESS_ERROR, "");
return baseResponse;
}
}
}

@ -38,25 +38,105 @@
<#list table.fields as f> <#list table.fields as f>
<#if f.isQuery> <#if f.isQuery>
<#if dataBase == 'ORACLE'> <#if dataBase == 'ORACLE'>
<#if f.fieldType.javaType() == "Boolean">
<if test="request.${f.getFName()} != null">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Byte">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Short">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Integer">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Long">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Float">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Double">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Character">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != ''">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "String"> <#if f.fieldType.javaType() == "String">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != ''"> <if test="request.${f.getFName()} != null and request.${f.getFName()} != ''">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}} AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}}
</if> </if>
</#if> </#if>
<#if f.fieldType.javaType() == "Date">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != ''">
AND to_char("${f.fieldName}",'yyyy-MM-dd HH24:mi:ss') = to_char(${r"#{"}request.${f.getFName()}},'yyyy-MM-dd HH24:mi:ss')
</if>
</#if>
<#elseif dataBase='MYSQL'>
<#if f.fieldType.javaType() == "Boolean"> <#if f.fieldType.javaType() == "Boolean">
<if test="request.${f.getFName()} != null"> <if test="request.${f.getFName()} != null">
AND "${f.fieldName}" = ${r"#{"}request.${f.getFName()}} AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Byte">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Short">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Integer">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Long">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Float">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Double">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != 0">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if>
</#if>
<#if f.fieldType.javaType() == "Character">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != ''">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if> </if>
</#if> </#if>
<#elseif dataBase='MYSQL'>
<#if f.fieldType.javaType() == "String"> <#if f.fieldType.javaType() == "String">
<if test="request.${f.getFName()} != null and request.${f.getFName()} != ''"> <if test="request.${f.getFName()} != null and request.${f.getFName()} != ''">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}} AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}}
</if> </if>
</#if> </#if>
<#if f.fieldType.javaType() == "Boolean"> <#if f.fieldType.javaType() == "Date">
<if test="request.${f.getFName()} != null"> <if test="request.${f.getFName()} != null">
AND `${f.fieldName}` = ${r"#{"}request.${f.getFName()}} AND DATE_FORMAT(`${f.fieldName}`,"%Y-%m-%d %T") = DATE_FORMAT(${r"#{"}request.${f.getFName()}},"%Y-%m-%d %T")
</if> </if>
</#if> </#if>
</#if> </#if>

@ -193,6 +193,8 @@ public class DictManagerImpl implements DictManager {
DictItemFindRequest dictItemFindRequest = new DictItemFindRequest(); DictItemFindRequest dictItemFindRequest = new DictItemFindRequest();
dictItemFindRequest.setDictName(request.getDictName()); dictItemFindRequest.setDictName(request.getDictName());
dictItemFindRequest.setValid(true);
dictItemFindRequest.setPageSize(0);
DictItemFindResponse dictItemFindResponse = dictItemManager.find(dictItemFindRequest, token); DictItemFindResponse dictItemFindResponse = dictItemManager.find(dictItemFindRequest, token);
if (dictItemFindResponse.hasError()) { if (dictItemFindResponse.hasError()) {
response.addErrors(dictItemFindResponse.getErrors()); response.addErrors(dictItemFindResponse.getErrors());

@ -129,8 +129,6 @@
nav.e(rsp.errors[0].message) nav.e(rsp.errors[0].message)
} else { } else {
nav.barFinish(); nav.barFinish();
var blob = utils.base64toBlob(rsp.base64);
utils.blobtoDown(rsp.name,blob);
} }
}); });
reader.readAsText(response.data, "utf-8"); reader.readAsText(response.data, "utf-8");

@ -10,14 +10,6 @@
<el-radio :label="false">否</el-radio> <el-radio :label="false">否</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<#elseif item.fieldType.javaType() =="String" && item.fieldType != "Dict">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}"></el-input>
</el-form-item>
<#elseif item.fieldType.javaType() =="String" && item.fieldType == "Dict">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input-dict v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" dict-name="${item.getFName()}" ></el-input-dict>
</el-form-item>
<#elseif item.fieldType.javaType() =="Byte"> <#elseif item.fieldType.javaType() =="Byte">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}"> <el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input-number v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" :min="-128" :max="127" :step="1" step-strictly></el-input-number> <el-input-number v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" :min="-128" :max="127" :step="1" step-strictly></el-input-number>
@ -46,13 +38,21 @@
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}"> <el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" minlength="0" maxlength="1"></el-input> <el-input v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" minlength="0" maxlength="1"></el-input>
</el-form-item> </el-form-item>
<#elseif item.fieldType.javaType() =="String" && item.fieldType != "Dict">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}"></el-input>
</el-form-item>
<#elseif item.fieldType.javaType() =="String" && item.fieldType == "Dict">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-input-dict v-model="vm.${item.getFName()}" clearable size="small" placeholder="请输入${item.fieldComment?default("")}" dict-name="${item.getFName()}" ></el-input-dict>
</el-form-item>
<#elseif item.fieldType.javaType() =="Date"> <#elseif item.fieldType.javaType() =="Date">
<el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}"> <el-form-item label="${item.fieldComment?default("")}" prop="${item.getFName()}">
<el-date-picker <el-date-picker
size="small" size="small"
v-model="vm.${item.getFName()}" v-model="vm.${item.getFName()}"
format="yyyy-MM-dd HH:dd:ss" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:dd:ss" value-format="yyyy-MM-dd HH:mm:ss"
type="datetime" type="datetime"
placeholder="选择日期时间"> placeholder="选择日期时间">
</el-date-picker> </el-date-picker>
@ -130,8 +130,8 @@
<el-date-picker <el-date-picker
size="small" size="small"
v-model="form.${item.getFName()}" v-model="form.${item.getFName()}"
format="yyyy-MM-dd HH:dd:ss" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:dd:ss" value-format="yyyy-MM-dd HH:mm:ss"
type="datetime" type="datetime"
placeholder="选择日期时间"> placeholder="选择日期时间">
</el-date-picker> </el-date-picker>

@ -36,12 +36,13 @@
<el-input v-model="form.dictComment" clearable size="small" placeholder="请输入字典描述"></el-input> <el-input v-model="form.dictComment" clearable size="small" placeholder="请输入字典描述"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="字典版本" prop="version" required> <el-form-item label="字典版本" prop="version" required>
<el-date-picker size="small" <el-date-picker
size="small"
v-model="form.version" v-model="form.version"
type="datetime" format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="选择日期时间" type="datetime"
align="right"> placeholder="选择日期时间">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="是否有效" prop="valid"> <el-form-item label="是否有效" prop="valid">

Loading…
Cancel
Save

Powered by TurnKey Linux.