// db {{{
-const Db = struct {
- const Self = @This();
-
- env: ?*lmdb.MDB_env = undefined,
- txn: ?*lmdb.MDB_txn = undefined,
- dbi: lmdb.MDB_dbi = undefined,
+const Prng = struct {
prng: std.Random.DefaultPrng = std.Random.DefaultPrng.init(0),
- pub fn gen_id(self: *Self) Id {
+ pub fn gen_id(self: *Prng, dbi: anytype) Id {
var id = self.prng.next();
- while (self.has(id)) {
+ while (dbi.has(id)) {
id = self.prng.next();
}
return id;
}
-
- pub fn open(self: *Self, name: [*c]const u8) void {
- _ = lmdb.mdb_env_create(&self.env);
- _ = lmdb.mdb_env_set_maxdbs(self.env, 10);
- _ = lmdb.mdb_env_set_mapsize(self.env, 1024 * 1024 * 1);
- _ = lmdb.mdb_env_open(self.env, name, lmdb.MDB_WRITEMAP, 0o664);
- // _ = lmdb.mdb_env_open(self.env, name, lmdb.MDB_NOSYNC | lmdb.MDB_WRITEMAP, 0o664);
- }
-
- pub fn close(self: *Self) void {
- lmdb.mdb_env_close(self.env);
- }
-
- pub fn begin(self: *Self, name: [*c]const u8) void {
- switch (lmdb.mdb_txn_begin(self.env, null, 0, &self.txn)) {
- 0 => {},
- else => |err| {
- std.debug.print("txn err: {}\n", .{err});
- },
- }
-
- // TODO: lmdb.MDB_INTEGERKEY?
- _ = lmdb.mdb_dbi_open(self.txn, name, lmdb.MDB_CREATE, &self.dbi);
- }
-
- pub fn commit(self: *Self) void {
- switch (lmdb.mdb_txn_commit(self.txn)) {
- 0 => {},
- lmdb.MDB_MAP_FULL => {
- std.debug.print("resize\n", .{});
- _ = lmdb.mdb_env_set_mapsize(self.env, 0);
- },
- else => |err| {
- std.debug.print("commit err: {}\n", .{err});
- },
- }
-
- // TODO: necessary?
- lmdb.mdb_dbi_close(self.env, self.dbi);
- }
-
- pub fn sync(self: *Self) void {
- switch (lmdb.mdb_env_sync(self.env, 1)) {
- 0 => {},
- else => |err| {
- std.debug.print("sync err: {}\n", .{err});
- },
- }
- }
-
- pub fn put(self: *Self, key: anytype, value: anytype) void {
- lmdb.put(self.txn, self.dbi, key, value);
- }
-
- pub fn get(self: *Self, key: anytype, comptime T: type) ?T {
- return lmdb.get(self.txn, self.dbi, key, T);
- }
-
- pub fn del(self: *Self, key: anytype) void {
- lmdb.del(self.txn, self.dbi, key);
- }
-
- pub fn has(self: *Self, key: anytype) bool {
- return lmdb.has(self.txn, self.dbi, key);
- }
};
+var prng = Prng{};
// }}}
}
}
-pub fn register_user(db: *Db, username: []const u8, password: []const u8) !void {
+pub fn register_user(env: *lmdb.Env, username: []const u8, password: []const u8) !void {
const username_array = try Username.fromSlice(username);
- db.begin("users");
- const user_id = db.gen_id();
- db.put(user_id, User{
- .username = username_array,
- .password_hash = try hash_password(password),
- });
- db.commit();
+ var user_id: Id = undefined;
- db.begin("ids");
- db.put(username_array.buffer, user_id);
- db.commit();
+ if (env.txn()) |*txn| {
+ defer txn.commit();
+ if (txn.dbi("users", Id, User)) |users| {
+ user_id = prng.gen_id(users);
+ users.put(user_id, User{
+ .username = username_array,
+ .password_hash = try hash_password(password),
+ });
+ }
+ }
- std.debug.print("id: {}\n", .{user_id});
+ if (env.txn()) |txn| {
+ defer txn.commit();
+ if (txn.dbi("user_ids", @TypeOf(username_array.buffer), Id)) |user_ids| {
+ user_ids.put(username_array.buffer, user_id);
+ }
+ }
- db.sync();
+ env.sync();
}
-pub fn login_user(db: *Db, username: []const u8, password: []const u8) ?SessionToken {
+pub fn login_user(env: *lmdb.Env, username: []const u8, password: []const u8) ?SessionToken {
const username_array = Username.fromSlice(username) catch return null;
- db.begin("ids");
- const user_id = db.get(username_array.buffer, Id) orelse return null;
- std.debug.print("id: {}\n", .{user_id});
- // TODO: maybe no commit?
- db.commit();
+ var user_id_maybe: ?Id = null;
+ var user_maybe: ?User = null;
- db.begin("users");
- const user = db.get(user_id, User) orelse return null;
- db.commit();
-
- if (verify_password(password, user.password_hash)) {
- db.begin("sessions");
- const session_token = db.gen_id();
- db.put(session_token, user_id);
- db.commit();
+ if (env.txn()) |txn| {
+ defer txn.abort();
+ if (txn.dbi("user_ids", @TypeOf(username_array.buffer), Id)) |user_ids| {
+ user_id_maybe = user_ids.get(username_array.buffer);
+ std.debug.print("id: {?}\n", .{user_id_maybe});
+ }
+ }
- db.sync();
+ if (user_id_maybe) |user_id| {
+ if (env.txn()) |txn| {
+ defer txn.abort();
+ if (txn.dbi("users", Id, User)) |users| {
+ user_maybe = users.get(user_id_maybe.?);
+ }
+ }
- return session_token;
- } else {
- return null;
+ if (user_maybe) |user| {
+ if (verify_password(password, user.password_hash)) {
+ if (env.txn()) |txn| {
+ defer txn.commit();
+ if (txn.dbi("sessions", Id, Id)) |sessions| {
+ const session_token = prng.gen_id(sessions);
+ sessions.put(session_token, user_id);
+ return session_token;
+ }
+ }
+ }
+ }
}
+
+ return null;
}
-fn logout_user(db: *Db, session_token: SessionToken) void {
- db.begin("sessions");
- db.del(session_token);
- db.commit();
+fn logout_user(env: *lmdb.Env, session_token: SessionToken) void {
+ if (env.txn()) |txn| {
+ defer txn.commit();
+ if (txn.dbi("sessions", Id, Id)) |sessions| {
+ sessions.del(session_token);
+ }
+ }
}
-fn get_session_user(db: *Db, session_token: SessionToken) ?User {
- db.begin("sessions");
- const user_id = db.get(session_token, Id) orelse return null;
- db.commit();
+fn get_session_user(env: *lmdb.Env, session_token: SessionToken) ?User {
+ var user_id_maybe: ?Id = null;
+
+ if (env.txn()) |txn| {
+ defer txn.abort();
+ if (txn.dbi("sessions", Id, Id)) |sessions| {
+ user_id_maybe = sessions.get(session_token);
+ }
+ }
- db.begin("users");
- const user = db.get(user_id, User) orelse return null;
- db.commit();
+ if (user_id_maybe) |user_id| {
+ if (env.txn()) |txn| {
+ defer txn.abort();
+ if (txn.dbi("users", Id, User)) |users| {
+ return users.get(user_id);
+ }
+ }
+ }
- return user;
+ return null;
}
// }}}
-fn list_users(db: *Db) void {
- _ = lmdb.mdb_txn_begin(db.env, null, 0, &db.txn);
-
- _ = lmdb.mdb_dbi_open(db.txn, "users", lmdb.MDB_CREATE, &db.dbi);
+fn list_users(env: *lmdb.Env) void {
+ if (env.txn()) |txn| {
+ defer txn.abort();
+ if (txn.dbi("users", Id, User)) |users| {
+ var cursor = users.cursor();
- var cursor: ?*lmdb.MDB_cursor = undefined;
- _ = lmdb.mdb_cursor_open(db.txn, db.dbi, &cursor);
+ var key: Id = undefined;
+ var user_maybe = cursor.get(&key, .First);
- var key: lmdb.MDB_val = undefined;
- var val: lmdb.MDB_val = undefined;
- var result = lmdb.mdb_cursor_get(cursor, &key, &val, lmdb.MDB_FIRST);
+ while (user_maybe) |user| {
+ std.debug.print("[{}] {s}\n", .{ key, user.username.constSlice() });
- while (result != lmdb.MDB_NOTFOUND) {
- const user_id = @as(*align(1) Id, @ptrCast(key.mv_data.?)).*;
- const user = @as(*align(1) User, @ptrCast(val.mv_data.?)).*;
-
- std.debug.print("[{}] {s}\n", .{ user_id, user.username.constSlice() });
-
- result = lmdb.mdb_cursor_get(cursor, &key, &val, lmdb.MDB_NEXT);
+ user_maybe = cursor.get(&key, .Next);
+ }
+ }
}
-
- _ = lmdb.mdb_cursor_close(cursor);
-
- _ = lmdb.mdb_dbi_close(db.env, db.dbi);
-
- _ = lmdb.mdb_txn_commit(db.txn);
}
pub fn main() !void {
defer server.deinit();
// lmdb
- var db = Db{};
- db.open("./db");
- defer db.close();
+ var env = lmdb.Env.open("db", 1024 * 1024 * 10);
+ defer env.close();
- list_users(&db);
+ list_users(&env);
accept: while (true) {
const conn = try server.accept();
if (get_cookie(&req, "session_token")) |session_token_str| {
const session_token = try std.fmt.parseUnsigned(SessionToken, session_token_str.constSlice(), 10);
- if (get_session_user(&db, session_token)) |user| {
+ if (get_session_user(&env, session_token)) |user| {
logged_in = .{
.user = user,
.session_token = session_token,
const password = get_value(&req, "password").?;
std.debug.print("New user: {s} {s}\n", .{ username, password });
- try register_user(&db, username, password);
+ try register_user(&env, username, password);
try redirect(&req, "/login");
} else if (std.mem.eql(u8, req.head.target, "/login")) {
const password = get_value(&req, "password").?;
std.debug.print("New login: {s} {s}\n", .{ username, password });
- if (login_user(&db, username, password)) |session_token| {
+ if (login_user(&env, username, password)) |session_token| {
var redirect_buffer = try std.BoundedArray(u8, 128).init(0);
try std.fmt.format(redirect_buffer.writer(), "/user/{s}", .{username});
}
} else if (std.mem.eql(u8, req.head.target, "/logout")) {
if (logged_in) |login| {
- logout_user(&db, login.session_token);
+ logout_user(&env, login.session_token);
try req.respond("", .{
.status = .see_other,
.extra_headers = &.{