Skip to content

Commit aede7e6

Browse files
committed
Init
1 parent c400a86 commit aede7e6

File tree

11 files changed

+277
-2
lines changed

11 files changed

+277
-2
lines changed

.env

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# server port
2+
PORT=7837
3+
4+
# server host
5+
HOST=127.0.0.1
6+
7+
# jwt secret, make sure you change it in production
8+
JWT_SECRET=54tr456yrtytr45654tr456yrtytr45654tr456yrtytr45654tr456yrtytr45654tr456yrtytr456
9+
JWT_USERS=username1,username2,xxxx,hhhh
10+
FILE_STORE_PATH=/Users/home/dev/electerm-sync-server-java/temp/file_store

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@
2222
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2323
hs_err_pid*
2424
replay_pid*
25+
/temp/
26+
.gradle
27+
build

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.configuration.updateBuildConfiguration": "automatic"
3+
}

README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,43 @@
1-
# electerm-sync-server-java
2-
electerm-sync-server-java
1+
# Java Electerm sync server
2+
3+
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fatrox%2Fsync-dotenv%2Fbadge)](https://github.com/electerm/electerm-sync-server-java/actions)
4+
5+
A simple electerm data sync server.
6+
7+
## Use
8+
9+
```bash
10+
git clone git@github.com:electerm/electerm-sync-server-java.git
11+
cd electerm-sync-server-java
12+
13+
# create env file, then edit .env
14+
cp sample.env .env
15+
16+
## run
17+
gradle run
18+
19+
## build
20+
gradle build
21+
22+
# would show something like
23+
# server running at http://127.0.0.1:7837
24+
25+
# in electerm sync settings, set custom sync server with:
26+
# server url: http://127.0.0.1:7837
27+
# JWT_SECRET: your JWT_SECRET in .env
28+
# JWT_USER_NAME: one JWT_USER in .env
29+
```
30+
31+
## Test
32+
33+
```bash
34+
npm run test
35+
```
36+
37+
## Write your own data store
38+
39+
Just take [src/ElectermSync/FileStore.java](src/ElectermSync/FileStore.java) as an example, write your own read/write method
40+
41+
## License
42+
43+
MIT

build.gradle

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
plugins {
3+
id 'java'
4+
id 'application'
5+
}
6+
7+
repositories {
8+
mavenCentral()
9+
}
10+
11+
java {
12+
toolchain {
13+
languageVersion.set(JavaLanguageVersion.of(17))
14+
}
15+
}
16+
17+
dependencies {
18+
implementation 'com.sparkjava:spark-core:2.9.3'
19+
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
20+
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
21+
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
22+
implementation 'com.google.code.gson:gson:2.8.9'
23+
implementation 'io.github.cdimascio:java-dotenv:5.2.1'
24+
implementation 'org.slf4j:slf4j-api:1.7.32'
25+
implementation 'ch.qos.logback:logback-classic:1.2.3'
26+
testImplementation 'junit:junit:4.13.2'
27+
}
28+
29+
application {
30+
mainClass.set('ElectermSync.App')
31+
}
32+
33+
sourceSets {
34+
main {
35+
java {
36+
srcDir 'src'
37+
}
38+
}
39+
}

sample.env

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# server port
2+
PORT=7837
3+
4+
# server host
5+
HOST=127.0.0.1
6+
7+
# jwt secret, make sure you change it in production
8+
JWT_SECRET=283hsdfye@!2@9oijnjSwda09
9+
JWT_USERS=username1,username2,xxxx,hhhh
10+
# FILE_STORE_PATH=/home/some/folder

src/ElectermSync/App.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package ElectermSync;
2+
import static spark.Spark.*;
3+
import io.jsonwebtoken.*;
4+
import com.google.gson.Gson;
5+
import java.util.Map;
6+
import java.io.File;
7+
import java.util.Arrays;
8+
import java.nio.charset.StandardCharsets;
9+
import java.util.Base64;
10+
11+
public class App {
12+
13+
public static void main(String[] args) {
14+
Gson gson = new Gson();
15+
16+
17+
Config dotenv = new Config();
18+
String secretOri = dotenv.getValue("JWT_SECRET");
19+
byte[] bytesToEncode = secretOri.getBytes(StandardCharsets.UTF_8);
20+
21+
// Encode the bytes using Base64
22+
String secret = Base64.getEncoder().encodeToString(bytesToEncode);
23+
24+
String ids = dotenv.getValue("JWT_USERS");
25+
String[] idArrStrings = ids.split(",");
26+
Jwts.parserBuilder().setSigningKey(secret).build();
27+
28+
port(Integer.parseInt((dotenv.getValue("PORT"))));
29+
30+
ipAddress(dotenv.getValue("HOST"));
31+
32+
before("/api/sync", (request, response) -> {
33+
String authHeader = request.headers("Authorization");
34+
try {
35+
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
36+
throw new JwtException("Missing or invalid token");
37+
} else {
38+
String token = authHeader.substring(7);
39+
Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(secret).build().parseClaimsJws(token);
40+
String id = claimsJws.getBody().get("id").toString();
41+
boolean found = Arrays.stream(idArrStrings).anyMatch(element -> element.equals(id));
42+
if (!found) {
43+
throw new JwtException("Unauthorized access");
44+
}
45+
request.attribute("jwtId", id);
46+
}
47+
} catch (JwtException ex) {
48+
halt(401, "Unauthorized: " + ex.getMessage());
49+
}
50+
});
51+
52+
get("/api/sync", (request, response) -> {
53+
String jwtId = request.attribute("jwtId");
54+
ReadResult r = FileStore.read(jwtId, dotenv);
55+
response.status(r.statusCode);
56+
return gson.toJson(r.fileData);
57+
});
58+
59+
put("/api/sync", (request, response) -> {
60+
String requestBody = request.body();
61+
String jwtId = request.attribute("jwtId");
62+
response.type("application/json");
63+
WriteResult r = FileStore.write(requestBody, jwtId, dotenv);
64+
response.status(r.statusCode);
65+
return r.message;
66+
});
67+
68+
after((request, response) -> {
69+
response.type("application/json");
70+
response.header("Content-Encoding", "gzip");
71+
});
72+
}
73+
}

src/ElectermSync/Config.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package ElectermSync;
2+
3+
import io.github.cdimascio.dotenv.Dotenv;
4+
5+
public class Config {
6+
private Dotenv dotenv;
7+
8+
public Config() {
9+
dotenv = Dotenv.load();
10+
}
11+
12+
public String getValue(String name) {
13+
return dotenv.get(name);
14+
}
15+
}

src/ElectermSync/FileStore.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
package ElectermSync;
3+
import java.nio.file.Files;
4+
import java.nio.file.Path;
5+
import java.io.IOException;
6+
import java.io.File;
7+
import java.util.Map;
8+
import java.util.HashMap;
9+
import com.fasterxml.jackson.databind.ObjectMapper;
10+
11+
public class FileStore {
12+
13+
public static void main(String[] args) {
14+
// Test code
15+
}
16+
17+
public static Path getFilePath (String userId, Config dotenv) {
18+
String storePath = dotenv.getValue("FILE_STORE_PATH");
19+
Path folder = storePath != null ? Path.of(storePath) : Path.of(System.getProperty("user.dir"));
20+
return folder.resolve(userId + ".json");
21+
}
22+
23+
public static WriteResult write(String jsonBody, String userId, Config dotenv) {
24+
Path filePath = getFilePath(userId, dotenv);
25+
try {
26+
Files.writeString(filePath, jsonBody);
27+
} catch (IOException e) {
28+
return new WriteResult("Error writing file", 500);
29+
}
30+
31+
return new WriteResult("ok", 200);
32+
}
33+
34+
public static ReadResult read(String userId, Config dotenv) {
35+
ObjectMapper objectMapper = new ObjectMapper();
36+
Path filePath = getFilePath(userId, dotenv);
37+
File file = filePath.toFile();
38+
39+
if (file.isFile()) {
40+
String fileContent;
41+
try {
42+
fileContent = Files.readString(filePath);
43+
Map<String, Object> fileData = objectMapper.readValue(fileContent, HashMap.class);
44+
return new ReadResult(fileData, 200);
45+
} catch (IOException e) {
46+
return new ReadResult("File read error", 500);
47+
}
48+
} else {
49+
return new ReadResult("File not found", 404);
50+
}
51+
}
52+
}

src/ElectermSync/ReadResult.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ElectermSync;
2+
3+
import java.util.Map;
4+
5+
public class ReadResult {
6+
public final Map<String, Object> fileData;
7+
public final int statusCode;
8+
9+
ReadResult(Map<String, Object> fileData, int statusCode) {
10+
this.fileData = fileData;
11+
this.statusCode = statusCode;
12+
}
13+
14+
ReadResult(String errorMessage, int statusCode) {
15+
this.fileData = Map.of("error", errorMessage);
16+
this.statusCode = statusCode;
17+
}
18+
}

0 commit comments

Comments
 (0)