Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7f82018f9 | |||
| a245f849e5 | |||
| 4c3312daca | |||
| a6983780f5 | |||
| 6e146c1577 | |||
| 49cbd328e6 | |||
| 1db28b5bc0 | |||
| 285343218b | |||
| f402122d0c | |||
| 4339e4fd98 | |||
| 16cbcdff60 | |||
| 8dc77f52e4 | |||
| b04b9b92a6 | |||
| 8d66ed4cbe | |||
| b0ee825e0a | |||
| cf0cf2f5e9 | |||
| 1302165ab2 |
+3
-3
@@ -8,8 +8,8 @@ WORKDIR /home/svelte/Umbrella/frontend
|
|||||||
RUN npm install && npm run build
|
RUN npm install && npm run build
|
||||||
|
|
||||||
|
|
||||||
FROM alpine AS java_build
|
FROM alpine:3.22 AS java_build
|
||||||
RUN apk add gradle fontconfig font-opensans openjdk21-jre
|
RUN apk add gradle
|
||||||
ADD . /Umbrella
|
ADD . /Umbrella
|
||||||
WORKDIR /Umbrella
|
WORKDIR /Umbrella
|
||||||
COPY --from=svelte_build /home/svelte/Umbrella/frontend/dist web/src/main/resources/web
|
COPY --from=svelte_build /home/svelte/Umbrella/frontend/dist web/src/main/resources/web
|
||||||
@@ -17,7 +17,7 @@ RUN gradle --no-daemon build
|
|||||||
|
|
||||||
|
|
||||||
FROM alpine
|
FROM alpine
|
||||||
RUN apk --no-cache add bash fontconfig font-opensans graphviz openjdk21-jre weasyprint \
|
RUN apk --no-cache add bash fontconfig font-opensans graphviz openjdk21-jre tzdata weasyprint \
|
||||||
&& adduser -D umbrella
|
&& adduser -D umbrella
|
||||||
WORKDIR /home/umbrella
|
WORKDIR /home/umbrella
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
FROM alpine:3.22
|
FROM alpine:3.22
|
||||||
LABEL Maintainer "Stephan Richter <s.richter@srsoftware.de>"
|
LABEL Maintainer "Stephan Richter"
|
||||||
ARG UID=1000
|
ARG UID=1000
|
||||||
ARG GID=1000
|
ARG GID=1000
|
||||||
RUN apk add bash npm
|
RUN apk add bash npm
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
*.db-journal
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"umbrella": {
|
||||||
|
"base_url": "http://127.0.0.1:5173",
|
||||||
|
"logging": {
|
||||||
|
"rootLevel": "INFO"
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"port": 8080
|
||||||
|
},
|
||||||
|
"threads": 16,
|
||||||
|
"modules": {
|
||||||
|
"notes": {
|
||||||
|
"database": "demodata/notes.db"
|
||||||
|
},
|
||||||
|
"document": {
|
||||||
|
"database": "demodata/documents.db",
|
||||||
|
"templates": "demodata/templates"
|
||||||
|
},
|
||||||
|
"wiki": {
|
||||||
|
"database": "demodata/wiki.db"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"database": "demodata/projects.db"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"database": "demodata/message.db",
|
||||||
|
"smtp": {
|
||||||
|
"pass": "none",
|
||||||
|
"port": 587,
|
||||||
|
"host": "none",
|
||||||
|
"user": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"database": "demodata/tags.db"
|
||||||
|
},
|
||||||
|
"bookmark": {
|
||||||
|
"database": "demodata/bookmark.db"
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"database": "demodata/tasks.db"
|
||||||
|
},
|
||||||
|
"journal": {
|
||||||
|
"database": "demodata/journal.db"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"database": "demodata/contacts.db"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"database": "demodata/files.db",
|
||||||
|
"base_dir": "demodata/filestore"
|
||||||
|
},
|
||||||
|
"company": {
|
||||||
|
"database": "demodata/company.db"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"database": "demodata/times.db"
|
||||||
|
},
|
||||||
|
"stock": {
|
||||||
|
"database": "demodata/stock.db"
|
||||||
|
},
|
||||||
|
"items": {},
|
||||||
|
"user": {
|
||||||
|
"database": "demodata/users.db"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -541,6 +541,11 @@ public class DocumentApi extends BaseHandler implements DocumentService {
|
|||||||
var userCompanyIds = companyService().listCompaniesOf(user).keySet();
|
var userCompanyIds = companyService().listCompaniesOf(user).keySet();
|
||||||
|
|
||||||
var documents = db.find(userCompanyIds,keys,fulltext);
|
var documents = db.find(userCompanyIds,keys,fulltext);
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
return sendContent(ex,mapValues(documents));
|
return sendContent(ex,mapValues(documents));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { useTinyRouter } from 'svelte-tiny-router';
|
import { useTinyRouter } from 'svelte-tiny-router';
|
||||||
import { api, post, target } from '../../urls.svelte.js';
|
import { api, get, post, target } from '../../urls.svelte.js';
|
||||||
import { error, yikes } from '../../warn.svelte';
|
import { error, warn, yikes } from '../../warn.svelte';
|
||||||
import { t } from '../../translations.svelte.js';
|
import { t } from '../../translations.svelte.js';
|
||||||
import { display } from '../../time.svelte';
|
import { display } from '../../time.svelte';
|
||||||
|
|
||||||
import Bookmark from '../bookmark/Template.svelte';
|
import Bookmark from '../bookmark/Template.svelte';
|
||||||
|
|
||||||
const router = useTinyRouter();
|
const router = useTinyRouter();
|
||||||
|
let counter = 9;
|
||||||
let bookmarks = $state(null);
|
let bookmarks = $state(null);
|
||||||
let companies = $state(null);
|
let companies = $state(null);
|
||||||
let documents = $state(null);
|
let documents = $state(null);
|
||||||
@@ -34,12 +35,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function doSearch(ignored){
|
function doSearch(ignored){
|
||||||
|
warn(t('searching…'));
|
||||||
let url = window.location.origin + window.location.pathname;
|
let url = window.location.origin + window.location.pathname;
|
||||||
if (key) url += '?key=' + encodeURI(key);
|
if (key) url += '?key=' + encodeURI(key);
|
||||||
window.history.replaceState(history.state, '', url);
|
window.history.replaceState(history.state, '', url);
|
||||||
|
|
||||||
const data = { key : key, fulltext : fulltext };
|
const data = { key : key, fulltext : fulltext };
|
||||||
|
|
||||||
post(api('bookmark/search'),data).then(handleBookmarks);
|
post(api('bookmark/search'),data).then(handleBookmarks);
|
||||||
post(api('company/search '),data).then(handleCompanies);
|
post(api('company/search '),data).then(handleCompanies);
|
||||||
post(api('document/search'),data).then(handleDocuments);
|
post(api('document/search'),data).then(handleDocuments);
|
||||||
@@ -51,6 +52,22 @@
|
|||||||
post(api('wiki/search' ),data).then(handleWikiPages);
|
post(api('wiki/search' ),data).then(handleWikiPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getTitle(key,module,entity_id){
|
||||||
|
get(api(module+'/'+entity_id)).then(res => setTitle(res,key,module))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setTitle(resp,key,module){
|
||||||
|
if (resp.ok){
|
||||||
|
const json = await resp.json();
|
||||||
|
if (json.name) notes[key].title = t(module)+": "+json.name;
|
||||||
|
if (json.title) notes[key].title = t(module)+": "+json.title;
|
||||||
|
if (module == 'document'){
|
||||||
|
notes[key].title = t(json.type)+" "+json.number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function onclick(e){
|
function onclick(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var target = e.target;
|
var target = e.target;
|
||||||
@@ -61,6 +78,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleBookmarks(resp){
|
async function handleBookmarks(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
bookmarks = Object.keys(res).length ? res : null;
|
bookmarks = Object.keys(res).length ? res : null;
|
||||||
@@ -70,6 +88,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleCompanies(resp){
|
async function handleCompanies(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
companies = Object.keys(json).length ? json : null;
|
companies = Object.keys(json).length ? json : null;
|
||||||
@@ -79,6 +98,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDocuments(resp){
|
async function handleDocuments(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
documents = Object.keys(json).length ? json : null;
|
documents = Object.keys(json).length ? json : null;
|
||||||
@@ -88,15 +108,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleNotes(resp){
|
async function handleNotes(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
notes = Object.keys(json).length ? json : null;
|
if ( Object.keys(json).length ) {
|
||||||
|
for (let key of Object.keys(json)){
|
||||||
|
let module = json[key].module;
|
||||||
|
let entity_id = json[key].entity_id;
|
||||||
|
json[key].title = t(module)+' '+entity_id;
|
||||||
|
getTitle(key,module,entity_id);
|
||||||
|
}
|
||||||
|
notes = json;
|
||||||
|
} else notes = null;
|
||||||
} else {
|
} else {
|
||||||
error(resp);
|
error(resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleProjects(resp){
|
async function handleProjects(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
projects = Object.keys(res).length ? res : null;
|
projects = Object.keys(res).length ? res : null;
|
||||||
@@ -106,6 +136,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleStock(resp){
|
async function handleStock(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
stock = Object.keys(res).length ? res : null;
|
stock = Object.keys(res).length ? res : null;
|
||||||
@@ -115,6 +146,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleTasks(resp){
|
async function handleTasks(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
tasks = Object.keys(res).length ? res : null;
|
tasks = Object.keys(res).length ? res : null;
|
||||||
@@ -124,6 +156,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleTimes(resp){
|
async function handleTimes(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
times = Object.keys(res).length ? res : null;
|
times = Object.keys(res).length ? res : null;
|
||||||
@@ -133,6 +166,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleWikiPages(resp){
|
async function handleWikiPages(resp){
|
||||||
|
quitOne();
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
const res = await resp.json();
|
const res = await resp.json();
|
||||||
pages = Object.keys(res).length ? res : null;
|
pages = Object.keys(res).length ? res : null;
|
||||||
@@ -140,6 +174,13 @@
|
|||||||
error(resp);
|
error(resp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function quitOne(){
|
||||||
|
counter--;
|
||||||
|
if (counter > 0) {
|
||||||
|
warn(t('searching…')+" "+counter);
|
||||||
|
} else yikes();
|
||||||
|
}
|
||||||
|
|
||||||
$effect(() => doSearch(key))
|
$effect(() => doSearch(key))
|
||||||
</script>
|
</script>
|
||||||
@@ -236,23 +277,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
{#if notes}
|
|
||||||
<fieldset>
|
|
||||||
<legend>
|
|
||||||
{t('notes')}
|
|
||||||
</legend>
|
|
||||||
<ul>
|
|
||||||
{#each Object.values(notes) as note}
|
|
||||||
<li>
|
|
||||||
<b>
|
|
||||||
<a href="/{note.module}/{note.entity_id}/view" {onclick} >{t(note.module)} {note.entity_id}:</a>
|
|
||||||
</b>
|
|
||||||
{@html target(note.text.rendered)}
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</fieldset>
|
|
||||||
{/if}
|
|
||||||
{#if times}
|
{#if times}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
@@ -308,3 +332,20 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if notes}
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
{t('notes')}
|
||||||
|
</legend>
|
||||||
|
<ul>
|
||||||
|
{#each Object.values(notes) as note}
|
||||||
|
<li>
|
||||||
|
<b>
|
||||||
|
<a href="/{note.module}/{note.entity_id}/view" {onclick} >{note.title}</a>
|
||||||
|
</b>
|
||||||
|
{@html target(note.text.rendered)}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -132,7 +132,23 @@ public class StockModule extends BaseHandler implements StockService {
|
|||||||
yield super.doGet(path,ex);
|
yield super.doGet(path,ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case null, default -> super.doGet(path,ex);
|
case null -> super.doGet(path,ex);
|
||||||
|
default -> {
|
||||||
|
try {
|
||||||
|
var id = Long.parseLong(head);
|
||||||
|
Item item = stockDb.loadItem(id);
|
||||||
|
Owner owner = item.owner().resolve();
|
||||||
|
if (owner instanceof Company company) {
|
||||||
|
if (!companyService().membership(company.id(),user.get().id())) throw forbidden("You are not allowed to access {0}",OBJECT);
|
||||||
|
}
|
||||||
|
if (owner instanceof UmbrellaUser u){
|
||||||
|
if (u.id() != user.get().id()) throw forbidden("You are not allowed to access {0}",OBJECT);
|
||||||
|
}
|
||||||
|
yield sendContent(ex,item);
|
||||||
|
} catch (NumberFormatException nfe){
|
||||||
|
yield super.doGet(path,ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (UmbrellaException e){
|
} catch (UmbrellaException e){
|
||||||
return send(ex,e);
|
return send(ex,e);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class SqliteDb extends BaseDb implements TagDB{
|
|||||||
public SqliteDb(Connection tagDb, Connection bmDb) {
|
public SqliteDb(Connection tagDb, Connection bmDb) {
|
||||||
super(tagDb);
|
super(tagDb);
|
||||||
bookmarks = new de.srsoftware.umbrella.bookmarks.SqliteDb(bmDb);
|
bookmarks = new de.srsoftware.umbrella.bookmarks.SqliteDb(bmDb);
|
||||||
|
createTables();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -278,6 +278,7 @@
|
|||||||
"saved": "gespeichert",
|
"saved": "gespeichert",
|
||||||
"save_object": "{object} speichern",
|
"save_object": "{object} speichern",
|
||||||
"search": "Suche",
|
"search": "Suche",
|
||||||
|
"searching…": "suche…",
|
||||||
"select_company" : "Wählen Sie eine ihrer Firmen:",
|
"select_company" : "Wählen Sie eine ihrer Firmen:",
|
||||||
"select_customer": "Kunde auswählen",
|
"select_customer": "Kunde auswählen",
|
||||||
"select_property": "Eigenschaft auswählen",
|
"select_property": "Eigenschaft auswählen",
|
||||||
|
|||||||
@@ -278,6 +278,7 @@
|
|||||||
"saved": "saved",
|
"saved": "saved",
|
||||||
"save_object": "save {object}",
|
"save_object": "save {object}",
|
||||||
"search": "search",
|
"search": "search",
|
||||||
|
"searching…": "searhcing…",
|
||||||
"select_company" : "select on of you companies:",
|
"select_company" : "select on of you companies:",
|
||||||
"select_customer": "select customer",
|
"select_customer": "select customer",
|
||||||
"select_property": "select property",
|
"select_property": "select property",
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ tasks.processResources {
|
|||||||
from("../frontend/dist") {
|
from("../frontend/dist") {
|
||||||
into("web")
|
into("web")
|
||||||
}
|
}
|
||||||
}
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|||||||
@@ -152,15 +152,27 @@ public class SqliteDb extends BaseDb implements WikiDb {
|
|||||||
@Override
|
@Override
|
||||||
public Map<Long, WikiPage> find(long userId, List<String> keys, boolean fulltext) {
|
public Map<Long, WikiPage> find(long userId, List<String> keys, boolean fulltext) {
|
||||||
try {
|
try {
|
||||||
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID,TABLE_PAGES_USERS,PAGE_ID).where(USER_ID,equal(userId));
|
|
||||||
for (var key : keys) query.where(TITLE,like("%"+key+"%"));
|
|
||||||
var rs = query.exec(db);
|
|
||||||
var map = new HashMap<Long,WikiPage>();
|
var map = new HashMap<Long,WikiPage>();
|
||||||
while (rs.next()) {
|
{
|
||||||
var page = WikiPage.of(rs);
|
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID, TABLE_PAGES_USERS, PAGE_ID).where(USER_ID, equal(userId));
|
||||||
map.put(page.id(),page);
|
for (var key : keys) query.where(TITLE, like("%" + key.replaceAll("[ÄäÖöÜüß]", "%") + "%"));
|
||||||
|
var rs = query.exec(db);
|
||||||
|
while (rs.next()) {
|
||||||
|
var page = WikiPage.of(rs);
|
||||||
|
map.put(page.id(), page);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
}
|
||||||
|
if (fulltext) {
|
||||||
|
var query = select(ALL).from(TABLE_PAGES).leftJoin(ID, TABLE_PAGES_USERS, PAGE_ID).where(USER_ID, equal(userId));
|
||||||
|
for (var key : keys) query.where(CONTENT, like("%" + key.replaceAll("[ÄäÖöÜüß]", "%") + "%"));
|
||||||
|
var rs = query.exec(db);
|
||||||
|
while (rs.next()) {
|
||||||
|
var page = WikiPage.of(rs);
|
||||||
|
map.put(page.id(), page);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
}
|
}
|
||||||
rs.close();
|
|
||||||
return map;
|
return map;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw failedToSearchDb(t(WIKI_PAGES)).causedBy(e);
|
throw failedToSearchDb(t(WIKI_PAGES)).causedBy(e);
|
||||||
|
|||||||
Reference in New Issue
Block a user