preparing svelte login, mastered translations
This commit is contained in:
@@ -13,8 +13,8 @@ application{
|
|||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
implementation(project(":translations"))
|
implementation(project(":translations"))
|
||||||
|
implementation(project(":user"))
|
||||||
implementation(project(":web"))
|
implementation(project(":web"))
|
||||||
implementation("de.srsoftware:tools.http:6.0.0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import static java.lang.System.Logger.Level.INFO;
|
|||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import de.srsoftware.tools.ColorLogger;
|
import de.srsoftware.tools.ColorLogger;
|
||||||
import de.srsoftware.umbrella.translations.Translations;
|
import de.srsoftware.umbrella.translations.Translations;
|
||||||
|
import de.srsoftware.umbrella.user.UserModule;
|
||||||
import de.srsoftware.umbrella.web.WebHandler;
|
import de.srsoftware.umbrella.web.WebHandler;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@@ -22,6 +23,7 @@ public class Application {
|
|||||||
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
|
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||||
server.setExecutor(Executors.newFixedThreadPool(threads));
|
server.setExecutor(Executors.newFixedThreadPool(threads));
|
||||||
new WebHandler().bindPath("/").on(server);
|
new WebHandler().bindPath("/").on(server);
|
||||||
|
new UserModule().bindPath("/api/user").on(server);
|
||||||
new Translations().bindPath("/api/translations").on(server);
|
new Translations().bindPath("/api/translations").on(server);
|
||||||
LOG.log(INFO,"Started web server at {0}",port);
|
LOG.log(INFO,"Started web server at {0}",port);
|
||||||
server.start();
|
server.start();
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ subprojects {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
|
implementation("de.srsoftware:tools.http:6.0.2")
|
||||||
implementation("de.srsoftware:tools.logging:1.3.2")
|
implementation("de.srsoftware:tools.logging:1.3.2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
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;
|
let translations_ready = false;
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await loadTranslation('de','Login');
|
await loadTranslation('de','Login');
|
||||||
@@ -12,10 +14,13 @@
|
|||||||
import Menu from "./Components/Menu.svelte";
|
import Menu from "./Components/Menu.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if translations_ready}
|
{#if translations_ready }
|
||||||
|
{#if user.username }
|
||||||
<Menu />
|
<Menu />
|
||||||
<Homepage />
|
<Homepage />
|
||||||
|
{:else}
|
||||||
<Login />
|
<Login />
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<p>Loading translations...</p>
|
<p>Loading translations...</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -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>
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import { t } from '../translations.svelte.js';
|
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>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
label { display: block; margin: 5px; }
|
label { display: block; margin: 5px; }
|
||||||
@@ -14,21 +24,22 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={doLogin}>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{t('login.Login')}</legend>
|
||||||
|
<label>
|
||||||
|
<input type="text" bind:value={credentials.username} required use:init />
|
||||||
|
<span>{t('login.Email_or_Username')}</span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="password" bind:value={credentials.password} required />
|
||||||
|
<span>{t('login.Password')}</span>
|
||||||
|
</label>
|
||||||
|
<button>{t('login.do_login')}</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t('login.Login')}</legend>
|
<legend>{t('login.OIDC_Login')}</legend>
|
||||||
<label>
|
|
||||||
<input type="text" />
|
|
||||||
<span>Email/Username</span>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input type="password" />
|
|
||||||
<span>Password</span>
|
|
||||||
</label>
|
|
||||||
<button>Login</button>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>OIDC Login</legend>
|
|
||||||
<button>SRSoftware</button>
|
<button>SRSoftware</button>
|
||||||
<button>ORC ID</button>
|
<button>ORC ID</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import { t } from '../translations.svelte.js';
|
||||||
|
import { user } from '../user.svelte.js';
|
||||||
|
|
||||||
|
function logout(){
|
||||||
|
user.username = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<nav>
|
<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>
|
</nav>
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
a {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background: black;
|
background: black;
|
||||||
color: orange;
|
color: orange;
|
||||||
@@ -14,6 +17,7 @@ input{
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
button{
|
button{
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ export async function loadTranslation(lang){
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function t(key){
|
export function t(key){
|
||||||
var keys = key.split('.');
|
|
||||||
let set = translations.values;
|
let set = translations.values;
|
||||||
|
var keys = key.split('.');
|
||||||
for (let key of keys){
|
for (let key of keys){
|
||||||
if (set[key]) {
|
if (!set[key]) return keys[keys.length-1];
|
||||||
set = set[key];
|
set = set[key];
|
||||||
} else return key;
|
|
||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
20
frontend/src/user.svelte.js
Normal file
20
frontend/src/user.svelte.js
Normal file
@@ -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!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
rootProject.name = "Umbrella25"
|
rootProject.name = "Umbrella25"
|
||||||
|
|
||||||
include("backend")
|
include("backend")
|
||||||
include("web")
|
|
||||||
include("translations")
|
include("translations")
|
||||||
|
include("user")
|
||||||
|
include("web")
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
description = "Umbrella : Translations"
|
description = "Umbrella : Translations"
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
implementation("de.srsoftware:tools.http:6.0.0")
|
|
||||||
implementation("org.json:json:20240303")
|
implementation("org.json:json:20240303")
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,17 @@
|
|||||||
/* © SRSoftware 2025 */
|
/* © SRSoftware 2025 */
|
||||||
package de.srsoftware.umbrella.translations;
|
package de.srsoftware.umbrella.translations;
|
||||||
|
|
||||||
import static java.lang.System.Logger.Level.INFO;
|
|
||||||
import static java.lang.System.Logger.Level.WARNING;
|
import static java.lang.System.Logger.Level.WARNING;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import de.srsoftware.tools.Path;
|
import de.srsoftware.tools.Path;
|
||||||
import de.srsoftware.tools.PathHandler;
|
import de.srsoftware.tools.PathHandler;
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class Translations extends PathHandler {
|
public class Translations extends PathHandler {
|
||||||
private static final System.Logger LOG = System.getLogger("Translations");
|
private static final System.Logger LOG = System.getLogger("Translations");
|
||||||
@@ -24,7 +20,6 @@ public class Translations extends PathHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doGet(Path path, HttpExchange ex) throws IOException {
|
public boolean doGet(Path path, HttpExchange ex) throws IOException {
|
||||||
LOG.log(INFO,"doGet({0},ex)",path);
|
|
||||||
allowOrigin(ex,"*");
|
allowOrigin(ex,"*");
|
||||||
if (path.empty())return sendContent(ex,501,"Language missing");
|
if (path.empty())return sendContent(ex,501,"Language missing");
|
||||||
var lang = path.pop();
|
var lang = path.pop();
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
{
|
{
|
||||||
'login' : {
|
"home" : {
|
||||||
'Login' : "Mist!"
|
"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
Normal file
6
user/build.gradle.kts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
description = "Umbrella : User"
|
||||||
|
|
||||||
|
dependencies{
|
||||||
|
implementation("de.srsoftware:tools.mime:1.1.2")
|
||||||
|
implementation("org.json:json:20240303")
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
description = "Umbrella : Web"
|
description = "Umbrella : Web"
|
||||||
|
|
||||||
dependencies{
|
|
||||||
implementation("de.srsoftware:tools.http:6.0.0")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
System.out.println("Copying from dist…")
|
System.out.println("Copying from dist…")
|
||||||
from("../frontend/dist") {
|
from("../frontend/dist") {
|
||||||
|
|||||||
Reference in New Issue
Block a user