diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8fecc9a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "nim/htmlutils"] + path = nim/htmlutils + url = ssh://git@nuc/mnt/git/htmlutils diff --git a/nim/build.nims b/nim/build.nims new file mode 100755 index 0000000..c957746 --- /dev/null +++ b/nim/build.nims @@ -0,0 +1,5 @@ +#!/usr/bin/env nim + +task build, "builds an example": + setCommand "js" + #os.copyfile("") diff --git a/nim/build.sh b/nim/build.sh new file mode 100755 index 0000000..84abd31 --- /dev/null +++ b/nim/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +nim js jpacrepo.nim +cp nimcache/jpacrepo.js static \ No newline at end of file diff --git a/nim/htmlutils b/nim/htmlutils new file mode 160000 index 0000000..3736513 --- /dev/null +++ b/nim/htmlutils @@ -0,0 +1 @@ +Subproject commit 37365137d3cc3ae7fd54b2f480b49ed4cbe73a39 diff --git a/nim/jpacrepo.nim b/nim/jpacrepo.nim new file mode 100644 index 0000000..cd2c765 --- /dev/null +++ b/nim/jpacrepo.nim @@ -0,0 +1,269 @@ +import dom +import htmlutils.utils +import json +import streams +import tables +import strutils +import sets +from sequtils import map, apply + +var pkgMap : OrderedTable[string, JsonNode] + +type DownloadPanel = ref object + badge : Node + listgroup : Node + pkgs : HashSet[string] + body : Node + +proc newDownloadPanel(parent : Node) : DownloadPanel = + let dp = DownloadPanel() + dp.pkgs = initSet[string]() + htmlDocument(document, parent): + "div": + class ["panel", "panel-default"] + "div": + class ["panel-heading"] + "h3": + class ["panel-title"] + "a": + #attrs {"data-toggle": "collapse", "href": "#dlist"} + text "Package list " + "span": + class ["badge", "pull-right"] + style {"display" :"none"} + cb: + dp.badge = node + + "ul": + class ["list-group"] + cb: + dp.listgroup = node + "div": + class ["panel-body"] + style { + "text-align" : "right", + "display" :"none" + } + "button": + class ["btn", "btn-primary"] + attrs {"type" : "button"} + "span": + class ["glyphicon", "glyphicon-download"] + cb: + node.appendChild(document.createTextNode(" Download")) + cb: + dp.body = node + dp + + +proc updateBadge(dp : DownloadPanel) = + let st = if dp.pkgs.len() > 0: "block" else: "none" + dp.body.style.display = st + dp.badge.style.display = st + dp.badge.textContent = $dp.pkgs.len() + +proc addPkg(dp : DownloadPanel, pkgfile : string) = + if not dp.pkgs.contains(pkgfile): + dp.pkgs.incl(pkgfile) + htmlDocument(document, dp.listgroup): + "li": + var listElement : Node + class ["list-group-item"] + text pkgfile + "span": + class ["glyphicon", "glyphicon-remove", "pull-right"] + cb: + let fn = proc(e : Event) = + dp.pkgs.excl(pkgfile) + listElement.remove() + dp.updateBadge + node.addEventListener("click", fn) + cb: + listElement = node + + dp.updateBadge + +proc createDropdown(parent : Node, data :seq[string], onchange : proc(value : string)) = + htmlDocument(document, parent): + "div": + var button : Node + class ["dropdown"] + "button": + class ["btn", "btn-default", "dropdown-toggle"] + attrs {"data-toggle": "dropdown", "type": "button"} + cb: + for line in data: + node.textContent = line + break + button = node + "ul": + class ["dropdown-menu"] + cb: + for line in data: + htmlDocument(document, node): + "li": + "a": + text line + cb: + let fn = proc(e: Event) = + button.textContent = node.textContent + onchange($node.textContent) + node.addEventListener("click", fn) + +type PkgTable = ref object + addButton : Node + +proc newPkgTable(parent: Node, searchString : string) : PkgTable = + var pkgtable = PkgTable() + var fragments = newSeq[string]() + for fragment in searchString.splitWhitespace(): + fragments.add(fragment) + var searchResult = newOrderedTable[string,JsonNode]() + for key, value in pkgMap: + for fragment in fragments: + if fragment in key: + searchResult.add(key,value) + for table in document.querySelectorAll("table.pkgtable"): + table.parentNode.removeChild(table) + htmlDocument(document, parent): + "table": + class ["table", "table-striped","pkgtable"] + "thead": + "tr": + "th": + "button": + class ["btn", "btn-default"] + attrs {"type": "button"} + "span": + class ["glyphicon", "glyphicon-plus"] + cb: + let txt = document.createTextNode(" Add") + node.appendChild(txt) + pkgtable.addButton = node + "th": + text "Name" + "th": + text "Version" + "th": + text "Arch" + "tbody": + cb: + var i = 0 + for name, versions in searchResult: + closureScope: + htmlDocument(document, node): + "tr": + var archCell : Node + "td": + "div": + class ["checkbox"] + "label": + "input": + attrs {"type" : "checkbox"} + "td": + text $name + "td": + cb: + var data = newSeq[string]() + for version, arches in versions: + data.add(version) + let vs = versions + let change_callback = proc(newValue : string) = + archCell.removeChildren() + var newdata = newSeq[string]() + for arch, pkgname in vs[newValue]: + newdata.add(arch) + createDropdown(archCell, newdata, proc(s : string) = discard) + createDropdown(node, data, change_callback) + "td": + cb: + archCell = node + var data = newSeq[string]() + var arches : JsonNode + for v, a in versions: + arches = a + for arch, pkgname in arches: + data.add(arch) + createDropdown(node, data, proc(s : string) = discard) + pkgtable + +var dp : DownloadPanel +# var pkgTable : PkgTable +htmlDocument document, document.body: + # "img": + # attrs {"src" : "img/background.bpg"} + # style { + # "width" : "100%", + # "position" : "fixed", + # "bottom" : "0", + # "left" : "0", + # "z-index" : "-10" + # } + "div": + var table : Node + style {"background-color" : "rgba(255, 255, 255, 0.25)"} + class ["container"] + "div": + style { + "margin-top" : "20px", + "background-color" : "rgba(224, 224, 224, 0.5)" + } + class ["jumbotron"] + "h1": + text "Jpacrepo" + "p": + text "Personal archlinux package repository" + "div": + "form": + class ["form-horizontal"] + "div": + class ["form-group"] + "label": + class ["control-label", "col-sm-2"] + text " Search package" + "div": + class ["col-sm-10"] + "input": + class ["form-control"] + attrs {"type" : "text"} + cb : + proc add2DownloadList(e : Event) = + let rows = table.querySelectorAll("tbody tr") + for row in rows: + let cbox = row.querySelector("td:first-child input") + if cbox.checked: + let pkgname = $row.querySelector("td:nth-child(2)").textContent + let version = $row.querySelector("td:nth-child(3) button").textContent + let arch = $row.querySelector("td:nth-child(4) button").textContent + dp.addPkg(pkgMap[pkgname][version][arch].getStr) + proc oninput(e : Event) = + if pkgMap.len == 0 or node.value.len < 2: return + let pkgtable = newPkgTable(table, $node.value) + pkgtable.addButton.addEventListener("click", add2DownloadList) + node.addEventListener("input", oninput) + "div": + class ["row"] + "div": + class ["col-sm-3"] + cb: + dp = newDownloadPanel(node) + "div": + class ["col-sm-9"] + cb: + table = node + + + + +let r = newXMLHTTPRequest() +let load_cb = proc(e : Event) = + let response = parseJson($r.responseText) + pkgMap = response.getFields() + # var keys = newSeq[string]() + # for key in response.getFields().keys(): + # keys.add(key) + # echo keys +r.addEventListener("load", load_cb) +r.open("get", "http://oggio88.soon.it/jpacrepo/rest/pkg/map") +r.setRequestHeader("Accept", "application/json") +r.send() diff --git a/nim/static/index.html b/nim/static/index.html new file mode 100644 index 0000000..c68ce6f --- /dev/null +++ b/nim/static/index.html @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/src/main/java/com/oggio88/jpacrepo/annotation/CORSManaged.java b/src/main/java/com/oggio88/jpacrepo/annotation/CORSManaged.java new file mode 100644 index 0000000..1802f05 --- /dev/null +++ b/src/main/java/com/oggio88/jpacrepo/annotation/CORSManaged.java @@ -0,0 +1,11 @@ +package com.oggio88.jpacrepo.annotation; + +import javax.ws.rs.NameBinding; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@NameBinding +@Retention(RetentionPolicy.RUNTIME) +public @interface CORSManaged +{ +} \ No newline at end of file diff --git a/src/main/java/com/oggio88/jpacrepo/context/ApplicationContext.java b/src/main/java/com/oggio88/jpacrepo/context/ApplicationContext.java index 702c158..578237c 100644 --- a/src/main/java/com/oggio88/jpacrepo/context/ApplicationContext.java +++ b/src/main/java/com/oggio88/jpacrepo/context/ApplicationContext.java @@ -33,6 +33,8 @@ public class ApplicationContext public boolean invalidateCache = true; + public boolean cors_enabled; + public ApplicationContext(String propertyFile) { systemProperties = new Properties(); @@ -65,6 +67,10 @@ public class ApplicationContext } } } + + String prop = System.getProperty("org.oggio88.jpacrepo.cors"); + if (prop != null) + cors_enabled = Boolean.parseBoolean(prop); } public Properties getSystemProperties() diff --git a/src/main/java/com/oggio88/jpacrepo/service/ApplicationConfig.java b/src/main/java/com/oggio88/jpacrepo/service/ApplicationConfig.java index a5c9998..abc7646 100644 --- a/src/main/java/com/oggio88/jpacrepo/service/ApplicationConfig.java +++ b/src/main/java/com/oggio88/jpacrepo/service/ApplicationConfig.java @@ -24,6 +24,7 @@ public class ApplicationConfig extends Application { HashSet