Support folders
Some checks are pending
/ build (push) Waiting to run

Closes #3

Signed-off-by: Minecon724 <git@m724.eu>
This commit is contained in:
Minecon724 2025-02-27 14:34:36 +01:00
parent 0aadf69a42
commit 121a2c6915
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
7 changed files with 57 additions and 33 deletions

View file

@ -1,5 +1,5 @@
name: my blog name: my blog
baseUrl: https://example.com baseUrl: https://example.com/blog
coolProperty: 1231 coolProperty: 1231
coolerProperty: coolerProperty:

View file

@ -133,7 +133,7 @@ public class BlogBuilder {
this.renderOptions = RenderOptions.fromConfig(workingDirectory.resolve("render.yml")); this.renderOptions = RenderOptions.fromConfig(workingDirectory.resolve("render.yml"));
if (template == null) if (template == null)
this.template = new TemplateRenderer(templateDirectory); this.template = new TemplateRenderer(site, templateDirectory);
LOGGER.debug("Copying assets..."); LOGGER.debug("Copying assets...");
copyTree(workingDirectory.resolve("assets"), outputDirectory.resolve("assets")); copyTree(workingDirectory.resolve("assets"), outputDirectory.resolve("assets"));
@ -144,7 +144,7 @@ public class BlogBuilder {
LOGGER.debug("Rendering meta..."); LOGGER.debug("Rendering meta...");
posts.sort(Comparator.comparing(Post::createdAt).reversed()); posts.sort(Comparator.comparing(Post::createdAt).reversed());
Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts)); Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(posts));
Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts)); Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts));
@ -154,6 +154,16 @@ public class BlogBuilder {
} }
} }
// TODO should server be here in builder really
public void startServer(boolean openBrowser) throws IOException {
var server = new Server(outputDirectory, site.directory());
server.start();
if (openBrowser) {
server.openBrowser();
}
}
private List<Post> renderPosts() throws IOException { private List<Post> renderPosts() throws IOException {
Files.createDirectory(outputDirectory.resolve("post")); Files.createDirectory(outputDirectory.resolve("post"));
var postDirectory = workingDirectory.resolve("posts"); var postDirectory = workingDirectory.resolve("posts");
@ -178,7 +188,7 @@ public class BlogBuilder {
continue; continue;
} }
var render = template.renderPost(site, post); var render = template.renderPost(post);
var outFile = outputDirectory.resolve("post").resolve(path); var outFile = outputDirectory.resolve("post").resolve(path);
try { try {

View file

@ -46,16 +46,7 @@ public class Main {
// BAD // BAD
LOGGER.info("Exported to {} in {} ms", outputDirectory.toAbsolutePath(), "%.2f".formatted((end - start) / 1000000.0)); LOGGER.info("Exported to {} in {} ms", outputDirectory.toAbsolutePath(), "%.2f".formatted((end - start) / 1000000.0));
/* Server process */ builder.startServer(openBrowser);
if (startServer) {
var server = new Server(outputDirectory);
server.start();
if (openBrowser) {
server.openBrowser();
}
}
} }
private static CommandLine getCommandLine(String[] args) { private static CommandLine getCommandLine(String[] args) {

View file

@ -17,18 +17,21 @@ import java.nio.file.Path;
public class Server { public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
private final Path webroot; private final Path sitePath;
private final String contextPath;
private InetSocketAddress listenAddress; private InetSocketAddress listenAddress;
/** /**
* Constructs a {@link Server} instance with the specified webroot directory * Constructs a {@link Server} instance with the specified webroot directory
* and the specified listening address. * and the specified listening address.
* *
* @param webroot the path to the webroot directory from which files will be served * @param sitePath the directory from which files will be served
* @param contextPath TODO explain
* @param listenAddress the address and port the server will listen on * @param listenAddress the address and port the server will listen on
*/ */
public Server(Path webroot, InetSocketAddress listenAddress) { public Server(Path sitePath, String contextPath, InetSocketAddress listenAddress) {
this.webroot = webroot; this.sitePath = sitePath;
this.contextPath = contextPath;
this.listenAddress = listenAddress; this.listenAddress = listenAddress;
} }
@ -36,10 +39,11 @@ public class Server {
* Constructs a {@link Server} instance with the specified webroot directory. * Constructs a {@link Server} instance with the specified webroot directory.
* The server will bind to the localhost address with a dynamically chosen port. * The server will bind to the localhost address with a dynamically chosen port.
* *
* @param webroot the path to the webroot directory from which files will be served * @param sitePath the directory from which files will be served
* @param contextPath TODO explain
*/ */
public Server(Path webroot) { public Server(Path sitePath, String contextPath) {
this(webroot, new InetSocketAddress("localhost", 0)); this(sitePath, contextPath, new InetSocketAddress("localhost", 0));
} }
/** /**
@ -48,8 +52,9 @@ public class Server {
* @throws IOException if an I/O error occurs during server initialization * @throws IOException if an I/O error occurs during server initialization
*/ */
public void start() throws IOException { public void start() throws IOException {
System.out.println(contextPath);
var server = HttpServer.create(listenAddress, 0); var server = HttpServer.create(listenAddress, 0);
server.createContext("/", SimpleFileServer.createFileHandler(webroot.toAbsolutePath())); server.createContext(contextPath, SimpleFileServer.createFileHandler(sitePath.toAbsolutePath()));
server.start(); server.start();
LOGGER.info("Server started on http:/{}", server.getAddress()); LOGGER.info("Server started on http:/{}", server.getAddress());
@ -62,7 +67,7 @@ public class Server {
*/ */
public void openBrowser() { public void openBrowser() {
try { try {
var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() }); var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() + contextPath });
var code = process.waitFor(); var code = process.waitFor();
if (code != 0) { if (code != 0) {
throw new Exception("Exit code " + code); throw new Exception("Exit code " + code);

View file

@ -14,12 +14,15 @@ import java.util.Map;
* *
* @param name the name of the site * @param name the name of the site
* @param baseUrl the base URL of the site * @param baseUrl the base URL of the site
* @param directory The directory that the site is installed into. Like "https://example.com/blog" turns to "/blog"
* @param custom a map of additional custom properties * @param custom a map of additional custom properties
*/ */
public record Site( public record Site(
String name, String name,
String baseUrl, String baseUrl,
String directory,
Map<String, Object> custom Map<String, Object> custom
) { ) {
/** /**
@ -53,8 +56,14 @@ public record Site(
} }
} }
String directory = null;
if (baseUrl != null) {
var temp = baseUrl.substring(baseUrl.indexOf(':') + 3);
directory = temp.substring(temp.indexOf('/'));
}
return new Site( return new Site(
name, baseUrl, custom name, baseUrl, directory, custom
); );
} }
} }

View file

@ -1,5 +1,6 @@
package eu.m724.blog.template; package eu.m724.blog.template;
import eu.m724.blog.data.Site;
import io.pebbletemplates.pebble.extension.AbstractExtension; import io.pebbletemplates.pebble.extension.AbstractExtension;
import io.pebbletemplates.pebble.extension.Function; import io.pebbletemplates.pebble.extension.Function;
import io.pebbletemplates.pebble.template.EvaluationContext; import io.pebbletemplates.pebble.template.EvaluationContext;
@ -9,6 +10,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class TemplateExtension extends AbstractExtension { public class TemplateExtension extends AbstractExtension {
private final Site site;
public TemplateExtension(Site site) {
this.site = site;
}
@Override @Override
public Map<String, Function> getFunctions() { public Map<String, Function> getFunctions() {
return Map.of( return Map.of(
@ -20,7 +27,7 @@ public class TemplateExtension extends AbstractExtension {
@Override @Override
public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
return "/static/" + args.get("path"); // TODO for more advanced stuff return site.directory() + "/static/" + args.get("path");
} }
}, },
"asset", new Function() { "asset", new Function() {
@ -31,9 +38,10 @@ public class TemplateExtension extends AbstractExtension {
@Override @Override
public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) { public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
return "/assets/" + args.get("path"); // TODO for more advanced stuff return site.directory() + "/assets/" + args.get("path");
} }
} }
// TODO make url_for that supports relative and absolute paths
); );
} }
} }

View file

@ -17,23 +17,26 @@ import java.util.Map;
* using the Pebble templating engine. * using the Pebble templating engine.
*/ */
public class TemplateRenderer { public class TemplateRenderer {
private final Site site;
private final PebbleTemplate indexTemplate, articleTemplate; private final PebbleTemplate indexTemplate, articleTemplate;
/** /**
* Constructs a TemplateRenderer instance for rendering templates from the specified directory. * Constructs a TemplateRenderer instance for rendering templates from the specified directory.
*
* @param site the {@link Site} this renderer renders
* @param templateDirectory the root directory containing the template files * @param templateDirectory the root directory containing the template files
*/ */
public TemplateRenderer(Path templateDirectory) { public TemplateRenderer(Site site, Path templateDirectory) {
var loader = new FileLoader(); var loader = new FileLoader();
loader.setPrefix(templateDirectory.toString()); loader.setPrefix(templateDirectory.toString());
loader.setSuffix(".html"); loader.setSuffix(".html");
var pebbleEngine = new PebbleEngine.Builder() var pebbleEngine = new PebbleEngine.Builder()
.loader(loader) .loader(loader)
.extension(new TemplateExtension()) .extension(new TemplateExtension(site))
.build(); .build();
this.site = site;
this.indexTemplate = pebbleEngine.getTemplate("index_template"); this.indexTemplate = pebbleEngine.getTemplate("index_template");
this.articleTemplate = pebbleEngine.getTemplate("article_template"); this.articleTemplate = pebbleEngine.getTemplate("article_template");
} }
@ -41,12 +44,11 @@ public class TemplateRenderer {
/** /**
* Renders the index page using this template. * Renders the index page using this template.
* *
* @param site the {@link Site} this index is for
* @param posts the {@link Post}s to be included in the index page * @param posts the {@link Post}s to be included in the index page
* @return the rendered index HTML page as a string * @return the rendered index HTML page as a string
* @throws IOException if an error occurs during the template evaluation * @throws IOException if an error occurs during the template evaluation
*/ */
public String renderIndex(Site site, List<Post> posts) throws IOException { public String renderIndex(List<Post> posts) throws IOException {
Map<String, Object> context = Map.of( Map<String, Object> context = Map.of(
"site", site, "site", site,
"articles", posts "articles", posts
@ -61,12 +63,11 @@ public class TemplateRenderer {
/** /**
* Renders the content of a post using this template. * Renders the content of a post using this template.
* *
* @param site the {@link Site} containing the post
* @param post the {@link Post} to be rendered * @param post the {@link Post} to be rendered
* @return the rendered post HTML page as a string * @return the rendered post HTML page as a string
* @throws IOException if an error occurs during template evaluation * @throws IOException if an error occurs during template evaluation
*/ */
public String renderPost(Site site, Post post) throws IOException { public String renderPost(Post post) throws IOException {
Map<String, Object> context = Map.of( Map<String, Object> context = Map.of(
"site", site, "site", site,
"article", post "article", post