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-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-05 10:12:40 +00:00
|
|
|
created_by: 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-05 10:12:40 +00:00
|
|
|
pub fn Partial(comptime T: type) type {
|
|
|
|
const t_fields = std.meta.fields(T);
|
|
|
|
var fields: [t_fields]std.builtin.Type.StructField = undefined;
|
|
|
|
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,
|
|
|
|
.fields = fields,
|
|
|
|
.decls = &.{},
|
|
|
|
.is_tuple = false,
|
|
|
|
} });
|
2022-12-03 15:09:29 +00:00
|
|
|
}
|
|
|
|
|
2022-12-05 10:12:40 +00:00
|
|
|
pub fn update(db: anytype, id: Uuid, meta: Partial(FileMeta), alloc: std.mem.Allocator) !void {
|
|
|
|
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-05 10:12:40 +00:00
|
|
|
if (meta.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-05 10:12:40 +00:00
|
|
|
try builder.appendSlice("\nLIMIT 1");
|
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-05 10:12:40 +00:00
|
|
|
pub fn create(db: anytype, created_by: Uuid, meta: FileMeta, data: []const u8, alloc: std.mem.Allocator) !void {
|
|
|
|
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-05 10:12:40 +00:00
|
|
|
.created_by = created_by,
|
|
|
|
.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-05 10:12:40 +00:00
|
|
|
.status = .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| {
|
|
|
|
std.log.err("Unable to remove file record in DB: {}", .{e});
|
|
|
|
};
|
|
|
|
return err;
|
|
|
|
};
|
|
|
|
|
|
|
|
try db.exec(
|
|
|
|
\\UPDATE file_upload
|
|
|
|
\\SET status = 'uploaded'
|
|
|
|
\\WHERE id = $1
|
|
|
|
\\LIMIT 1
|
|
|
|
, .{id}, alloc);
|
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-05 10:12:40 +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
|
|
|
|
\\LIMIT 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);
|
|
|
|
}
|