summaryrefslogtreecommitdiff
path: root/backend/api/src/util.zig
diff options
context:
space:
mode:
Diffstat (limited to 'backend/api/src/util.zig')
-rw-r--r--backend/api/src/util.zig271
1 files changed, 271 insertions, 0 deletions
diff --git a/backend/api/src/util.zig b/backend/api/src/util.zig
new file mode 100644
index 0000000..19fa456
--- /dev/null
+++ b/backend/api/src/util.zig
@@ -0,0 +1,271 @@
+const z = @import( "std" );
+const zap = @import( "zap" );
+const net = @import( "net-util.zig" );
+const json = @import( "json" );
+const getty = @import( "getty" );
+
+const ArenaAllocator = z.heap.ArenaAllocator;
+const GettyResult = getty.de.Result;
+pub var gpa = z.heap.GeneralPurposeAllocator( .{ .thread_safe = true, .stack_trace_frames = 64 } ){};
+pub const alloc = gpa.allocator();
+
+const Status = zap.StatusCode;
+const Request = zap.Request;
+
+const pbkdf2 = z.crypto.pwhash.pbkdf2;
+
+const aes256Encrypt = z.crypto.aead.aes_gcm.Aes256Gcm.encrypt;
+const aes256Decrypt = z.crypto.aead.aes_gcm.Aes256Gcm.decrypt;
+
+pub const @"json.ignore.unknown" = struct {
+ pub const attributes = .{
+ .Container = .{ .ignore_unknown_fields = true },
+ };
+};
+
+pub fn MergeTypes( comptime t1: type, comptime t2: type ) type {
+ const t1info = @typeInfo( t1 ).Struct;
+ const t2info = @typeInfo( t2 ).Struct;
+
+ comptime if( t1info.is_tuple ) return t2;
+ comptime if( t2info.is_tuple ) return t1;
+
+ var t3info = t1info;
+ t3info.fields = t1info.fields ++ t2info.fields;
+
+ var t1s = @typeInfo( t1 );
+ t1s.Struct = t3info;
+
+ return @Type( t1s );
+}
+
+pub fn JsonResponse( comptime t: type ) type {
+ return struct {
+ v: t,
+ @"#alloc": ArenaAllocator,
+ pub fn deinit( self: @This() ) void {
+ self.@"#alloc".deinit();
+ }
+ };
+}
+
+///freed by caller
+pub fn jsonParse( comptime t: type, str: []const u8 ) !JsonResponse(t) {
+ return jsonParseAlloc( t, str, alloc );
+}
+
+///freed by caller
+pub fn jsonParseAlloc( comptime t: type, str: []const u8, a: z.mem.Allocator ) !JsonResponse(t) {
+ var arena = ArenaAllocator.init( a );
+ const allocator = arena.allocator();
+
+ const ret = json.fromSlice( allocator, t, str ) catch |e| {
+ arena.deinit();
+ return e;
+ };
+ var obj: JsonResponse(t) = undefined;
+ obj.v = ret.value;
+ obj.@"#alloc" = arena;
+
+ return obj;
+}
+
+///has to be freed using page_allocator
+pub fn jsonStringify( obj: anytype ) ![]const u8 {
+ var arr = z.ArrayList( u8 ).init( alloc );
+ try json.toWriter( alloc, obj, arr.writer() );
+ return arr.toOwnedSlice();
+}
+
+pub fn jsonStringifyAlloc( obj: anytype, a: z.mem.Allocator ) ![]const u8 {
+ var arr = z.ArrayList( u8 ).init( a );
+ try json.toWriter( alloc, obj, arr.writer() );
+ return arr.toOwnedSlice();
+}
+
+///has to be freed by caller with page_allocator
+///10mb max
+pub fn readFile( path: []const u8 ) ![]const u8 {
+ return readFileAlloc( path, alloc );
+}
+
+pub fn readFileAlloc( path: []const u8, a: z.mem.Allocator ) ![]const u8 {
+ const file = try z.fs.cwd().openFile( path, .{} );
+ defer file.close();
+
+ var reader = z.io.bufferedReader( file.reader() );
+ var stream = reader.reader();
+
+ const contents = try stream.readAllAlloc( a, 1024 * 10000 );
+ return contents[0..contents.len];
+}
+
+pub fn fileExists( path: []const u8 ) bool {
+ const file = z.fs.cwd().openFile( path, .{} ) catch return false;
+ _ = file.stat() catch return false;
+ file.close();
+
+ return true;
+}
+
+pub fn writeFile( path: []const u8, contents: []const u8 ) !void {
+ z.fs.cwd().deleteFile( path ) catch |e| {
+ switch (e) {
+ error.FileNotFound => {},
+ else => return e
+ }
+ };
+ const file = try z.fs.cwd().createFile( path, .{} );
+ defer file.close();
+
+ var writer = file.writer();
+ writer.writeAll( contents ) catch return error.OutOfMemory;
+}
+
+pub fn deleteFile( path: []const u8 ) !void {
+ z.fs.cwd().deleteFile( path ) catch |e| {
+ switch (e) {
+ error.FileNotFound => {},
+ else => return e
+ }
+ };
+}
+
+pub fn hexToBytesAlloc( hex: []const u8, a: z.mem.Allocator ) ![]const u8 {
+ var bytes = try a.alloc( u8, hex.len / 2 );
+ var i: u32 = 0;
+ while( i < hex.len ) : (i += 2) {
+ z.debug.print( "byte: {s}\n", .{ hex[i..i+1] } );
+ const byte = try z.fmt.parseInt( u8, hex[i..i+1], 16 );
+ bytes[i/2] = byte;
+ }
+
+ return bytes;
+}
+
+pub fn hexToBytes( hex: []const u8 ) ![]const u8 {
+ return hexToBytesAlloc( hex, alloc );
+}
+
+///EXTREMELY GAY but zig crypto lib has a bug that makes it not compile.
+///IMPORTANT: FIX THIS!!!!!
+pub fn readFileCrypto( path: []const u8 ) ![]const u8 {
+ const proc = try z.process.Child.run( .{
+ .allocator = alloc,
+ .argv = &.{ "node", "../chat-reader.cjs", "read", path }
+ } );
+ defer alloc.free( proc.stdout );
+ defer alloc.free( proc.stderr );
+
+ if( proc.stdout[0] == '0' )
+ return error.FileNotFound;
+
+ const buf = alloc.dupe( u8, proc.stdout );
+ return buf;
+}
+
+pub fn writeFileCrypto( path: []const u8, buf: []const u8 ) ![]const u8 {
+ const proc = try z.process.Child.run( .{
+ .allocator = alloc,
+ .argv = &.{ "node", "../chat-reader.cjs", "write", path, buf }
+ } );
+ defer alloc.free( proc.stdout );
+ defer alloc.free( proc.stderr );
+
+ const ret = alloc.dupe( u8, proc.stdout );
+ return ret;
+}
+
+pub const Validator = struct {
+ pub fn isValidChar( c: u8 ) bool {
+ if( c >= 'a' and c <= 'z' )
+ return true;
+ if( c >= 'A' and c <= 'Z' )
+ return true;
+ if( c >= '0' and c <= '9' )
+ return true;
+ if( c == '-' or c == '_' )
+ return true;
+ if( c == '.' )
+ return true;
+ if( c == '@' )
+ return true;
+
+ return false;
+ }
+
+ pub fn isValidCharSystem( c: u8 ) bool {
+ const allowlist = ",;:/?=!#$^&*()+~[]{}<>|` ";
+ for( allowlist ) |allowc| {
+ if( allowc == c )
+ return true;
+ }
+
+ return isValidChar( c );
+ }
+
+ pub fn isValidCharFont( c: u8 ) bool {
+ if( c == ' ' )
+ return true;
+ if( !isValidChar( c ) )
+ return false;
+ return true;
+ }
+
+ pub fn isValidCharEmail( c: u8 ) bool {
+ if( !isValidChar( c ) )
+ return false;
+ return true;
+ }
+
+ pub fn isValidStrSystem( str: []const u8 ) bool {
+ if( str.len > 2048 )
+ return false;
+ for( str ) |c| {
+ if( !isValidCharSystem( c ) )
+ return false;
+ }
+ return true;
+ }
+
+ pub fn isValidStrNickname( str: []const u8 ) bool {
+ if( str.len > 24 )
+ return false;
+ if( str.len < 2 )
+ return false;
+ for( str ) |c| {
+ if( !isValidCharEmail( c ) )
+ return false;
+ }
+ return true;
+ }
+
+ pub fn isValidStrEmail( str: []const u8 ) bool {
+ if( str.len > 127 )
+ return false;
+ if( str.len < 5 )
+ return false;
+ var has_at = false;
+ var has_dot = false;
+ for( str ) |c| {
+ if( !isValidCharEmail( c ) )
+ return false;
+
+ if( c == '@' ) has_at = true;
+ if( c == '.' ) has_dot = true;
+ }
+ return true;
+ }
+
+ pub fn isValidStrFont( str: []const u8 ) bool {
+ if( str.len > 64 )
+ return false;
+ if( str.len < 3 )
+ return false;
+ for( str ) |c| {
+ if( !isValidCharFont( c ) )
+ return false;
+ }
+ return true;
+ }
+};