Add migrations, add private streams
This commit is contained in:
parent
cade2df6a5
commit
59b3dc6f68
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ obj/
|
||||||
/packages/
|
/packages/
|
||||||
/data/
|
/data/
|
||||||
app.db
|
app.db
|
||||||
|
app.db.bak
|
||||||
|
|
|
@ -2,26 +2,26 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash.Controllers {
|
namespace RTMPDash.Controllers;
|
||||||
[ApiController, Route("/api/authenticate")]
|
|
||||||
public class RtmpAuthController : ControllerBase {
|
|
||||||
[HttpGet]
|
|
||||||
public string Get() {
|
|
||||||
var db = new AppDb.DbConn();
|
|
||||||
if (!db.Users.Any(p => p.StreamKey == Request.Query["name"])) {
|
|
||||||
Response.StatusCode = 403;
|
|
||||||
return "unauthorized";
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = db.Users.FirstOrDefault(p => p.StreamKey == Request.Query["name"]);
|
[ApiController, Route("/api/authenticate")]
|
||||||
|
public class RtmpAuthController : ControllerBase {
|
||||||
Response.Headers.Add("x-rtmp-user", user!.Username);
|
[HttpGet]
|
||||||
|
public string Get() {
|
||||||
if (user.AllowRestream && !string.IsNullOrWhiteSpace(user.RestreamTargets))
|
var db = new AppDb.DbConn();
|
||||||
Response.Headers.Add("x-rtmp-target", user.RestreamTargets);
|
if (!db.Users.Any(p => p.StreamKey == Request.Query["name"])) {
|
||||||
|
Response.StatusCode = 403;
|
||||||
Response.StatusCode = 302;
|
return "unauthorized";
|
||||||
return "authorized as " + user!.Username;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var user = db.Users.FirstOrDefault(p => p.StreamKey == Request.Query["name"]);
|
||||||
|
|
||||||
|
Response.Headers.Add("x-rtmp-user", user!.IsPrivate ? user!.PrivateAccessKey : user!.Username);
|
||||||
|
|
||||||
|
if (user.AllowRestream && !string.IsNullOrWhiteSpace(user.RestreamTargets))
|
||||||
|
Response.Headers.Add("x-rtmp-target", user.RestreamTargets);
|
||||||
|
|
||||||
|
Response.StatusCode = 302;
|
||||||
|
return "authorized as " + user!.Username;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,35 +5,32 @@ using LinqToDB.Configuration;
|
||||||
using LinqToDB.Data;
|
using LinqToDB.Data;
|
||||||
using RTMPDash.DataModels.Tables;
|
using RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
namespace RTMPDash.DataModels {
|
namespace RTMPDash.DataModels;
|
||||||
public class AppDb {
|
|
||||||
public class ConnectionStringSettings : IConnectionStringSettings {
|
|
||||||
public string ConnectionString { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string ProviderName { get; set; }
|
|
||||||
public bool IsGlobal => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Settings : ILinqToDBSettings {
|
public class AppDb {
|
||||||
public IEnumerable<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
|
public class ConnectionStringSettings : IConnectionStringSettings {
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ProviderName { get; set; }
|
||||||
|
public bool IsGlobal => false;
|
||||||
|
}
|
||||||
|
|
||||||
public string DefaultConfiguration => "SQLite";
|
public class Settings : ILinqToDBSettings {
|
||||||
public string DefaultDataProvider => "SQLite";
|
public IEnumerable<IDataProviderSettings> DataProviders => Enumerable.Empty<IDataProviderSettings>();
|
||||||
|
|
||||||
public IEnumerable<IConnectionStringSettings> ConnectionStrings {
|
public string DefaultConfiguration => "SQLite";
|
||||||
get {
|
public string DefaultDataProvider => "SQLite";
|
||||||
yield return new ConnectionStringSettings {
|
|
||||||
Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=app.db;"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DbConn : DataConnection {
|
public IEnumerable<IConnectionStringSettings> ConnectionStrings {
|
||||||
public DbConn() : base("db") { }
|
get { yield return new ConnectionStringSettings { Name = "db", ProviderName = "SQLite", ConnectionString = @"Data Source=app.db;" }; }
|
||||||
|
|
||||||
public ITable<User> Users => GetTable<User>();
|
|
||||||
public ITable<Invite> Invites => GetTable<Invite>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DbConn : DataConnection {
|
||||||
|
public DbConn() : base("db") { }
|
||||||
|
|
||||||
|
public ITable<User> Users => GetTable<User>();
|
||||||
|
public ITable<Invite> Invites => GetTable<Invite>();
|
||||||
|
public ITable<DbInfo> DbInfo => GetTable<DbInfo>();
|
||||||
|
}
|
||||||
}
|
}
|
9
DataModels/Tables/DbInfo.cs
Normal file
9
DataModels/Tables/DbInfo.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
using LinqToDB.Mapping;
|
||||||
|
|
||||||
|
namespace RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
|
[Table(Name = "DbInfo")]
|
||||||
|
public class DbInfo {
|
||||||
|
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
||||||
|
[Column(Name = "DbVer"), NotNull] public int DbVer { get; set; }
|
||||||
|
}
|
|
@ -1,18 +1,20 @@
|
||||||
using LinqToDB.Mapping;
|
using LinqToDB.Mapping;
|
||||||
|
|
||||||
namespace RTMPDash.DataModels.Tables {
|
namespace RTMPDash.DataModels.Tables;
|
||||||
[Table(Name = "Users")]
|
|
||||||
public class User {
|
[Table(Name = "Users")]
|
||||||
[Column(Name = "Username"), PrimaryKey] public string Username { get; set; }
|
public class User {
|
||||||
[Column(Name = "Password")] public string Password { get; set; }
|
[Column(Name = "Username"), PrimaryKey] public string Username { get; set; }
|
||||||
[Column(Name = "IsAdmin")] public bool IsAdmin { get; set; }
|
[Column(Name = "Password")] public string Password { get; set; }
|
||||||
[Column(Name = "AllowRestream")] public bool AllowRestream { get; set; }
|
[Column(Name = "IsAdmin")] public bool IsAdmin { get; set; }
|
||||||
[Column(Name = "StreamKey")] public string StreamKey { get; set; }
|
[Column(Name = "AllowRestream")] public bool AllowRestream { get; set; }
|
||||||
[Column(Name = "PronounSubject")] public string PronounSubject { get; set; }
|
[Column(Name = "StreamKey")] public string StreamKey { get; set; }
|
||||||
[Column(Name = "PronounPossessive")] public string PronounPossessive { get; set; }
|
[Column(Name = "PronounSubject")] public string PronounSubject { get; set; }
|
||||||
[Column(Name = "ChatUrl")] public string ChatUrl { get; set; }
|
[Column(Name = "PronounPossessive")] public string PronounPossessive { get; set; }
|
||||||
[Column(Name = "AnnouncementUrl")] public string AnnouncementUrl { get; set; }
|
[Column(Name = "ChatUrl")] public string ChatUrl { get; set; }
|
||||||
[Column(Name = "RestreamTargets")] public string RestreamTargets { get; set; }
|
[Column(Name = "AnnouncementUrl")] public string AnnouncementUrl { get; set; }
|
||||||
[Column(Name = "RestreamUrls")] public string RestreamUrls { get; set; }
|
[Column(Name = "RestreamTargets")] public string RestreamTargets { get; set; }
|
||||||
}
|
[Column(Name = "RestreamUrls")] public string RestreamUrls { get; set; }
|
||||||
|
[Column(Name = "IsPrivate")] public bool IsPrivate { get; set; }
|
||||||
|
[Column(Name = "PrivateAccessKey")] public string PrivateAccessKey { get; set; }
|
||||||
}
|
}
|
75
Migrations.cs
Normal file
75
Migrations.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Data;
|
||||||
|
using RTMPDash.DataModels;
|
||||||
|
using RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
|
namespace RTMPDash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Initial migrations should create all tables & an admin user and print a randomly generated password for the admin user
|
||||||
|
* *
|
||||||
|
*/
|
||||||
|
public static class Migrations {
|
||||||
|
public const int DbVer = 1;
|
||||||
|
|
||||||
|
private static readonly List<Migration> _migrations = new() {
|
||||||
|
new Migration(1, "ALTER TABLE Users ADD IsPrivate INTEGER DEFAULT 0 NOT NULL"), new Migration(1, "ALTER TABLE Users ADD PrivateAccessKey TEXT")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void RunMigrations() {
|
||||||
|
using var db = new AppDb.DbConn();
|
||||||
|
|
||||||
|
if (db.DataProvider.GetSchemaProvider().GetSchema(db).Tables.All(t => t.TableName != "DbInfo")) {
|
||||||
|
db.CreateTable<DbInfo>();
|
||||||
|
db.InsertWithIdentity(new DbInfo { DbVer = 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbinfo = db.DbInfo.First();
|
||||||
|
var ccolor = Console.ForegroundColor;
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
Console.WriteLine($"Database version: {db.DbInfo.ToList().First().DbVer}");
|
||||||
|
|
||||||
|
var migrationsToRun = _migrations.FindAll(p => p.IntroducedWithDbVer > db.DbInfo.First().DbVer);
|
||||||
|
if (migrationsToRun.Count == 0) {
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
Console.WriteLine("No migrations to run.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
migrationsToRun.ForEach(RunMigration);
|
||||||
|
dbinfo.DbVer = DbVer;
|
||||||
|
db.Update(dbinfo);
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||||
|
Console.WriteLine($"Database version is now: {DbVer}");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
Console.WriteLine("Finished running migrations.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.ForegroundColor = ccolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunMigration(Migration mig) => mig.Run();
|
||||||
|
|
||||||
|
private class Migration {
|
||||||
|
private readonly string _sql;
|
||||||
|
public readonly int IntroducedWithDbVer;
|
||||||
|
|
||||||
|
public Migration(int introducedWithDbVer, string sql) {
|
||||||
|
IntroducedWithDbVer = introducedWithDbVer;
|
||||||
|
_sql = sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run() {
|
||||||
|
using var db = new AppDb.DbConn();
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkCyan;
|
||||||
|
Console.Write("Running migration: ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
Console.WriteLine(_sql);
|
||||||
|
db.Execute(_sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,11 +24,29 @@ else {
|
||||||
<p>Thanks for using @Program.SiteName. If you have any issues, please contact me on @Html.Raw(Program.ContactInfo)</p>
|
<p>Thanks for using @Program.SiteName. If you have any issues, please contact me on @Html.Raw(Program.ContactInfo)</p>
|
||||||
<hr/>
|
<hr/>
|
||||||
<p>Please subscribe to the <a href="@Program.ServiceAnnouncementUrl" target="_blank">Service Announcements Channel</a> to get informed about maintenance and other important things.</p>
|
<p>Please subscribe to the <a href="@Program.ServiceAnnouncementUrl" target="_blank">Service Announcements Channel</a> to get informed about maintenance and other important things.</p>
|
||||||
<hr>
|
|
||||||
<p class="mb-0">When using OBS, the stream key and leading slash before it is to be left out in the RTMP url.</p>
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<p class="mb-0">For low-latancy streams, please set your keyframe interval to 1-2 seconds. Otherwise, automatic or something in the range of 4-8 seconds is fine.</p>
|
<p class="mb-0">For low-latancy streams, please set your keyframe interval to 1-2 seconds. Otherwise, automatic or something in the range of 4-8 seconds is fine.</p>
|
||||||
</div>
|
</div>
|
||||||
|
if (StreamUtils.ListLiveUsers().Contains(user.Username) && user.IsPrivate) {
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<h4 class="alert-heading">Warning!</h4>
|
||||||
|
<p class="mb-0">
|
||||||
|
You set your stream to private, but have not restarted your stream since.
|
||||||
|
<br/>
|
||||||
|
While this setting is applied immediately, the old player URL will remain accessible until you stop and restart your stream.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (StreamUtils.ListLiveUsers().Contains(user.PrivateAccessKey) && !user.IsPrivate) {
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<h4 class="alert-heading">Warning!</h4>
|
||||||
|
<p class="mb-0">
|
||||||
|
You set your stream to public, but have not restarted your stream since.
|
||||||
|
<br/>
|
||||||
|
While this setting is applied immediately, the public player will not work until you restart your stream.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text" style="width:23ch">Profile URL</span>
|
<span class="input-group-text" style="width:23ch">Profile URL</span>
|
||||||
|
@ -42,10 +60,21 @@ else {
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text" style="width:23ch">Player URL</span>
|
<span class="input-group-text" style="width:23ch">Player URL</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" id="input-playerurl" value="@Program.PlayerDomain/@user.Username" disabled>
|
@if (user.IsPrivate) {
|
||||||
<div class="input-group-append">
|
<input type="text" class="form-control" id="input-playerurl" value="@Program.PlayerDomain/@user.PrivateAccessKey" disabled>
|
||||||
<button class="btn btn-outline-secondary" role="button" id="button-copy-playerurl" onclick="copyToClipboard(document.getElementById('input-playerurl').value);">Copy</button>
|
<div class="input-group-append">
|
||||||
</div>
|
<button onclick="ajax_and_reload('private_toggle')" class="btn btn-outline-info" role="button" id="button-toggle-private">Private</button>
|
||||||
|
<button class="btn btn-outline-secondary" role="button" id="button-copy-playerurl" onclick="copyToClipboard(document.getElementById('input-playerurl').value);">Copy</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<input type="text" class="form-control" id="input-playerurl" value="@Program.PlayerDomain/@user.Username" disabled>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button onclick="ajax_and_reload('private_toggle')" class="btn btn-outline-success" role="button" id="button-toggle-private">Public</button>
|
||||||
|
<button class="btn btn-outline-secondary" role="button" id="button-copy-playerurl" onclick="copyToClipboard(document.getElementById('input-playerurl').value);">Copy</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
|
@ -61,9 +90,10 @@ else {
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text" style="width:23ch">Stream URL</span>
|
<span class="input-group-text" style="width:23ch">Stream URL</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" id="input-streamurl" value="@Program.IngressDomain/ingress/@user.StreamKey" disabled>
|
<input type="text" class="form-control" id="input-streamurl" value="@Program.IngressDomain/ingress" disabled>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
@if (StreamUtils.IsLive(user.Username, stats)) {
|
@* ReSharper disable once ConvertIfStatementToSwitchStatement *@
|
||||||
|
@if (!user.IsPrivate && StreamUtils.IsLive(user.Username, stats)) {
|
||||||
var uptime = TimeSpan.FromMilliseconds(StreamUtils.GetClientTime(user.Username, stats)).StripMilliseconds();
|
var uptime = TimeSpan.FromMilliseconds(StreamUtils.GetClientTime(user.Username, stats)).StripMilliseconds();
|
||||||
if (user.AllowRestream && !string.IsNullOrWhiteSpace(user.RestreamTargets)) {
|
if (user.AllowRestream && !string.IsNullOrWhiteSpace(user.RestreamTargets)) {
|
||||||
if (StreamUtils.GetClientTime(user.Username, stats) > 5000) {
|
if (StreamUtils.GetClientTime(user.Username, stats) > 5000) {
|
||||||
|
@ -84,6 +114,27 @@ else {
|
||||||
}
|
}
|
||||||
<button class="btn btn-dark" role="button" disabled>@uptime.ToString("c")</button>
|
<button class="btn btn-dark" role="button" disabled>@uptime.ToString("c")</button>
|
||||||
}
|
}
|
||||||
|
else if (user.IsPrivate && StreamUtils.IsLive(user.PrivateAccessKey, stats)) {
|
||||||
|
var uptime = TimeSpan.FromMilliseconds(StreamUtils.GetClientTime(user.PrivateAccessKey, stats)).StripMilliseconds();
|
||||||
|
if (user.AllowRestream && !string.IsNullOrWhiteSpace(user.RestreamTargets)) {
|
||||||
|
if (StreamUtils.GetClientTime(user.Username, stats) > 5000) {
|
||||||
|
var restreams = StreamUtils.CountLiveRestreams(user.PrivateAccessKey, stats);
|
||||||
|
if (restreams > 0) {
|
||||||
|
<button class="btn btn-success" role="button" style="width:20ch" disabled>Live & restreaming</button>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<button class="btn btn-warning" role="button" style="width:22ch" disabled>Live & restream down</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<button class="btn btn-dark" role="button" style="width:13ch" disabled>Starting...</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<button class="btn btn-success" role="button" style="width:13ch" disabled>Live</button>
|
||||||
|
}
|
||||||
|
<button class="btn btn-dark" role="button" disabled>@uptime.ToString("c")</button>
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
<button class="btn btn-danger" role="button" style="width:13ch" disabled>No data</button>
|
<button class="btn btn-danger" role="button" style="width:13ch" disabled>No data</button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,80 +5,84 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class DashboardModel : PageModel {
|
|
||||||
public void OnGet() { }
|
|
||||||
|
|
||||||
public void OnPost() {
|
public class DashboardModel : PageModel {
|
||||||
if (!Request.HasFormContentType
|
public void OnGet() { }
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["action"])
|
|
||||||
|| string.IsNullOrWhiteSpace(HttpContext.Session.GetString("authenticatedUser")))
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var db = new AppDb.DbConn();
|
public void OnPost() {
|
||||||
var user = db.Users.FirstOrDefault(p => p.Username == HttpContext.Session.GetString("authenticatedUser"));
|
if (!Request.HasFormContentType || string.IsNullOrWhiteSpace(Request.Form["action"]) || string.IsNullOrWhiteSpace(HttpContext.Session.GetString("authenticatedUser")))
|
||||||
|
return;
|
||||||
|
|
||||||
if (Request.Form["action"] == "password_change") {
|
using var db = new AppDb.DbConn();
|
||||||
var newPass = Request.Form["pass"];
|
var user = db.Users.FirstOrDefault(p => p.Username == HttpContext.Session.GetString("authenticatedUser"));
|
||||||
user!.Password = newPass.ToString().Sha256();
|
|
||||||
db.Update(user);
|
|
||||||
Response.Redirect("/Logout");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Request.Form["action"] == "chaturl_set") {
|
if (Request.Form["action"] == "password_change") {
|
||||||
user!.ChatUrl = Request.Form["value"];
|
var newPass = Request.Form["pass"];
|
||||||
|
user!.Password = newPass.ToString().Sha256();
|
||||||
|
db.Update(user);
|
||||||
|
Response.Redirect("/Logout");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Request.Form["action"] == "chaturl_set") {
|
||||||
|
user!.ChatUrl = Request.Form["value"];
|
||||||
|
db.Update(user);
|
||||||
|
Response.Redirect("/Dashboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Request.Form["action"] == "announceurl_set") {
|
||||||
|
user!.AnnouncementUrl = Request.Form["value"];
|
||||||
|
db.Update(user);
|
||||||
|
Response.Redirect("/Dashboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user!.AllowRestream) {
|
||||||
|
if (Request.Form["action"] == "restream_urls_set") {
|
||||||
|
user!.RestreamUrls = Request.Form["value"];
|
||||||
db.Update(user);
|
db.Update(user);
|
||||||
Response.Redirect("/Dashboard");
|
Response.Redirect("/Dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Request.Form["action"] == "announceurl_set") {
|
if (Request.Form["action"] == "restream_targets_set") {
|
||||||
user!.AnnouncementUrl = Request.Form["value"];
|
var newtgts = Request.Form["value"].ToString();
|
||||||
|
if (newtgts.Contains("localhost") || newtgts.Contains("127.0.0.1") || newtgts.Contains(user.StreamKey))
|
||||||
|
return;
|
||||||
|
|
||||||
|
user!.RestreamTargets = newtgts;
|
||||||
db.Update(user);
|
db.Update(user);
|
||||||
Response.Redirect("/Dashboard");
|
Response.Redirect("/Dashboard");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (user!.AllowRestream) {
|
if (Request.Form["action"] == "pronoun_subj_set") {
|
||||||
if (Request.Form["action"] == "restream_urls_set") {
|
var target = string.IsNullOrWhiteSpace(Request.Form["value"]) ? "they" : Request.Form["value"].ToString();
|
||||||
user!.RestreamUrls = Request.Form["value"];
|
user!.PronounSubject = target.ToLowerInvariant();
|
||||||
db.Update(user);
|
db.Update(user);
|
||||||
Response.Redirect("/Dashboard");
|
Response.Redirect("/Dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Request.Form["action"] == "restream_targets_set") {
|
if (Request.Form["action"] == "pronoun_poss_set") {
|
||||||
var newtgts = Request.Form["value"].ToString();
|
var target = string.IsNullOrWhiteSpace(Request.Form["value"]) ? "their" : Request.Form["value"].ToString();
|
||||||
if (newtgts.Contains("localhost")
|
user!.PronounPossessive = target.ToLowerInvariant();
|
||||||
|| newtgts.Contains("127.0.0.1")
|
db.Update(user);
|
||||||
|| newtgts.Contains(user.StreamKey))
|
Response.Redirect("/Dashboard");
|
||||||
return;
|
}
|
||||||
|
|
||||||
user!.RestreamTargets = newtgts;
|
if (Request.Form["action"] == "streamkey_reset") {
|
||||||
db.Update(user);
|
user!.StreamKey = Guid.NewGuid().ToString();
|
||||||
Response.Redirect("/Dashboard");
|
db.Update(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Request.Form["action"] == "private_toggle") {
|
||||||
|
if (user.IsPrivate) {
|
||||||
|
user!.IsPrivate = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
user!.PrivateAccessKey = Guid.NewGuid().ToString();
|
||||||
|
user!.IsPrivate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Request.Form["action"] == "pronoun_subj_set") {
|
db.Update(user);
|
||||||
var target = string.IsNullOrWhiteSpace(Request.Form["value"])
|
|
||||||
? "they"
|
|
||||||
: Request.Form["value"].ToString();
|
|
||||||
user!.PronounSubject = target.ToLowerInvariant();
|
|
||||||
db.Update(user);
|
|
||||||
Response.Redirect("/Dashboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Request.Form["action"] == "pronoun_poss_set") {
|
|
||||||
var target = string.IsNullOrWhiteSpace(Request.Form["value"])
|
|
||||||
? "their"
|
|
||||||
: Request.Form["value"].ToString();
|
|
||||||
user!.PronounPossessive = target.ToLowerInvariant();
|
|
||||||
db.Update(user);
|
|
||||||
Response.Redirect("/Dashboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Request.Form["action"] == "streamkey_reset") {
|
|
||||||
user!.StreamKey = Guid.NewGuid().ToString();
|
|
||||||
db.Update(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
@page
|
@page
|
||||||
|
@using RTMPDash.DataModels
|
||||||
@model IndexModel
|
@model IndexModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Home";
|
ViewData["Title"] = "Home";
|
||||||
var liveUsers = StreamUtils.ListLiveUsers();
|
var db = new AppDb.DbConn();
|
||||||
|
var allStreams = StreamUtils.ListLiveUsers();
|
||||||
|
var allUsers = db.Users.Where(p => !p.IsPrivate).Select(p => p.Username);
|
||||||
|
var liveUsers = allStreams.Intersect(allUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">Welcome</h1>
|
<h1 class="display-4">Welcome</h1>
|
||||||
@if (liveUsers.Count > 0) {
|
@if (liveUsers.Any()) {
|
||||||
<p>The following users are currently live:</p>
|
<p>The following users are currently live:</p>
|
||||||
<div class="btn-group btn-group" role="group">
|
<div class="btn-group btn-group" role="group">
|
||||||
@foreach (var user in liveUsers) {
|
@foreach (var user in liveUsers) {
|
||||||
|
|
|
@ -6,40 +6,46 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
using RTMPDash.DataModels.Tables;
|
using RTMPDash.DataModels.Tables;
|
||||||
|
|
||||||
namespace RTMPDash.Pages {
|
namespace RTMPDash.Pages;
|
||||||
public class RegisterModel : PageModel {
|
|
||||||
public void OnPost() {
|
|
||||||
if (!Request.HasFormContentType
|
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["user"])
|
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["pass"])
|
|
||||||
|| string.IsNullOrWhiteSpace(Request.Form["code"]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
using var db = new AppDb.DbConn();
|
public class RegisterModel : PageModel {
|
||||||
if (!db.Invites.Any(p => p.Code == Request.Form["code"]))
|
public void OnPost() {
|
||||||
return;
|
if (!Request.HasFormContentType
|
||||||
|
|| string.IsNullOrWhiteSpace(Request.Form["user"])
|
||||||
|
|| string.IsNullOrWhiteSpace(Request.Form["pass"])
|
||||||
|
|| string.IsNullOrWhiteSpace(Request.Form["code"]))
|
||||||
|
return;
|
||||||
|
|
||||||
var user = db.Users.FirstOrDefault(p => p.Username == Request.Form["user"].ToString());
|
using var db = new AppDb.DbConn();
|
||||||
if (user != null) {
|
if (!db.Invites.Any(p => p.Code == Request.Form["code"]))
|
||||||
//user already exists
|
return;
|
||||||
Response.Redirect("/Register?e=user_exists");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = new User {
|
var user = db.Users.FirstOrDefault(p => p.Username == Request.Form["user"].ToString());
|
||||||
Username = Request.Form["user"].ToString(),
|
if (user != null) {
|
||||||
Password = Request.Form["pass"].ToString().Sha256(),
|
//user already exists
|
||||||
StreamKey = Guid.NewGuid().ToString(),
|
Response.Redirect("/Register?e=user_exists");
|
||||||
PronounSubject = "they",
|
return;
|
||||||
PronounPossessive = "their",
|
|
||||||
AllowRestream = true
|
|
||||||
};
|
|
||||||
|
|
||||||
db.Insert(user);
|
|
||||||
|
|
||||||
db.Delete(db.Invites.First(p => p.Code == Request.Form["code"]));
|
|
||||||
|
|
||||||
HttpContext.Session.SetString("authenticatedUser", user.Username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (db.Users.Any(p => p.StreamKey == Request.Form["user"] || p.PrivateAccessKey == Request.Form["user"])) {
|
||||||
|
//user invalid
|
||||||
|
Response.Redirect("/Register?e=user_invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = new User {
|
||||||
|
Username = Request.Form["user"].ToString(),
|
||||||
|
Password = Request.Form["pass"].ToString().Sha256(),
|
||||||
|
StreamKey = Guid.NewGuid().ToString(),
|
||||||
|
PronounSubject = "they",
|
||||||
|
PronounPossessive = "their",
|
||||||
|
AllowRestream = true
|
||||||
|
};
|
||||||
|
|
||||||
|
db.Insert(user);
|
||||||
|
|
||||||
|
db.Delete(db.Invites.First(p => p.Code == Request.Form["code"]));
|
||||||
|
|
||||||
|
HttpContext.Session.SetString("authenticatedUser", user.Username);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@
|
||||||
}
|
}
|
||||||
var user = db.Users.First(p => p.Username == Model.User);
|
var user = db.Users.First(p => p.Username == Model.User);
|
||||||
var stats = StreamUtils.GetStatsObject();
|
var stats = StreamUtils.GetStatsObject();
|
||||||
var live = StreamUtils.IsLive(user.Username, stats);
|
var live = StreamUtils.IsLive(user.Username, stats) && !user.IsPrivate;
|
||||||
Stream stream = null;
|
Stream stream = null;
|
||||||
if (live) {
|
if (live) {
|
||||||
stream = stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.FirstOrDefault(p => p.Name == user.Username);
|
stream = stats.Server.Applications.First(p => p.Name == "ingress").MethodLive.Streams.FirstOrDefault(p => p.Name == user.Username);
|
||||||
|
|
50
Program.cs
50
Program.cs
|
@ -5,34 +5,34 @@ using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using RTMPDash.DataModels;
|
using RTMPDash.DataModels;
|
||||||
|
|
||||||
namespace RTMPDash {
|
namespace RTMPDash;
|
||||||
public class Program {
|
|
||||||
public const string SiteName = "chaos.stream";
|
|
||||||
public const string IngressDomain = "rtmp://chaos.stream";
|
|
||||||
public const string RootDomain = "https://chaos.stream";
|
|
||||||
public const string PlayerDomain = "https://live.on.chaos.stream";
|
|
||||||
public const string FragmentDomain = "https://cdn.chaos.stream";
|
|
||||||
public const string StatsDomain = "https://stats.chaos.stream";
|
|
||||||
public const string PrivacyEmail = "chaosstream-privacy@zotan.email";
|
|
||||||
public const string CopyrightEmail = "chaosstream-copyright@zotan.email";
|
|
||||||
public const string AbuseEmail = "chaosstream-abuse@zotan.email";
|
|
||||||
public const string ServiceAnnouncementUrl = "https://t.me/chaosstream";
|
|
||||||
public const string ServiceStatusUrl = "https://status.chaos.stream";
|
|
||||||
|
|
||||||
public const string ContactInfo =
|
public class Program {
|
||||||
"<a href=\"https://t.me/zotan\" target=\"_blank\">Telegram</a>, <a href=\"https://threema.id/S59S9U8J\" target=\"_blank\">Threema</a>, or via <a href=\"mailto:chaosstream-contact@zotan.email\" target=\"_blank\">email</a>.";
|
public const string SiteName = "chaos.stream";
|
||||||
|
public const string IngressDomain = "rtmp://chaos.stream";
|
||||||
|
public const string RootDomain = "https://chaos.stream";
|
||||||
|
public const string PlayerDomain = "https://live.on.chaos.stream";
|
||||||
|
public const string FragmentDomain = "https://cdn.chaos.stream";
|
||||||
|
public const string StatsDomain = "https://stats.chaos.stream";
|
||||||
|
public const string PrivacyEmail = "chaosstream-privacy@zotan.email";
|
||||||
|
public const string CopyrightEmail = "chaosstream-copyright@zotan.email";
|
||||||
|
public const string AbuseEmail = "chaosstream-abuse@zotan.email";
|
||||||
|
public const string ServiceAnnouncementUrl = "https://t.me/chaosstream";
|
||||||
|
public const string ServiceStatusUrl = "https://status.chaos.stream";
|
||||||
|
|
||||||
public static void Main(string[] args) {
|
public const string ContactInfo =
|
||||||
DataConnection.DefaultSettings = new AppDb.Settings();
|
"<a href=\"https://t.me/zotan\" target=\"_blank\">Telegram</a>, <a href=\"https://threema.id/S59S9U8J\" target=\"_blank\">Threema</a>, or via <a href=\"mailto:chaosstream-contact@zotan.email\" target=\"_blank\">email</a>.";
|
||||||
ThreadPool.SetMinThreads(100, 100);
|
|
||||||
CreateHostBuilder(args).Build().Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
public static void Main(string[] args) {
|
||||||
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
DataConnection.DefaultSettings = new AppDb.Settings();
|
||||||
|
ThreadPool.SetMinThreads(100, 100);
|
||||||
|
Migrations.RunMigrations();
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TimeExtensions {
|
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||||
public static TimeSpan StripMilliseconds(this TimeSpan time) => new(time.Days, time.Hours, time.Minutes, time.Seconds);
|
}
|
||||||
}
|
|
||||||
|
public static class TimeExtensions {
|
||||||
|
public static TimeSpan StripMilliseconds(this TimeSpan time) => new(time.Days, time.Hours, time.Minutes, time.Seconds);
|
||||||
}
|
}
|
Loading…
Reference in a new issue