Compare commits
10 Commits
b14862fc7d
...
06e8949b8e
Author | SHA1 | Date | |
---|---|---|---|
06e8949b8e
|
|||
1890d1d7b5
|
|||
ca1b378a14
|
|||
f48a201406
|
|||
5e7f0b179b
|
|||
1a708699f5
|
|||
ec29250ca9
|
|||
ae0e4de23e
|
|||
b1a7bcc9a8
|
|||
b1297b3194
|
20
.gitea/workflows/build.yaml
Normal file
20
.gitea/workflows/build.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: woryzen
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Execute Gradle build
|
||||
env:
|
||||
PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }}
|
||||
run: ./gradlew build publish
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "nim/lib/htmlutils"]
|
||||
path = nim/lib/htmlutils
|
||||
url = git.woggioni.net:htmlutils
|
||||
|
5
Jenkinsfile
vendored
5
Jenkinsfile
vendored
@@ -19,5 +19,10 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
cleanWs deleteDirs: true, skipWhenFailed: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
22
build.gradle
22
build.gradle
@@ -37,7 +37,7 @@ allprojects {
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://woggioni.net/mvn/'
|
||||
url = getProperty('gitea.maven.url')
|
||||
content {
|
||||
includeGroup 'net.woggioni'
|
||||
includeGroup 'com.lys'
|
||||
@@ -119,18 +119,17 @@ dependencies {
|
||||
|
||||
Provider<War> warTaskProvider = tasks.named('war', War) { War it ->
|
||||
from(configurations.frontend)
|
||||
archiveVersion = ''
|
||||
}
|
||||
|
||||
tasks.named('deploy2Wildfly', Deploy2WildflyTask) { d2w ->
|
||||
d2w.rpcPort = 9990
|
||||
d2w.rpcUsername = 'woggioni'
|
||||
// d2w.rpcUsername = 'woggioni'
|
||||
// d2w.rpcUsername = 'admin'
|
||||
d2w.rpcPassword = '123456'
|
||||
// d2w.rpcPassword = '123456'
|
||||
d2w.deploymentName = 'jpacrepo.war'
|
||||
}
|
||||
|
||||
|
||||
File warPath = tasks.named("war", War).get().archiveFile.get().getAsFile()
|
||||
tasks.named("test", Test) {
|
||||
jvmArgs = [
|
||||
'--add-opens', 'java.naming/javax.naming.spi=ALL-UNNAMED',
|
||||
@@ -142,9 +141,20 @@ tasks.named("test", Test) {
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://mvn.woggioni.net/'
|
||||
name = "Gitea"
|
||||
url = uri(getProperty('gitea.maven.url'))
|
||||
|
||||
credentials(HttpHeaderCredentials) {
|
||||
name = "Authorization"
|
||||
value = "token ${System.getenv()["PUBLISHER_TOKEN"]}"
|
||||
}
|
||||
|
||||
authentication {
|
||||
header(HttpHeaderAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
|
@@ -1,3 +1,8 @@
|
||||
jpacrepo.version=2024.02.12
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
|
||||
lys.version=2024.02.12
|
||||
jpacrepo.version=2024.04.14
|
||||
|
||||
lys.version=2024.04.14
|
||||
|
||||
gitea.maven.url=https://gitea.woggioni.net/api/packages/woggioni/maven
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
gradlew
vendored
14
gradlew
vendored
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
alias(catalog.plugins.envelope)
|
||||
alias(catalog.plugins.sambal)
|
||||
}
|
||||
|
||||
import net.woggioni.gradle.envelope.EnvelopeJarTask
|
||||
@@ -10,6 +11,7 @@ dependencies {
|
||||
implementation project(':jpacrepo-api')
|
||||
implementation catalog.slf4j.api
|
||||
implementation catalog.log4j.slf4j.impl
|
||||
implementation catalog.picocli
|
||||
|
||||
runtimeOnly catalog.jboss.ejb.client
|
||||
|
||||
@@ -20,21 +22,21 @@ java {
|
||||
}
|
||||
|
||||
tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
||||
options.javaModuleMainClass = 'net.woggioni.jpacrepo.client.Main'
|
||||
options.javaModuleMainClass = 'net.woggioni.jpacrepo.client.JpacrepoCli'
|
||||
// options.compilerArgs += [
|
||||
// '--add-reads', 'javax.ejb=ALL-UNNAMED'
|
||||
// ]
|
||||
}
|
||||
|
||||
Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named('envelopeJar', EnvelopeJarTask.class) {
|
||||
mainClass = 'net.woggioni.jpacrepo.client.Main'
|
||||
mainClass = 'net.woggioni.jpacrepo.client.JpacrepoCli'
|
||||
}
|
||||
|
||||
tasks.register('run', JavaExec) { JavaExec t ->
|
||||
classpath(project.sourceSets.main.output.dirs)
|
||||
classpath(project.sourceSets.main.runtimeClasspath)
|
||||
mainModule = 'net.woggioni.jpacrepo.client'
|
||||
mainClass = 'net.woggioni.jpacrepo.client.Main'
|
||||
mainClass = 'net.woggioni.jpacrepo.client.JpacrepoCli'
|
||||
// String modulePath = project.sourceSets.main.runtimeClasspath.files.stream()
|
||||
// .map(File::toString)
|
||||
// .collect(Collectors.joining(System.getProperty('path.separator')))
|
||||
|
@@ -5,4 +5,5 @@ module net.woggioni.jpacrepo.client {
|
||||
requires org.slf4j;
|
||||
requires org.apache.logging.log4j;
|
||||
requires net.woggioni.jpacrepo.api;
|
||||
requires info.picocli;
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package net.woggioni.jpacrepo.client;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
|
||||
abstract class AbstractVersionProvider implements CommandLine.IVersionProvider {
|
||||
private final String version;
|
||||
private final String vcsHash;
|
||||
|
||||
@SneakyThrows
|
||||
protected AbstractVersionProvider(String specificationTitle) {
|
||||
String version = null;
|
||||
String vcsHash = null;
|
||||
Enumeration<URL> it = getClass().getClassLoader().getResources(JarFile.MANIFEST_NAME);
|
||||
while (it.hasMoreElements()) {
|
||||
URL manifestURL = it.nextElement();
|
||||
Manifest mf = new Manifest();
|
||||
try (InputStream is = manifestURL.openStream()) {
|
||||
mf.read(is);
|
||||
}
|
||||
Attributes mainAttributes = mf.getMainAttributes();
|
||||
if (Objects.equals(specificationTitle, mainAttributes.getValue(Attributes.Name.SPECIFICATION_TITLE))) {
|
||||
version = mainAttributes.getValue(Attributes.Name.SPECIFICATION_VERSION);
|
||||
vcsHash = mainAttributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
}
|
||||
}
|
||||
if (version == null || vcsHash == null) {
|
||||
throw new RuntimeException("Version information not found in manifest");
|
||||
}
|
||||
this.version = version;
|
||||
this.vcsHash = vcsHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getVersion() {
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
return new String[] { version, vcsHash };
|
||||
} else {
|
||||
return new String[] { version };
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
package net.woggioni.jpacrepo.client;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.woggioni.jpacrepo.api.service.FileSystemSynchronizer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import picocli.CommandLine;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NameClassPair;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import java.util.Properties;
|
||||
|
||||
class VersionProvider extends AbstractVersionProvider {
|
||||
protected VersionProvider() {
|
||||
super(("jpacrepo-client"));
|
||||
}
|
||||
}
|
||||
|
||||
@Slf4j
|
||||
@CommandLine.Command(
|
||||
name = "jpacrepo-cli",
|
||||
versionProvider = VersionProvider.class)
|
||||
public class JpacrepoCli implements Runnable {
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-ssl", "--tls"},
|
||||
description = "Connect using SSL/TLS"
|
||||
)
|
||||
private boolean ssl = false;
|
||||
@CommandLine.Option(
|
||||
names = {"-H", "--host"},
|
||||
description = "Wildfly server hostname"
|
||||
)
|
||||
private String host = "localhost";
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"-P", "--port"},
|
||||
description = "Wildfly server RPC port"
|
||||
)
|
||||
private int port = 8080;
|
||||
|
||||
@CommandLine.Option(
|
||||
required = true,
|
||||
names = {"-u", "--user"},
|
||||
description = "Wildfly RPC username"
|
||||
)
|
||||
private String rpcUsername;
|
||||
|
||||
@CommandLine.Option(
|
||||
required = true,
|
||||
names = {"-p", "--password"},
|
||||
description = "Wildfly RPC password"
|
||||
)
|
||||
private String rpcPassword;
|
||||
|
||||
private static void traverseJndiNode(String nodeName, Context context) {
|
||||
try {
|
||||
NamingEnumeration<NameClassPair> list = context.list(nodeName);
|
||||
while (list.hasMore()) {
|
||||
String childName = nodeName + "" + list.next().getName();
|
||||
System.out.println(childName);
|
||||
traverseJndiNode(childName, context);
|
||||
}
|
||||
} catch (NamingException ex) {
|
||||
// We reached a leaf
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private Context createContext() {
|
||||
Properties prop = new Properties();
|
||||
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
|
||||
|
||||
prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
|
||||
if(ssl) {
|
||||
prop.put(Context.PROVIDER_URL, String.format("remote+https://%s:%d", host, port));
|
||||
} else {
|
||||
prop.put(Context.PROVIDER_URL, String.format("http-remoting://%s:%d", host, port));
|
||||
}
|
||||
prop.put(Context.SECURITY_PRINCIPAL, rpcUsername);
|
||||
prop.put(Context.SECURITY_CREDENTIALS, rpcPassword);
|
||||
|
||||
prop.put("jboss.naming.client.ejb.context", true);
|
||||
return new InitialContext(prop);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void run() {
|
||||
Context ctx = createContext();
|
||||
// traverseJndiNode("/", context);
|
||||
final FileSystemSynchronizer service = (FileSystemSynchronizer) ctx.lookup(
|
||||
"/jpacrepo/PackageSynchronizerEJB!net.woggioni.jpacrepo.api.service.FileSystemSynchronizer"
|
||||
);
|
||||
service.syncDb();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Logger log = LoggerFactory.getLogger("jpacrepo-cli");
|
||||
CommandLine commandLine = new CommandLine(new JpacrepoCli());
|
||||
commandLine.setExecutionExceptionHandler((ex, cl, parseResult) -> {
|
||||
log.error(ex.getLocalizedMessage(), ex);
|
||||
return CommandLine.ExitCode.SOFTWARE;
|
||||
});
|
||||
System.exit(commandLine.execute(args));
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package net.woggioni.jpacrepo.client;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.woggioni.jpacrepo.api.service.PacmanServiceRemote;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NameClassPair;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
public class Main {
|
||||
|
||||
private static void traverseJndiNode(String nodeName, Context context) {
|
||||
try {
|
||||
NamingEnumeration<NameClassPair> list = context.list(nodeName);
|
||||
while (list.hasMore()) {
|
||||
String childName = nodeName + "" + list.next().getName();
|
||||
System.out.println(childName);
|
||||
traverseJndiNode(childName, context);
|
||||
}
|
||||
} catch (NamingException ex) {
|
||||
// We reached a leaf
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void main(String[] args) {
|
||||
Properties prop = new Properties();
|
||||
// InputStream in = Main.class.getClassLoader().getResourceAsStream("jboss-ejb-client.properties");
|
||||
// prop.load(in);
|
||||
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
|
||||
|
||||
prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
|
||||
prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
|
||||
// prop.put(Context.PROVIDER_URL, "http-remoting://nuc:8080");
|
||||
// prop.put(Context.PROVIDER_URL, "remote://odroid-u3:4447");
|
||||
prop.put(Context.SECURITY_PRINCIPAL, "walter");
|
||||
prop.put(Context.SECURITY_CREDENTIALS, "27ff5990757d1d");
|
||||
// prop.put(Context.SECURITY_PRINCIPAL, "luser");
|
||||
// prop.put(Context.SECURITY_CREDENTIALS, "123456");
|
||||
|
||||
prop.put("jboss.naming.client.ejb.context", true);
|
||||
Context context = new InitialContext(prop);
|
||||
Context ctx = new InitialContext(prop);
|
||||
traverseJndiNode("/", context);
|
||||
// final PacmanService stateService = (PacmanService) ctx.lookup("/jpacrepo-1.0/remote/PacmanServiceEJB!service.PacmanService");
|
||||
final PacmanServiceRemote service = (PacmanServiceRemote) ctx.lookup(
|
||||
"/jpacrepo-2.0-SNAPSHOT/PacmanServiceEJB!net.woggioni.jpacrepo.api.service.PacmanServiceRemote"
|
||||
);
|
||||
// List<PkgData> pkgs = service.searchPackage("google-earth", null, null, 1, 10);
|
||||
// System.out.println(new XStream().toXML(pkgs));
|
||||
service.syncDB();
|
||||
}
|
||||
}
|
@@ -1,11 +0,0 @@
|
||||
package net.woggioni.jpacrepo.experimental;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class JPQLTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
alias(catalog.plugins.kotlin.multiplatform)
|
||||
id "org.jetbrains.kotlin.plugin.serialization" version "1.9.22"
|
||||
alias(catalog.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,16 @@ kotlin {
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation catalog.klevtree
|
||||
implementation catalog.khtml
|
||||
implementation group: 'org.jetbrains.kotlinx', name: 'kotlinx-serialization-json', version: '1.6.2'
|
||||
// implementation(npm("bootstrap", "5.3.2"))
|
||||
// implementation(npm("bootstrap-icons", "1.11.3"))
|
||||
// implementation(npm("style-loader", "3.3.4"))
|
||||
// implementation(npm("css-loader", "6.10.0"))
|
||||
|
||||
implementation(npm("bootstrap", "5.3.2"))
|
||||
implementation(npm("bootstrap-icons", "1.11.3"))
|
||||
implementation(npm("style-loader", "3.3.4"))
|
||||
implementation(npm("css-loader", "6.10.0"))
|
||||
implementation(npm("postcss-loader", "8.1.0"))
|
||||
implementation(npm("sass", "1.71.1"))
|
||||
implementation(npm("sass-loader", "14.1.1"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +35,10 @@ kotlin {
|
||||
browser {
|
||||
runTask {
|
||||
sourceMaps = true
|
||||
devServer.port = 8080
|
||||
devServer.port = 5080
|
||||
cssSupport {
|
||||
enabled.set(false)
|
||||
}
|
||||
}
|
||||
webpackTask {
|
||||
sourceMaps = false
|
||||
|
@@ -1,182 +0,0 @@
|
||||
package net.woggioni.jpacrepo
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.startCoroutine
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.js.Promise
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.events.Event
|
||||
|
||||
object HtmlUtils {
|
||||
fun Element.removeChildren() {
|
||||
while (true) {
|
||||
removeChild(firstChild ?: break)
|
||||
}
|
||||
}
|
||||
|
||||
fun Element.on(eventName : String, eventListener : (Event) -> Unit) {
|
||||
addEventListener(eventName, eventListener)
|
||||
}
|
||||
|
||||
fun launch(block: suspend () -> Unit) {
|
||||
block.startCoroutine(object : Continuation<Unit> {
|
||||
override val context: CoroutineContext get() = EmptyCoroutineContext
|
||||
override fun resumeWith(result: Result<Unit>) {}
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun <T> Promise<T>.await(): T = suspendCoroutine { cont ->
|
||||
then({ cont.resume(it) }, { cont.resumeWithException(it) })
|
||||
}
|
||||
}
|
||||
|
||||
class HtmlBuilder private constructor(private val doc : Document, val el: Element) {
|
||||
|
||||
companion object {
|
||||
fun <T> of(doc : Document, el: Element, cb : HtmlBuilder.(el : Element) -> T) : T {
|
||||
return HtmlBuilder(doc, el).cb(el)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> dfd(
|
||||
name : String,
|
||||
attrs : Map<String, String>,
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T {
|
||||
val child = doc.createElement(name)
|
||||
for((key, value) in attrs) {
|
||||
child.setAttribute(key, value)
|
||||
}
|
||||
el.appendChild(child)
|
||||
return HtmlBuilder(doc, child).cb(child)
|
||||
}
|
||||
|
||||
fun <T> html(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("html", attrs, cb)
|
||||
fun <T> head(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("head", attrs, cb)
|
||||
fun <T> body(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("body", attrs, cb)
|
||||
|
||||
fun <T> use(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("use", attrs, cb)
|
||||
fun <T> svg(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("svg", attrs, cb)
|
||||
fun <T> div(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("div", attrs, cb)
|
||||
fun <T> header(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("header", attrs, cb)
|
||||
fun <T> main(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("main", attrs, cb)
|
||||
fun <T> footer(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("footer", attrs, cb)
|
||||
fun <T> a(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("a", attrs, cb)
|
||||
fun <T> meta(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("meta", attrs, cb)
|
||||
fun <T> script(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("script", attrs, cb)
|
||||
fun <T> link(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("link", attrs, cb)
|
||||
fun <T> title(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("title", attrs, cb)
|
||||
fun <T> p(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("p", attrs, cb)
|
||||
fun <T> span(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("span", attrs, cb)
|
||||
fun <T> i(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("i", attrs, cb)
|
||||
fun <T> del(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("del", attrs, cb)
|
||||
fun <T> s(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("s", attrs, cb)
|
||||
fun <T> ins(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ins", attrs, cb)
|
||||
fun <T> u(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("u", attrs, cb)
|
||||
fun <T> b(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("b", attrs, cb)
|
||||
fun <T> small(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("small", attrs, cb)
|
||||
fun <T> strong(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("strong", attrs, cb)
|
||||
fun <T> em(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("em", attrs, cb)
|
||||
fun <T> mark(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("mark", attrs, cb)
|
||||
fun <T> obj(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("object", attrs, cb)
|
||||
fun <T> h1(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h1", attrs, cb)
|
||||
fun <T> h2(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h2", attrs, cb)
|
||||
fun <T> h3(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h3", attrs, cb)
|
||||
fun <T> h4(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h4", attrs, cb)
|
||||
fun <T> h5(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h5", attrs, cb)
|
||||
fun <T> h6(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("h6", attrs, cb)
|
||||
fun <T> table(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("table", attrs, cb)
|
||||
fun <T> thead(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("thead", attrs, cb)
|
||||
fun <T> tbody(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tbody", attrs, cb)
|
||||
fun <T> tfoot(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tfoot", attrs, cb)
|
||||
fun <T> tr(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("tr", attrs, cb)
|
||||
fun <T> th(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("th", attrs, cb)
|
||||
fun <T> td(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("td", attrs, cb)
|
||||
fun <T> ol(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ol", attrs, cb)
|
||||
fun <T> ul(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("ul", attrs, cb)
|
||||
fun <T> li(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("li", attrs, cb)
|
||||
fun <T> img(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("img", attrs, cb)
|
||||
fun <T> form(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("form", attrs, cb)
|
||||
fun <T> label(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("label", attrs, cb)
|
||||
fun <T> button(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("button", attrs, cb)
|
||||
fun <T> input(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("input", attrs, cb)
|
||||
fun <T> select(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("select", attrs, cb)
|
||||
fun <T> option(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("option", attrs, cb)
|
||||
fun <T> meter(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("meter", attrs, cb)
|
||||
fun <T> nav(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("nav", attrs, cb)
|
||||
fun <T> menu(attrs : Map<String, String> = emptyMap(),
|
||||
cb : HtmlBuilder.(el : Element) -> T) : T = dfd("menu", attrs, cb)
|
||||
|
||||
|
||||
fun classes(vararg classes : String) {
|
||||
for(cls in classes) el.classList.add(cls)
|
||||
}
|
||||
|
||||
fun attr(key: String, value : String) {
|
||||
el.setAttribute(key, value)
|
||||
}
|
||||
|
||||
fun text(txt : String) {
|
||||
el.appendChild(doc.createTextNode(txt))
|
||||
}
|
||||
|
||||
fun on(eventName : String, cb: (Event) -> Unit) {
|
||||
el.addEventListener(eventName, cb)
|
||||
}
|
||||
}
|
@@ -6,11 +6,12 @@ import kotlinx.browser.document
|
||||
import kotlinx.browser.window
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.woggioni.jpacrepo.HtmlUtils.await
|
||||
import net.woggioni.jpacrepo.HtmlUtils.launch
|
||||
import net.woggioni.jpacrepo.HtmlUtils.on
|
||||
import net.woggioni.jpacrepo.HtmlUtils.removeChildren
|
||||
import net.woggioni.jpacrepo.common.JpacrepoCommons.toHexString
|
||||
import net.woggioni.khtml.Khtml
|
||||
import net.woggioni.khtml.Khtml.Companion.await
|
||||
import net.woggioni.khtml.Khtml.Companion.launch
|
||||
import net.woggioni.khtml.Khtml.Companion.on
|
||||
import net.woggioni.khtml.Khtml.Companion.removeChildren
|
||||
import net.woggioni.klevtree.LevTrie
|
||||
import org.w3c.dom.CustomEvent
|
||||
import org.w3c.dom.Element
|
||||
@@ -132,7 +133,7 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
|
||||
while (children.length > 0) {
|
||||
children[0]!!.remove()
|
||||
}
|
||||
HtmlBuilder.of(document, root) {
|
||||
Khtml.of(document, root) {
|
||||
table {
|
||||
classes("table", "table-striped", "pkgtable")
|
||||
thead {
|
||||
@@ -209,9 +210,9 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
|
||||
?.let { it[selectedVersion] }
|
||||
availablePackages?.keys?.first()
|
||||
}
|
||||
var selectedCompressionFormat = getFirstAvailableCompressionFormat()
|
||||
var selectedCompressionFormat: CompressionFormat?
|
||||
|
||||
fun HtmlBuilder.createCompressionFormatDropdown() {
|
||||
fun Khtml.createCompressionFormatDropdown() {
|
||||
val availablePackages = packages
|
||||
?.let { it[selectedVersion] }
|
||||
availablePackages?.keys?.let { compressionFormats ->
|
||||
@@ -236,24 +237,21 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
|
||||
|
||||
val compressionFormatCell = td {
|
||||
createCompressionFormatDropdown()
|
||||
el
|
||||
}
|
||||
val fileNameCell = td {
|
||||
selectedPackage?.let {
|
||||
text(it.second.fileName)
|
||||
}
|
||||
el
|
||||
}
|
||||
val sizeCell = td {
|
||||
selectedPackage?.let {
|
||||
text(it.second.size.toHumanReadableByteSize())
|
||||
}
|
||||
el
|
||||
}
|
||||
on(REFRESH_TABLE_ROW_EVENT_NAME) { evt ->
|
||||
on(REFRESH_TABLE_ROW_EVENT_NAME) {
|
||||
selectedCompressionFormat = getFirstAvailableCompressionFormat()
|
||||
compressionFormatCell.removeChildren()
|
||||
HtmlBuilder.of(document, compressionFormatCell) {
|
||||
Khtml.of(document, compressionFormatCell) {
|
||||
createCompressionFormatDropdown()
|
||||
}
|
||||
selectedPackage = archive.pkgMap[arch]?.let {
|
||||
@@ -278,9 +276,9 @@ suspend fun buildTable(root: Element, searchTerm: String, commonElement: Element
|
||||
}
|
||||
}
|
||||
|
||||
private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
|
||||
val root = el
|
||||
private fun Khtml.buildPackageSearchSection(commonElement: Element) {
|
||||
var selectedPackageName: String? = null
|
||||
val root = element
|
||||
div {
|
||||
classes("row", "g-5")
|
||||
div {
|
||||
@@ -354,7 +352,7 @@ private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
|
||||
}
|
||||
div { table ->
|
||||
classes("row", "align-items-md-stretch")
|
||||
root.on(REFRESH_TABLE_EVENT_NAME) { evt ->
|
||||
root.on(REFRESH_TABLE_EVENT_NAME) { evt : Event ->
|
||||
launch {
|
||||
(evt as? CustomEvent)?.let { pnuv ->
|
||||
table.let { t ->
|
||||
@@ -370,7 +368,8 @@ private fun HtmlBuilder.buildPackageSearchSection(commonElement: Element) {
|
||||
}
|
||||
|
||||
fun main(vararg args: String) {
|
||||
HtmlBuilder.of(document, document.body as HTMLElement) {
|
||||
js("require ('./scss/styles.scss');")
|
||||
Khtml.of(document, document.body as HTMLElement) {
|
||||
main { main ->
|
||||
div {
|
||||
classes("container", "py-4")
|
||||
@@ -391,7 +390,7 @@ fun main(vararg args: String) {
|
||||
div {
|
||||
classes("container")
|
||||
div {
|
||||
val commonElement = el
|
||||
val commonElement = element
|
||||
classes("row", "align-items-start")
|
||||
div {
|
||||
classes("col-3")
|
||||
@@ -404,7 +403,7 @@ fun main(vararg args: String) {
|
||||
text("Package Cart")
|
||||
}
|
||||
}
|
||||
fun HtmlBuilder.createSelectedPackageList() {
|
||||
fun Khtml.createSelectedPackageList() {
|
||||
if (selectedPackages.isNotEmpty()) {
|
||||
div {
|
||||
classes("card-body")
|
||||
@@ -432,7 +431,7 @@ fun main(vararg args: String) {
|
||||
}
|
||||
button {
|
||||
classes("btn", "btn-primary", "w-100")
|
||||
el.asDynamic().style.marginTop = "10px"
|
||||
element.asDynamic().style.marginTop = "10px"
|
||||
val totalSize = selectedPackages
|
||||
.asSequence()
|
||||
.map(PkgTuple::size)
|
||||
@@ -452,9 +451,9 @@ fun main(vararg args: String) {
|
||||
.map(PkgTuple::fileName)
|
||||
.joinToString(" ")
|
||||
temporaryForm.appendChild(textField)
|
||||
el.appendChild(temporaryForm)
|
||||
element.appendChild(temporaryForm)
|
||||
temporaryForm.submit()
|
||||
el.removeChild(temporaryForm)
|
||||
element.removeChild(temporaryForm)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +462,7 @@ fun main(vararg args: String) {
|
||||
div {
|
||||
createSelectedPackageList()
|
||||
commonElement.on(REFRESH_SELECTED_PACKAGES_EVENT_NAME) {
|
||||
el.removeChildren()
|
||||
element.removeChildren()
|
||||
createSelectedPackageList()
|
||||
}
|
||||
}
|
||||
@@ -473,7 +472,6 @@ fun main(vararg args: String) {
|
||||
classes("col-9")
|
||||
buildPackageSearchSection(commonElement)
|
||||
}
|
||||
el
|
||||
}
|
||||
}
|
||||
footer {
|
||||
|
@@ -4,15 +4,7 @@
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="stylesheet" href="css/jpacrepo.css"/>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
|
||||
crossorigin="anonymous"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="jpacrepo-frontend.js" defer="true"></script>
|
||||
<title>Hello World!</title>
|
||||
<title>Jpacrepo</title>
|
||||
</head>
|
||||
</html>
|
2
jpacrepo-frontend/src/jsMain/resources/scss/styles.scss
Normal file
2
jpacrepo-frontend/src/jsMain/resources/scss/styles.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "bootstrap/scss/bootstrap";
|
||||
@import "bootstrap-icons/font/bootstrap-icons.min.css";
|
3
jpacrepo-frontend/webpack.config.d/bootstrap.js
vendored
Normal file
3
jpacrepo-frontend/webpack.config.d/bootstrap.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
config.module.rules.push({test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, type: 'asset'});
|
||||
config.module.rules.push({test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, type: 'asset'});
|
||||
config.module.rules.push({test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource'});
|
1
jpacrepo-frontend/webpack.config.d/css.js
Normal file
1
jpacrepo-frontend/webpack.config.d/css.js
Normal file
@@ -0,0 +1 @@
|
||||
config.module.rules.push({ test: /\.css$/, use: ["style-loader", { loader: "css-loader", options: {sourceMap: false} } ] });
|
2
jpacrepo-frontend/webpack.config.d/sass.js
Normal file
2
jpacrepo-frontend/webpack.config.d/sass.js
Normal file
@@ -0,0 +1,2 @@
|
||||
config.module.rules.push({ test: /\.s[ac]ss$/, use: ["style-loader", { loader: "css-loader", options: {sourceMap: false} }, "sass-loader" ] });
|
||||
|
@@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env nim
|
||||
|
||||
task build, "builds an example":
|
||||
setCommand "js"
|
||||
#os.copyfile("")
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
nim js src/jpacrepo.nim
|
||||
cp nimcache/jpacrepo.js static
|
Submodule nim/lib/htmlutils deleted from 5ee04565fa
@@ -1 +0,0 @@
|
||||
path="lib/htmlutils/src"
|
@@ -1,375 +0,0 @@
|
||||
import dom
|
||||
import htmlutils/tree
|
||||
import htmlutils/utils
|
||||
import json
|
||||
import tables
|
||||
import strutils
|
||||
import sets
|
||||
from sequtils import map, apply
|
||||
|
||||
var pkgMap : JsonNode
|
||||
|
||||
const serverURL {.strdefine.}: string = ""
|
||||
|
||||
proc last[T](s : seq[T]) : T = s[s.len - 1]
|
||||
proc formatByteSize(size : BiggestInt) : string = size.float64.formatEng(precision=1, siPrefix=true, unit = "B")
|
||||
|
||||
type DownloadPanel = ref object
|
||||
badge : Element
|
||||
listgroup : Element
|
||||
pkgs : HashSet[string]
|
||||
footer : Element
|
||||
sizeLabel : Element
|
||||
size : BiggestInt
|
||||
|
||||
proc newDownloadPanel(parent : Element) : DownloadPanel =
|
||||
let dp = DownloadPanel()
|
||||
dp.pkgs = initHashSet[string]()
|
||||
htmlTreeAppend(parent):
|
||||
"div":
|
||||
classList = ["panel", "panel-default"]
|
||||
"div":
|
||||
classList = ["panel-heading"]
|
||||
"h3":
|
||||
classList = ["panel-title"]
|
||||
"a":
|
||||
text = "Package list "
|
||||
"span":
|
||||
classList = ["badge", "pull-right"]
|
||||
style {"display" :"none"}
|
||||
cb:
|
||||
dp.badge = elem
|
||||
|
||||
"ul":
|
||||
classList = ["list-group"]
|
||||
cb:
|
||||
dp.listgroup = elem
|
||||
"div":
|
||||
classList = ["panel-footer"]
|
||||
style = {
|
||||
"text-align" : "right",
|
||||
"display" :"none"
|
||||
}
|
||||
"span":
|
||||
style = {
|
||||
"font-weight" : "bold",
|
||||
"padding-right" : "10px"
|
||||
}
|
||||
cb:
|
||||
dp.sizeLabel = elem
|
||||
"button":
|
||||
classList = ["btn", "btn-primary"]
|
||||
attrs = {"type" : "button"}
|
||||
"span":
|
||||
classList = ["glyphicon", "glyphicon-download"]
|
||||
cb:
|
||||
elem.appendChild(document.createTextNode(" Download"))
|
||||
let clickHandler = proc(e : Event) =
|
||||
let pkglist : seq[string] = sequtils.toSeq(dp.pkgs.items())
|
||||
let form = cast[Formelement](document.createElement("form"))
|
||||
form.style.display = "none"
|
||||
form.setAttribute("method", "post")
|
||||
form.setAttribute("action", serverURL & "api/pkg/downloadTar")
|
||||
let tf= document.createElement("input")
|
||||
tf.setAttribute("name", "pkgs")
|
||||
let txt = sequtils.foldl(pkglist, a & " " & b)
|
||||
echo txt
|
||||
tf.value(txt)
|
||||
form.appendChild(tf)
|
||||
document.body.appendChild(form)
|
||||
form.submit()
|
||||
elem.addEventListener("click", clickHandler)
|
||||
cb:
|
||||
dp.footer = elem
|
||||
dp
|
||||
|
||||
|
||||
proc updateBadge(dp : DownloadPanel) =
|
||||
let st = if dp.pkgs.len() > 0: "block" else: "none"
|
||||
dp.footer.style.display = st
|
||||
dp.badge.style.display = st
|
||||
dp.badge.textContent = $dp.pkgs.len()
|
||||
|
||||
proc updateSize(dp : DownloadPanel) =
|
||||
dp.sizeLabel.textContent = "Total: " & dp.size.formatByteSize
|
||||
|
||||
proc addPkg(dp : DownloadPanel, pkgfile : string) =
|
||||
if not dp.pkgs.contains(pkgfile):
|
||||
dp.pkgs.incl(pkgfile)
|
||||
let req = newXMLHTTPRequest()
|
||||
let load_cb = proc(e : Event) =
|
||||
let sz = parseInt($req.responseText)
|
||||
dp.size += sz
|
||||
dp.updateSize
|
||||
htmlTreeAppend(dp.listgroup):
|
||||
"li":
|
||||
var listElement : Element
|
||||
classList = ["list-group-item"]
|
||||
text = pkgfile
|
||||
"span":
|
||||
classList = ["glyphicon", "glyphicon-remove", "pull-right"]
|
||||
cb:
|
||||
let fn = proc(e : Event) =
|
||||
dp.pkgs.excl(pkgfile)
|
||||
listElement.remove()
|
||||
dp.updateBadge
|
||||
dp.size -= sz
|
||||
dp.updateSize
|
||||
elem.addEventListener("click", fn)
|
||||
cb:
|
||||
listElement = elem
|
||||
req.addEventListener("load", load_cb)
|
||||
req.open("get", serverURL & "api/pkg/filesize/" & pkgfile)
|
||||
req.setRequestHeader("Accept", "application/json")
|
||||
req.send()
|
||||
dp.updateBadge
|
||||
|
||||
proc readTableRow(arch : string, row : Element) : JsonNode =
|
||||
let pkgname = $row.querySelector("td:nth-child(2)").textContent
|
||||
let version = $row.querySelector("td:nth-child(3) button").textContent
|
||||
let filename = $row.querySelector("td:nth-child(4) button").textContent
|
||||
for candidate in pkgMap[arch][pkgname][version]:
|
||||
if filename == candidate["fileName"].getStr:
|
||||
return candidate
|
||||
|
||||
proc createDropdown(parent : Element, data :seq[string], onchange : proc(value : string)) =
|
||||
|
||||
htmlTreeAppend(parent):
|
||||
"span":
|
||||
var button : Element
|
||||
classList = ["dropdown"]
|
||||
"button":
|
||||
classList = ["btn", "btn-default", "dropdown-toggle"]
|
||||
attrs = {"data-toggle": "dropdown", "type": "button"}
|
||||
cb:
|
||||
elem.textContent = data[0]
|
||||
button = elem
|
||||
"ul":
|
||||
classList = ["dropdown-menu"]
|
||||
cb:
|
||||
for line in data:
|
||||
htmlTreeAppend(elem):
|
||||
"li":
|
||||
"a":
|
||||
text = line
|
||||
cb:
|
||||
let fn = proc(e: Event) =
|
||||
button.textContent = elem.textContent
|
||||
onchange($elem.textContent)
|
||||
elem.addEventListener("click", fn)
|
||||
|
||||
type PkgTable = ref object
|
||||
addButton : Element
|
||||
|
||||
proc newPkgTable(parent: Element, arch: string, searchString : string, addButtonCallback : proc(e : Event)) : 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[arch]:
|
||||
for fragment in fragments:
|
||||
if fragment in key:
|
||||
searchResult[key] = value
|
||||
for node in parent.querySelectorAll("table.pkgtable"):
|
||||
parent.removeChild(node)
|
||||
htmlTreeAppend(parent):
|
||||
"table":
|
||||
classList = ["table", "table-striped","pkgtable"]
|
||||
"thead":
|
||||
"tr":
|
||||
"th":
|
||||
"button":
|
||||
classList = ["btn", "btn-default"]
|
||||
attrs = {"type": "button"}
|
||||
"span":
|
||||
classList = ["glyphicon", "glyphicon-plus"]
|
||||
cb:
|
||||
let txt = document.createTextNode(" Add")
|
||||
elem.appendChild(txt)
|
||||
pkgtable.addButton = elem
|
||||
"th":
|
||||
text = "Name"
|
||||
"th":
|
||||
text = "Version"
|
||||
"th":
|
||||
text = "File name"
|
||||
"th":
|
||||
text = "Installed size"
|
||||
"tbody":
|
||||
cb:
|
||||
var i = 0
|
||||
for name, versions in searchResult.mpairs:
|
||||
|
||||
closureScope:
|
||||
htmlTreeAppend(elem):
|
||||
"tr":
|
||||
var row : Element
|
||||
var fileNameCell : Element
|
||||
var sizeCell : Element
|
||||
let size_change_callback = proc(newValue : string) =
|
||||
sizeCell.textContent = readTableRow(arch, row)["size"].getInt.formatByteSize
|
||||
|
||||
"td":
|
||||
"div":
|
||||
classList = ["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) =
|
||||
fileNameCell.removeChildren()
|
||||
var newdata = newSeq[string]()
|
||||
for arch, pkgname in vs[newValue]:
|
||||
newdata.add(arch)
|
||||
createDropdown(fileNameCell, newdata, size_change_callback)
|
||||
size_change_callback(newValue)
|
||||
createDropdown(elem, data, change_callback)
|
||||
"td":
|
||||
cb:
|
||||
fileNameCell = elem
|
||||
var data = newSeq[string]()
|
||||
var files : JsonNode
|
||||
for v, f in versions:
|
||||
files = f
|
||||
break
|
||||
for file in files:
|
||||
data.add(file["fileName"].getStr)
|
||||
createDropdown(elem, data, size_change_callback)
|
||||
"td":
|
||||
cb:
|
||||
sizeCell = elem
|
||||
for v, files in versions:
|
||||
for file in files:
|
||||
elem.textContent = file["size"].getInt.formatByteSize
|
||||
return
|
||||
cb:
|
||||
row = elem
|
||||
pkgtable.addButton.addEventListener("click", addButtonCallback)
|
||||
pkgtable
|
||||
|
||||
var selectedArch : string
|
||||
var archButtons : Element
|
||||
var table : Element
|
||||
var searchString : string
|
||||
var add2DownloadList : proc(e : Event)
|
||||
|
||||
proc updateTable(e : Event) =
|
||||
if pkgMap.len == 0 or searchString.len < 2: return
|
||||
discard newPkgTable(table, selectedArch, searchString, add2DownloadList)
|
||||
|
||||
var dp : DownloadPanel
|
||||
# var pkgTable : PkgTable
|
||||
htmlTreeappend document.body:
|
||||
# "img":
|
||||
# attrs {"src" : "img/background.bpg"}
|
||||
# style {
|
||||
# "width" : "100%",
|
||||
# "position" : "fixed",
|
||||
# "bottom" : "0",
|
||||
# "left" : "0",
|
||||
# "z-index" : "-10"
|
||||
# }
|
||||
"div":
|
||||
style = {"background-color" : "rgba(255, 255, 255, 0.25)"}
|
||||
classList = ["container"]
|
||||
"div":
|
||||
style = {
|
||||
"margin-top" : "20px",
|
||||
"background-color" : "rgba(224, 224, 224, 0.5)"
|
||||
}
|
||||
classList = ["jumbotron"]
|
||||
"h1":
|
||||
text = "Jpacrepo"
|
||||
"p":
|
||||
text = "Personal archlinux package repository"
|
||||
"div":
|
||||
"form":
|
||||
classList = ["form-horizontal"]
|
||||
"div":
|
||||
classList = ["form-group"]
|
||||
"label":
|
||||
classList = ["control-label", "col-sm-2"]
|
||||
text = " Search package"
|
||||
"div":
|
||||
classList = ["col-sm-10"]
|
||||
"input":
|
||||
classList = ["form-control"]
|
||||
attrs = {"type" : "text"}
|
||||
cb :
|
||||
add2DownloadList = proc(e : Event) =
|
||||
let rows = table.querySelectorAll("tbody tr")
|
||||
for row in rows:
|
||||
let cbox = row.querySelector("td:first-child input")
|
||||
if cbox.checked:
|
||||
dp.addPkg(readTableRow(selectedArch, row)["fileName"].getStr)
|
||||
oninput:
|
||||
searchString = $elem.value
|
||||
updateTable(event)
|
||||
"div":
|
||||
classList = ["row"]
|
||||
"div":
|
||||
classList = ["col-sm-3"]
|
||||
cb:
|
||||
dp = newDownloadPanel(elem)
|
||||
"div":
|
||||
"div":
|
||||
classList = ["btn-group", "btn-group-justified"]
|
||||
attrs = { "role" : "group" }
|
||||
cb:
|
||||
archButtons = elem
|
||||
classList = ["col-sm-9"]
|
||||
cb:
|
||||
table = elem
|
||||
|
||||
|
||||
|
||||
let r = newXMLHTTPRequest()
|
||||
let load_cb = proc(e : Event) =
|
||||
pkgMap = parseJson($r.responseText)
|
||||
var buttons = newSeq[Element]()
|
||||
var index = 0
|
||||
for arch in pkgMap.keys:
|
||||
closureScope:
|
||||
htmlTreeAppend(archButtons):
|
||||
"div":
|
||||
classList = ["btn-group"]
|
||||
attrs = { "role" : "group"}
|
||||
"button":
|
||||
classList = ["btn", "btn-default"]
|
||||
attrs = {
|
||||
"type": "button",
|
||||
}
|
||||
text = arch
|
||||
let thisButtonIndex = index
|
||||
let thisButtonArch = arch
|
||||
onclick:
|
||||
let update = thisButtonArch != selectedArch
|
||||
selectedArch = thisButtonArch
|
||||
for i in 0..<buttons.len:
|
||||
let btn = buttons[i]
|
||||
if(i == thisButtonIndex):
|
||||
btn.classList.toggle("active")
|
||||
else:
|
||||
btn.classList.remove("active")
|
||||
if update:
|
||||
updateTable(event)
|
||||
cb:
|
||||
buttons.add(elem)
|
||||
if index == 0:
|
||||
selectedArch = arch
|
||||
elem.classList.add("active")
|
||||
inc(index)
|
||||
# createDropdown(archDropDown, data, arch_change_callback)
|
||||
|
||||
r.addEventListener("load", load_cb)
|
||||
r.open("get", serverURL & "api/pkg/map")
|
||||
r.setRequestHeader("Accept", "application/json")
|
||||
r.send()
|
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="jpacrepo.css" type="text/css">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
|
||||
<script src="jpacrepo.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@@ -1,12 +1,13 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://woggioni.net/mvn/'
|
||||
url = getProperty('gitea.maven.url')
|
||||
content {
|
||||
includeGroup 'net.woggioni.gradle'
|
||||
includeGroup 'net.woggioni.gradle.lombok'
|
||||
includeGroup 'net.woggioni.gradle.wildfly'
|
||||
includeGroup 'net.woggioni.gradle.envelope'
|
||||
includeGroup 'net.woggioni.gradle.sambal'
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
@@ -16,7 +17,7 @@ pluginManagement {
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://woggioni.net/mvn/'
|
||||
url = getProperty('gitea.maven.url')
|
||||
content {
|
||||
includeGroup 'com.lys'
|
||||
}
|
||||
@@ -26,6 +27,7 @@ dependencyResolutionManagement {
|
||||
catalog {
|
||||
from group: 'com.lys', name: 'lys-catalog', version: getProperty('lys.version')
|
||||
version("slf4j", "1.7.36")
|
||||
version('jzstd', '2024.04.14')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -307,10 +307,6 @@ public class PacmanWebService {
|
||||
public Response downloadTar(@FormParam("pkgs") String formData) {
|
||||
String[] files = URLDecoder.decode(formData, StandardCharsets.UTF_8).split(" ");
|
||||
Fun<java.nio.file.Path, Long> fun = Files::size;
|
||||
long estimatedSize = Arrays.stream(files)
|
||||
.map(ctx::getFile)
|
||||
.mapToLong(fun::apply)
|
||||
.sum();
|
||||
Arrays.stream(files)
|
||||
.filter(fileName -> !Files.exists(ctx.getFile(fileName)))
|
||||
.forEach(fileName -> {
|
||||
@@ -341,7 +337,6 @@ public class PacmanWebService {
|
||||
}
|
||||
};
|
||||
return Response.ok(stream)
|
||||
.header("Content-Length", estimatedSize)
|
||||
.header("Content-Disposition", "attachment; filename=pkgs.tar")
|
||||
.build();
|
||||
}
|
||||
|
@@ -9,6 +9,6 @@
|
||||
},
|
||||
"verify-token-audience": true,
|
||||
"confidential-port": 443,
|
||||
"token-signature-algorithm": "ES512",
|
||||
"token-signature-algorithm": "RS256",
|
||||
"use-resource-role-mappings": true
|
||||
}
|
||||
|
@@ -85,7 +85,6 @@ public class ClientTest {
|
||||
assert Response.Status.CREATED.getStatusCode() == response.getStatus();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashTest(@TempDir Path testDir) {
|
||||
ClassLoader cl = getClass().getClassLoader();
|
||||
Stream.of("gvfs-nfs-1.50.2-1-x86_64.pkg.tar.zst")
|
||||
@@ -116,7 +115,6 @@ public class ClientTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeStatelessBean() throws Exception {
|
||||
Properties prop = new Properties();
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream("jboss-ejb-client.properties");
|
||||
@@ -124,35 +122,18 @@ public class ClientTest {
|
||||
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
|
||||
|
||||
prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
|
||||
// prop.put(Context.PROVIDER_URL, "http-remoting://localhost:1234");
|
||||
prop.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
|
||||
// prop.put(Context.PROVIDER_URL, "remote://odroid-u3:4447");
|
||||
prop.put(Context.SECURITY_PRINCIPAL, "walter");
|
||||
prop.put(Context.SECURITY_CREDENTIALS, "27ff5990757d1d");
|
||||
// prop.put(Context.SECURITY_PRINCIPAL, "luser");
|
||||
// prop.put(Context.SECURITY_CREDENTIALS, "123456");
|
||||
prop.put(Context.SECURITY_PRINCIPAL, "luser");
|
||||
prop.put(Context.SECURITY_CREDENTIALS, "123456");
|
||||
|
||||
prop.put("jboss.naming.client.ejb.context", true);
|
||||
Context context = new InitialContext(prop);
|
||||
Context ctx = new InitialContext(prop);
|
||||
traverseJndiNode("/", context);
|
||||
// final PacmanService stateService = (PacmanService) ctx.lookup("/jpacrepo-1.0/remote/PacmanServiceEJB!service.PacmanService");
|
||||
final FileSystemSynchronizer service = (FileSystemSynchronizer) ctx.lookup(
|
||||
"/jpacrepo/PackageSynchronizerEJB!net.woggioni.jpacrepo.api.service.FileSystemSynchronizer"
|
||||
);
|
||||
// List<PkgData> pkgs = service.searchPackage("google-earth", null, null, 1, 10);
|
||||
// System.out.println(new XStream().toXML(pkgs));
|
||||
service.syncDb();
|
||||
// service.searchPkgId("jre8-openjdk", null, null, null)
|
||||
// .stream()
|
||||
// .map(service::getPackage)
|
||||
// .filter(Objects::nonNull)
|
||||
// .map(PkgData::getFileName)
|
||||
// .forEach(System.out::println);
|
||||
// final TestEJB service = (TestEJB) ctx.lookup(
|
||||
// "/jpacrepo-2023.07TestEJBImpl!net.woggioni.jpacrepo.api.service.TestEJB"
|
||||
// );
|
||||
// System.out.println(service.hello());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user