fediglam/src/api/services/files.zig

174 lines
4.3 KiB
Zig
Raw Normal View History

2022-11-21 11:58:54 +00:00
const std = @import("std");
2022-12-05 10:12:40 +00:00
const sql = @import("sql");
2022-11-21 11:58:54 +00:00
const util = @import("util");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
2022-12-05 10:12:40 +00:00
pub const FileStatus = enum {
uploading,
uploaded,
external,
deleted,
2022-12-06 09:48:36 +00:00
pub const jsonStringify = util.jsonSerializeEnumAsString;
2022-11-21 11:58:54 +00:00
};
2022-12-05 10:12:40 +00:00
pub const FileUpload = struct {
2022-11-21 11:58:54 +00:00
id: Uuid,
2022-12-03 15:09:29 +00:00
2022-12-06 09:48:36 +00:00
owner_id: Uuid,
2022-11-21 11:58:54 +00:00
size: usize,
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
filename: []const u8,
description: ?[]const u8,
content_type: ?[]const u8,
2022-12-03 15:09:29 +00:00
sensitive: bool,
2022-12-05 10:12:40 +00:00
status: FileStatus,
2022-11-21 11:58:54 +00:00
created_at: DateTime,
2022-12-03 15:09:29 +00:00
updated_at: DateTime,
};
2022-12-05 10:12:40 +00:00
pub const FileMeta = struct {
2022-12-03 15:09:29 +00:00
filename: []const u8,
description: ?[]const u8,
content_type: ?[]const u8,
sensitive: bool,
2022-11-21 11:58:54 +00:00
};
2022-12-06 09:48:36 +00:00
pub fn get(db: anytype, id: Uuid, alloc: std.mem.Allocator) !FileUpload {
return try db.queryRow(
FileUpload,
\\SELECT
\\ id,
\\ owner_id,
\\ size,
\\ filename,
\\ description,
\\ content_type,
\\ sensitive,
\\ status,
\\ created_at,
\\ updated_at
\\FROM file_upload
\\WHERE id = $1
\\LIMIT 1
,
.{id},
alloc,
);
}
pub const PartialMeta = Partial(FileMeta);
2022-12-05 10:12:40 +00:00
pub fn Partial(comptime T: type) type {
const t_fields = std.meta.fields(T);
2022-12-06 09:48:36 +00:00
var fields: [t_fields.len]std.builtin.Type.StructField = undefined;
2022-12-05 10:12:40 +00:00
for (std.meta.fields(T)) |f, i| fields[i] = .{
.name = f.name,
.field_type = ?f.field_type,
.default_value = &@as(?f.field_type, null),
.is_comptime = false,
.alignment = @alignOf(?f.field_type),
};
return @Type(.{ .Struct = .{
.layout = .Auto,
2022-12-06 09:48:36 +00:00
.fields = &fields,
2022-12-05 10:12:40 +00:00
.decls = &.{},
.is_tuple = false,
} });
2022-12-03 15:09:29 +00:00
}
2022-12-06 09:48:36 +00:00
pub fn update(db: anytype, id: Uuid, meta: PartialMeta, alloc: std.mem.Allocator) !void {
2022-12-05 10:12:40 +00:00
var builder = sql.QueryBuilder.init(alloc);
defer builder.deinit();
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
try builder.appendSlice("UPDATE file_upload");
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
if (meta.filename) |_| try builder.set("filename", "$2");
if (meta.description) |_| try builder.set("description", "$3");
if (meta.content_type) |_| try builder.set("content_type", "$4");
if (meta.sensitive) |_| try builder.set("sensitive", "$5");
2022-12-03 15:09:29 +00:00
2022-12-06 09:48:36 +00:00
if (builder.set_statements_appended == 0) return error.NoChange;
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
try builder.andWhere("id = $1");
2022-12-03 15:09:29 +00:00
2022-12-06 09:48:36 +00:00
std.log.debug("{any}", .{meta});
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
try db.exec(try builder.terminate(), .{
id,
meta.filename orelse null,
meta.description orelse null,
meta.content_type orelse null,
meta.sensitive orelse null,
2022-12-03 15:09:29 +00:00
}, alloc);
}
2022-12-06 09:48:36 +00:00
pub fn create(db: anytype, owner_id: Uuid, meta: FileMeta, data: []const u8, alloc: std.mem.Allocator) !Uuid {
2022-12-05 10:12:40 +00:00
const id = Uuid.randV4(util.getThreadPrng());
const now = DateTime.now();
try db.insert("file_upload", .{
2022-12-03 15:09:29 +00:00
.id = id,
2022-11-21 11:58:54 +00:00
2022-12-06 09:48:36 +00:00
.owner_id = owner_id,
2022-12-05 10:12:40 +00:00
.size = data.len,
.filename = meta.filename,
.description = meta.description,
.content_type = meta.content_type,
.sensitive = meta.sensitive,
2022-12-03 15:09:29 +00:00
2022-12-06 09:48:36 +00:00
.status = FileStatus.uploading,
2022-12-03 15:09:29 +00:00
2022-12-05 10:12:40 +00:00
.created_at = now,
.updated_at = now,
2022-12-03 15:09:29 +00:00
}, alloc);
2022-12-05 10:12:40 +00:00
saveFile(id, data) catch |err| {
db.exec("DELETE FROM file_upload WHERE ID = $1", .{id}, alloc) catch |e| {
2022-12-06 09:48:36 +00:00
std.log.err("Unable to remove file {} record in DB: {}", .{ id, e });
2022-12-05 10:12:40 +00:00
};
return err;
};
try db.exec(
\\UPDATE file_upload
\\SET status = 'uploaded'
\\WHERE id = $1
, .{id}, alloc);
2022-12-06 09:48:36 +00:00
return id;
2022-12-03 15:09:29 +00:00
}
2022-12-05 10:12:40 +00:00
pub fn delete(db: anytype, id: Uuid, alloc: std.mem.Allocator) !void {
var dir = try std.fs.cwd().openDir(data_root, .{});
defer dir.close();
2022-12-03 15:09:29 +00:00
2022-12-06 09:48:36 +00:00
try dir.deleteFile(&id.toCharArray());
2022-11-21 11:58:54 +00:00
2022-12-05 10:12:40 +00:00
try db.exec(
\\DELETE FROM file_upload
\\WHERE id = $1
, .{id}, alloc);
2022-12-03 15:09:29 +00:00
}
2022-11-21 11:58:54 +00:00
2022-12-03 15:09:29 +00:00
const data_root = "./files";
fn saveFile(id: Uuid, data: []const u8) !void {
var dir = try std.fs.cwd().openDir(data_root, .{});
defer dir.close();
2022-11-21 11:58:54 +00:00
2022-12-03 15:09:29 +00:00
var file = try dir.createFile(&id.toCharArray(), .{ .exclusive = true });
defer file.close();
2022-11-21 11:58:54 +00:00
2022-12-03 15:09:29 +00:00
try file.writer().writeAll(data);
try file.sync();
}
2022-11-21 11:58:54 +00:00
2022-12-03 15:09:29 +00:00
pub fn deref(alloc: std.mem.Allocator, id: Uuid) ![]const u8 {
var dir = try std.fs.cwd().openDir(data_root, .{});
defer dir.close();
2022-11-21 11:58:54 +00:00
2022-12-03 15:09:29 +00:00
return dir.readFileAlloc(alloc, &id.toCharArray(), 1 << 32);
}