Created repo

This commit is contained in:
F43nd1r 2017-05-17 17:42:01 +02:00
parent 5fad221028
commit 56c99abeb6
43 changed files with 1892 additions and 0 deletions

25
.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

13
annotation/build.gradle Normal file
View file

@ -0,0 +1,13 @@
group 'com.faendir'
version 'unspecified'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
}

View file

@ -0,0 +1,17 @@
package com.faendir.acra.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Lukas
* @since 14.05.2017
*/
@Inherited
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface AutoDiscoverView {
}

View file

@ -0,0 +1,16 @@
group 'com.faendir'
version 'unspecified'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'com.squareup:javapoet:1.9.0'
compile 'com.google.auto.service:auto-service:1.0-rc2'
compile project(':annotation')
}

View file

@ -0,0 +1,66 @@
package com.faendir.acra.processor;
import com.faendir.acra.annotation.AutoDiscoverView;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author Lukas
* @since 14.05.2017
*/
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> views = roundEnv.getElementsAnnotatedWith(AutoDiscoverView.class);
if (!views.isEmpty()) {
try {
JavaFile.builder("com.faendir.acra.gen", TypeSpec.classBuilder("ViewDefinition")
.addModifiers(Modifier.PUBLIC)
.addMethod(MethodSpec.methodBuilder("getViewClasses")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(ParameterizedTypeName.get(ClassName.get(List.class), ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(Object.class))))
.addCode("return $T.asList($L);", TypeName.get(Arrays.class), views.stream()
.filter(element -> !element.getModifiers().contains(Modifier.ABSTRACT))
.map(element -> ((TypeElement) element).getQualifiedName().toString() + ".class")
.reduce((s1, s2) -> s1 + ", " + s2).orElse(""))
.build())
.build())
.skipJavaLangImports(true)
.indent(" ")
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to generate classes");
}
}
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(AutoDiscoverView.class.getName());
}
}

46
backend/build.gradle Normal file
View file

@ -0,0 +1,46 @@
buildscript {
ext {
springBootVersion = '1.5.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id "net.ltgt.apt" version "0.10"
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
group 'com.faendir'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-mongodb')
compile('org.springframework.boot:spring-boot-starter-security')
compile('com.vaadin:vaadin-spring-boot-starter')
compile('org.codeartisans:org.json:20161124')
compile('org.apache.commons:commons-collections4:4.1')
compile('org.ocpsoft.prettytime:prettytime:3.2.7.Final')
compileOnly project(':annotation')
apt project(':annotationprocessor')
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
dependencyManagement {
imports {
mavenBom "com.vaadin:vaadin-bom:8.0.6"
}
}
war {
archiveName = "acra.war"
version = version
}

172
backend/gradlew vendored Normal file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
backend/gradlew.bat vendored Normal file
View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1,27 @@
package com.faendir.acra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import java.security.SecureRandom;
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class BackendApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(BackendApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
@Bean
public SecureRandom secureRandom(){
return new SecureRandom();
}
}

View file

@ -0,0 +1,34 @@
package com.faendir.acra.data;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* @author Lukas
* @since 22.03.2017
*/
@Document
public class App {
private String id;
private String name;
private String password;
public App() {
}
public App(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public String getId() {
return id;
}
}

View file

@ -0,0 +1,38 @@
package com.faendir.acra.data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import java.security.SecureRandom;
import java.util.List;
/**
* @author Lukas
* @since 22.03.2017
*/
@Component
public class AppManager {
private final SecureRandom secureRandom;
private final AppRepository appRepository;
@Autowired
public AppManager(SecureRandom secureRandom, AppRepository appRepository) {
this.secureRandom = secureRandom;
this.appRepository = appRepository;
}
public App createNewApp(String name){
byte[] bytes = new byte[12];
secureRandom.nextBytes(bytes);
return appRepository.save(new App(name, Base64Utils.encodeToString(bytes)));
}
public List<App> getApps(){
return appRepository.findAll();
}
public App getApp(String id){
return appRepository.findOne(id);
}
}

View file

@ -0,0 +1,10 @@
package com.faendir.acra.data;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
* @author Lukas
* @since 22.03.2017
*/
public interface AppRepository extends MongoRepository<App, String> {
}

View file

@ -0,0 +1,41 @@
package com.faendir.acra.data;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author Lukas
* @since 13.05.2017
*/
public class Bug {
private List<Report> reports;
private String trace;
private int versionCode;
public Bug(Report report){
reports = new ArrayList<>();
this.trace = report.getContent().getString("STACK_TRACE");
this.versionCode = report.getContent().getInt("APP_VERSION_CODE");
}
public boolean is(Report report){
return report.getContent().getString("STACK_TRACE").equals(trace) && report.getContent().getInt("APP_VERSION_CODE") == versionCode;
}
public List<Report> getReports() {
return reports;
}
public Date getLastDate(){
return reports.stream().map(Report::getDate).reduce((d1, d2) -> d1.after(d2) ? d1 : d2).orElse(new Date());
}
public String getTrace() {
return trace;
}
public int getVersionCode() {
return versionCode;
}
}

View file

@ -0,0 +1,26 @@
package com.faendir.acra.data;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
/**
* @author Lukas
* @since 13.05.2017
*/
@Configuration
public class MongoConfiguration {
@Bean
public MappingMongoConverter mongoConverter(MongoDbFactory mongoFactory, MongoMappingContext mongoMappingContext) throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement("%&&%");
return mongoConverter;
}
}

View file

@ -0,0 +1,40 @@
package com.faendir.acra.data;
import org.json.JSONObject;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
/**
* @author Lukas
* @since 22.03.2017
*/
@Document
public class Report {
private String id;
@Indexed
private String app;
private JSONObject content;
public Report() {
}
public Report(JSONObject content, String app) {
id = content == null ? null : content.getString("REPORT_ID");
this.content = content;
this.app = app;
}
public JSONObject getContent() {
return content;
}
public Date getDate() {
return ReportUtils.getDateFromString(content.getString("USER_CRASH_DATE"));
}
public String getId() {
return id;
}
}

View file

@ -0,0 +1,35 @@
package com.faendir.acra.data;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Lukas
* @since 22.03.2017
*/
@Component
public class ReportManager {
private final ReportRepository reportRepository;
@Autowired
public ReportManager(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public Report newReport(JSONObject content) {
return reportRepository.save(new Report(content, SecurityContextHolder.getContext().getAuthentication().getName()));
}
public List<Report> getReports(String app) {
return reportRepository.findAll(Example.of(new Report(null, app)));
}
public Report getReport(String id) {
return reportRepository.findOne(id);
}
}

View file

@ -0,0 +1,10 @@
package com.faendir.acra.data;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
* @author Lukas
* @since 22.03.2017
*/
public interface ReportRepository extends MongoRepository<Report, String> {
}

View file

@ -0,0 +1,37 @@
package com.faendir.acra.data;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* @author Lukas
* @since 13.05.2017
*/
public final class ReportUtils {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
public static List<Bug> getBugs(List<Report> reports) {
List<Bug> bugs = new ArrayList<>();
for (Report report : reports) {
bugs.stream().filter(bug -> bug.is(report)).findAny().orElseGet(() -> {
Bug bug = new Bug(report);
bugs.add(bug);
return bug;
}).getReports().add(report);
}
return bugs;
}
static Date getDateFromString(String s) {
try {
return dateFormat.parse(s);
} catch (ParseException e) {
return new Date();
}
}
}

View file

@ -0,0 +1,24 @@
package com.faendir.acra.security;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
* @author Lukas
* @since 13.05.2017
*/
public class SecurityInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}
@Override
protected String[] getServletMappings() {
return new String[0];
}
}

View file

@ -0,0 +1,21 @@
package com.faendir.acra.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
public final class SecurityUtils {
private SecurityUtils() {
}
public static boolean isLoggedIn() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && authentication.isAuthenticated();
}
public static boolean hasRole(String role) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && authentication.getAuthorities().contains(new SimpleGrantedAuthority(role));
}
}

View file

@ -0,0 +1,49 @@
package com.faendir.acra.security;
import com.vaadin.server.VaadinService;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import com.vaadin.server.VaadinSession;
/**
* A custom {@link SecurityContextHolderStrategy} that stores the {@link SecurityContext} in the Vaadin Session.
*/
public class VaadinSessionSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
@Override
public void clearContext() {
getSession().setAttribute(SecurityContext.class, null);
}
@Override
public SecurityContext getContext() {
VaadinSession session = getSession();
SecurityContext context = session.getAttribute(SecurityContext.class);
if (context == null) {
context = createEmptyContext();
session.setAttribute(SecurityContext.class, context);
}
return context;
}
@Override
public void setContext(SecurityContext context) {
getSession().setAttribute(SecurityContext.class, context);
}
@Override
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
private static VaadinSession getSession() {
VaadinSession session = VaadinSession.getCurrent();
if (session == null) {
session = new VaadinSession(VaadinService.getCurrent());
VaadinSession.setCurrent(session);
}
return session;
}
}

View file

@ -0,0 +1,107 @@
package com.faendir.acra.security;
import com.faendir.acra.data.App;
import com.faendir.acra.data.AppManager;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* @author Lukas
* @since 22.03.2017
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
static {
SecurityContextHolder.setStrategyName(VaadinSessionSecurityContextHolderStrategy.class.getName());
}
private final String user;
private final String password;
private final AppManager appManager;
private final AuthenticationProvider authenticationProvider = new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication instanceof UsernamePasswordAuthenticationToken) {
if (user.equals(authentication.getName())) {
if (password.equals(authentication.getCredentials())) {
return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AuthorityUtils.createAuthorityList("ROLE_ADMIN"));
} else {
throw new BadCredentialsException("Password mismatch for user " + authentication.getName());
}
}
App app = appManager.getApp(authentication.getName());
if (app != null) {
if (app.getPassword().equals(authentication.getCredentials())) {
Authentication auth = new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AuthorityUtils.createAuthorityList("ROLE_REPORTER"));
VaadinSession session = VaadinSession.getCurrent();
if (session == null) session = new VaadinSession(VaadinService.getCurrent());
VaadinSession.setCurrent(session);
SecurityContext securityContext = new SecurityContextImpl();
securityContext.setAuthentication(auth);
session.setAttribute(SecurityContext.class, securityContext);
return auth;
} else {
throw new BadCredentialsException("Password mismatch for user " + authentication.getName());
}
} else {
throw new UsernameNotFoundException("Username " + authentication.getName() + " not found");
}
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
};
@Autowired
public WebSecurityConfig(@Value("${security.user.name}") String user, @Value("${security.user.password}") String password, AppManager appManager) {
this.user = user;
this.password = password;
this.appManager = appManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers().disable()
.httpBasic();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
}

View file

@ -0,0 +1,33 @@
package com.faendir.acra.service;
import com.faendir.acra.data.ReportManager;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* @author Lukas
* @since 22.03.2017
*/
@RestController
public class ReportService {
private final ReportManager reportManager;
@Autowired
public ReportService(ReportManager reportManager) {
this.reportManager = reportManager;
}
@PreAuthorize("hasRole('REPORTER')")
@RequestMapping(value = "/report", consumes = MediaType.APPLICATION_JSON_VALUE)
public void report(@RequestBody String content) throws IOException {
JSONObject jsonObject = new JSONObject(content);
reportManager.newReport(jsonObject);
}
}

View file

@ -0,0 +1,84 @@
package com.faendir.acra.ui;
import com.faendir.acra.security.SecurityUtils;
import com.faendir.acra.util.Style;
import com.vaadin.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.LoginForm;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* @author Lukas
* @since 22.03.2017
*/
@SpringUI
@Theme("acratheme")
public class BackendUI extends UI {
private final AuthenticationManager authenticationManager;
private final ApplicationContext applicationContext;
private final VerticalLayout content;
@Autowired
public BackendUI(AuthenticationManager authenticationManager, ApplicationContext applicationContext) {
this.authenticationManager = authenticationManager;
this.applicationContext = applicationContext;
content = new VerticalLayout();
content.setSizeFull();
Style.NO_PADDING.apply(content);
}
@Override
protected void init(VaadinRequest request) {
if (SecurityUtils.isLoggedIn()) {
showMain();
} else {
LoginForm loginForm = new LoginForm();
loginForm.addLoginListener(event -> login(event.getLoginParameter("username"), event.getLoginParameter("password")));
setContent(loginForm);
}
}
private boolean login(String username, String password) {
try {
Authentication token = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
VaadinService.reinitializeSession(VaadinService.getCurrentRequest());
SecurityContextHolder.getContext().setAuthentication(token);
showMain();
return true;
} catch (AuthenticationException ex) {
return false;
}
}
private void showMain() {
NavigationManager navigationManager = applicationContext.getBean(NavigationManager.class);
HorizontalLayout header = new HorizontalLayout(new Button("Up", e -> navigationManager.navigateBack()));
Style.apply(header, Style.MARGIN_TOP, Style.MARGIN_LEFT, Style.MARGIN_RIGHT);
VerticalLayout root = new VerticalLayout(header, content);
root.setExpandRatio(content, 1);
root.setSizeFull();
Style.NO_PADDING.apply(root);
setContent(root);
}
@UIScope
@Bean
public VerticalLayout mainView() {
return content;
}
}

View file

@ -0,0 +1,73 @@
package com.faendir.acra.ui;
import com.faendir.acra.gen.ViewDefinition;
import com.faendir.acra.ui.view.NamedView;
import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewProvider;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author Lukas
* @since 14.05.2017
*/
@UIScope
@Component
public class NavigationManager {
private final Navigator navigator;
private final ApplicationContext applicationContext;
private final List<Class<?>> views;
private final List<String> backStack;
@Autowired
public NavigationManager(UI ui, VerticalLayout mainView, ApplicationContext applicationContext) {
navigator = new Navigator(ui, mainView);
backStack = new ArrayList<>();
this.applicationContext = applicationContext;
navigator.addProvider(new ViewProvider() {
@Override
public String getViewName(String viewAndParameters) {
String name = viewAndParameters.split("/", 2)[0];
if (views.stream().map(applicationContext::getBean).map(NamedView.class::cast).map(NamedView::getName).anyMatch(name::equals))
return name;
return null;
}
@Override
public View getView(String viewName) {
return views.stream().map(applicationContext::getBean).map(NamedView.class::cast).filter(view -> view.getName().equals(viewName)).findAny().orElse(null);
}
});
views = ViewDefinition.getViewClasses();
String target = Optional.ofNullable(ui.getPage().getLocation().getFragment()).orElse("").replace("!", "");
backStack.add(target);
navigator.navigateTo(target);
}
public void navigateTo(Class<? extends NamedView> namedView, String contentId) {
String target = applicationContext.getBean(namedView).getName() + (contentId == null ? "" : "/" + contentId);
backStack.add(0, target);
navigator.navigateTo(target);
}
public void navigateBack() {
if (backStack.size() < 2) {
backStack.set(0, "");
navigator.navigateTo("");
} else {
backStack.remove(0);
navigator.navigateTo(backStack.get(0));
}
}
}

View file

@ -0,0 +1,73 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.App;
import com.faendir.acra.data.AppManager;
import com.faendir.acra.data.Report;
import com.faendir.acra.data.ReportManager;
import com.faendir.acra.util.Style;
import com.vaadin.data.ValueProvider;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Label;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Lukas
* @since 13.05.2017
*/
@UIScope
@Component
public class AppView extends NamedView {
private final AppManager appManager;
private final ReportManager reportManager;
@Autowired
public AppView(AppManager appManager, ReportManager reportManager) {
this.appManager = appManager;
this.reportManager = reportManager;
}
private <T> Grid.Column addColumn(Grid<T> grid, ValueProvider<T, String> valueProvider, String caption) {
Grid.Column column = grid.addColumn(valueProvider);
column.setId(caption);
column.setCaption(caption);
return column;
}
@Override
public String getName() {
return "app";
}
@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
App app = appManager.getApp(event.getParameters());
List<Report> reportList = reportManager.getReports(app.getId());
VerticalLayout statistics = new VerticalLayout(new Label("Coming soon"));
statistics.setCaption("Statistics");
statistics.setSizeFull();
String location = UI.getCurrent().getPage().getLocation().toASCIIString();
location = location.substring(0, location.indexOf('#'));
VerticalLayout properties = new VerticalLayout(new Label(String.format("Required ACRA configuration:<br><code>formUri = \"%sreport\",<br>" +
"formUriBasicAuthLogin = \"%s\",<br>formUriBasicAuthPassword = \"%s\",<br>httpMethod = HttpSender.Method.POST,<br>reportType = HttpSender.Type.JSON</code>",
location, app.getId(), app.getPassword()), ContentMode.HTML));
properties.setCaption("Properties");
properties.setSizeFull();
TabSheet tabSheet = new TabSheet(new BugTab(reportList, getNavigationManager()), new ReportList(reportList, getNavigationManager()), statistics, properties);
tabSheet.setSizeFull();
VerticalLayout content = new VerticalLayout(tabSheet);
content.setSizeFull();
Style.apply(content, Style.NO_PADDING, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
setCompositionRoot(content);
setSizeFull();
}
}

View file

@ -0,0 +1,46 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.Bug;
import com.faendir.acra.data.Report;
import com.faendir.acra.data.ReportUtils;
import com.faendir.acra.ui.NavigationManager;
import com.faendir.acra.util.StringUtils;
import com.faendir.acra.util.Style;
import com.vaadin.event.selection.SelectionEvent;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.VerticalLayout;
import java.util.List;
/**
* @author Lukas
* @since 17.05.2017
*/
public class BugTab extends CustomComponent {
private final VerticalLayout root;
private final NavigationManager navigationManager;
public BugTab(List<Report> reportList, NavigationManager navigationManager) {
this.navigationManager = navigationManager;
MyGrid<Bug> bugs = new MyGrid<>(null, ReportUtils.getBugs(reportList));
bugs.setSizeFull();
bugs.addColumn(bug -> String.valueOf(bug.getReports().size()), "Reports");
bugs.addColumn(bug -> StringUtils.distanceFromNowAsString(bug.getLastDate()), "Latest Report");
bugs.addColumn(bug -> String.valueOf(bug.getVersionCode()), "Version");
bugs.addColumn(bug -> bug.getTrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
bugs.addSelectionListener(this::handleBugSelection);
root = new VerticalLayout(bugs);
Style.NO_PADDING.apply(root);
root.setSizeFull();
setCompositionRoot(root);
setSizeFull();
setCaption("Bugs");
}
private void handleBugSelection(SelectionEvent<Bug> e) {
if(root.getComponentCount() == 2){
root.removeComponent(root.getComponent(1));
}
e.getFirstSelectedItem().ifPresent(bug -> root.addComponent(new ReportList(bug.getReports(), navigationManager)));
}
}

View file

@ -0,0 +1,23 @@
package com.faendir.acra.ui.view;
import com.vaadin.data.ValueProvider;
import com.vaadin.ui.Grid;
import java.util.Collection;
/**
* @author Lukas
* @since 14.05.2017
*/
public class MyGrid<T> extends Grid<T> {
public MyGrid(String caption, Collection<T> items) {
super(caption, items);
}
public Grid.Column addColumn(ValueProvider<T, String> valueProvider, String caption) {
Grid.Column column = addColumn(valueProvider);
column.setId(caption);
column.setCaption(caption);
return column;
}
}

View file

@ -0,0 +1,31 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.annotation.AutoDiscoverView;
import com.faendir.acra.ui.NavigationManager;
import com.vaadin.navigator.View;
import com.vaadin.ui.CustomComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
* @author Lukas
* @since 14.05.2017
*/
@AutoDiscoverView
@Component
public abstract class NamedView extends CustomComponent implements View {
private NavigationManager navigationManager;
public abstract String getName();
NavigationManager getNavigationManager() {
return navigationManager;
}
@Lazy
@Autowired
public void setNavigationManager(NavigationManager navigationManager) {
this.navigationManager = navigationManager;
}
}

View file

@ -0,0 +1,74 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.App;
import com.faendir.acra.data.AppManager;
import com.faendir.acra.data.ReportManager;
import com.faendir.acra.util.Style;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.components.grid.FooterCell;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Lukas
* @since 23.03.2017
*/
@UIScope
@Component
public class Overview extends NamedView {
private final AppManager appManager;
private final ReportManager reportManager;
private MyGrid<App> grid;
@Autowired
public Overview(AppManager appManager, ReportManager reportManager) {
this.appManager = appManager;
this.reportManager = reportManager;
}
private void addApp() {
Window window = new Window("New App");
TextField name = new TextField("Name");
Button create = new Button("Create");
create.addClickListener(e -> {
appManager.createNewApp(name.getValue());
window.close();
grid.setItems(appManager.getApps());
});
VerticalLayout layout = new VerticalLayout(name, create);
window.setContent(layout);
window.center();
UI.getCurrent().addWindow(window);
}
@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
grid = new MyGrid<>("Apps", appManager.getApps());
grid.setSizeFull();
Grid.Column column = grid.addColumn(App::getName, "Name");
grid.addColumn(app -> String.valueOf(reportManager.getReports(app.getId()).size()), "Reports");
FooterCell footerCell = grid.appendFooterRow().getCell(column.getId());
Button add = new Button("New App");
add.setSizeFull();
add.addClickListener(e -> addApp());
footerCell.setComponent(add);
VerticalLayout layout = new VerticalLayout(grid);
Style.apply(layout, Style.NO_PADDING, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
setCompositionRoot(layout);
grid.addItemClickListener(e -> getNavigationManager().navigateTo(AppView.class, e.getItem().getId()));
}
@Override
public String getName() {
return "";
}
}

View file

@ -0,0 +1,29 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.Report;
import com.faendir.acra.ui.NavigationManager;
import com.faendir.acra.util.StringUtils;
import java.util.List;
/**
* @author Lukas
* @since 14.05.2017
*/
public class ReportList extends MyGrid<Report> {
public ReportList(List<Report> reportList, NavigationManager navigationManager) {
super("Reports", reportList);
setSizeFull();
addColumn(report -> StringUtils.distanceFromNowAsString(report.getDate()), "Date");
addReportColumn("APP_VERSION_NAME", "App Version");
addReportColumn( "ANDROID_VERSION", "Android Version");
addReportColumn("PHONE_MODEL", "Device");
addColumn(report -> report.getContent().getString("STACK_TRACE").split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
addItemClickListener(e -> navigationManager.navigateTo(ReportView.class, e.getItem().getId()));
}
private void addReportColumn(String key, String caption) {
addColumn(report -> report.getContent().getString(key), caption);
}
}

View file

@ -0,0 +1,80 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.Report;
import com.faendir.acra.data.ReportManager;
import com.faendir.acra.util.Style;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
/**
* @author Lukas
* @since 13.05.2017
*/
@UIScope
@org.springframework.stereotype.Component
public class ReportView extends NamedView {
private final ReportManager reportManager;
@Autowired
public ReportView(ReportManager reportManager) {
this.reportManager = reportManager;
}
private Stream<Component> getLayoutForEntry(String key, Object value) {
return Stream.of(new Label(key, ContentMode.PREFORMATTED), getComponentForContent(value));
}
private GridLayout getLayoutForMap(Map<String, ?> map) {
GridLayout layout = new GridLayout(2, 1, map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).flatMap(entry -> getLayoutForEntry(entry.getKey(), entry.getValue())).toArray(Component[]::new));
layout.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
layout.setSpacing(false);
layout.setMargin(false);
return layout;
}
private Component getComponentForContent(Object value) {
if (value instanceof Map) {
//noinspection unchecked
return getLayoutForMap((Map<String, ?>) value);
} else if (value instanceof List) {
//noinspection unchecked
List<Object> values = (List<Object>) value;
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < values.size(); i++) {
map.put(String.valueOf(i), values.get(0));
}
return getLayoutForMap(map);
}
return new Label(value.toString(), ContentMode.PREFORMATTED);
}
@Override
public String getName() {
return "report";
}
@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
Report report = reportManager.getReport(event.getParameters());
Component content = getLayoutForMap(report.getContent().toMap());
Panel panel = new Panel(content);
panel.setSizeFull();
Style.apply(this, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);
setCompositionRoot(panel);
setSizeFull();
}
}

View file

@ -0,0 +1,16 @@
package com.faendir.acra.util;
import org.ocpsoft.prettytime.PrettyTime;
import java.util.Date;
/**
* @author Lukas
* @since 14.05.2017
*/
public class StringUtils {
public static String distanceFromNowAsString(Date dateTime){
return new PrettyTime().format(dateTime);
}
}

View file

@ -0,0 +1,35 @@
package com.faendir.acra.util;
import com.vaadin.ui.Component;
/**
* @author Lukas
* @since 17.05.2017
*/
public enum Style {
NO_PADDING("no-padding"),
PADDING_LEFT("padding-left"),
PADDING_TOP("padding-top"),
PADDING_RIGHT("padding-right"),
PADDING_BOTTOM("padding-bottom"),
MARGIN_LEFT("margin-left"),
MARGIN_TOP("margin-top"),
MARGIN_RIGHT("margin-right"),
MARGIN_BOTTOM("margin-bottom"),
BACKGROUND_LIGHT_GRAY("background-light-gray");
final String name;
Style(String name) {
this.name = name;
}
public void apply(Component component) {
component.addStyleName(name);
}
public static void apply(Component component, Style... styles){
for (Style style : styles){
style.apply(component);
}
}
}

View file

@ -0,0 +1,68 @@
@import "../valo/valo.scss";
@mixin acratheme {
@include valo;
.v-gridlayout-slot {
box-shadow: inset -1px -1px 0px 0px black;
.v-widget {
display: inline;
padding-bottom: 4px;
pre {
padding-left: 6px;
padding-right: 6px;
display: inline;
}
}
}
.v-gridlayout-slot:last-child {
box-shadow: inset -1px 0px 0px 0px black;
}
.v-gridlayout-slot:nth-last-child(2) {
box-shadow: inset -1px 0px 0px 0px black;
}
.no-padding {
padding: 0 !important;
}
$padding: 10px;
.padding-left {
padding-left: $padding !important;
}
.padding-top {
padding-top: $padding !important;
}
.padding-right {
padding-right: $padding !important;
}
.padding-bottom {
padding-bottom: $padding !important;
}
$margin: 10px;
.margin-left {
margin-left: $margin;
}
.margin-top {
margin-top: $margin;
}
.margin-right {
margin-right: $margin;
}
.margin-bottom {
margin-bottom: $margin;
}
.background-light-gray {
background: lightgray;
}
}

View file

@ -0,0 +1,7 @@
/* This file is automatically managed and will be overwritten from time to time. */
/* Do not manually edit this file. */
/* Import and include this mixin into your project theme to include the addon themes */
@mixin addons {
}

View file

@ -0,0 +1,5 @@
@import "addons.scss";
@import "acratheme.scss";
@include addons;
@include acratheme;

View file

@ -0,0 +1,8 @@
spring.data.mongodb.port=27017
spring.data.mongodb.host=127.0.0.1
security.user.name=admin
security.user.password=admin
security.user.role=USER
server.context-path=/acra

7
build.gradle Normal file
View file

@ -0,0 +1,7 @@
allprojects{
repositories {
mavenCentral()
}
}

2
gradle.properties Normal file
View file

@ -0,0 +1,2 @@
org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

172
gradlew vendored Normal file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored Normal file
View file

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

4
settings.gradle Normal file
View file

@ -0,0 +1,4 @@
include 'backend'
include 'annotation'
include 'annotationprocessor'