Browse Source

preparing svelte login, mastered translations

feature/document
Stephan Richter 4 months ago
parent
commit
71cf6ec96d
  1. 2
      backend/build.gradle.kts
  2. 2
      backend/src/main/java/de/srsoftware/umbrella/backend/Application.java
  3. 2
      build.gradle.kts
  4. 9
      frontend/src/App.svelte
  5. 6
      frontend/src/Components/Homepage.svelte
  6. 29
      frontend/src/Components/Login.svelte
  7. 12
      frontend/src/Components/Menu.svelte
  8. 4
      frontend/src/app.css
  9. 5
      frontend/src/translations.svelte.js
  10. 20
      frontend/src/user.svelte.js
  11. 3
      settings.gradle.kts
  12. 1
      translations/build.gradle.kts
  13. 7
      translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java
  14. 11
      translations/src/main/resources/de.json
  15. 6
      user/build.gradle.kts
  16. 54
      user/src/main/java/de/srsoftware/umbrella/user/UserModule.java
  17. 4
      web/build.gradle.kts

2
backend/build.gradle.kts

@ -13,8 +13,8 @@ application{ @@ -13,8 +13,8 @@ application{
dependencies{
implementation(project(":translations"))
implementation(project(":user"))
implementation(project(":web"))
implementation("de.srsoftware:tools.http:6.0.0")
}
tasks.jar {

2
backend/src/main/java/de/srsoftware/umbrella/backend/Application.java

@ -7,6 +7,7 @@ import static java.lang.System.Logger.Level.INFO; @@ -7,6 +7,7 @@ import static java.lang.System.Logger.Level.INFO;
import com.sun.net.httpserver.HttpServer;
import de.srsoftware.tools.ColorLogger;
import de.srsoftware.umbrella.translations.Translations;
import de.srsoftware.umbrella.user.UserModule;
import de.srsoftware.umbrella.web.WebHandler;
import java.io.IOException;
import java.net.InetSocketAddress;
@ -22,6 +23,7 @@ public class Application { @@ -22,6 +23,7 @@ public class Application {
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.setExecutor(Executors.newFixedThreadPool(threads));
new WebHandler().bindPath("/").on(server);
new UserModule().bindPath("/api/user").on(server);
new Translations().bindPath("/api/translations").on(server);
LOG.log(INFO,"Started web server at {0}",port);
server.start();

2
build.gradle.kts

@ -40,7 +40,7 @@ subprojects { @@ -40,7 +40,7 @@ subprojects {
dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
implementation("de.srsoftware:tools.http:6.0.2")
implementation("de.srsoftware:tools.logging:1.3.2")
}

9
frontend/src/App.svelte

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
<script>
import { onMount } from 'svelte';
import { loadTranslation } from './translations.svelte.js'
import { loadTranslation } from './translations.svelte.js';
import { user } from './user.svelte.js';
let translations_ready = false;
onMount(async () => {
await loadTranslation('de','Login');
@ -12,10 +14,13 @@ @@ -12,10 +14,13 @@
import Menu from "./Components/Menu.svelte";
</script>
{#if translations_ready}
{#if translations_ready }
{#if user.username }
<Menu />
<Homepage />
{:else}
<Login />
{/if}
{:else}
<p>Loading translations...</p>
{/if}

6
frontend/src/Components/Homepage.svelte

@ -1 +1,5 @@ @@ -1 +1,5 @@
<h1>Welcome</h1>
<script>
import { t } from '../translations.svelte.js';
import { user } from '../user.svelte.js';
</script>
<h1>{t('home.Welcome')}, {user.username}</h1>

29
frontend/src/Components/Login.svelte

@ -1,5 +1,15 @@ @@ -1,5 +1,15 @@
<script>
import { t } from '../translations.svelte.js';
import { tryLogin } from '../user.svelte.js';
let credentials = { username : null, password : null }
function doLogin(ev){
tryLogin(credentials);
}
function init(element){
element.focus();
}
</script>
<style>
label { display: block; margin: 5px; }
@ -14,21 +24,22 @@ @@ -14,21 +24,22 @@
}
</style>
<fieldset>
<form on:submit|preventDefault={doLogin}>
<fieldset>
<legend>{t('login.Login')}</legend>
<label>
<input type="text" />
<span>Email/Username</span>
<input type="text" bind:value={credentials.username} required use:init />
<span>{t('login.Email_or_Username')}</span>
</label>
<label>
<input type="password" />
<span>Password</span>
<input type="password" bind:value={credentials.password} required />
<span>{t('login.Password')}</span>
</label>
<button>Login</button>
</fieldset>
<button>{t('login.do_login')}</button>
</fieldset>
</form>
<fieldset>
<legend>OIDC Login</legend>
<legend>{t('login.OIDC_Login')}</legend>
<button>SRSoftware</button>
<button>ORC ID</button>
</fieldset>

12
frontend/src/Components/Menu.svelte

@ -1,3 +1,13 @@ @@ -1,3 +1,13 @@
<script>
import { t } from '../translations.svelte.js';
import { user } from '../user.svelte.js';
function logout(){
user.username = null;
}
</script>
<nav>
<a href="/">Home</a>
<a href="/">{t('nav.Home')}</a>
<a href="https://svelte.dev/tutorial/svelte/state" target="_blank">{t('nav.Tutorial')}</a>
<a href="#" on:click={logout}>Logout</a>
</nav>

4
frontend/src/app.css

@ -1,3 +1,6 @@ @@ -1,3 +1,6 @@
a {
color: orange;
}
body {
background: black;
color: orange;
@ -14,6 +17,7 @@ input{ @@ -14,6 +17,7 @@ input{
border-radius: 4px;
padding: 3px;
margin: 3px;
color: orange;
}
button{

5
frontend/src/translations.svelte.js

@ -8,12 +8,11 @@ export async function loadTranslation(lang){ @@ -8,12 +8,11 @@ export async function loadTranslation(lang){
}
export function t(key){
var keys = key.split('.');
let set = translations.values;
var keys = key.split('.');
for (let key of keys){
if (set[key]) {
if (!set[key]) return keys[keys.length-1];
set = set[key];
} else return key;
}
return set;
}

20
frontend/src/user.svelte.js

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
export const user = $state({
name : null
})
export async function tryLogin(credentials){
var url = `${location.protocol}//${location.host.replace('5173','8080')}/api/user/login`;
let response = await fetch(url,{
headers: {
'Content-Type':'application/json'
},
method: 'POST',
body: JSON.stringify(credentials)
});
if (response.ok){
const json = await response.json();
for (var key of Object.keys(json)) user[key] = json[key];
} else {
alert("Login failed!");
}
}

3
settings.gradle.kts

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
rootProject.name = "Umbrella25"
include("backend")
include("web")
include("translations")
include("user")
include("web")

1
translations/build.gradle.kts

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
description = "Umbrella : Translations"
dependencies{
implementation("de.srsoftware:tools.http:6.0.0")
implementation("org.json:json:20240303")
}

7
translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java

@ -1,21 +1,17 @@ @@ -1,21 +1,17 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.translations;
import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.WARNING;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.PathHandler;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
public class Translations extends PathHandler {
private static final System.Logger LOG = System.getLogger("Translations");
@ -24,7 +20,6 @@ public class Translations extends PathHandler { @@ -24,7 +20,6 @@ public class Translations extends PathHandler {
@Override
public boolean doGet(Path path, HttpExchange ex) throws IOException {
LOG.log(INFO,"doGet({0},ex)",path);
allowOrigin(ex,"*");
if (path.empty())return sendContent(ex,501,"Language missing");
var lang = path.pop();

11
translations/src/main/resources/de.json

@ -1,5 +1,12 @@ @@ -1,5 +1,12 @@
{
'login' : {
'Login' : "Mist!"
"home" : {
"Welcome" : "Willkommen"
},
"login" : {
"do_login" : "anmelden",
"Email_or_Username": "Email oder Nutzername",
"Login" : "Anmeldung",
"OIDC_Login" : "Anmeldung mit OIDC",
"Password" : "Passwort"
}
}

6
user/build.gradle.kts

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
description = "Umbrella : User"
dependencies{
implementation("de.srsoftware:tools.mime:1.1.2")
implementation("org.json:json:20240303")
}

54
user/src/main/java/de/srsoftware/umbrella/user/UserModule.java

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.user;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.tools.MimeType;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.PathHandler;
import de.srsoftware.tools.SessionToken;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import static de.srsoftware.tools.MimeType.MIME_JSON;
public class UserModule extends PathHandler {
private HttpExchange addCors(HttpExchange ex){
var headers = ex.getResponseHeaders();
headers.add("Allow-Origin","*");
headers.add("Access-Control-Allow-Origin","*");
headers.add("Access-Control-Allow-Headers","Content-Type");
return ex;
}
@Override
public boolean doOptions(Path path, HttpExchange ex) throws IOException {
return sendEmptyResponse(200,addCors(ex));
}
@Override
public boolean doPost(Path path, HttpExchange ex) throws IOException {
addCors(ex);
var p = path.toString();
switch (p){
case "login": return postLogin(ex);
}
return super.doPost(path, ex);
}
private boolean postLogin(HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.has("username") && json.get("username") instanceof String username)) return sendContent(ex,402,"Username missing");
if (!(json.has("password") && json.get("password") instanceof String password)) return sendContent(ex,402,"Password missing");
if (!username.equals(password)) return sendContent(ex,401,"Login failed");
var sessionId = UUID.randomUUID().toString();
new SessionToken(sessionId).addTo(ex);
ex.getResponseHeaders().add("Content-Type", MIME_JSON);
return sendContent(ex,200,new JSONObject(Map.of("username",username)).toString());
}
}

4
web/build.gradle.kts

@ -1,9 +1,5 @@ @@ -1,9 +1,5 @@
description = "Umbrella : Web"
dependencies{
implementation("de.srsoftware:tools.http:6.0.0")
}
tasks.processResources {
System.out.println("Copying from dist…")
from("../frontend/dist") {

Loading…
Cancel
Save