Compare commits

..

10 commits

20 changed files with 184 additions and 70 deletions

2
.gitignore vendored
View file

@ -3,4 +3,4 @@ zig-cache/
*.core
static/
.jetzig
src/app/database/
src/app/database/data.db-journal

View file

@ -28,7 +28,7 @@ pub fn build(b: *std.Build) !void {
// All dependencies **must** be added to imports above this line.
try jetzig.jetzigInit(b, exe, .{.zmpl_version = .v2});
try jetzig.jetzigInit(b, exe, .{ .zmpl_version = .v2 });
b.installArtifact(exe);

View file

@ -14,20 +14,16 @@
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{
.jetzig = .{
.url = "https://github.com/jetzig-framework/jetzig/archive/68ca4d3bbad05880e6e38fc6d10e7a24e772081e.tar.gz",
.hash = "122047ebabab1dfe1bb7f96ffdac98410ffb4850b06dc6b1670ce74aaeaa49bdc08e",
},
.iguanas = .{
.dependencies = .{ .jetzig = .{
.url = "https://github.com/jetzig-framework/jetzig/archive/88abe2243d87b28ad110c5fe9fe90b716a6c6583.tar.gz",
.hash = "12200d15cef72d4c37a01c930db53c3ffafdc05142462afddeab818701a0291327d1",
}, .iguanas = .{
.url = "https://github.com/jetzig-framework/iguanas/archive/89c2abf29de0bc31054a9a6feac5a6a83bab0459.tar.gz",
.hash = "12202fd319a5ab4e124b00e8ddea474d07c19c4e005d77b6c29fc44860904ea01a5c",
},
.sqlite = .{
}, .sqlite = .{
.url = "https://github.com/vrischmann/zig-sqlite/archive/6af21bb.tar.gz",
.hash = "12202799861d92c7880d1a74f95be099752d7f0420a0c9628dc923ca2b1a22b7bda8",
}
},
} },
.paths = .{
// This makes *all* files, recursively, included in this package. It is generally
// better to explicitly list the files and directories instead, to insure that

View file

@ -3,8 +3,23 @@
* <link rel="stylesheet" href="/styles.css" />
*
*/
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
.message {
font-weight: bold;
.title {
font-family: 'Roboto';
font-size: 3rem;
font-weight: 500;
margin-left: 20px;
margin-right: 40px;
margin-top: 20px;
}
.header-link{
font-family: 'Roboto';
font-size: 1.5rem;
margin-right: 20px;
}
#replaceMe{
font-family:'Courier New';
}

BIN
src/app/database/data.db Normal file

Binary file not shown.

22
src/app/lib/db.zig Normal file
View file

@ -0,0 +1,22 @@
pub const addArtist = \\INSERT INTO artists ('artist', 'plays', 'url') VALUES (?,?)
;
pub const addTrack = \\INSERT INTO tracks ('artist', 'track', 'album', 'plays', 'url') VALUES (?,?,?,?)
;
pub const getArtist = \\SELECT artist, plays FROM artists WHERE artist == ?
;
pub const getTrack = \\SELECT artist, track, album, plays FROM tracks WHERE track == ?
;
pub const getTrackSearch = \\SELECT track, url, form FROM tracks WHERE track LIKE '%' || ? || '%'
;
pub const getArtistSearch = \\SELECT artist, url, form FROM artists WHERE artist LIKE '%' || ? || '%'
;
pub const getAlbumSearch = \\SELECT album, url, form FROM albums WHERE album LIKE '%' || ? || '%'
;
pub const total_search = getArtistSearch ++ " UNION " ++ getAlbumSearch ++ " UNION " ++ getTrackSearch;

View file

@ -1,3 +1,4 @@
const std = @import("std");
const jetzig = @import("jetzig");
/// `src/app/views/root.zig` represents the root URL `/`
@ -18,7 +19,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
var root = try data.object();
// Add a string to the root object.
try root.put("message", data.string("Welcome to Jetzig!"));
try root.put("welcome_message", data.string("Welcome to Jetzig!"));
// Request params have the same type as a `data.object()` so they can be inserted them
// directly into the response data. Fetch `http://localhost:8080/?message=hello` to set the
@ -26,9 +27,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
// present.
const params = try request.params();
if (params.get("message")) |value| {
try root.put("message_param", value);
}
try root.put("message_param", params.get("message"));
// Set arbitrary response headers as required. `content-type` is automatically assigned for
// HTML, JSON responses.

View file

@ -1,20 +0,0 @@
<!--// Renders the `message` response data value.-->
<h3 class="message text-[#39b54a]">{.message}</h3>
<div><img class="p-3 mx-auto" src="/jetzig.png" /></div>
<div>
<a href="https://github.com/jetzig-framework/zmpl">
<img class="p-3 m-3 mx-auto" src="/zmpl.png" />
</a>
</div>
<div>Visit <a class="font-bold text-[#39b54a]" href="https://jetzig.dev/">jetzig.dev</a> to get started.
<div>Join our Discord server and introduce yourself:</div>
<div>
<a class="font-bold text-[#39b54a]">Search for an artist, album, or song:</a>
<input type="text" name="search" hx-include="[name='search']" hx-get="/test" hx-trigger="keyup[keyCode==13]">
<button type="button" hx-include="[name='search']" hx-get="/test">You whattup</button>
</div>
</div>

View file

@ -0,0 +1,6 @@
<b class="title text-[#123456]">Zuletzt</b>
<a class="header-link" href="/artists">Artists</a>
<a class="header-link" href="/albums">Albums</a>
<a class="header-link" href="/tracks">Tracks</a>
<a class="header-link" href="/stats">Stats</a>
<hr>

View file

View file

View file

View file

@ -0,0 +1,26 @@
<div>
<fieldset>
Top:
<input type="radio" label="Artist" name="q" value="artist" hx-get="/stats" hx-target="#update" hx-include="[name='t']" hx-swap="outerHTML" hx-trigger="click" checked>Artist</input>
<input type="radio" label="Album" name="q" value="album" hx-get="/stats" hx-target="#update" hx-include="[name='t']" hx-swap="outerHTML" hx-trigger="click">Album</input>
<input type="radio" label="Track" name="q" value="track" hx-get="/stats" hx-target="#update" hx-include="[name='t']" hx-swap="outerHTML" hx-trigger="click">Track</input>
</fieldset>
<fieldset>
of:
<input type="radio" label="Day" name="t" value="day" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click" checked>Day</input>
<input type="radio" label="Week" name="t" value="week" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">Week</input>
<input type="radio" label="Month" name="t" value="month" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">Month</input>
<input type="radio" label="3 Months" name="t" value="quarter" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">3 Months</input>
<input type="radio" label="6 Months" name="t" value="half" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">6 Months</input>
<input type="radio" label="Current Year" name="t" value="begin" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">Current Year</input>
<input type="radio" label="365 Days" name="t" value="year" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">365 days</input>
<input type="radio" label="All Time" name="t" value="all" hx-get="/stats" hx-target="#update" hx-include="[name='q']" hx-swap="outerHTML" hx-trigger="click">All Time</input>
</fieldset>
<tr>
<td colspan="3">
<b id="update">hyello</b>
</td>
</tr>
</div>

View file

@ -8,15 +8,7 @@
</head>
<body>
<div class="text-center pt-10 m-auto">
<!-- If present, renders the `message_param` response data value, add `?message=hello` to the
// URL to see the output: -->
<h2 class="param text-3xl text-[#f7931e]">{.message_param}</h2>
<!--// Renders `src/app/views/root/_content.zmpl` with the same template data available:-->
<div>
@partial root/content
</div>
</div>
@partial root/header
@partial root/top
</body>
</html>

52
src/app/views/search.zig Normal file
View file

@ -0,0 +1,52 @@
const std = @import("std");
const jetzig = @import("jetzig");
const queries = @import("../lib/db.zig");
const sqlite = @import("sqlite");
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
var root = try data.array();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
var db = try sqlite.Db.init(.{
.mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" },
.open_flags = .{
.write = true,
.create = true,
},
.threading_mode = .MultiThread,
});
const params = try request.params();
const queryOrNull: ?[]const u8 = if (params.get("q")) |param| param.string.value else null;
if (queryOrNull) |query| {
// sql.search(query, db);
var artistSearch = try db.prepare(queries.total_search);
defer artistSearch.deinit();
const artistResults = try artistSearch.all(
struct {
artist: []u8,
url: []u8,
form: []u8,
},
arena.allocator(),
.{},
.{ .artist = query, .album = query, .track = query },
);
for (artistResults) |r| {
std.log.debug("artist: {s}, url: {s}", .{ r.artist, r.url });
try root.append(data.string(r.artist));
try root.append(data.string(r.url));
try root.append(data.string(r.form));
//std.log.debug("{s}", .{r});
}
} else {
return request.render(.bad_request);
}
//const query = params.get("q");
//try root.put("q",query);
//try root.put("q", data.string("Welcome"));
return request.render(.ok);
}

View file

@ -0,0 +1,16 @@
<table>
<tr>
<th>Artist</th>
<th>Album</th>
<th>Track</th>
</tr>
@zig {
for (zmpl.items(.array), 0..) |u,i| {
if(@mod(i,3)==0){
<tr>
<td id={{zmpl.items(.array)[i+2].string.value}}><a href={{zmpl.items(.array)[i+1].string.value}}>{{u}}</a></td>
</tr>
}
}
}
</table>

7
src/app/views/stats.zig Normal file
View file

@ -0,0 +1,7 @@
const std = @import("std");
const jetzig = @import("jetzig");
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
_ = data;
return request.render(.ok);
}

View file

@ -0,0 +1,3 @@
<div>
<span>Content goes here</span>
</div>

View file

@ -87,35 +87,35 @@ pub const jetzig_options = struct {
};
pub fn main() !void {
var db = try sqlite.Db.init(.{
.mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" },
.open_flags = .{
.write = true,
.create = true,
},
.threading_mode = .MultiThread,
});
//var db = try sqlite.Db.init(.{
// .mode = sqlite.Db.Mode{ .File = "/home/swebb/Source/zuletzt/src/app/database/data.db" },
// .open_flags = .{
// .write = true,
// .create = true,
// },
// .threading_mode = .MultiThread,
//});
//const create =
// \\CREATE TABLE artists ('artist', 'plays')
//;
const query =
\\INSERT INTO artists ('artist', 'plays') VALUES (?,?)
;
//const query =
// \\INSERT INTO artists ('artist', 'plays') VALUES (?,?)
//;
//var build = try db.prepare(create);
//defer build.deinit();
//try build.exec(.{},.{});
var stmt = try db.prepare(query);
defer stmt.deinit();
//var stmt = try db.prepare(query);
//defer stmt.deinit();
try stmt.exec(.{}, .{
.artist = "Wilco",
.plays = 2500,
});
//try stmt.exec(.{}, .{
// .artist = "Wilco",
// .plays = 2500,
//});
var gpa = std.heap.GeneralPurposeAllocator(.{}){};