summaryrefslogtreecommitdiff
path: root/private
diff options
context:
space:
mode:
Diffstat (limited to 'private')
-rw-r--r--private/Makefile8
-rw-r--r--private/build.zig40
-rw-r--r--private/build.zig.zon17
-rw-r--r--private/src/db.zig151
-rw-r--r--private/src/main.zig5
-rw-r--r--private/src/util.zig4
6 files changed, 225 insertions, 0 deletions
diff --git a/private/Makefile b/private/Makefile
new file mode 100644
index 0000000..64cac0a
--- /dev/null
+++ b/private/Makefile
@@ -0,0 +1,8 @@
+debug:
+ $(info building in)
+ $(info .-*[DEBUG]*-.)
+ zig build -Doptimize=Debug
+release:
+ $(info building in)
+ $(info .-*[RELEASE]*-.)
+ zig build -Doptimize=ReleaseFast
diff --git a/private/build.zig b/private/build.zig
new file mode 100644
index 0000000..eae283b
--- /dev/null
+++ b/private/build.zig
@@ -0,0 +1,40 @@
+const z = @import( "std" );
+
+var b: *z.Build = undefined;
+var target: z.Build.ResolvedTarget = undefined;
+var optimize: z.builtin.OptimizeMode = undefined;
+
+fn import( name: []const u8 ) z.Build.Module.Import {
+ const i = b.dependency( name, .{ .target = target, .optimize = optimize } );
+ return .{ .name = name, .module = i.module( name ) };
+}
+
+pub fn build( _b: *z.Build ) void {
+ b = _b;
+ target = b.standardTargetOptions( .{} );
+ optimize = b.standardOptimizeOption( .{} );
+
+ const exe = b.addExecutable( .{
+ .name = "moneyboard-backend",
+ .root_module = b.createModule( .{
+ .root_source_file = b.path( "src/main.zig" ),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ import( "sqlite" ),
+ },
+ } ),
+ } );
+
+ b.installArtifact( exe );
+
+ const run_step = b.step( "run", "Run the app" );
+ const run_cmd = b.addRunArtifact( exe );
+ run_step.dependOn( &run_cmd.step );
+
+ run_cmd.step.dependOn( b.getInstallStep() );
+
+ if( b.args ) |args| {
+ run_cmd.addArgs( args );
+ }
+}
diff --git a/private/build.zig.zon b/private/build.zig.zon
new file mode 100644
index 0000000..1ccf1fd
--- /dev/null
+++ b/private/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .moneyboard,
+ .version = "0.0.0",
+ .fingerprint = 0xeafff85b61626f1c,
+ .minimum_zig_version = "0.15.2",
+ .dependencies = .{
+ .sqlite = .{
+ .url = "git+https://github.com/vrischmann/zig-sqlite#0b75ba276d34de3141ec1697e3ef7c836d7b38e7",
+ .hash = "sqlite-3.48.0-F2R_a8eODgD9X8XjvWlZ1K7C-tDuHae4FuShM2N-yIT2",
+ },
+ },
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ },
+}
diff --git a/private/src/db.zig b/private/src/db.zig
new file mode 100644
index 0000000..ee8908b
--- /dev/null
+++ b/private/src/db.zig
@@ -0,0 +1,151 @@
+const z = @import( "std" );
+const sql = @import( "sqlite" );
+
+const u = @import( "util.zig" );
+
+const alloc = u.alloc;
+
+const ArenaAlloc = z.heap.ArenaAllocator;
+const Mutex = z.Thread.Mutex;
+const Db = sql.Db;
+
+pub threadlocal var dbi: Interface = undefined;
+pub threadlocal var dbi_init: bool = false;
+
+var db_mutex = Mutex{};
+
+pub fn create( dbfile: [:0]const u8 ) !void {
+ dbiCheck() catch try init( dbfile );
+
+ const create_users_q =
+ \\CREATE TABLE IF NOT EXISTS users (
+ \\ uuid TEXT PRIMARY KEY,
+ \\ email TEXT,
+ \\ tokens TEXT,
+ \\ login_token TEXT,
+ \\ token_resetdate INTEGER,
+ \\ api_tokens TEXT,
+ \\ created_at INTEGER,
+ \\ subscription_data TEXT
+ \\)
+ ;
+
+ try dbi.put( create_users_q, .{} );
+ // ensure all fields actually exist in the database
+ dbi.put( "ALTER TABLE users ADD COLUMN email TEXT", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN tokens TEXT", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN login_token TEXT", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN token_resetdate INTEGER", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN api_tokens TEXT", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN created_at INTEGER", .{} ) catch {};
+ dbi.put( "ALTER TABLE users ADD COLUMN subscription_data TEXT", .{} ) catch {};
+
+ const create_userdata_q =
+ \\CREATE TABLE IF NOT EXISTS user_data (
+ \\ uuid TEXT PRIMARY KEY,
+ \\ nickname TEXT,
+ \\ prompt_data TEXT,
+ \\ site_prefs TEXT,
+ \\ chat_files TEXT
+ \\)
+ ;
+
+ try dbi.put( create_userdata_q, .{} );
+ // ensure all fields actually exist in the database
+ dbi.put( "ALTER TABLE user_data ADD COLUMN nickname INTEGER", .{} ) catch {};
+ dbi.put( "ALTER TABLE user_data ADD COLUMN prompt_data INTEGER", .{} ) catch {};
+ dbi.put( "ALTER TABLE user_data ADD COLUMN site_prefs INTEGER", .{} ) catch {};
+ dbi.put( "ALTER TABLE user_data ADD COLUMN chat_files INTEGER", .{} ) catch {};
+}
+
+pub fn init( dbfile: [:0]const u8 ) !void {
+ if( dbi_init ) return error.DbiAlreadyInitialized;
+
+ const db = try sql.Db.init( .{
+ .mode = Db.Mode{ .File = dbfile },
+ .open_flags = .{
+ .write = true,
+ .create = true
+ },
+ .threading_mode = .MultiThread
+ } );
+
+ dbi = .{ .db = db };
+ dbi_init = true;
+}
+
+fn dbiCheck() !void {
+ if( !dbi_init ) return error.DbiNotInitialized;
+}
+
+///methods of t get ignored
+pub fn Row( comptime t: type ) type {
+ return struct {
+ v: t,
+ alloc: ArenaAlloc,
+ pub fn deinit( self: @This() ) void {
+ self.alloc.deinit();
+ }
+ };
+}
+
+pub const Interface = struct {
+ const Self = Interface;
+ db: Db,
+ pub fn deinit( self: *Interface ) void {
+ self.db.deinit();
+ }
+
+ pub fn put( self: *Interface, comptime query: []const u8, value: anytype ) !void {
+ try dbiCheck();
+ db_mutex.lock();
+ defer db_mutex.unlock();
+
+ var statement = try self.db.prepare( query );
+ defer statement.deinit();
+
+ try statement.exec( .{}, value );
+ }
+
+ ///returns the first row of the query
+ pub fn one( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !t {
+ try dbiCheck();
+ db_mutex.lock();
+ defer db_mutex.unlock();
+
+ var statement = try self.db.prepare( query );
+ defer statement.deinit();
+
+ const ret = try statement.one( t, .{}, value );
+ return ret.?;
+ }
+
+ ///returns all rows of the query
+ ///must be freed by caller
+ pub fn all( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) ![]t {
+ try dbiCheck();
+ db_mutex.lock();
+ defer db_mutex.unlock();
+
+ var statement = try self.db.prepare( query );
+ defer statement.deinit();
+
+ return try statement.all( t, alloc, .{}, value );
+ }
+
+ pub fn oneAlloc( self: *Interface, comptime t: type, comptime query: []const u8, value: anytype ) !Row(t) {
+ try dbiCheck();
+ db_mutex.lock();
+ defer db_mutex.unlock();
+
+ var arena = ArenaAlloc.init( alloc );
+ const a = arena.allocator();
+
+ var statement = try self.db.prepareDynamic( query );
+ defer statement.deinit();
+
+ const ret = try statement.oneAlloc( t, a, .{}, value );
+ if( ret == null ) return error.QueryError;
+ return .{ .v = ret.?, .alloc = arena };
+ }
+};
diff --git a/private/src/main.zig b/private/src/main.zig
new file mode 100644
index 0000000..5f397b8
--- /dev/null
+++ b/private/src/main.zig
@@ -0,0 +1,5 @@
+const z = @import( "std" );
+
+pub fn main() !void {
+ z.debug.print( "hello world\n", .{} );
+}
diff --git a/private/src/util.zig b/private/src/util.zig
new file mode 100644
index 0000000..923c196
--- /dev/null
+++ b/private/src/util.zig
@@ -0,0 +1,4 @@
+const z = @import( "std" );
+
+pub var gpa = z.heap.GeneralPurposeAllocator( .{ .thread_safe = true, .stack_trace_frames = 64 } ){};
+pub const alloc = gpa.allocator();