public DbWriter GetWriter() { var writer = (DbWriter)dbFactory.CreateWriter(); spawnedWriters.Add(writer); return(writer); }
/// <summary> /// Update our modules with the given module view (parses code, sets up data, etc.) /// </summary> /// <param name="module"></param> /// <returns></returns> public LoadedModule?UpdateModule(ContentView module, bool force = true) { if (!force && loadedModules.ContainsKey(module.name) && loadedModules[module.name].lastRevisionId == module.lastRevisionId) { return(null); } //if(!force && loadedModules.ContainsKey(module.name)) // return null; var getValue = new Func <string, string?>((s) => { if (module.values.ContainsKey(s)) { return(module.values[s].ToString()); } else { return(null); } }); var getValueNum = new Func <string, long>((s) => { long result = 0; long.TryParse(getValue(s), out result); return(result); }); //no matter if it's an update or whatever, have to just rebuild the module var mod = new LoadedModule(); mod.script = new Script(); mod.script.DoString(module.text); //This could take a LONG time. mod.lastRevisionId = module.lastRevisionId; mod.contentId = module.id; var getData = new Func <string, Dictionary <string, string?> >((k) => { var command = mod.dataConnection?.CreateCommand() ?? throw new InvalidOperationException("No connection to module storage!"); command.CommandText = $"SELECT key, value FROM {module.name} WHERE key LIKE $key"; command.Parameters.AddWithValue("$key", k); var result = new Dictionary <string, string?>(); using (var reader = command.ExecuteReader()) { while (reader.Read()) { result[reader.GetString(0)] = reader.IsDBNull(1) ? null : reader.GetString(1); } } return(result); }); var sendRawMessage = new Action <long, long, string>((uid, room, message) => { using var writer = dbFactory.CreateWriter(); var realMessage = new MessageView() { createUserId = mod.currentUserId, contentId = room, receiveUserId = uid, text = message, module = module.name, createDate = DateTime.Now }; //, mod.currentUserId); try { writer.WriteAsync(realMessage, mod.currentUserId).Wait(); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw(); } //addMessage( }); mod.script.Globals["getdata"] = new Func <string, string?>((k) => { var result = getData(k); if (result.ContainsKey(k)) { return(result[k]); } else { return(null); } }); mod.script.Globals["getalldata"] = getData; mod.script.Globals["setdata"] = new Action <string, string>((k, v) => { var command = mod.dataConnection?.CreateCommand() ?? throw new InvalidOperationException("No connection to module storage!"); var vtext = "$value"; command.Parameters.AddWithValue("$key", k); if (v != null) { command.Parameters.AddWithValue("$value", v); } else { vtext = "NULL"; } command.CommandText = $"INSERT OR REPLACE INTO {module.name} (key, value) values($key, {vtext})"; command.ExecuteNonQuery(); }); mod.script.Globals["getvalue"] = getValue; mod.script.Globals["getvaluenum"] = getValueNum; mod.script.Globals["prntdbg"] = new Action <string>((m) => { //mod.debug.Enqueue($"[{mod.currentRequester.userId}:{mod.currentFunction}|{string.Join(",", mod.currentArgs)}] {m} ({DateTime.Now})"); mod.debug.Enqueue($"[{mod.currentUserId}:{mod.currentFunction}|{string.Join(",", mod.currentArgs)}] {m} ({DateTime.Now})"); while (mod.debug.Count > config.MaxDebugSize) { mod.debug.Dequeue(); } }); //mod.script.Globals["sendrawmessage"] = sendRawMessage; mod.script.Globals["usermessage"] = new Action <long, string>((uid, message) => sendRawMessage(uid, mod.currentParentId, message)); //sendRawMessage(uid, 0, message)); mod.script.Globals["broadcastmessage"] = new Action <string>((message) => sendRawMessage(0, mod.currentParentId, message)); var pluralize = new Func <int, string, string>((i, s) => s + (i == 1 ? "" : "s")); var agoize = new Func <double, string, string>((d, s) => (int)d + " " + pluralize((int)d, s) + " ago"); mod.script.Globals["pluralize"] = pluralize; mod.script.Globals["gettimestamp"] = new Func <string>(() => DateTime.Now.ToString()); mod.script.Globals["timesincetimestamp"] = new Func <string, string>((t) => { TimeSpan diff = DateTime.Now - Convert.ToDateTime(t); if (diff.TotalSeconds < 10) { return("Moments ago"); } else if (diff.TotalSeconds < 60) { return(agoize(diff.TotalSeconds, "second")); } else if (diff.TotalMinutes < 60) { return(agoize(diff.TotalMinutes, "minute")); } else if (diff.TotalHours < 24) { return(agoize(diff.TotalHours, "hour")); } else if (diff.TotalDays < 30) { return(agoize(diff.TotalDays, "day")); } else if (diff.TotalDays < 365) { return(agoize(diff.TotalDays / 30, "month")); } else { return(agoize(diff.TotalDays / 365, "year")); } }); mod.script.Globals["b64encode"] = new Func <string, string?>(s => { if (s == null) { return(null); } var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(s); return(System.Convert.ToBase64String(plainTextBytes).Replace('/', '!')); }); mod.script.Globals["b64decode"] = new Func <string, string?>(s => { if (s == null) { return(null); } var base64EncodedBytes = System.Convert.FromBase64String(s.Replace('!', '/')); return(System.Text.Encoding.UTF8.GetString(base64EncodedBytes)); }); mod.script.Globals["getbroadcastid"] = new Func <long>(() => mod.currentParentId); SetupDatabaseForModule(module.name); mod.subcommands = ParseAllSubcommands(mod) ?? new Dictionary <string, ModuleSubcommandInfo?>(); UpdateLoadedModule(module.name, mod); return(mod); }
public async Task <ContentView> UploadFile(ContentView newView, UploadFileConfig fileConfig, Stream fileData, long requester) { fileData.Seek(0, SeekOrigin.Begin); if (fileData.Length == 0) { throw new RequestException("No data uploaded!"); } if (fileConfig.quantize >= 0 && (fileConfig.quantize < config.MinQuantize || fileConfig.quantize > config.MaxQuantize)) { throw new RequestException($"Quantize must be between {config.MinQuantize} and {config.MaxQuantize}"); } int trueQuantize = 0; var tempLocation = GetAndMakeTempPath(); //Work is done locally for quantize/etc var manipResult = await imageManip.FitToSizeAndSave(fileData, tempLocation, config.MaxSize); newView.literalType = manipResult.MimeType; AddImageRender(manipResult.RenderCount + manipResult.LoadCount); try { var meta = new Dictionary <string, object>() { { "size", manipResult.SizeInBytes }, { "width", manipResult.Width }, { "height", manipResult.Height } }; //OK the quantization step. This SHOULD modify the view for us! if (fileConfig.quantize > 0) { trueQuantize = await TryQuantize(fileConfig.quantize, newView, tempLocation); } if (trueQuantize > 0) { meta["quantize"] = trueQuantize; } //We now have the metadata newView.meta = JsonConvert.SerializeObject(meta); using var tempWriter = dbFactory.CreateWriter(); //This is QUITE dangerous: a file could be created in the api first and THEN the file write fails! newView = await tempWriter.WriteAsync(newView, requester); try { await SaveMainDataAsync(tempLocation, newView.hash, newView.literalType ?? ""); } catch (Exception ex) { logger.LogError($"FILE WRITE '{newView.hash}'({newView.id}) FAILED: {ex}"); await tempWriter.DeleteAsync <ContentView>(newView.id, requester); } } finally { System.IO.File.Delete(tempLocation); } //The view is already done. return(newView); }
/// <summary> /// Move all the given messages (by id) to the given parent. Fails early if any message is not found, or a problem is found /// with permissions before the move. May still fail in the middle. /// </summary> /// <param name="messageIds"></param> /// <param name="newParent"></param> /// <param name="requester"></param> /// <param name="message"></param> /// <returns></returns> public async Task <List <MessageView> > RethreadMessagesAsync(List <long> messageIds, long newParent, long requester, string message = "") { using var search = dbFactory.CreateSearch(); using var writer = dbFactory.CreateWriter(); var user = await search.GetById <UserView>(RequestType.user, requester, true); if (string.IsNullOrWhiteSpace(message)) { message = $"Rethreading {messageIds.Count} comments to content {newParent}"; } //Go lookup the messages var messages = await search.SearchSingleType <MessageView>(requester, new SearchRequest() { type = RequestType.message.ToString(), fields = "*", query = "id in @ids", order = "id" }, new Dictionary <string, object>() { { "ids", messageIds } }); var leftovers = messageIds.Except(messages.Select(x => x.id)).ToList(); if (leftovers.Count > 0) { throw new RequestException($"(Precheck): Some messages were not found: {string.Join(",", leftovers)}"); } //Get a copy of them but with the parent reset var newMessages = messages.Select(x => { var m = mapper.Map <MessageView>(x); m.contentId = newParent; return(m); }).OrderBy(x => x.id).ToList(); //Now, verify them all. If ANY of them throw an error, it will happen BEFORE work was performed for (var i = 0; i < newMessages.Count; i++) { await writer.ValidateWorkGeneral(newMessages[i], messages[i], user, UserAction.update); } //OK, now we can write them var result = new List <MessageView>(); foreach (var m in newMessages) { result.Add(await writer.WriteAsync(m, requester, message)); } await writer.WriteAdminLog(new Db.AdminLog() { type = AdminLogType.rethread, initiator = requester, target = newParent, text = $"User {user.username} ({user.id}) rethreaded {result.Count} messages into content {newParent}" }); return(result); }