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
baseUrl: https://example.com
baseUrl: https://example.com/blog
coolProperty: 1231
coolerProperty:

View file

@ -133,7 +133,7 @@ public class BlogBuilder {
this.renderOptions = RenderOptions.fromConfig(workingDirectory.resolve("render.yml"));
if (template == null)
this.template = new TemplateRenderer(templateDirectory);
this.template = new TemplateRenderer(site, templateDirectory);
LOGGER.debug("Copying assets...");
copyTree(workingDirectory.resolve("assets"), outputDirectory.resolve("assets"));
@ -144,7 +144,7 @@ public class BlogBuilder {
LOGGER.debug("Rendering meta...");
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));
@ -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 {
Files.createDirectory(outputDirectory.resolve("post"));
var postDirectory = workingDirectory.resolve("posts");
@ -178,7 +188,7 @@ public class BlogBuilder {
continue;
}
var render = template.renderPost(site, post);
var render = template.renderPost(post);
var outFile = outputDirectory.resolve("post").resolve(path);
try {

View file

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

View file

@ -17,18 +17,21 @@ import java.nio.file.Path;
public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
private final Path webroot;
private final Path sitePath;
private final String contextPath;
private InetSocketAddress listenAddress;
/**
* Constructs a {@link Server} instance with the specified webroot directory
* 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
*/
public Server(Path webroot, InetSocketAddress listenAddress) {
this.webroot = webroot;
public Server(Path sitePath, String contextPath, InetSocketAddress listenAddress) {
this.sitePath = sitePath;
this.contextPath = contextPath;
this.listenAddress = listenAddress;
}
@ -36,10 +39,11 @@ public class Server {
* Constructs a {@link Server} instance with the specified webroot directory.
* 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) {
this(webroot, new InetSocketAddress("localhost", 0));
public Server(Path sitePath, String contextPath) {
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
*/
public void start() throws IOException {
System.out.println(contextPath);
var server = HttpServer.create(listenAddress, 0);
server.createContext("/", SimpleFileServer.createFileHandler(webroot.toAbsolutePath()));
server.createContext(contextPath, SimpleFileServer.createFileHandler(sitePath.toAbsolutePath()));
server.start();
LOGGER.info("Server started on http:/{}", server.getAddress());
@ -62,7 +67,7 @@ public class Server {
*/
public void openBrowser() {
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();
if (code != 0) {
throw new Exception("Exit code " + code);

View file

@ -14,12 +14,15 @@ import java.util.Map;
*
* @param name the name 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
*/
public record Site(
String name,
String baseUrl,
String directory,
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(
name, baseUrl, custom
name, baseUrl, directory, custom
);
}
}

View file

@ -1,5 +1,6 @@
package eu.m724.blog.template;
import eu.m724.blog.data.Site;
import io.pebbletemplates.pebble.extension.AbstractExtension;
import io.pebbletemplates.pebble.extension.Function;
import io.pebbletemplates.pebble.template.EvaluationContext;
@ -9,6 +10,12 @@ import java.util.List;
import java.util.Map;
public class TemplateExtension extends AbstractExtension {
private final Site site;
public TemplateExtension(Site site) {
this.site = site;
}
@Override
public Map<String, Function> getFunctions() {
return Map.of(
@ -20,7 +27,7 @@ public class TemplateExtension extends AbstractExtension {
@Override
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() {
@ -31,9 +38,10 @@ public class TemplateExtension extends AbstractExtension {
@Override
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.
*/
public class TemplateRenderer {
private final Site site;
private final PebbleTemplate indexTemplate, articleTemplate;
/**
* 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
*/
public TemplateRenderer(Path templateDirectory) {
public TemplateRenderer(Site site, Path templateDirectory) {
var loader = new FileLoader();
loader.setPrefix(templateDirectory.toString());
loader.setSuffix(".html");
var pebbleEngine = new PebbleEngine.Builder()
.loader(loader)
.extension(new TemplateExtension())
.extension(new TemplateExtension(site))
.build();
this.site = site;
this.indexTemplate = pebbleEngine.getTemplate("index_template");
this.articleTemplate = pebbleEngine.getTemplate("article_template");
}
@ -41,12 +44,11 @@ public class TemplateRenderer {
/**
* 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
* @return the rendered index HTML page as a string
* @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(
"site", site,
"articles", posts
@ -61,12 +63,11 @@ public class TemplateRenderer {
/**
* 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
* @return the rendered post HTML page as a string
* @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(
"site", site,
"article", post