public static NpgsqlConnection OpenNewConnection() { CommonController.LogDebug("Opening new SQL connection"); var conn = new NpgsqlConnection(GetPgsqlConfig()); conn.Open(); return(conn); }
private static async Task InstallGet(HttpContext context) { CommonController.SetHTMLContentType(context); var playerURL = GetLatestPlayerInstallerURL(); var editorURL = GetLatestEditorInstallerURL(); var templateContext = new TemplateContext(new { playerURL, editorURL }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\install.liquid", templateContext)); }
private static void OnCreate(object sender, FileSystemEventArgs e) { CommonController.LogDebug($"File Created: {e.FullPath}"); try { templateCache.TryAdd(e.FullPath, parser.Parse(File.ReadAllText(e.FullPath))); } catch { Debug.WriteLine("Error updating template: " + e); } }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); CommonController.baseURL = "https://localhost:5001"; } else { app.UseHsts(); CommonController.baseURL = "https://vivista.net"; } CommonController.wwwroot = env.WebRootPath; app.UseStaticFiles(); app.UseTus(context => { if (!context.Request.Path.StartsWithSegments(new PathString("/api/file")) || context.Request.Method == "GET") { return(null); } var guid = new Guid(context.Request.Headers["guid"]); string path = Path.Combine(VideoController.baseFilePath, guid.ToString()); return(new DefaultTusConfiguration { Store = new TusDiskStore(path), UrlPath = "/api/file", Events = new Events { OnAuthorizeAsync = VideoController.AuthorizeUploadTus, OnBeforeCreateAsync = async createContext => Directory.CreateDirectory(path), //NOTE(Simon): Do not allow deleting by someone trying to exploit the protocol OnBeforeDeleteAsync = async deleteContext => deleteContext.FailRequest(""), OnFileCompleteAsync = VideoController.ProcessUploadTus, } }); }); //Task.Run(PeriodicFunction); app.Run(async(context) => { var watch = Stopwatch.StartNew(); var authenticateTask = UserSessions.GetLoggedInUser(context); PrintDebugData(context); SetJSONContentType(context); await authenticateTask; watch.Stop(); CommonController.LogDebug($"request preamble: {watch.Elapsed.TotalMilliseconds} ms"); await router.RouteAsync(context.Request, context); }); }
private static async Task VideosGetApi(HttpContext context) { var args = context.Request.Query; string author = args["author"].ToString().ToLowerInvariant().Trim(); int? userid = null; DateTime?uploadDate; if (!Int32.TryParse(args["offset"].ToString(), out int offset) || offset < 0) { offset = 0; } if (!Int32.TryParse(args["count"].ToString(), out int count) || count > 100 || count < 0) { count = indexCountDefault; } using var connection = Database.OpenNewConnection(); //TODO(Simon): Fuzzy search for username if (!String.IsNullOrEmpty(author)) { userid = await UserController.UserIdFromUsername(author, connection); } if (!Int32.TryParse(args["agedays"].ToString(), out int daysOld) || daysOld < 0) { uploadDate = null; } else { uploadDate = DateTime.UtcNow.AddDays(-daysOld); } var videos = new VideoResponse(); try { //TODO(Simon): There might be a faster way to get the count, while also executing just 1 query: add "count(*) OVER() AS total_count" to query videos.totalcount = await connection.QuerySingleAsync <int>(@"select count(*) from videos v inner join users u on v.userid = u.userid where (@userid::int is NULL or v.userid=@userid) and (@uploadDate::timestamp is NULL or v.timestamp>=@uploadDate)" , new { userid, uploadDate }); videos.videos = await GetIndexVideos(IndexTab.MostWatched, count, offset, connection); videos.count = videos.videos.AsList().Count; videos.page = videos.totalcount > 0 ? offset / videos.totalcount + 1 : 1; await context.Response.Body.WriteAsync(Utf8Json.JsonSerializer.SerializeUnsafe(videos)); } catch (Exception e) { await CommonController.WriteError(context, "Something went wrong while processing this request", StatusCodes.Status500InternalServerError, e); } }
private static void OnChange(object sender, FileSystemEventArgs e) { CommonController.LogDebug($"File Change: {e.FullPath}"); try { string raw = File.ReadAllText(e.FullPath); templateCache[e.FullPath] = parser.Parse(raw); } catch { Debug.WriteLine("Error updating template: " + e); } }
private static void OnRename(object sender, RenamedEventArgs e) { CommonController.LogDebug($"File Renamed: {e.OldFullPath} => {e.FullPath}"); templateCache.Remove(e.OldFullPath); try { templateCache.TryAdd(e.FullPath, parser.Parse(File.ReadAllText(e.FullPath))); } catch { Debug.WriteLine("Error updating template: " + e); } }
public static async Task ProcessUploadTus(FileCompleteContext arg) { CommonController.LogDebug("Begin processing upload"); var context = arg.HttpContext; var headers = context.Request.Headers; if (Enum.TryParse <UploadFileType>(headers["type"].ToString(), out var type)) { if (Guid.TryParse(headers["guid"], out var guid)) { var newFilename = headers["filename"]; //NOTE(Simon): Check if provided filename is a guid if (!String.IsNullOrEmpty(newFilename)) { CommonController.LogDebug("Upload has correct headers"); var path = Path.Combine(baseFilePath, guid.ToString()); var tusFilePath = Path.Combine(path, arg.FileId); string newFilePath; switch (type) { case UploadFileType.Video: case UploadFileType.Meta: case UploadFileType.Tags: case UploadFileType.Chapters: newFilePath = Path.Combine(path, newFilename); break; case UploadFileType.Extra: newFilePath = Path.Combine(path, "extra", newFilename); break; case UploadFileType.Miniature: newFilePath = Path.Combine(path, "areaMiniatures", newFilename); break; default: await CommonController.WriteError(context, "Unknown file type", StatusCodes.Status400BadRequest); return; } CommonController.LogDebug($"Creating directory for {newFilePath} and moving uploaded file there"); Directory.CreateDirectory(Path.GetDirectoryName(newFilePath)); File.Move(tusFilePath, newFilePath, true); await((ITusTerminationStore)arg.Store).DeleteFileAsync(arg.FileId, arg.CancellationToken); } else { await CommonController.WriteError(arg.HttpContext, "The project being uploaded is corrupted", StatusCodes.Status400BadRequest); } } } }
public static async Task <string> Render(HttpContext httpContext, string templateName, TemplateContext context, BaseLayout layout = BaseLayout.Web) { CommonController.LogDebug($"Begin Rendering {templateName}"); var watch = Stopwatch.StartNew(); if (templateCache.TryGetValue(templateName, out var template)) { CommonController.LogDebug("Template found"); var user = await UserSessions.GetLoggedInUser(httpContext); string result; if (context == null) { context = new TemplateContext(); context.Options.MemberAccessStrategy = defaultAccessStrategy; context.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await template.RenderAsync(); } else { //NOTE(Simon): This lib requires users to register all used models by their type name. //NOTE(cont.): This block prevents having to do it manually. context.Options.MemberAccessStrategy = defaultAccessStrategy; context.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await template.RenderAsync(context); } if (layout != BaseLayout.None) { var content = new TemplateContext(new { content = result }); context.Options.MemberAccessStrategy = defaultAccessStrategy; content.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await layoutCache[layout].RenderAsync(content); } watch.Stop(); CommonController.LogDebug($"rendering: {watch.Elapsed.TotalMilliseconds} ms"); return(result); } else { Console.WriteLine("Template not found"); throw new Exception($"Something went wrong while rendering {templateName}"); } }
private static async Task ThumbnailGetApi(HttpContext context) { var args = context.Request.Query; string id = args["id"]; if (Guid.TryParse(id, out _)) { string filename = Path.Combine(baseFilePath, id, "thumb.jpg"); context.Response.Headers.Add(HeaderNames.CacheControl, $"max-age={24 * 60 * 60}"); await CommonController.WriteFile(context, filename, "image/jpg", "thumb.jpg"); } else { await CommonController.Write404(context); } }
private static async Task FilesGetApi(HttpContext context) { var args = context.Request.Query; string videoid = args["videoid"]; if (String.IsNullOrEmpty(videoid)) { await CommonController.Write404(context); return; } if (Guid.TryParse(videoid, out var guid)) { using var connection = Database.OpenNewConnection(); var video = await GetVideo(guid, connection); if (video.isPublic) { try { string path = Path.Combine(baseFilePath, guid.ToString()); var di = new DirectoryInfo(path); var files = di.GetFiles("", SearchOption.AllDirectories); var filenames = files.Select(x => x.FullName.Substring(path.Length + 1)); await context.Response.Body.WriteAsync(Utf8Json.JsonSerializer.SerializeUnsafe(new { array = filenames })); await AddVideoDownload(guid, connection); } catch (Exception e) { await CommonController.WriteError(context, "Something went wrong while processing this request", StatusCodes.Status500InternalServerError, e); } } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task EditVideoPost(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoId)) { var video = await GetVideo(videoId, connection); if (UserOwnsVideo(video, user.userid)) { var form = context.Request.Form; video.title = form["title"]; video.description = form["description"]; //TODO(Simon): Deduplicate tags. Should be cleaned by frontend, but may be malicious data. string[] tags = form["tags"].ToString().Split(','); var deduplicatedTags = new HashSet <string>(tags); video.tags = deduplicatedTags.ToList(); if (Int32.TryParse(form["privacy"], out var privacyInt)) { video.privacy = (VideoPrivacy)privacyInt; } await AddOrUpdateVideo(video, connection); context.Response.Redirect("/my_videos"); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task UserGet(HttpContext context) { CommonController.SetHTMLContentType(context); var username = context.Request.Query["name"].ToString(); if (!String.IsNullOrEmpty(username)) { using var connection = Database.OpenNewConnection(); var user = await UserController.UserFromUsername(username, connection); if (user != null) { const int countPerPage = 20; int page = 1; if (context.Request.Query.ContainsKey("page")) { Int32.TryParse(context.Request.Query["page"], out page); } int offset = (page - 1) * countPerPage; var numVideos = await NumPublicVideosForUser(user.userid, connection); var VideosTask = PublicVideosForUser(user.userid, countPerPage, offset, connection); var pagination = new Pagination(numVideos, countPerPage, offset); var templateContext = new TemplateContext(new { videos = await VideosTask, user, pagination }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\user.liquid", templateContext)); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task ReportBugPostApi(HttpContext context) { var form = context.Request.Form; var problem = form["problem"]; var repro = form["repro"]; var email = form["email"]; if (!String.IsNullOrEmpty(problem) && !String.IsNullOrEmpty(repro)) { using var connection = Database.OpenNewConnection(); bool success = await AddBugReport(problem, repro, email, connection); await context.Response.Body.WriteAsync(Utf8Json.JsonSerializer.SerializeUnsafe(new { success })); } else { await CommonController.WriteError(context, "This request is missing data", StatusCodes.Status400BadRequest); } }
public async Task RouteAsync(HttpRequest request, HttpContext context) { CommonController.LogDebug("Attempt routing"); if (routes.TryGetValue(new Route(request.Method, request.Path), out var del)) { CommonController.LogDebug("Route found"); var watch = Stopwatch.StartNew(); await del.Invoke(context); CommonController.LogDebug("Route function invocation succesful"); watch.Stop(); CommonController.LogDebug($"controller: {watch.Elapsed.TotalMilliseconds} ms"); } else { await routes[new Route("", "404")].Invoke(context); } }
static HTMLRenderer() { var files = Directory.EnumerateFiles(templateDirectory, "*.liquid", SearchOption.AllDirectories); foreach (var file in files) { var normalizedFile = file.Replace('/', '\\'); var rawTemplate = File.ReadAllText(file); var template = parser.Parse(rawTemplate); templateCache[normalizedFile] = template; } CommonController.LogDebug("Templates found:"); foreach (var file in templateCache) { CommonController.LogDebug($"\t{file.Key}"); } InitWatcher(); }
public static async Task <User> GetLoggedInUser(HttpContext context) { CommonController.LogDebug("Begin searching for logged in user"); string sessionToken = context.Request.Cookies["session"]; if (String.IsNullOrEmpty(sessionToken)) { CommonController.LogDebug("No session cookie"); sessionToken = context.Request.Query["token"].ToString(); if (String.IsNullOrEmpty(sessionToken)) { CommonController.LogDebug("No session in query either. No user found"); return(null); } } if (cache[sessionToken] != null) { CommonController.LogDebug("Session in cache"); return((User)cache[sessionToken]); } else { CommonController.LogDebug("Session not in cache. Retrieving from db"); var user = await GetLoggedInUserSkipCache(sessionToken); if (user == null) { CommonController.LogDebug("Bad token. No user found."); return(null); } else { cache.Set(sessionToken, user, defaultPolicy); return(user); } } }
private static async Task IndexGet(HttpContext context) { CommonController.SetHTMLContentType(context); var tabString = context.Request.Query["tab"].ToString(); var tab = tabString switch { "new" => IndexTab.New, "popular" => IndexTab.Popular, _ => IndexTab.MostWatched }; int count = 20; int offset = 0; using var connection = Database.OpenNewConnection(); var videos = await GetIndexVideos(tab, count, offset, connection); var templateContext = new TemplateContext(new { videos, tab = tab.ToString() }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\index.liquid", templateContext)); }
private static async Task UpdateVideoPrivacyPost(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoid)) { var video = await GetVideo(videoid, connection); if (UserOwnsVideo(video, user.userid)) { if (Int32.TryParse(context.Request.Form["video-privacy"], out int privacy)) { await SetVideoPrivacy(video.id, (VideoPrivacy)privacy, connection); } } } context.Response.Redirect("/my_videos"); }
private static async Task FinishUploadApi(HttpContext context) { var form = context.Request.Form; if (Guid.TryParse(form["id"], out var guid)) { var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; var video = await GetVideo(guid, connection); if (user != null && await UserOwnsVideo(guid, user.userid, connection)) { var directoryPath = Path.Combine(baseFilePath, guid.ToString()); var videoPath = Path.Combine(directoryPath, "main.mp4"); var thumbPath = Path.Combine(directoryPath, "thumb.jpg"); var process = new Process(); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; string ffmpegArgs = $"-hide_banner -loglevel error -y -ss 00:00:05 -i {videoPath} -frames:v 1 -q:v 3 -s 720x360 {thumbPath}"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { //process.StartInfo.FileName = @"\bin\ffmpeg.exe"; process.StartInfo.FileName = "/usr/bin/ffmpeg"; process.StartInfo.Arguments = $"{ffmpegArgs}"; //process.StartInfo.Arguments = $"-c ffmpeg {ffmpegArgs}"; CommonController.LogDebug($"Running following command: {process.StartInfo.Arguments}"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { process.StartInfo.FileName = "cmd.exe"; process.StartInfo.Arguments = $"/C ffmpeg.exe {ffmpegArgs}"; CommonController.LogDebug($"Running following command: {process.StartInfo.Arguments}"); } process.Start(); await process.WaitForExitAsync(); CommonController.LogDebug($"Ffmpeg exit code: {process.ExitCode}"); if (process.ExitCode == 0) { CommonController.LogDebug("Thumbnail generated succesfully"); video.privacy = VideoPrivacy.Unlisted; video.downloadsize = (int)GetDirectorySize(new DirectoryInfo(directoryPath)); video.length = await FfmpegGetVideoLength(videoPath); var meta = ReadMetaFile(Path.Combine(directoryPath, "meta.json")); video.title = meta.title; video.description = meta.description; CommonController.LogDebug($"Video Privacy: {video.privacy}"); CommonController.LogDebug($"Video Size: {video.downloadsize}"); CommonController.LogDebug($"Video length: {video.length}"); if (await AddOrUpdateVideo(video, connection)) { CommonController.LogDebug("Database row updated succesfully"); await context.Response.WriteAsJsonAsync(new { success = true }); CleanPartialUploads(video.id); } else { Console.WriteLine("Error while updating database row for video"); await CommonController.WriteError(context, "{}", StatusCodes.Status500InternalServerError); } } else { Console.WriteLine("Error while generating thumbnail"); Console.WriteLine($"\t {process.StandardOutput.ReadToEnd()}"); Console.WriteLine($"\t {process.StandardError.ReadToEnd()}"); await CommonController.WriteError(context, "{}", StatusCodes.Status500InternalServerError); } } else { CommonController.LogDebug("User does not own video"); await CommonController.WriteError(context, "{}", StatusCodes.Status401Unauthorized); } } else { await CommonController.WriteError(context, "{}", StatusCodes.Status400BadRequest); } }