├── .gitignore ├── BlazorDiffusion.Client ├── App.razor ├── Auth │ └── ServiceStackStateProvider.cs ├── BlazorDiffusion.Client.csproj ├── Pages │ ├── Albums.razor │ ├── Albums.razor.cs │ ├── Create.razor │ ├── Create.razor.cs │ ├── Docs.razor │ ├── Favorites.razor │ ├── Favorites.razor.cs │ ├── Index.razor │ ├── Index.razor.cs │ ├── Profile.razor │ ├── SignIn.razor │ ├── SignUp.razor │ ├── Test.razor │ ├── TodoMvc.razor │ ├── admin │ │ ├── Albums.razor │ │ ├── ArtifactComments.razor │ │ ├── Artifacts.razor │ │ ├── Artists.razor │ │ ├── Creatives.razor │ │ ├── Index.razor │ │ ├── Modifiers.razor │ │ ├── SearchStats.razor │ │ ├── Signups.razor │ │ └── _Imports.razor │ └── ssb │ │ ├── Album.razor │ │ ├── Albums.razor │ │ ├── Image.razor │ │ ├── Latest.razor │ │ └── Top.razor ├── Program.cs ├── Properties │ └── launchSettings.json ├── Shared │ ├── AdminLayout.razor │ ├── AlbumTitle.razor │ ├── ArtifactEdit.razor │ ├── ArtifactGallery.razor │ ├── ArtifactGallery.razor.cs │ ├── ArtifactImage.razor │ ├── ArtifactMenu.razor │ ├── ArtifactMenu.razor.cs │ ├── AvatarImage.razor │ ├── CreativeEdit.razor │ ├── EditProfile.razor │ ├── FormLoading.razor │ ├── GettingStarted.razor │ ├── Header.razor │ ├── Home.razor │ ├── Ignore.razor │ ├── MainLayout.razor │ ├── ModalForm.razor │ ├── NewAlbum.razor │ ├── NewReport.razor │ ├── ShellCommand.razor │ ├── SimpleSlideOver.razor │ ├── StaticGallery.razor │ ├── StaticImage.razor │ ├── Tabs.razor │ ├── Tabs.razor.cs │ ├── Toggle.razor │ └── admin │ │ ├── ArtifactAutoQueryGrid.razor │ │ └── ArtifactReportsAutoQueryGrid.razor ├── UI │ ├── AppComponentBase.cs │ ├── AppCss.cs │ ├── ArtifactExtensions.cs │ ├── BlazorModels.cs │ ├── CreativeExtensions.cs │ ├── KeyboardNavigation.cs │ ├── MarkdownUtils.cs │ ├── Ssg.cs │ ├── UiExtensions.cs │ └── UserState.cs ├── _Imports.razor ├── package.json ├── tailwind.config.js ├── tailwind.input.css └── wwwroot │ ├── .nojekyll │ ├── .well-known │ └── microsoft-identity-association.json │ ├── CNAME │ ├── _headers │ ├── _index.html │ ├── _redirects │ ├── appsettings.Production.json │ ├── content │ ├── community-rules.md │ ├── deploy.md │ ├── hosting.md │ └── prerender.md │ ├── css │ ├── app.css │ ├── markdown.css │ └── typography.css │ ├── favicon.ico │ ├── img │ ├── bg-body.svg │ ├── blazor.svg │ ├── blur-cyan.png │ └── blur-indigo.png │ ├── index.html │ ├── js │ ├── artifact-comments.js │ ├── artifact-icons.js │ ├── artifact-info.js │ ├── comments.js │ ├── custom.js │ ├── dtos.mjs │ ├── error-summary.js │ ├── form.js │ ├── input-comment.js │ ├── servicestack-blazor.js │ └── static.js │ ├── latest │ ├── 10.html │ ├── 11.html │ ├── 12.html │ ├── 13.html │ ├── 14.html │ ├── 15.html │ ├── 16.html │ ├── 2.html │ ├── 3.html │ ├── 4.html │ ├── 5.html │ ├── 6.html │ ├── 7.html │ ├── 8.html │ └── 9.html │ ├── lib │ ├── js │ │ ├── htmx.js │ │ └── htmxclasses.js │ └── mjs │ │ ├── servicestack-client.min.mjs │ │ ├── servicestack-client.mjs │ │ ├── vue.min.mjs │ │ └── vue.mjs │ └── tailwind │ ├── README.txt │ └── ServiceStack.Blazor.html ├── BlazorDiffusion.ServiceInterface ├── AdminServices.cs ├── AlbumServices.cs ├── AppUserQuotas.cs ├── ArtifactMetaServices.cs ├── BackgroundMqServices.cs ├── BlazorDiffusion.ServiceInterface.csproj ├── CreativeServerExtensions.cs ├── CreativeService.cs ├── CustomUserSession.cs ├── DataService.cs ├── DbExtensions.cs ├── DbFunctions.cs ├── HtmlTemplate.cs ├── ImageDetails.cs ├── MyServices.cs ├── Score.cs ├── SearchService.cs ├── SsgServies.cs ├── StatUtils.cs ├── TodosServices.cs ├── Validators │ ├── CreativeValidators.cs │ └── FindSimilarArtifactsValidator.cs └── VueServices.cs ├── BlazorDiffusion.ServiceModel ├── Admin.cs ├── Albums.cs ├── Analytics.cs ├── AppConfig.cs ├── AppData.cs ├── AppErrors.cs ├── AppRoles.cs ├── AppUser.cs ├── Artifacts.cs ├── Artists.cs ├── BlazorDiffusion.ServiceModel.csproj ├── Comments.cs ├── Creatives.cs ├── Databases.cs ├── Icons.cs ├── Modifiers.cs ├── Properties │ └── launchSettings.json ├── QuotaError.cs ├── Ssg.cs ├── Tag.cs ├── Tasks.cs ├── Todos.cs └── User.cs ├── BlazorDiffusion.Tests ├── App_Data │ └── seed │ │ ├── album-artifacts.csv │ │ ├── album-likes.csv │ │ ├── albums.csv │ │ ├── artifact-likes.csv │ │ ├── artists.csv │ │ └── modifiers.txt ├── App_Files │ ├── artifacts │ │ └── 2022 │ │ │ └── 10 │ │ │ └── 20 │ │ │ ├── 42753907 │ │ │ ├── metadata.json │ │ │ ├── output_1140958129.png │ │ │ ├── output_1159930719.png │ │ │ ├── output_1437184716.png │ │ │ ├── output_199094354.png │ │ │ ├── output_2922589731.png │ │ │ ├── output_3282754617.png │ │ │ ├── output_3486285260.png │ │ │ ├── output_3529434359.png │ │ │ └── output_663104678.png │ │ │ ├── 47519396 │ │ │ ├── metadata.json │ │ │ ├── output_1359909046.png │ │ │ ├── output_1946842138.png │ │ │ ├── output_1950710229.png │ │ │ ├── output_2102961529.png │ │ │ ├── output_2296583688.png │ │ │ ├── output_390010331.png │ │ │ ├── output_4022298746.png │ │ │ ├── output_4266588710.png │ │ │ └── output_722875130.png │ │ │ └── 54857882 │ │ │ ├── metadata.json │ │ │ ├── output_1290639599.png │ │ │ ├── output_1312358835.png │ │ │ ├── output_1515285252.png │ │ │ ├── output_1676765929.png │ │ │ ├── output_2248701330.png │ │ │ ├── output_2633527491.png │ │ │ ├── output_4023030651.png │ │ │ ├── output_4097140727.png │ │ │ └── output_4294945848.png │ ├── avatars │ │ ├── ciri.png │ │ ├── emma.png │ │ └── out │ │ │ ├── ciri_128.png │ │ │ └── emma_128.png │ └── sync.bat ├── ArtifactTests.cs ├── BlazorDiffusion.Tests.csproj ├── CreativeServiceMockedTests.cs ├── CreativeServiceTests.cs ├── FtsTasks.cs ├── ImageDetailsTests.cs ├── ImportTasks.cs ├── IntegrationTest.cs ├── MigrationTasks.cs ├── PrerenderTasks.cs ├── Properties │ └── launchSettings.json ├── TestDatabase.cs ├── UnitTest.cs └── appsettings.json ├── BlazorDiffusion.sln ├── BlazorDiffusion ├── App_Data │ └── seed │ │ ├── album-artifacts.csv │ │ ├── album-likes.csv │ │ ├── albums.csv │ │ ├── artifact-likes.csv │ │ ├── artists.csv │ │ └── modifiers.txt ├── AssetUtils.cs ├── BlazorDiffusion.csproj ├── Configure.AppHost.cs ├── Configure.Auth.cs ├── Configure.AuthRepository.cs ├── Configure.AutoQuery.cs ├── Configure.Db.Migrations.cs ├── Configure.Db.cs ├── Configure.Mq.cs ├── Configure.Profiling.cs ├── Configure.Ui.cs ├── MarkdownUtils.cs ├── MigrationUtils.cs ├── Migrations │ ├── Migration1000.cs │ ├── Migration1001.cs │ ├── Migration1002.cs │ ├── Migration1003.cs │ └── Migration1004.cs ├── Pages │ ├── Error.cshtml │ ├── Error.cshtml.cs │ ├── Index.cshtml │ ├── Index.cshtml.cs │ ├── Shared │ │ ├── _ArtifactImage.cshtml │ │ ├── _LayoutEmpty.cshtml │ │ ├── _LayoutStatic.cshtml │ │ ├── _StaticGallery.cshtml │ │ └── _StaticImage.cshtml │ ├── _EmptyLayout.cshtml │ ├── _Layout.cshtml │ ├── _ViewImports.cshtml │ ├── _ViewStart.cshtml │ └── ssg │ │ ├── Album.cshtml │ │ ├── Album.cshtml.cs │ │ ├── Albums.cshtml │ │ ├── Empty │ │ └── Home.cshtml │ │ ├── Image.cshtml │ │ ├── Image.cshtml.cs │ │ ├── Latest.cshtml │ │ ├── Latest.cshtml.cs │ │ └── Top.cshtml ├── Program.cs ├── Properties │ └── launchSettings.json ├── R2VirtualFilesProvider.cs ├── StableDiffusionClient.cs ├── TrackingCircuitHandler.cs ├── _index.html ├── appsettings.Development.json ├── appsettings.json └── proto │ ├── dashboard.proto │ ├── engines.proto │ ├── generation.proto │ ├── project.proto │ └── tensors.proto ├── Dockerfile ├── README.md ├── license.txt └── sync.bat /BlazorDiffusion.Client/App.razor: -------------------------------------------------------------------------------- 1 | @inject NavigationManager NavigationManager 2 | @inject ServiceStackStateProvider AuthStateProvider 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 |

Authenticating...

14 | 15 |
16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 |
25 |

404

26 |
27 |
28 |

Page not found

29 |

Please check the URL in the address bar and try again.

30 |
31 |
32 | Go back home 33 | Contact support 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 | @code { 45 | Type GetPageLayout(RouteData route) => route.PageType?.FirstAttribute()?.LayoutType ?? typeof(MainLayout); 46 | 47 | async Task login() 48 | { 49 | var loginUrl = NavigationManager.GetLoginUrl(); 50 | await AuthStateProvider.LogoutIfAuthenticatedAsync(); 51 | NavigationManager.NavigateTo(loginUrl, true); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Auth/ServiceStackStateProvider.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Blazor; 3 | 4 | namespace BlazorDiffusion; 5 | 6 | /// 7 | /// Manages App Authentication State 8 | /// 9 | public class ServiceStackStateProvider : BlazorWasmAuthenticationStateProvider 10 | { 11 | public ServiceStackStateProvider(BlazorWasmAuthContext context, ILogger log) 12 | : base(context, log) { } 13 | } 14 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/BlazorDiffusion.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | enable 5 | enable 6 | 7 | 8 | BlazorDiffusion 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/Albums.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using BlazorDiffusion.Shared; 3 | using BlazorDiffusion.UI; 4 | using Ljbc1994.Blazor.IntersectionObserver; 5 | using Microsoft.AspNetCore.Components; 6 | 7 | namespace BlazorDiffusion.Pages; 8 | 9 | public partial class Albums : AppAuthComponentBase 10 | { 11 | [Inject] IIntersectionObserverService ObserverService { get; set; } = default!; 12 | [Inject] ILogger Log { get; set; } = default!; 13 | 14 | ApiResult api = new(); 15 | List results = new(); 16 | public ElementReference BottomElement { get; set; } 17 | IntersectionObserver? bottomObserver; 18 | 19 | bool hasMore; 20 | 21 | protected override async Task OnParametersSetAsync() 22 | { 23 | await base.OnParametersSetAsync(); 24 | 25 | api = await ApiAsync(new GetAlbumIds()); 26 | if (api.Succeeded) 27 | { 28 | results = await UserState.GetAlbumsByIdsAsync(api.Response!.Results.Take(UserState.InitialTake)); 29 | hasMore = results.Count >= UserState.InitialTake; 30 | } 31 | } 32 | async Task SaveAppPrefsAsync() 33 | { 34 | await UserState.SaveAppPrefsAsync(); 35 | StateHasChanged(); 36 | } 37 | 38 | async Task fetchResults(int count) 39 | { 40 | var nextResults = await UserState.GetAlbumsByIdsAsync(api.Response!.Results.Take(count)); 41 | hasMore = nextResults.Count >= count; 42 | setResults(nextResults); 43 | } 44 | 45 | void setResults(List results) 46 | { 47 | this.results = results; 48 | StateHasChanged(); 49 | } 50 | 51 | async Task loadMore() 52 | { 53 | log("Albums loadMore({0}) {1}...", hasMore, results.Count + UserState.NextPage); 54 | if (hasMore) 55 | { 56 | await fetchResults(results.Count + UserState.NextPage); 57 | } 58 | } 59 | 60 | protected override async Task OnAfterRenderAsync(bool firstRender) 61 | { 62 | if (firstRender) 63 | { 64 | await SetupObserver(); 65 | } 66 | } 67 | 68 | public async Task SetupObserver() 69 | { 70 | try 71 | { 72 | bottomObserver = await ObserverService.Observe(BottomElement, async (entries) => 73 | { 74 | var entry = entries.FirstOrDefault(); 75 | if (entry?.IsIntersecting == true) 76 | { 77 | await loadMore(); 78 | } 79 | StateHasChanged(); 80 | }); 81 | } 82 | catch (Exception e) 83 | { 84 | // throws on initial load 85 | Log.LogError("Albums ObserverService.Observe(BottomElement): {0}", e.ToString()); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/Docs.razor: -------------------------------------------------------------------------------- 1 | @page "/docs/{path?}" 2 | @inherits AppComponentBase 3 | @inject HttpClient Http 4 | @inject IJSRuntime JsRuntime 5 | @using Markdig 6 | @using Markdig.Syntax 7 | 8 | 9 | 10 | 11 | @if (render.Response?.Preview != null) 12 | { 13 |
14 |
15 | @((MarkupString)render.Response!.Preview) 16 |
17 |
18 | } 19 | else if (render.Error == null) 20 | { 21 | 22 | } 23 | 24 | 25 | 26 | @code { 27 | [Parameter] 28 | public string? Path { get; set; } 29 | 30 | ApiResult render { get; set; } = new(); 31 | 32 | async Task loadDoc() => 33 | render = await MarkdownUtils.LoadDocumentAsync(Path!, doc => Http.GetStringAsync($"/content/{doc.FileName}")); 34 | 35 | protected override async Task OnParametersSetAsync() => await loadDoc(); 36 | protected override async Task OnInitializedAsync() => await loadDoc(); 37 | } 38 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/Profile.razor: -------------------------------------------------------------------------------- 1 | @page "/profile" 2 | @attribute [Authorize] 3 | @inherits AppAuthComponentBase 4 | @inject ServiceStackStateProvider AuthProvider 5 | 6 |
7 |
8 |

Profile Overview

9 |
10 |
11 |
12 |
13 | 14 | 15 | 16 |
17 |
18 |

Welcome back,

19 |

@User.GetDisplayName()

20 | @if (User.GetRoles().Length > 0) 21 | { 22 |
23 | @foreach (var role in User.GetRoles()) 24 | { 25 | 27 | @role 28 | 29 | } 30 |
31 | } 32 | @if (User.GetPermissions().Length > 0) 33 | { 34 |
35 | @foreach (var perm in User.GetPermissions()) 36 | { 37 | 39 | @perm 40 | 41 | } 42 |
43 | } 44 |
45 |
46 |
47 | 48 | Sign Out 49 | 50 |
51 |
52 |
53 |
54 |
55 | 56 | @code { 57 | async Task logout() { 58 | await AuthProvider.LogoutAsync(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/SignUp.razor: -------------------------------------------------------------------------------- 1 | @page "/signup" 2 | @inherits AppAuthComponentBase 3 | @inject ServiceStackStateProvider authProvider 4 | @inject NavigationManager NavigationManager 5 | 6 | @if (IsAuthenticated) 7 | { 8 | NavigationManager.NavigateTo(NavigationManager.GetReturnUrl(), true); 9 | return; 10 | } 11 | 12 |

13 | Sign Up 14 |

15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | Sign Up 37 | 38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 46 | @code { 47 | string[] VisibleFields => new[]{ 48 | nameof(request.DisplayName), nameof(request.Email), nameof(request.Password), nameof(request.ConfirmPassword) }; 49 | 50 | Register request = new(); 51 | 52 | ApiResult api = new(); 53 | 54 | void SetUser() 55 | { 56 | request.DisplayName = "New User"; 57 | request.Email = "new@user.com"; 58 | request.Password = request.ConfirmPassword = "p@55wOrd"; 59 | request.AutoLogin = true; 60 | } 61 | 62 | async Task submit() 63 | { 64 | api.ClearErrors(); 65 | 66 | if (request.Email.IsNullOrEmpty()) 67 | api.AddFieldError(nameof(Register.Email), "Email is required"); 68 | 69 | if (request.Password.IsNullOrEmpty()) 70 | api.AddFieldError(nameof(Register.Password), "Password is required"); 71 | else if (request.ConfirmPassword != request.Password) 72 | api.AddFieldError(nameof(Register.ConfirmPassword), "Passwords do not match"); 73 | 74 | if (api.Failed) return; 75 | 76 | api.IsLoading = true; 77 | api = await ApiAsync(request); 78 | 79 | if (api.Succeeded) 80 | { 81 | if (request.AutoLogin == true) 82 | { 83 | await authProvider.SignInAsync(api.Response!); 84 | NavigationManager.NavigateTo(NavigationManager.GetReturnUrl(), true); 85 | } 86 | else 87 | { 88 | NavigationManager.NavigateTo("/signin?return=" + NavigationManager.GetReturnUrl(), true); 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/Test.razor: -------------------------------------------------------------------------------- 1 | @page "/test" 2 | @inject NavigationManager NavigationManager 3 | 4 |

Test

5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 | 15 | @code { 16 | async Task logout() { 17 | NavigationManager.NavigateTo("/auth/logout", forceLoad:true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/ArtifactComments.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/artifact/comments" 2 | @attribute [Authorize(Roles = "Admin")] 3 | 4 | @inject NavigationManager NavigationManager 5 | 6 | 7 | 8 | Artifact Comments 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/Artifacts.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/artifacts" 2 | @attribute [Authorize(Roles = "Admin")] 3 | @using BlazorDiffusion.Shared.admin; 4 | 5 | 6 | 7 | Creative Artifacts 8 | 9 | 10 | 11 | 12 | 13 | @if (Tab == "Reported") 14 | { 15 | 16 | } 17 | else if (Tab == "Stats") 18 | { 19 | 20 | } 21 | else if (Tab == "Comments") 22 | { 23 | 24 | } 25 | else if (Tab == "CommentReports") 26 | { 27 | 28 | } 29 | else //All 30 | { 31 | 32 | } 33 | 34 | @code { 35 | public string? Tab { get; set; } 36 | public Dictionary TabOptions = new() 37 | { 38 | {"All","All"}, 39 | {"Reported","Reported Artifacts"}, 40 | {"Stats","Stats"}, 41 | {"Comments","Comments"}, 42 | {"CommentReports","Reports"}, 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/Artists.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/artists" 2 | @attribute [Authorize(Roles = "Admin")] 3 | @using BlazorDiffusion.Shared.admin; 4 | 5 | @inject NavigationManager NavigationManager 6 | 7 | 8 | 9 | Artists 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/admin" 2 | @attribute [Authorize(Roles = "Admin")] 3 | 4 |
5 |
6 |
7 | Blazor Diffusion 8 |
9 |
10 |
11 |

12 | Creative Admin 13 |

14 |

15 | Manage Creatives support tables 16 |

17 |
18 |
19 | 20 | 21 | 22 | Create and Manage Creatives 23 | 24 | 25 | Create and Manage Creative Artifacts 26 | 27 | 28 | Create and Manage Artists 29 | 30 | 31 | Create and Manage Modifiers 32 | 33 | 34 | Create and Manage Albums 35 | 36 | 37 | View and Manage Signups 38 | 39 | 40 | View Search Stats 41 | 42 | 43 | 44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/Modifiers.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/modifiers" 2 | @attribute [Authorize(Roles = "Admin")] 3 | 4 | @inject NavigationManager NavigationManager 5 | 6 | 7 | 8 | Modifiers 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/SearchStats.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/search-stats" 2 | @attribute [Authorize(Roles = "Admin")] 3 | 4 | @inject NavigationManager NavigationManager 5 | 6 | 7 | 8 | Search Stats 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/Signups.razor: -------------------------------------------------------------------------------- 1 | @page "/admin/signups" 2 | @attribute [Authorize(Roles = "Admin")] 3 | 4 | @inject NavigationManager NavigationManager 5 | 6 | 7 | 8 | Signups 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/admin/_Imports.razor: -------------------------------------------------------------------------------- 1 | @layout AdminLayout -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/ssb/Latest.razor: -------------------------------------------------------------------------------- 1 | @page "/ssb/latest" 2 | @inherits AppComponentBase 3 | 4 |

Latest Images

5 | 6 | 7 | 8 | @code { 9 | public const int GridColumns = 4; 10 | 11 | SearchArtifacts request = new(); 12 | List results = new(); 13 | HashSet resultIds = new(); 14 | bool hasMore; 15 | 16 | ApiResult> api = new(); 17 | GalleryResults GalleryResults = new(); 18 | 19 | protected override async Task OnParametersSetAsync() 20 | { 21 | await base.OnParametersSetAsync(); 22 | 23 | request.Skip = 0; 24 | request.Take = UserState.StaticTake; 25 | request.Show = "latest"; 26 | api = await ApiAsync(request); 27 | clearResults(); 28 | 29 | if (api.Succeeded) 30 | { 31 | if (api.Response?.Results != null) 32 | { 33 | addResults(api.Response.Results); 34 | } 35 | } 36 | } 37 | 38 | void setResults(IEnumerable results) 39 | { 40 | this.results = results.ToList(); 41 | GalleryResults = X.Apply(GalleryResults.Clone(), x => x.Artifacts = this.results.ShuffleGridArtifacts(GridColumns).ToList()); 42 | StateHasChanged(); 43 | } 44 | 45 | void clearResults() 46 | { 47 | results.Clear(); 48 | resultIds.Clear(); 49 | } 50 | 51 | void addResults(List artifacts, bool reset = false) 52 | { 53 | if (reset) 54 | clearResults(); 55 | 56 | hasMore = artifacts.Count >= request.Take; 57 | foreach (var artifact in artifacts) 58 | { 59 | if (resultIds.Contains(artifact.Id)) 60 | continue; 61 | 62 | resultIds.Add(artifact.Id); 63 | results.Add(artifact); 64 | } 65 | setResults(results); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Pages/ssb/Top.razor: -------------------------------------------------------------------------------- 1 | @page "/ssb/top" 2 | @inherits AppComponentBase 3 | 4 |

Top Images

5 | 6 | 7 | 8 | @code { 9 | public const int GridColumns = 4; 10 | 11 | SearchArtifacts request = new(); 12 | List results = new(); 13 | HashSet resultIds = new(); 14 | bool hasMore; 15 | 16 | ApiResult> api = new(); 17 | GalleryResults GalleryResults = new(); 18 | 19 | protected override async Task OnParametersSetAsync() 20 | { 21 | await base.OnParametersSetAsync(); 22 | 23 | request.Skip = 0; 24 | request.Take = UserState.StaticTake; 25 | request.Show = "top"; 26 | api = await ApiAsync(request); 27 | clearResults(); 28 | 29 | if (api.Succeeded) 30 | { 31 | if (api.Response?.Results != null) 32 | { 33 | addResults(api.Response.Results); 34 | } 35 | } 36 | } 37 | 38 | void setResults(IEnumerable results) 39 | { 40 | this.results = results.ToList(); 41 | GalleryResults = X.Apply(GalleryResults.Clone(), x => x.Artifacts = this.results.ShuffleGridArtifacts(GridColumns).ToList()); 42 | StateHasChanged(); 43 | } 44 | 45 | void clearResults() 46 | { 47 | results.Clear(); 48 | resultIds.Clear(); 49 | } 50 | 51 | void addResults(List artifacts, bool reset = false) 52 | { 53 | if (reset) 54 | clearResults(); 55 | 56 | hasMore = artifacts.Count >= request.Take; 57 | foreach (var artifact in artifacts) 58 | { 59 | if (resultIds.Contains(artifact.Id)) 60 | continue; 61 | 62 | resultIds.Add(artifact.Id); 63 | results.Add(artifact); 64 | } 65 | setResults(results); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.AspNetCore.Components.Authorization; 7 | using Microsoft.AspNetCore.Components.Web; 8 | using Blazor.Extensions.Logging; 9 | using ServiceStack.Blazor; 10 | using BlazorDiffusion; 11 | using BlazorDiffusion.UI; 12 | using Ljbc1994.Blazor.IntersectionObserver; 13 | using BlazorDiffusion.ServiceModel; 14 | 15 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 16 | builder.Services.AddLogging(c => c 17 | .AddBrowserConsole() 18 | .SetMinimumLevel(LogLevel.Debug) 19 | ); 20 | builder.RootComponents.Add("#app"); 21 | builder.RootComponents.Add("head::after"); 22 | builder.Services.AddOptions(); 23 | builder.Services.AddAuthorizationCore(); 24 | 25 | if (builder.HostEnvironment.IsProduction()) 26 | { 27 | builder.Logging.SetMinimumLevel(LogLevel.Information); 28 | } 29 | 30 | // Use / for local or CDN resources 31 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 32 | 33 | var apiBaseUrl = builder.Configuration["ApiBaseUrl"] ?? builder.HostEnvironment.BaseAddress; 34 | #if DEBUG 35 | apiBaseUrl = builder.HostEnvironment.BaseAddress; 36 | #endif 37 | Console.WriteLine($"{builder.HostEnvironment.Environment} apiBaseUrl: {apiBaseUrl}"); 38 | builder.Services.AddBlazorApiClient(apiBaseUrl, client => client.Timeout = TimeSpan.FromSeconds(300)); 39 | builder.Services.AddScoped(); 40 | builder.Services.AddScoped(s => s.GetRequiredService()); 41 | 42 | builder.Services.AddScoped(); 43 | builder.Services.AddScoped(); 44 | builder.Services.AddIntersectionObserver(); 45 | 46 | var app = builder.Build(); 47 | 48 | BlazorConfig.Set(new BlazorConfig 49 | { 50 | IsWasm = true, 51 | Services = app.Services, 52 | ApiBaseUrl = apiBaseUrl, 53 | AssetsBasePath = "https://cdn.diffusion.works", 54 | FallbackAssetsBasePath = "https://pub-97bba6b94a944260b10a6e7d4bf98053.r2.dev", 55 | EnableLogging = true, 56 | EnableVerboseLogging = builder.HostEnvironment.IsDevelopment(), 57 | DefaultProfileUrl = Icons.AnonUserUri, 58 | OnApiErrorAsync = (request, apiError) => 59 | { 60 | BlazorConfig.Instance.GetLog()?.LogDebug("\n\nOnApiErrorAsync(): {0}", apiError.Error.GetDetailedError()); 61 | return Task.CompletedTask; 62 | } 63 | }); 64 | 65 | await app.RunAsync(); 66 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:8345", 7 | "sslPort": 44311 8 | } 9 | }, 10 | "profiles": { 11 | "BlazorDiffusion": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "IIS Express": { 22 | "commandName": "IISExpress", 23 | "launchBrowser": true, 24 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/AlbumTitle.razor: -------------------------------------------------------------------------------- 1 | @inherits AppAuthComponentBase 2 | 3 | @if (Album != null) 4 | { 5 |
6 |
@Album.Name
7 |
8 | @if (UserState.HasLiked(Album)) 9 | { 10 | 12 | undo like 13 | 14 | 15 | } 16 | else 17 | { 18 | 20 | like album 21 | 22 | 23 | } 24 |
25 |
26 | } 27 | 28 | @code { 29 | [Parameter, EditorRequired] public AlbumResult? Album { get; set; } 30 | 31 | async Task LikeAlbumAsync() 32 | { 33 | if (Album == null) return; 34 | await UserState.LikeAlbumAsync(Album); 35 | StateHasChanged(); 36 | } 37 | 38 | async Task UnlikeAlbumAsync() 39 | { 40 | if (Album == null) return; 41 | await UserState.UnlikeAlbumAsync(Album); 42 | StateHasChanged(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/ArtifactImage.razor: -------------------------------------------------------------------------------- 1 | @inherits UiComponentBase 2 | 3 | @if (Artifact != null) 4 | { 5 |
6 | @Artifact.Prompt useSrc = Artifact.GetImageErrorUrl(useSrc))> 9 |
10 | } 11 | 12 | @code { 13 | [Inject] public UserState UserState { get; set; } = default!; 14 | [Parameter, EditorRequired] public Artifact? Artifact { get; set; } = default!; 15 | [Parameter] public int? MinSize { get; set; } 16 | [Parameter] public string ImageClass { get; set; } 17 | 18 | int width => MinSize == null ? Artifact!.Width 19 | : (int)(Artifact!.Width > Artifact.Height 20 | ? (Artifact.Width / (double)Artifact.Height) * MinSize.Value 21 | : MinSize.Value); 22 | 23 | int height => MinSize == null ? Artifact!.Height 24 | : (int)(Artifact!.Height > Artifact.Width 25 | ? (Artifact.Height / (double)Artifact.Width) * MinSize.Value 26 | : MinSize.Value); 27 | 28 | string? useSrc; 29 | 30 | protected override async Task OnParametersSetAsync() 31 | { 32 | await base.OnParametersSetAsync(); 33 | useSrc = null; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/AvatarImage.razor: -------------------------------------------------------------------------------- 1 | @inherits UiComponentBase 2 | 3 | @if (User?.GetPublicUrl() != null) 4 | { 5 | @User.Handle useSrc = User.GetImageErrorUrl(useSrc))> 8 | } 9 | else 10 | { 11 | 12 | 13 | 14 | } 15 | 16 | 17 | @code { 18 | [Parameter, EditorRequired] public UserResult User { get; set; } 19 | string? useSrc; 20 | } 21 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/FormLoading.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime JsRuntime 2 | @inject NavigationManager NavigationManager 3 | 4 | @if (Loading) 5 | { 6 |
7 | @if (Icon) 8 | { 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | } 27 | @Text 28 |
29 | } 30 | 31 | @code { 32 | [Parameter] 33 | public bool Loading { get; set; } 34 | 35 | [Parameter] 36 | public bool Icon { get; set; } = true; 37 | 38 | [Parameter] 39 | public string Text { get; set; } = "loading..."; 40 | 41 | [Parameter] 42 | public string? @class { get; set; } 43 | } 44 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/Ignore.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | @code { 4 | [Parameter] public RenderFragment? ChildContent { get; set; } 5 | } 6 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/ModalForm.razor: -------------------------------------------------------------------------------- 1 | @inherits UiComponentBase 2 | 3 | 13 | 14 | @code { 15 | [Parameter, EditorRequired] public RenderFragment? ChildContent { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/NewAlbum.razor: -------------------------------------------------------------------------------- 1 | @inherits AppAuthComponentBase 2 | 3 | 4 |
5 | 6 |
7 |
8 |
9 | Create new Album 10 | 11 | 12 | 13 |
14 |
15 | 16 |
17 |
18 |
19 |
20 |
21 |
22 | Cancel 23 | Submit 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | @code { 32 | [Parameter, EditorRequired] public Artifact Artifact { get; set; } = default!; 33 | [Parameter] public EventCallback Done { get; set; } 34 | 35 | string[] VisibleFields => new[] { 36 | nameof(CreateAlbum.Name), 37 | }; 38 | 39 | CreateAlbum request = new(); 40 | ApiResult api = new(); 41 | 42 | protected override async Task OnParametersSetAsync() 43 | { 44 | await base.OnParametersSetAsync(); 45 | request = new(); 46 | } 47 | 48 | async Task OnDone() 49 | { 50 | await Done.InvokeAsync(); 51 | } 52 | 53 | async Task submit() 54 | { 55 | request.ArtifactIds = new() { Artifact.Id }; 56 | request.PrimaryArtifactId = Artifact.Id; 57 | api = await ApiAsync(request); 58 | if (api.Succeeded) 59 | { 60 | await loadUserState(force: true); 61 | await OnDone(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/NewReport.razor: -------------------------------------------------------------------------------- 1 | @inherits AppAuthComponentBase 2 | 3 | 4 |
5 | 6 |
7 |
8 |
9 | Report Image 10 | 11 | 12 | 13 |
14 |
15 | ()) /> 16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 | Cancel 26 | Submit 27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 | @code { 35 | [Parameter, EditorRequired] public Artifact Artifact { get; set; } = default!; 36 | [Parameter] public EventCallback Done { get; set; } 37 | 38 | string[] VisibleFields => new[] { 39 | nameof(ArtifactReport.Type), 40 | nameof(ArtifactReport.Description), 41 | }; 42 | CreateArtifactReport request = new(); 43 | ApiResult api = new(); 44 | 45 | protected override async Task OnParametersSetAsync() 46 | { 47 | await base.OnParametersSetAsync(); 48 | request = new(); 49 | } 50 | 51 | async Task OnDone() 52 | { 53 | await Done.InvokeAsync(); 54 | } 55 | 56 | async Task submit() 57 | { 58 | request.ArtifactId = Artifact.Id; 59 | api = await ApiAsync(request); 60 | if (api.Succeeded) 61 | { 62 | await OnDone(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/ShellCommand.razor: -------------------------------------------------------------------------------- 1 | @inject IJSRuntime JS 2 | 3 |
4 |
5 | 6 | sh 7 |
8 | @if (SuccessText != string.Empty) 9 | { 10 |
11 |
12 |
13 | 16 |
17 |
18 |

19 | @SuccessText 20 |

21 |
22 |
23 |
24 | } 25 |
26 | 27 | @code { 28 | [Parameter] 29 | public string? @class { get; set; } 30 | 31 | [Parameter] 32 | public RenderFragment? ChildContent { get; set; } 33 | 34 | string SuccessText { get; set; } = string.Empty; 35 | 36 | private ElementReference elCmd; 37 | 38 | async Task copyCommand(MouseEventArgs e) 39 | { 40 | SuccessText = "copied"; 41 | var text = await JS.InvokeAsync("JS.invoke", elCmd, "innerText"); 42 | await JS.InvokeVoidAsync("navigator.clipboard.writeText", text); 43 | await Task.Delay(3_000); 44 | SuccessText = string.Empty; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/StaticGallery.razor: -------------------------------------------------------------------------------- 1 | @inherits AppAuthComponentBase 2 | 3 |
4 | @foreach (var artifact in Artifacts.OrEmpty()) 5 | { 6 | 7 | } 8 |
9 | 10 | @code { 11 | [Inject] public NavigationManager NavigationManager { get; set; } 12 | [Parameter] public GalleryResults Results { get; set; } = new(); 13 | [Parameter] public int GridColumns { get; set; } = 5; 14 | 15 | public List Artifacts => Results.Artifacts; 16 | } 17 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/Tabs.razor: -------------------------------------------------------------------------------- 1 | @inherits UiComponentBase 2 | 3 |
4 |
5 | 6 | 7 | 13 |
14 | 27 |
-------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/Tabs.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.AspNetCore.Components.Routing; 3 | 4 | namespace BlazorDiffusion.Shared 5 | { 6 | public partial class Tabs : IDisposable 7 | { 8 | string _selectedTab; 9 | string SelectedTab 10 | { 11 | get => _selectedTab; 12 | set 13 | { 14 | if (value != Tab) 15 | { 16 | TabChanged.InvokeAsync(value); 17 | _selectedTab = value; 18 | } 19 | } 20 | } 21 | 22 | [Parameter] 23 | public Dictionary TabOptions { get; set; } 24 | 25 | [Parameter] 26 | public EventCallback TabChanged { get; set; } 27 | 28 | [Parameter] 29 | public string Tab { get; set; } 30 | 31 | [Parameter] public RenderFragment ChildContent { get; set; } 32 | [Parameter] public RenderFragment TabTemplate { get; set; } 33 | 34 | [Inject] public NavigationManager NavigationManager { get; set; } 35 | 36 | public string QueryStringName = "tab"; 37 | 38 | 39 | 40 | protected override async Task OnInitializedAsync() 41 | { 42 | NavigationManager.LocationChanged += HandleLocationChanged; 43 | await UpdateTab(); 44 | await base.OnInitializedAsync(); 45 | } 46 | 47 | async Task UpdateTab(string? location = null) 48 | { 49 | var uri = new Uri(location ?? NavigationManager.Uri); 50 | var queryStrings = System.Web.HttpUtility.ParseQueryString(uri.Query); 51 | if (queryStrings.AllKeys.Any(x => x == QueryStringName)) 52 | { 53 | var tab = queryStrings.Get(QueryStringName); 54 | SelectedTab = tab; 55 | } 56 | else 57 | { 58 | SelectedTab = TabOptions.Keys.First(); 59 | } 60 | StateHasChanged(); 61 | } 62 | 63 | async Task ChangeTab(string tab) 64 | { 65 | SelectedTab = tab; 66 | string uri = NavigationManager.Uri.SetQueryParam(QueryStringName, tab); 67 | if (TabOptions.Keys.First() == tab) 68 | { 69 | uri = NavigationManager.Uri.SetQueryParam(QueryStringName, null); 70 | } 71 | NavigationManager.NavigateTo(uri); 72 | } 73 | 74 | void HandleLocationChanged(object? sender, LocationChangedEventArgs args) 75 | { 76 | UpdateTab(args.Location).ConfigureAwait(false); 77 | } 78 | 79 | public void Dispose() => NavigationManager.LocationChanged -= HandleLocationChanged; 80 | 81 | async Task TabSelection(ChangeEventArgs e) 82 | { 83 | var tab = e.Value?.ToString(); 84 | SelectedTab = tab; 85 | string uri = NavigationManager.Uri.SetQueryParam(QueryStringName, tab); 86 | if (TabOptions.Keys.First() == tab || tab == null) 87 | { 88 | uri = NavigationManager.Uri.SetQueryParam(QueryStringName, null); 89 | } 90 | NavigationManager.NavigateTo(uri); 91 | } 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/Toggle.razor: -------------------------------------------------------------------------------- 1 | @typeparam TValue 2 | @inherits CheckboxInputBase 3 | 11 | 12 | @code { 13 | public void OnChange(MouseEventArgs e) 14 | { 15 | CurrentValueAsBool = !CurrentValueAsBool; 16 | } 17 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/admin/ArtifactAutoQueryGrid.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | @code { 39 | AutoQueryGrid? grid; 40 | 41 | string ImageSizeDescription(Artifact artifact) 42 | { 43 | if (artifact.Width == artifact.Height) 44 | return "Square"; 45 | if (artifact.Width > artifact.Height) 46 | return "Landscape"; 47 | return "Portrait"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/Shared/admin/ArtifactReportsAutoQueryGrid.razor: -------------------------------------------------------------------------------- 1 | @inherits AuthBlazorComponentBase 2 | @inject NavigationManager NavigationManager 3 | 4 | 5 | 6 | 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | @code { 45 | AutoQueryGrid? grid; 46 | 47 | void NavigateToArtifact(int artifactId) 48 | { 49 | NavigationManager.NavigateTo($"/admin/artifacts?id={artifactId}&tab=All"); 50 | } 51 | 52 | async Task SaveAsync(ArtifactReport reviewItem) 53 | { 54 | var request = new DeleteArtifactReport 55 | { 56 | ArtifactId = reviewItem.ArtifactId 57 | }; 58 | var api = await ApiAsync(request); 59 | await grid!.RefreshAsync(); 60 | } 61 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/AppComponentBase.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using Microsoft.AspNetCore.Components; 3 | using Microsoft.JSInterop; 4 | using ServiceStack.Blazor; 5 | using System.Collections.Concurrent; 6 | using System.Security.Claims; 7 | 8 | namespace BlazorDiffusion.UI; 9 | 10 | /// 11 | /// For Pages and Components that make use of ServiceStack functionality, e.g. Client 12 | /// 13 | public abstract class AppComponentBase : BlazorComponentBase 14 | { 15 | } 16 | 17 | /// 18 | /// For Pages and Components requiring Authentication 19 | /// 20 | public abstract class AppAuthComponentBase : AuthBlazorComponentBase 21 | { 22 | public bool IsModerator => IsAuthenticated && User.HasRole(AppRoles.Moderator); 23 | [Inject] public UserState UserState { get; set; } = default!; 24 | [Inject] public KeyboardNavigation KeyboardNavigation { get; set; } 25 | [Inject] ILogger Log { get; set; } 26 | 27 | protected override async Task OnInitializedAsync() 28 | { 29 | SetTitle(AppData.Title); 30 | await base.OnInitializedAsync(); 31 | } 32 | 33 | protected async Task loadUserState(bool force = false) 34 | { 35 | var task = UserState.LoadAnonAsync(force); 36 | if (IsAuthenticated) 37 | { 38 | await UserState.LoadAsync(force); 39 | } 40 | await task; 41 | } 42 | 43 | public void RegisterKeyboardNavigation(Func target) 44 | { 45 | log("KEYNAV {0} registered", GetType().Name); 46 | KeyboardNavigation.Register(target); 47 | } 48 | 49 | public void DeregisterKeyboardNavigation(Func target) 50 | { 51 | log("KEYNAV {0} de-registered", GetType().Name); 52 | KeyboardNavigation.Deregister(target); 53 | } 54 | } 55 | 56 | public enum AppPage 57 | { 58 | Search, 59 | Create, 60 | Favorites, 61 | } 62 | 63 | public enum PageView 64 | { 65 | Report, 66 | NewAlbum, 67 | EditProfile, 68 | } 69 | 70 | public enum ImageSize 71 | { 72 | Square, 73 | Portrait, 74 | Landscape, 75 | } 76 | 77 | public enum CreateMenu 78 | { 79 | History, 80 | } 81 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/AppCss.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorDiffusion.UI; 2 | 3 | public static class AppCss 4 | { 5 | // tailwind needs to see full classes 6 | static Dictionary GridClasses = new() 7 | { 8 | ["1"] = "grid-cols-1", 9 | ["2"] = "grid-cols-2", 10 | ["3"] = "grid-cols-3", 11 | ["4"] = "grid-cols-4", 12 | ["5"] = "grid-cols-5", 13 | ["6"] = "grid-cols-6", 14 | ["7"] = "grid-cols-7", 15 | ["8"] = "grid-cols-8", 16 | ["9"] = "grid-cols-9", 17 | ["10"] = "grid-cols-10", 18 | ["11"] = "grid-cols-11", 19 | ["12"] = "grid-cols-12", 20 | }; 21 | 22 | public static string GetGridClass(int columns) => GetGridClass(columns.ToString()); 23 | 24 | public static string GetGridClass(string columns) 25 | { 26 | return GridClasses.TryGetValue(columns, out var cls) 27 | ? cls 28 | : "grid-cols-6"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/ArtifactExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using System.Security.Claims; 3 | using ServiceStack.Blazor; 4 | 5 | namespace BlazorDiffusion.UI; 6 | 7 | public static class ArtifactExtensions 8 | { 9 | 10 | public static bool CanExploreSimilarTo(this Artifact artifact) => artifact.PerceptualHash != null; 11 | 12 | public static bool IsModerated(this Artifact artifact) => artifact.Nsfw == true || artifact.Quality < 0; 13 | 14 | public static string GetBorderColor(this Artifact artifact, int? activeId, UserState userState) 15 | { 16 | return artifact.Id == activeId 17 | ? "border-cyan-500" 18 | : userState.HasLiked(artifact) 19 | ? "border-red-700" 20 | : userState.IsModerator() && artifact.IsModerated() 21 | ? "border-gray-500" 22 | : userState.HasArtifactInAlbum(artifact) 23 | ? "border-green-700" 24 | : artifact.Background != null ? "border-black" : "border-transparent"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/BlazorModels.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | 3 | namespace BlazorDiffusion.UI; 4 | 5 | public class GalleryResults 6 | { 7 | public List Artifacts { get; set; } = new(); 8 | public Artifact? Selected { get; set; } 9 | public Artifact? Viewing { get; set; } 10 | public Creative? Creative { get; set; } 11 | public AlbumResult[] CreativeAlbums { get; set; } = Array.Empty(); 12 | public int? GridColumns { get; set; } 13 | 14 | public GalleryResults(List? artifacts = null) 15 | { 16 | Artifacts = artifacts ?? new(); 17 | } 18 | 19 | public async Task LoadAsync(UserState userState, int? selectedId, int? viewingId) 20 | { 21 | if (selectedId != Selected?.Id || viewingId != Viewing?.Id) 22 | { 23 | Selected = await userState.GetArtifactAsync(selectedId); 24 | Viewing = await userState.GetArtifactAsync(viewingId); 25 | Creative = await userState.GetCreativeAsync(Selected?.CreativeId); 26 | CreativeAlbums = await userState.GetCreativeInAlbumsAsync(Selected?.CreativeId); 27 | if (CreativeAlbums.Length > 0) 28 | await userState.LoadArtifactsAsync(CreativeAlbums.Where(x => x.PrimaryArtifactId != null).Select(x => x.PrimaryArtifactId!.Value)); 29 | } 30 | 31 | return this; 32 | } 33 | 34 | public GalleryResults Clone() => new GalleryResults 35 | { 36 | Artifacts = Artifacts, 37 | Selected = Selected, 38 | Viewing = Viewing, 39 | Creative = Creative, 40 | CreativeAlbums = CreativeAlbums, 41 | GridColumns = GridColumns, 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/KeyboardNavigation.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorDiffusion.UI; 2 | 3 | /// 4 | /// Ensure only the Active target component receives Nav Keys 5 | /// 6 | public class KeyboardNavigation 7 | { 8 | public Func? Active { get; set; } 9 | 10 | public async Task SendKeyAsync(string key) 11 | { 12 | if (Active != null) 13 | await Active.Invoke(key); 14 | } 15 | 16 | public void Register(Func target) => Active = target; 17 | public void Deregister(Func target) 18 | { 19 | if (Active == target) 20 | Active = null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/MarkdownUtils.cs: -------------------------------------------------------------------------------- 1 | using Markdig; 2 | using Markdig.Syntax; 3 | using ServiceStack; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorDiffusion.UI; 10 | 11 | public class MarkdownFileInfo 12 | { 13 | public string? Path { get; set; } 14 | public string? FileName { get; set; } 15 | public string? Title { get; set; } 16 | public string? Summary { get; set; } 17 | public DateTime? Date { get; set; } 18 | public string? Content { get; set; } 19 | public string? Preview { get; set; } 20 | } 21 | 22 | public static class MarkdownUtils 23 | { 24 | public static Dictionary Cache = new(); 25 | 26 | public static async Task> LoadDocumentAsync(string name, Func> resolverAsync) 27 | { 28 | try 29 | { 30 | if (Cache.TryGetValue(name, out var cachedDoc)) 31 | return ApiResult.Create(cachedDoc); 32 | 33 | var pipeline = new MarkdownPipelineBuilder() 34 | .UseYamlFrontMatter() 35 | .UseAdvancedExtensions() 36 | .Build(); 37 | var writer = new StringWriter(); 38 | var renderer = new Markdig.Renderers.HtmlRenderer(writer); 39 | pipeline.Setup(renderer); 40 | 41 | var doc = new MarkdownFileInfo 42 | { 43 | Path = name, 44 | FileName = $"{name}.md", 45 | }; 46 | doc.Content = await resolverAsync(doc); 47 | 48 | var document = Markdown.Parse(doc.Content!, pipeline); 49 | renderer.Render(document); 50 | var block = document 51 | .Descendants() 52 | .FirstOrDefault(); 53 | 54 | var metaObj = block? 55 | .Lines // StringLineGroup[] 56 | .Lines // StringLine[] 57 | .Select(x => $"{x}\n") 58 | .ToList() 59 | .Select(x => x.Replace("---", string.Empty)) 60 | .Where(x => !string.IsNullOrWhiteSpace(x)) 61 | .Select(x => KeyValuePairs.Create(x.LeftPart(':').Trim(), x.RightPart(':').Trim())) 62 | .ToObjectDictionary(); 63 | metaObj?.PopulateInstance(doc); 64 | 65 | await writer.FlushAsync(); 66 | doc.Preview = writer.ToString(); 67 | Cache[name] = doc; 68 | return ApiResult.Create(doc); 69 | } 70 | catch (Exception ex) 71 | { 72 | return ApiResult.CreateError(ex.AsResponseStatus()); 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/Ssg.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | 3 | namespace BlazorDiffusion.UI; 4 | 5 | public interface IGetPageModel 6 | { 7 | Task OnGetAsync(); 8 | } 9 | 10 | public class ArtifactImageParams 11 | { 12 | public Artifact Artifact { get; set; } 13 | public string? Class { get; set; } 14 | public string? ImageClass { get; set; } 15 | public int? MinSize { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/UI/UiExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace BlazorDiffusion.UI; 4 | 5 | public static class UiExtensions 6 | { 7 | public static int? GetInt(this NameValueCollection query, string name) => 8 | X.Map(query[name], x => int.TryParse(x, out var num) ? num : (int?)null); 9 | } 10 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net 2 | @using System.Net.Http 3 | @using System.Net.Http.Json 4 | @using System.Collections.Generic 5 | @using Microsoft.JSInterop 6 | @using Microsoft.AspNetCore.Components.Forms 7 | @using Microsoft.AspNetCore.Components.Routing 8 | @using Microsoft.AspNetCore.Components.Web 9 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 10 | @using Microsoft.AspNetCore.Authorization 11 | @using Microsoft.AspNetCore.Components.Authorization 12 | @using Microsoft.Extensions.Logging 13 | @using ServiceStack 14 | @using ServiceStack.Html 15 | @using ServiceStack.Blazor 16 | @using ServiceStack.Blazor.Components 17 | @using ServiceStack.Blazor.Components.Tailwind 18 | @using BlazorDiffusion 19 | @using BlazorDiffusion.UI 20 | @using BlazorDiffusion.Shared 21 | @using BlazorDiffusion.ServiceModel 22 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "ui:dev": "npx tailwindcss@v3 -c tailwind.config.js -i ./tailwind.input.css -o ./wwwroot/css/app.css --watch", 4 | "ui:build": "npx tailwindcss@v3 -c tailwind.config.js -i ./tailwind.input.css -o ./wwwroot/css/app.css --minify", 5 | "postinstall": "cd ../BlazorDiffusion && dotnet run --AppTasks=migrate", 6 | "migrate": "cd ../BlazorDiffusion && dotnet run BlazorDiffusion.csproj --AppTasks=migrate", 7 | "revert:last": "cd ../BlazorDiffusion && dotnet run --AppTasks=migrate.revert:last", 8 | "revert:all": "cd ../BlazorDiffusion && dotnet run --AppTasks=migrate.revert:all" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["**/*.razor", "**/*.cshtml", "**/*.cs", "**/*.html", "**/*.js"], 3 | darkMode: 'class', 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | } 9 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Client/wwwroot/.nojekyll -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/.well-known/microsoft-identity-association.json: -------------------------------------------------------------------------------- 1 | { 2 | "associatedApplications": [ 3 | { 4 | "applicationId": "ec242717-76cb-4aff-b530-624afee41319" 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/CNAME: -------------------------------------------------------------------------------- 1 | {DEPLOY_CDN} -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/_headers: -------------------------------------------------------------------------------- 1 | /assets/* 2 | cache-control: max-age=31536000 3 | cache-control: immutable 4 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/_redirects: -------------------------------------------------------------------------------- 1 | /api/* {DEPLOY_API}/api/:splat 200 2 | /auth/* {DEPLOY_API}/auth/:splat 200 3 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/appsettings.Production.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApiBaseUrl": "https://{DEPLOY_API}" 3 | } 4 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/content/community-rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Community Rules 3 | summary: Configuring your GitHub repo for SSH and CDN deployments 4 | date: 2023-01-01 5 | --- 6 | 7 | ## Community Rules 8 | 9 | BlazorDiffusion is where anyone is welcome to discover the amazing world of Stable Diffusion. 10 | We want to keep it a welcome place, so we have created this ruleset to help guide the content posted on BlazorDiffusion. 11 | 12 | If you see a post or comment that breaks the rules, we welcome you to report it to the our moderators. 13 | 14 | These rules apply to all community aspects on BlazorDiffusion: all parts of a public post (title, description, tags, visual content), comments, links, and messages. 15 | Moderators consider context and intent while enforcing the community rules. 16 | 17 | - No nudity or sexually explicit content. 18 | - Provocative, inflammatory, unsettling, or suggestive content should be marked as Mature. 19 | - No hate speech, abuse, or harassment. 20 | - No content that condones illegal or violent activity. 21 | - No gore or shock content. 22 | - No posting personal information. 23 | 24 | ### Good Sharing Practices 25 | 26 | Considering these tips when sharing with the BlazorDiffusion community will help ensure you're contributing great content. 27 | 28 | #### 1. Value 29 | - Good sharing means posting content which brings value to the community. Content which opens up a discussion, shares something new and unique, or has a deeper story to tell beyond the image itself is content that generally brings value. Ask yourself first: is this something I would be interested in seeing if someone else posted it? 30 | #### 2. Transparency 31 | - We expect that the original poster (OP) will be explicit about if and how they are connected to the content they are posting. Trying to hide that relationship, or not explaining it well to others, is a common feature of bad sharing. 32 | #### 3. Respect 33 | - Good sharing means knowing when the community has spoken through upvotes and downvotes and respecting that. You should avoid constantly reposting content to User Submitted that gets downvoted. This kind of spamming annoys the community, and it won't make your posts any more popular. 34 | Repeated violations of the good sharing practices after warning may result in account ban. 35 | 36 | 37 | If content breaks these community rules, it will be removed and the original poster warned about the removal. 38 | Warnings will expire. If multiple submissions break the rules in a short time frame, warnings will accumulate, which could lead to a 24-hour suspension, and further, a ban. 39 | 40 | If you aren't sure if your post fits the community rules, please don't post it. 41 | Just because you've seen a rule-breaking image posted somewhere else on BlazorDiffusion doesn't mean it's okay for you to repost it. 42 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/css/markdown.css: -------------------------------------------------------------------------------- 1 | /* override typography */ 2 | .prose { 3 | max-width: 65ch; 4 | color: rgb(55 65 81); /*text-gray-700*/ 5 | } 6 | .prose ol, .prose ul { 7 | list-style: none; 8 | margin: 0; 9 | padding: 0; 10 | } 11 | .prose img { 12 | max-width: 100%; 13 | } 14 | .prose pre { 15 | overflow-x: auto; 16 | min-width: fit-content; 17 | max-width: calc(100vw - 350px); 18 | background: #1E1E1E; 19 | } 20 | .prose code { 21 | color: #3b82f6; 22 | font-weight: 400; 23 | font-size: .875em; 24 | background-color: #eff6ff; 25 | border-radius: 0.25rem; 26 | padding: 0.25em 0.5rem; 27 | } 28 | .prose code::before, .prose code::after { 29 | content: "" 30 | } 31 | .prose a { 32 | color: inherit; 33 | font-weight: 500; 34 | text-decoration: underline; 35 | } 36 | .prose a:hover { 37 | opacity: .8; 38 | color: #4b5563; 39 | } 40 | .prose blockquote { 41 | border-left-style: solid; 42 | } 43 | 44 | .dark .prose, .dark .prose td { 45 | color: rgb(209 213 219); /*text-gray-300*/ 46 | } 47 | .dark .prose h1, .dark .prose h2, .dark .prose h3, .dark .prose h4, .dark .prose h5, .dark .prose h6, .dark .prose th { 48 | color: rgb(243 244 246); /*text-gray-100*/ 49 | } 50 | .dark .prose code { 51 | background-color: rgb(30 58 138); /*text-blue-900*/ 52 | color: rgb(243 244 246); /*text-gray-100*/ 53 | } 54 | .dark .prose pre>code { 55 | background-color: transparent; 56 | } 57 | 58 | @media (min-width: 1024px) { 59 | .lg\:prose-xl { 60 | font-size: 1.25rem; 61 | line-height: 1.8; 62 | } 63 | } 64 | .youtube { 65 | width: 896px; 66 | height: 525px; 67 | } 68 | @media (max-width: 896px) { 69 | .youtube { 70 | width: 100%; 71 | height: auto; 72 | } 73 | } 74 | .prose pre::-webkit-scrollbar { 75 | width: 8px; 76 | height: 8px; 77 | background: #2d3748; 78 | } 79 | .prose pre::-webkit-scrollbar-thumb { 80 | background-color: rgb(100 116 139); 81 | } 82 | 83 | .html-format { 84 | max-width: unset; 85 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/img/blazor.svg: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/img/blur-cyan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Client/wwwroot/img/blur-cyan.png -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/img/blur-indigo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Client/wwwroot/img/blur-indigo.png -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/js/artifact-info.js: -------------------------------------------------------------------------------- 1 | import { computed } from "vue" 2 | import { useClient } from './static.js' 3 | import { CreateArtifactLike, DeleteArtifactLike } from './dtos.mjs' 4 | 5 | export default { 6 | template: /*html*/` 7 |
8 | 9 | undo like 10 | 11 | 12 | 13 | like image 14 | 15 | 16 |
17 | `, 18 | props:['artifactId'], 19 | setup(props) { 20 | const hasLiked = computed(() => AppData.UserArtifact.liked) 21 | const { api, apiVoid, error, loading } = useClient() 22 | const { artifactId } = props 23 | 24 | function unlikeArtifact() { 25 | AppData.UserArtifact.liked = false 26 | apiVoid(new DeleteArtifactLike({ artifactId })) 27 | .then(r => { 28 | if (!r.succeeded) { 29 | AppData.UserArtifact.liked = true 30 | } 31 | }) 32 | } 33 | 34 | function likeArtifact() { 35 | AppData.UserArtifact.liked = true 36 | api(new CreateArtifactLike({ artifactId })) 37 | .then(r => { 38 | if (!r.succeeded) { 39 | AppData.UserArtifact.liked = false 40 | } 41 | }) 42 | } 43 | 44 | return { 45 | AppData, 46 | hasLiked, 47 | unlikeArtifact, 48 | likeArtifact, 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/js/comments.js: -------------------------------------------------------------------------------- 1 | var { $1 } = exports 2 | 3 | 4 | let ArtifactId = map($1('[data-artifact]'), x => parseInt(x.getAttribute('data-artifact'))) 5 | 6 | export default { 7 | template: `
`, 8 | data() { 9 | return { count: ArtifactId || 0 } 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/js/custom.js: -------------------------------------------------------------------------------- 1 | /* globals */ 2 | 3 | const origScrollTo = window.scrollTo; 4 | window.scrollTo = (x, y) => { 5 | const shouldSkip = true 6 | if (x === 0 && y === 0 && shouldSkip) 7 | return 8 | return origScrollTo.apply(this, arguments) 9 | } 10 | function map(o, f) { return o == null ? null : f(o) } 11 | function prerenderedPage() { return '' } 12 | 13 | var cls = (function () { 14 | var button = 'inline-flex justify-center rounded-md border border-transparent py-2 px-4 text-sm font-medium shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 ' 15 | return { 16 | buttons: { 17 | primary: button + 'dark:ring-offset-black focus:ring-2 focus:ring-offset-2 text-white bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800', 18 | secondary: button + 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-400 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700 focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:ring-offset-black', 19 | }, 20 | form: { 21 | legend: "text-base font-medium text-gray-900 dark:text-gray-100 text-center mb-4" 22 | }, 23 | } 24 | })(); 25 | 26 | /* becomes reactive in static.js */ 27 | var AppData = { 28 | init: false, 29 | Auth: null 30 | } 31 | 32 | var ApiBaseUrl = location.hostname === 'blazordiffusion.com' 33 | ? 'https://api.blazordiffusion.com' 34 | : location.origin 35 | 36 | var client, Apps, Components; 37 | 38 | const DiffusionBrand = ` 39 | Diffusion Works 40 | 41 | diffusion.works 42 | ` 43 | 44 | if (document.referrer.startsWith('https://diffusion.works') || document.referrer.startsWith('https://localhost:5002')) { 45 | document.addEventListener('DOMContentLoaded', e => { 46 | document.querySelector('header a').outerHTML = DiffusionBrand 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/js/error-summary.js: -------------------------------------------------------------------------------- 1 | import { computed, useAttrs, inject } from "vue" 2 | import { classNames, errorResponseExcept } from "@servicestack/client" 3 | 4 | export default { 5 | template: /*html*/` 6 |
7 |
8 |
9 | 12 |
13 |
14 |

{{ errorSummary }}

15 |
16 |
17 |
18 | `, 19 | props: ['status', 'except'], 20 | setup(props) { 21 | let ctx = inject('ApiState', undefined) 22 | const errorSummary = computed(() => props.status || map(ctx, x => x.error.value) 23 | ? errorResponseExcept.call({ responseStatus: props.status || map(ctx, x => x.error.value) }, props.except) 24 | : null) 25 | 26 | return { 27 | cls, 28 | classNames, 29 | errorSummary, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/js/input-comment.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from "vue" 2 | import { CreateArtifactComment } from './dtos.mjs' 3 | import { useClient } from './static.js' 4 | 5 | export default { 6 | template: /*html*/` 7 |
8 |
9 | 10 |
11 | 14 |
15 | {{ remainingChars }} 16 | 17 |
18 |
19 |
20 |
21 | 22 |
23 |
24 | `, 25 | props: ['artifactId','replyId'], 26 | emits: ['updated'], 27 | setup(props, { attrs, emit }) { 28 | 29 | let content = ref('') 30 | let remainingChars = computed(() => 280 - content.value.length) 31 | let { api, error, loading } = useClient() 32 | 33 | function submit() { 34 | const { artifactId, replyId } = props 35 | api(new CreateArtifactComment({ artifactId, replyId, content })).then(r => { 36 | if (r.succeeded) { 37 | content.value = '' 38 | emit('updated', r.response) 39 | } 40 | }) 41 | } 42 | 43 | return { 44 | cls, 45 | loading, 46 | content, 47 | remainingChars, 48 | error, 49 | submit, 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BlazorDiffusion.Client/wwwroot/tailwind/README.txt: -------------------------------------------------------------------------------- 1 | Source code of components containing tailwind classes that also need to be included in tailwind's generated /css/app.css 2 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/AdminServices.cs: -------------------------------------------------------------------------------- 1 | using Amazon.Runtime.Internal; 2 | using BlazorDiffusion.ServiceModel; 3 | using ServiceStack; 4 | using ServiceStack.OrmLite; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace BlazorDiffusion.ServiceInterface; 12 | 13 | public class AdminServices : Service 14 | { 15 | public async Task Any(AdminData request) 16 | { 17 | var tables = new (string Label, Type Type)[] 18 | { 19 | ("Albums", typeof(Album)), 20 | ("AlbumArtifacts", typeof(AlbumArtifact)), 21 | ("AlbumLikes", typeof(AlbumLike)), 22 | ("Artifacts", typeof(Artifact)), 23 | ("ArtifactLikes", typeof(ArtifactLike)), 24 | ("ArtifactComments", typeof(ArtifactComment)), 25 | ("ArtifactCommentVotes", typeof(ArtifactCommentVote)), 26 | ("Artists", typeof(Artist)), 27 | ("Modifiers", typeof(Modifier)), 28 | ("Creatives", typeof(Creative)), 29 | ("CreativeArtists", typeof(CreativeArtist)), 30 | ("CreativeModifiers", typeof(CreativeModifier)), 31 | }; 32 | var analyticsTables = new (string Label, Type Type)[] 33 | { 34 | ("ArtifactStats", typeof(ArtifactStat)), 35 | ("SearchStats", typeof(SearchStat)), 36 | ("Signups", typeof(Signup)), 37 | }; 38 | 39 | var dialect = Db.GetDialectProvider(); 40 | 41 | var totalSql = tables.Map(x => $"SELECT '{x.Label}', COUNT(*) FROM {dialect.GetQuotedTableName(x.Type.GetModelMetadata())}") 42 | .Join(" UNION "); 43 | var results = await Db.DictionaryAsync(totalSql); 44 | 45 | var analyticsTotalSql = analyticsTables.Map(x => $"SELECT '{x.Label}', COUNT(*) FROM {dialect.GetQuotedTableName(x.Type.GetModelMetadata())}") 46 | .Join(" UNION "); 47 | var analyticsDb = HostContext.AppHost.GetDbConnection(Databases.Analytics); 48 | foreach (var entry in await analyticsDb.DictionaryAsync(analyticsTotalSql)) 49 | { 50 | results[entry.Key] = entry.Value; 51 | } 52 | 53 | return new AdminDataResponse 54 | { 55 | PageStats = tables.Union(analyticsTables).Map(x => new PageStats 56 | { 57 | Label = x.Label, 58 | Total = results[x.Label], 59 | }) 60 | }; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/BlazorDiffusion.ServiceInterface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | CA1822;CA2208 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/CreativeServerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorDiffusion.ServiceModel; 3 | using System.Collections.Generic; 4 | using ServiceStack; 5 | using ServiceStack.Web; 6 | using System.Linq; 7 | 8 | namespace BlazorDiffusion; 9 | 10 | public static class CreativeServerExtensions 11 | { 12 | public const string SystemUserId = "2"; 13 | 14 | public static T WithAudit(this T row, IRequest req, DateTime? date = null) where T : AuditBase => 15 | row.WithAudit(req.GetSession().UserAuthId, date); 16 | 17 | public static T WithAudit(this T row, string? by, DateTime? date = null) where T : AuditBase 18 | { 19 | by ??= SystemUserId; 20 | var useDate = date ?? DateTime.UtcNow; 21 | if (string.IsNullOrEmpty(row.CreatedBy)) 22 | { 23 | row.CreatedBy = by; 24 | row.CreatedDate = useDate; 25 | } 26 | row.ModifiedBy = by; 27 | row.ModifiedDate = useDate; 28 | return row; 29 | } 30 | 31 | public static AlbumResult ToAlbumResult(this Album album) 32 | { 33 | var to = new AlbumResult 34 | { 35 | Id = album.Id, 36 | AlbumRef = album.RefId, 37 | Name = album.Name, 38 | Slug= album.Slug, 39 | OwnerRef = album.OwnerRef, 40 | PrimaryArtifactId = album.PrimaryArtifactId, 41 | Score = album.Score, 42 | // Show latest artifacts added first 43 | ArtifactIds = album.PrimaryArtifactId == null 44 | ? album.Artifacts.OrderByDescending(x => x.Id).Map(x => x.ArtifactId) 45 | : X.Apply(new List { album.PrimaryArtifactId.Value }, 46 | ids => ids.AddRange(album.Artifacts.Where(x => x.ArtifactId != album.PrimaryArtifactId.Value) 47 | .OrderByDescending(x => x.Id).Select(x => x.ArtifactId))), 48 | }; 49 | return to; 50 | } 51 | 52 | public static string ConstructPrompt(this string userPrompt, List modifiers, List artists) 53 | { 54 | var finalPrompt = userPrompt; 55 | if (modifiers.Count > 0) 56 | finalPrompt += ", " + modifiers.Select(x => x.Name).Join(", "); 57 | if (artists.Count > 0) 58 | finalPrompt += $" by {artists.Select(x => x.GetArtistName()).Join(", ")}"; 59 | return finalPrompt; 60 | } 61 | 62 | public static string GetArtistName(this Artist artist) => string.IsNullOrEmpty(artist.FirstName) 63 | ? artist.LastName 64 | : $"{artist.FirstName} {artist.LastName}"; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/CustomUserSession.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using BlazorDiffusion.ServiceModel; 3 | 4 | namespace BlazorDiffusion.ServiceInterface; 5 | 6 | // Add any additional metadata properties you want to store in the Users Typed Session 7 | public class CustomUserSession : AuthUserSession 8 | { 9 | public string Handle { get; set; } 10 | public string Avatar { get; set; } 11 | 12 | public int GetUserId() => UserAuthId.ToInt(); 13 | } 14 | 15 | public static class UsersExtensions 16 | { 17 | public static CustomUserSession ToUserSession(this AppUser appUser) 18 | { 19 | var session = appUser.ConvertTo(); 20 | session.Id = SessionExtensions.CreateRandomSessionId(); 21 | session.IsAuthenticated = true; 22 | session.FromToken = true; // use embedded roles 23 | return session; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/DbExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BlazorDiffusion.ServiceModel; 5 | using ServiceStack; 6 | using ServiceStack.OrmLite; 7 | 8 | namespace BlazorDiffusion.ServiceInterface; 9 | 10 | public static class DbExtensions 11 | { 12 | public static async Task GetUserProfileAsync(this IDbConnection db, int userId) => 13 | await db.SingleAsync(db.From().Where(x => x.Id == userId)); 14 | 15 | public static async Task GetUserResultAsync(this IDbConnection db, int userId) 16 | { 17 | var likes = new Likes 18 | { 19 | ArtifactIds = await db.ColumnAsync(db.From().Where(x => x.AppUserId == userId).Select(x => x.ArtifactId).OrderByDescending(x => x.Id)), 20 | AlbumIds = await db.ColumnAsync(db.From().Where(x => x.AppUserId == userId).Select(x => x.AlbumId).OrderByDescending(x => x.Id)), 21 | }; 22 | 23 | var userAlbums = await db.LoadSelectAsync(x => x.OwnerId == userId && x.DeletedDate == null); 24 | var albums = userAlbums.OrderByDescending(x => x.Artifacts.Max(x => x.Id)).ToList(); 25 | var albumResults = albums.Map(x => x.ToAlbumResult()); 26 | 27 | var userInfo = await db.SingleAsync<(string refId, string handle, string avatar, string profileUrl)>(db.From() 28 | .Where(x => x.Id == userId).Select(x => new { x.RefIdStr, x.Handle, x.Avatar, x.ProfileUrl })); 29 | 30 | return new UserResult 31 | { 32 | RefId = userInfo.refId, 33 | Handle = userInfo.handle, 34 | Avatar = userInfo.avatar, 35 | ProfileUrl = userInfo.profileUrl, 36 | Likes = likes, 37 | Albums = albumResults, 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/DbFunctions.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using CoenM.ImageHash; 3 | using Microsoft.Data.Sqlite; 4 | using ServiceStack.OrmLite; 5 | using System; 6 | using System.Data; 7 | 8 | namespace BlazorDiffusion.ServiceInterface; 9 | public static class DbFunctions 10 | { 11 | public static void RegisterImgCompare(this IDbConnection db) 12 | { 13 | var sqliteConn = (SqliteConnection)db.ToDbConnection(); 14 | sqliteConn.CreateFunction("imgcompare", (Int64? hash1, Int64? hash2) 15 | => hash1 == null || hash2 == null 16 | ? 0 17 | : CompareHash.Similarity((ulong)hash1, (ulong)hash2)); 18 | } 19 | public static void RegisterBgCompare(this IDbConnection db) 20 | { 21 | var sqliteConn = (SqliteConnection)db.ToDbConnection(); 22 | sqliteConn.CreateFunction("bgcompare", (string a, string b) => ImageUtils.BackgroundCompare(a, b)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/HtmlTemplate.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace BlazorDiffusion; 6 | 7 | public class HtmlTemplate 8 | { 9 | public string Contents { get; set; } 10 | public string PreTitle { get; set; } 11 | public string PreHead { get; set; } 12 | public string PreBody { get; set; } 13 | public string PostBody { get; set; } 14 | 15 | const string TitleMarker = ""; 16 | const string HeadMarker = ""; 17 | const string BodyMarker = ""; 18 | 19 | public Dictionary ComponentTypes { get; set; } = new(); 20 | 21 | public void RegisterComponent() => ComponentTypes[typeof(T).FullName!] = typeof(T); 22 | public Type? GetComponentType(string typeName) => ComponentTypes.TryGetValue(typeName, out var c) ? c : null; 23 | 24 | public static HtmlTemplate Create(string contents) 25 | { 26 | string? preTitle = null; 27 | string? preHead = null; 28 | string? preBody = null; 29 | string? postBody = null; 30 | 31 | var remaining = contents; 32 | preTitle = remaining.LeftPart(TitleMarker); 33 | remaining = remaining.RightPart(TitleMarker); 34 | preHead = remaining.LeftPart(HeadMarker); 35 | remaining = remaining.RightPart(HeadMarker); 36 | preBody = remaining.LeftPart(BodyMarker); 37 | postBody = remaining.RightPart(BodyMarker); 38 | 39 | return new HtmlTemplate 40 | { 41 | Contents = contents, 42 | PreTitle = preTitle, 43 | PreHead = preHead, 44 | PreBody = preBody, 45 | PostBody = postBody, 46 | }; 47 | } 48 | 49 | public static string CreateMeta(string url = "", string title = "", string description = "", string image = "") 50 | { 51 | var useUrl = url.IndexOf("://") >= 0 52 | ? url 53 | : "https://blazordiffusion.com".CombineWith(url); 54 | 55 | return $@" 56 | 57 | 58 | 59 | 60 | 61 | 62 | "; 63 | } 64 | 65 | public string Render(string title = "", string head = "", string body = "") 66 | { 67 | return PreTitle 68 | + title 69 | + PreHead 70 | + head 71 | + PreBody 72 | + body 73 | + PostBody; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/MyServices.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using BlazorDiffusion.ServiceModel; 3 | using System.Threading.Tasks; 4 | using ServiceStack.OrmLite; 5 | using System; 6 | 7 | namespace BlazorDiffusion.ServiceInterface; 8 | 9 | public class MyServices : Service 10 | { 11 | public async Task Any(GetUserProfile request) 12 | { 13 | var session = await SessionAsAsync(); 14 | var userProfile = await Db.GetUserProfileAsync(session.UserAuthId.ToInt()); 15 | return new GetUserProfileResponse { 16 | Result = userProfile 17 | }; 18 | } 19 | 20 | public async Task Any(UpdateUserProfile request) 21 | { 22 | var session = await SessionAsAsync(); 23 | var userId = session.GetUserId(); 24 | 25 | var userInfo = await Db.SingleAsync(Db.From() 26 | .Where(x => x.Id == userId)); 27 | 28 | if (string.IsNullOrWhiteSpace(request.Handle)) 29 | request.Handle = null; 30 | if (string.IsNullOrWhiteSpace(request.Avatar)) 31 | request.Avatar = null; 32 | 33 | if (request.Handle != null && !request.Handle.IsValidVarName()) 34 | throw new ArgumentException("Invalid chars in Handle", nameof(request.Handle)); 35 | 36 | if (request.Handle != null && await Db.ExistsAsync(x => x.Handle == request.Handle && x.Id != userId)) 37 | throw new ArgumentException("Handle already taken", nameof(request.Handle)); 38 | 39 | await Db.UpdateOnlyAsync(() => new AppUser { 40 | DisplayName = request.DisplayName ?? userInfo.DisplayName, 41 | Handle = request.Handle, 42 | Avatar = request.Avatar ?? userInfo.Avatar, 43 | }, where: x => x.Id == userId); 44 | 45 | return new UserProfile { 46 | DisplayName = request.DisplayName ?? userInfo.DisplayName, 47 | Handle = request.Handle, 48 | Avatar = request.Avatar ?? userInfo.Avatar, 49 | }; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/StatUtils.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Auth; 3 | using ServiceStack.Web; 4 | using System; 5 | 6 | namespace BlazorDiffusion.ServiceModel; 7 | 8 | public static class StatUtils 9 | { 10 | public static T WithRequest(this T stat, IRequest req, IAuthSession session) where T : StatBase 11 | { 12 | if (session.IsAuthenticated) 13 | { 14 | stat.AppUserId = session.UserAuthId?.ToInt(); 15 | } 16 | 17 | stat.RawUrl = req.RawUrl; 18 | stat.RemoteIp = req.RemoteIp; 19 | stat.CreatedDate = DateTime.UtcNow; 20 | 21 | return stat; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/TodosServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using ServiceStack; 4 | using BlazorDiffusion.ServiceModel; 5 | 6 | namespace BlazorDiffusion.ServiceInterface; 7 | 8 | public class TodosServices : Service 9 | { 10 | public IAutoQueryData AutoQuery { get; set; } 11 | 12 | static readonly PocoDataSource Todos = PocoDataSource.Create(new Todo[] 13 | { 14 | new () { Id = 1, Text = "Learn" }, 15 | new () { Id = 2, Text = "Blazor", IsFinished = true }, 16 | }, nextId: x => x.Select(e => e.Id).Max()); 17 | 18 | public object Get(QueryTodos query) 19 | { 20 | var db = Todos.ToDataSource(query, Request); 21 | return AutoQuery.Execute(query, AutoQuery.CreateQuery(query, Request, db), db); 22 | } 23 | 24 | public Todo Post(CreateTodo request) 25 | { 26 | var newTodo = new Todo { Id = Todos.NextId(), Text = request.Text }; 27 | Todos.Add(newTodo); 28 | return newTodo; 29 | } 30 | 31 | public Todo Put(UpdateTodo request) 32 | { 33 | var todo = request.ConvertTo(); 34 | Todos.TryUpdateById(todo, todo.Id); 35 | return todo; 36 | } 37 | 38 | // Handles Deleting the Todo item 39 | public void Delete(DeleteTodo request) => Todos.TryDeleteById(request.Id); 40 | 41 | public void Delete(DeleteTodos request) => Todos.TryDeleteByIds(request.Ids); 42 | } 43 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/Validators/CreativeValidators.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using ServiceStack.FluentValidation; 3 | 4 | namespace BlazorDiffusion.ServiceInterface.Validators; 5 | 6 | public class CreateCreativeValidator : AbstractValidator 7 | { 8 | public CreateCreativeValidator() 9 | { 10 | RuleFor(x => x.UserPrompt).NotEmpty(); 11 | RuleFor(x => x.Height) 12 | .Must(x => x is >= 256 and <= 1024) 13 | .When(x => x.Height != null) 14 | .WithMessage("Height must be between 256 and 1024."); 15 | RuleFor(x => x.Width) 16 | .Must(x => x is >= 256 and <= 1024) 17 | .When(x => x.Width != null) 18 | .WithMessage("Width must be between 256 and 1024."); 19 | RuleFor(x => x.Images) 20 | .Must(x => x is > 0 and < 10) 21 | .When(x => x.Images != null) 22 | .WithMessage("Images must be between 1 and 9."); 23 | RuleFor(x => x.Steps) 24 | .Must(x => x is >= 10 and <= 150) 25 | .When(x => x.Steps != null) 26 | .WithMessage("Steps must be between 10 and 150."); 27 | RuleFor(x => x.ModifierIds).NotEmpty() 28 | .WithMessage("Must specify at least one Modifier"); 29 | } 30 | } -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceInterface/Validators/FindSimilarArtifactsValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using ServiceStack.FluentValidation; 3 | 4 | namespace BlazorDiffusion.ServiceInterface.Validators; 5 | 6 | public class FindSimilarArtifactsValidator : AbstractValidator 7 | { 8 | public FindSimilarArtifactsValidator() 9 | { 10 | RuleFor(x => x.Skip) 11 | .GreaterThan(0) 12 | .When(x => x.Skip != null); 13 | RuleFor(x => x.Take) 14 | .GreaterThan(10) 15 | .LessThan(100) 16 | .When(x => x.Take != null); 17 | } 18 | } -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Admin.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using System.Collections.Generic; 3 | 4 | namespace BlazorDiffusion.ServiceModel; 5 | /*Admin APIs*/ 6 | 7 | [Tag(Tag.Admin)] 8 | [ValidateIsAdmin] 9 | public class AdminQueryArtifactComments : QueryDb { } 10 | 11 | [Tag(Tag.Admin)] 12 | [ValidateIsAdmin] 13 | [AutoApply(Behavior.AuditModify)] 14 | public class AdminUpdateArtifactComment : IPatchDb, IReturn 15 | { 16 | public int Id { get; set; } 17 | public int? ReplyId { get; set; } 18 | [ValidateLength(1, 280)] 19 | public string? Content { get; set; } 20 | public string? Notes { get; set; } 21 | [Input(Type = "select", EvalAllowableValues = "AppData.FlagReasons")] 22 | public string? FlagReason { get; set; } 23 | } 24 | 25 | [Tag(Tag.Admin)] 26 | [ValidateIsAdmin] 27 | public class AdminDeleteArtifactComment : IDeleteDb, IReturnVoid 28 | { 29 | public int Id { get; set; } 30 | } 31 | 32 | [Tag(Tag.Admin)] 33 | [ValidateIsAdmin] 34 | public class AdminQueryArtifactCommentReports : QueryDb { } 35 | 36 | [Tag(Tag.Admin)] 37 | [ValidateIsAdmin] 38 | public class AdminUpdateArtifactCommentReport : IPatchDb, IReturn 39 | { 40 | public int Id { get; set; } 41 | public PostReport? PostReport { get; set; } 42 | public string? Description { get; set; } 43 | } 44 | 45 | [Tag(Tag.Admin)] 46 | [ValidateIsAdmin] 47 | public class AdminDeleteArtifactCommentReport : IDeleteDb, IReturnVoid 48 | { 49 | public int Id { get; set; } 50 | } 51 | 52 | 53 | [Tag(Tag.Admin)] 54 | [ValidateIsAdmin] 55 | public class AdminData : IGet, IReturn { } 56 | 57 | public class PageStats 58 | { 59 | public string Label { get; set; } 60 | public int Total { get; set; } 61 | } 62 | 63 | public class AdminDataResponse 64 | { 65 | public List PageStats { get; set; } 66 | public ResponseStatus ResponseStatus { get; set; } 67 | } 68 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Analytics.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.DataAnnotations; 3 | using System; 4 | 5 | namespace BlazorDiffusion.ServiceModel; 6 | 7 | [NamedConnection(Databases.Analytics)] 8 | public class StatBase 9 | { 10 | public string RefId { get; set; } 11 | public int? AppUserId { get; set; } 12 | public string RawUrl { get; set; } 13 | public string RemoteIp { get; set; } 14 | public DateTime CreatedDate { get; set; } 15 | } 16 | 17 | public enum StatType 18 | { 19 | Download, 20 | } 21 | 22 | [Icon(Svg = Icons.Stats)] 23 | public class ArtifactStat : StatBase 24 | { 25 | [AutoIncrement] 26 | public int Id { get; set; } 27 | 28 | public StatType Type { get; set; } 29 | public int ArtifactId { get; set; } 30 | public string Source { get; set; } 31 | public string Version { get; set; } 32 | } 33 | 34 | [Icon(Svg = Icons.Stats)] 35 | public class SearchStat : StatBase 36 | { 37 | [AutoIncrement] 38 | public int Id { get; set; } 39 | public string? Query { get; set; } 40 | public string? Similar { get; set; } 41 | public string? User { get; set; } 42 | public string? Modifier { get; set; } 43 | public string? Artist { get; set; } 44 | public string? Album { get; set; } 45 | public string? Show { get; set; } 46 | public string? Source { get; set; } 47 | 48 | public int? ArtifactId { get; set; } 49 | public int? AlbumId { get; set; } 50 | public int? ModifierId { get; set; } 51 | public int? ArtistId { get; set; } 52 | } 53 | 54 | public enum SignupType 55 | { 56 | Updates, 57 | Beta, 58 | } 59 | 60 | [Icon(Svg = Icons.Signup)] 61 | public class Signup : StatBase 62 | { 63 | [AutoIncrement] 64 | public int Id { get; set; } 65 | public SignupType Type { get; set; } 66 | public string Email { get; set; } 67 | public string? Name { get; set; } 68 | public DateTime? CancelledDate { get; set; } 69 | } 70 | 71 | 72 | [Tag(Tag.Analytics)] 73 | public class CreateSignup : ICreateDb, IReturn // IReturnVoid -> support cast EmptyResponse -> byte[] 74 | { 75 | public SignupType Type { get; set; } 76 | [ValidateNotEmpty, ValidateEmail] 77 | public string Email { get; set; } 78 | public string? Name { get; set; } 79 | } 80 | 81 | 82 | [Tag(Tag.Analytics)] 83 | [ValidateHasRole(AppRoles.Moderator)] 84 | public class QueryArtifactStats : QueryDb { } 85 | 86 | [Tag(Tag.Analytics)] 87 | [ValidateHasRole(AppRoles.Moderator)] 88 | public class QuerySearchStats : QueryDb { } 89 | 90 | 91 | [Tag(Tag.Analytics)] 92 | [ValidateHasRole(AppRoles.Moderator)] 93 | public class QuerySignups : QueryDb { } 94 | 95 | [Tag(Tag.Analytics)] 96 | [ValidateHasRole(AppRoles.Moderator)] 97 | public class UpdateSignup : IPatchDb, IReturn 98 | { 99 | public int Id { get; set; } 100 | public SignupType? Type { get; set; } 101 | [ValidateEmail] 102 | public string? Email { get; set; } 103 | public string? Name { get; set; } 104 | public DateTime? CancelledDate { get; set; } 105 | } 106 | 107 | [Tag(Tag.Analytics)] 108 | [ValidateHasRole(AppRoles.Moderator)] 109 | public class DeleteSignup : IDeleteDb, IReturnVoid 110 | { 111 | public int Id { get; set; } 112 | } 113 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/AppConfig.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack.Caching; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorDiffusion.ServiceModel; 9 | 10 | public class AppConfig 11 | { 12 | public static AppConfig Instance = new(); 13 | 14 | public string BaseUrl { get; set; } 15 | public string ApiBaseUrl { get; set; } 16 | public string WwwBaseUrl { get; set; } 17 | public string CdnBaseUrl { get; set; } 18 | public string R2AccessId { get; set; } 19 | public string R2AccessKey { get; set; } 20 | public string ArtifactBucket { get; set; } 21 | public string CdnBucket { get; set; } 22 | public string R2Account { get; set; } 23 | public string AssetsBasePath { get; set; } 24 | public string FallbackAssetsBasePath { get; set; } 25 | public HashSet BanWords { get; set; } = new(StringComparer.OrdinalIgnoreCase) 26 | { 27 | "panties", 28 | "breasts", 29 | "hispanic", 30 | }; 31 | 32 | /// 33 | /// Ignore saving creatives + pre-rendering pages to avoid Hot Reload reloading page 34 | /// 35 | public bool DisableWrites { get; set; } 36 | public TimeSpan SyncTasksInterval { get; set; } 37 | public static AppConfig Set(AppConfig instance) => Instance = instance; 38 | } 39 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/AppData.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace BlazorDiffusion.ServiceModel; 8 | 9 | public class AppData 10 | { 11 | public const string Title = "Blazor Diffusion"; 12 | 13 | public const int MaxArtifactSize = 10 * 1024 * 1024; 14 | public const int MaxAvatarSize = 1024 * 1024; 15 | 16 | public static AppData Instance { get; private set; } = new(); 17 | 18 | List DefaultLinks { get; set; } = new() { }; 19 | List AdminLinks { get; set; } = new() { 20 | new NavItem { Label = "Admin", Href = "/admin" }, 21 | }; 22 | public static List GetNavItems(bool isAdmin) => isAdmin ? Instance.AdminLinks : Instance.DefaultLinks; 23 | 24 | public static List CategoryGroups = new Group[] { 25 | new() { Name = "Scene", Items = new[] { "Quality", "Style", "Aesthetic", "Features", "Medium", "Setting", "Theme" } }, 26 | new() { Name = "Effects", Items = new[] { "Effects", "CGI", "Filters", "Lenses", "Photography", "Lighting", "Color" } }, 27 | new() { Name = "Art Style", Items = new[] { "Art Movement", "Art Style", "18 Century", "19 Century", "20 Century", "21 Century" } }, 28 | new() { Name = "Mood", Items = new[] { "Positive Mood", "Negative Mood" } }, 29 | }.ToList(); 30 | 31 | string[]? categories; 32 | public string[] Categories => categories ??= CategoryGroups.SelectMany(x => x.Items).OrderBy(x => x).ToArray(); 33 | 34 | public string[] FlagReasons => Enum.GetNames(typeof(PostReport)); 35 | } 36 | 37 | public class Group 38 | { 39 | public string Name { get; set; } 40 | public string[] Items { get; set; } 41 | } 42 | 43 | public class AppSource 44 | { 45 | public const string Albums = "albums"; 46 | public const string Top = "top"; 47 | public const string User = "user"; 48 | public const string InAlbum = "in"; 49 | } 50 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/AppErrors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorDiffusion.ServiceModel; 8 | 9 | public static class AppErrors 10 | { 11 | public const string QuotaExceeded = nameof(QuotaExceeded); 12 | } 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/AppRoles.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorDiffusion; 2 | 3 | public static class AppRoles 4 | { 5 | public const string Admin = nameof(Admin); 6 | public const string Moderator = nameof(Moderator); 7 | public const string Creator = nameof(Creator); 8 | 9 | public static string[] All { get; set; } = { Admin, Moderator, Creator }; 10 | } 11 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/AppUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using ServiceStack; 5 | using ServiceStack.Auth; 6 | using ServiceStack.DataAnnotations; 7 | 8 | namespace BlazorDiffusion.ServiceModel; 9 | 10 | public class AppUser : IUserAuth 11 | { 12 | [AutoIncrement] 13 | public int Id { get; set; } 14 | public string UserName { get; set; } 15 | public string DisplayName { get; set; } 16 | public string FirstName { get; set; } 17 | public string LastName { get; set; } 18 | [Index(Unique = true)] 19 | public string? Handle { get; set; } 20 | public string Company { get; set; } 21 | [Index] 22 | public string Email { get; set; } 23 | public string? ProfileUrl { get; set; } 24 | [Input(Type="file"), UploadTo("avatars")] 25 | public string? Avatar { get; set; } //overrides ProfileUrl 26 | public string? LastLoginIp { get; set; } 27 | public bool IsArchived { get; set; } 28 | public DateTime? ArchivedDate { get; set; } 29 | public DateTime? LastLoginDate { get; set; } 30 | public string PhoneNumber { get; set; } 31 | public DateTime? BirthDate { get; set; } 32 | public string BirthDateRaw { get; set; } 33 | public string Address { get; set; } 34 | public string Address2 { get; set; } 35 | public string City { get; set; } 36 | public string State { get; set; } 37 | public string Country { get; set; } 38 | public string Culture { get; set; } 39 | public string FullName { get; set; } 40 | public string Gender { get; set; } 41 | public string Language { get; set; } 42 | public string MailAddress { get; set; } 43 | public string Nickname { get; set; } 44 | public string PostalCode { get; set; } 45 | public string TimeZone { get; set; } 46 | public Dictionary Meta { get; set; } 47 | public string PrimaryEmail { get; set; } 48 | [IgnoreDataMember] public string Salt { get; set; } 49 | [IgnoreDataMember] public string PasswordHash { get; set; } 50 | [IgnoreDataMember] public string DigestHa1Hash { get; set; } 51 | public List Roles { get; set; } 52 | public List Permissions { get; set; } 53 | public int? RefId { get; set; } 54 | public string RefIdStr { get; set; } 55 | public int InvalidLoginAttempts { get; set; } 56 | public DateTime? LastLoginAttempt { get; set; } 57 | public DateTime? LockedDate { get; set; } 58 | public DateTime CreatedDate { get; set; } 59 | public DateTime ModifiedDate { get; set; } 60 | } 61 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Artists.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ServiceStack; 3 | using ServiceStack.DataAnnotations; 4 | 5 | namespace BlazorDiffusion.ServiceModel; 6 | 7 | [Tag(Tag.Artists)] 8 | public class QueryArtists : QueryDb {} 9 | 10 | [Tag(Tag.Artists)] 11 | [ValidateHasRole(AppRoles.Moderator)] 12 | [AutoApply(Behavior.AuditCreate)] 13 | public class CreateArtist : ICreateDb, IReturn 14 | { 15 | public string? FirstName { get; set; } 16 | [ValidateNotEmpty, Required] 17 | public string LastName { get; set; } 18 | public int? YearDied { get; set; } 19 | [Input(Type = "tag"), FieldCss(Field = "col-span-12")] 20 | public List? Type { get; set; } 21 | } 22 | 23 | [Tag(Tag.Artists)] 24 | [ValidateHasRole(AppRoles.Moderator)] 25 | [AutoApply(Behavior.AuditModify)] 26 | public class UpdateArtist : IPatchDb, IReturn 27 | { 28 | public int Id { get; set; } 29 | public string? FirstName { get; set; } 30 | public string? LastName { get; set; } 31 | public int? YearDied { get; set; } 32 | [Input(Type = "tag"), FieldCss(Field = "col-span-12")] 33 | public List? Type { get; set; } 34 | } 35 | 36 | [Tag(Tag.Artists)] 37 | [ValidateHasRole(AppRoles.Moderator)] 38 | [AutoApply(Behavior.AuditSoftDelete)] 39 | public class DeleteArtist : IDeleteDb, IReturnVoid 40 | { 41 | public int Id { get; set; } 42 | } 43 | 44 | [Icon(Svg = Icons.Artist)] 45 | public class Artist : AuditBase 46 | { 47 | [AutoIncrement] 48 | public int Id { get; set; } 49 | public string? FirstName { get; set; } 50 | public string LastName { get; set; } 51 | public int? YearDied { get; set; } 52 | public List? Type { get; set; } 53 | [Default(0)] 54 | public int Score { get; set; } 55 | [Default(0)] 56 | public int Rank { get; set; } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/BlazorDiffusion.ServiceModel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Databases.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | 4 | namespace BlazorDiffusion.ServiceModel; 5 | 6 | public static class Databases 7 | { 8 | // Keep heavy writes of stats + analytics in separate DB 9 | public const string Analytics = nameof(Analytics); 10 | } 11 | 12 | public static class Users 13 | { 14 | public static AppUser Admin = new() 15 | { 16 | Id = 1, 17 | Email = "admin@email.com", 18 | DisplayName = "Admin User", 19 | RefIdStr = "b496e043-3e5b-4410-b0e5-1c9cca04c07f", 20 | Roles = new() { AppRoles.Admin }, 21 | }; 22 | public static AppUser System = new() 23 | { 24 | Id = 2, 25 | Email = "system@email.com", 26 | DisplayName = "System", 27 | RefIdStr = "cd1bbe7e-2038-4b43-9086-32c790485588", 28 | Roles = new() { AppRoles.Moderator }, 29 | }; 30 | public static AppUser Demis = new() 31 | { 32 | Id = 3, 33 | Email = "demis@servicestack.com", 34 | DisplayName = "Demis", 35 | RefIdStr = "865d5f4a-4c58-461d-b1b8-2aac005cd2bc", 36 | Roles = new() { AppRoles.Moderator }, 37 | Handle = "mythz", 38 | Avatar = "/avatars/86/865d5f4a-4c58-461d-b1b8-2aac005cd2bc/kerrigan_128.png", 39 | }; 40 | public static AppUser Darren = new() 41 | { 42 | Id = 4, 43 | Email = "darren@servicestack.com", 44 | DisplayName = "Darren", 45 | RefIdStr = "16846ea4-2bb6-4c58-a999-985dac3c31a2", 46 | Roles = new() { AppRoles.Moderator }, 47 | }; 48 | public static AppUser Test = new() 49 | { 50 | Id = 5, 51 | Email = "test@user.com", 52 | DisplayName = "Test", 53 | RefIdStr = "3823c5af-d0b6-4738-8601-bd91bf6f9771", 54 | Handle = "imagineer", 55 | }; 56 | 57 | public static AppUser GetUserById(string? userId) => string.IsNullOrEmpty(userId) 58 | ? System 59 | : GetUserById(int.Parse(userId)); 60 | public static AppUser GetUserById(int? userId) => userId switch 61 | { 62 | 1 => Admin, 63 | 2 => System, 64 | 3 => Demis, 65 | 4 => Darren, 66 | 5 => Test, 67 | _ => System, 68 | }; 69 | 70 | public static bool IsAdminOrSystem(int? appUserId) => appUserId == 1 || appUserId == 2; 71 | } 72 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Modifiers.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.DataAnnotations; 3 | 4 | namespace BlazorDiffusion.ServiceModel; 5 | 6 | 7 | [Tag(Tag.Modifiers)] 8 | public class QueryModifiers : QueryDb { } 9 | 10 | [Tag(Tag.Modifiers)] 11 | [ValidateHasRole(AppRoles.Moderator)] 12 | [AutoApply(Behavior.AuditCreate)] 13 | public class CreateModifier : ICreateDb, IReturn 14 | { 15 | [ValidateNotEmpty, Required] 16 | public string Name { get; set; } 17 | [ValidateNotEmpty, Required] 18 | [Input(Type="select", EvalAllowableValues = "AppData.Categories")] 19 | public string Category { get; set; } 20 | public string? Description { get; set; } 21 | } 22 | 23 | [Tag(Tag.Modifiers)] 24 | [ValidateHasRole(AppRoles.Moderator)] 25 | [AutoApply(Behavior.AuditModify)] 26 | public class UpdateModifier : IPatchDb, IReturn 27 | { 28 | public int Id { get; set; } 29 | public string? Name { get; set; } 30 | [Input(Type = "select", EvalAllowableValues = "AppData.Categories")] 31 | public string? Category { get; set; } 32 | public string? Description { get; set; } 33 | } 34 | 35 | [Tag(Tag.Modifiers)] 36 | [ValidateHasRole(AppRoles.Moderator)] 37 | [AutoApply(Behavior.AuditSoftDelete)] 38 | public class DeleteModifier : IDeleteDb, IReturnVoid 39 | { 40 | public int Id { get; set; } 41 | } 42 | 43 | [Icon(Svg = Icons.Modifier)] 44 | public class Modifier : AuditBase 45 | { 46 | [AutoIncrement] 47 | public int Id { get; set; } 48 | public string Name { get; set; } 49 | public string Category { get; set; } 50 | public string? Description { get; set; } 51 | [Default(0)] 52 | public int Score { get; set; } 53 | [Default(0)] 54 | public int Rank { get; set; } 55 | } 56 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "BlazorDiffusion.ServiceModel": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "https://localhost:56612;http://localhost:56615" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/QuotaError.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using System; 3 | 4 | namespace BlazorDiffusion.ServiceModel; 5 | 6 | public class QuotaError 7 | { 8 | public string ErrorCode { get; set; } 9 | public string Message { get; set; } 10 | 11 | public TimeSpan TimeRemaining { get; set; } 12 | public int CreditsUsed { get; set; } 13 | public int CreditsRequested { get; set; } 14 | public int CreditsRemaining { get; set; } 15 | public int DailyQuota { get; set; } 16 | public string? RequestedDetails { get; set; } 17 | 18 | public ResponseStatus ToResponseStatus() => new() 19 | { 20 | ErrorCode = ErrorCode, 21 | Message = Message, 22 | Meta = new() 23 | { 24 | [nameof(TimeRemaining)] = TimeRemaining.ToString("hh\\:mm\\:ss"), 25 | [nameof(DailyQuota)] = $"{DailyQuota}", 26 | [nameof(CreditsUsed)] = $"{CreditsUsed}", 27 | [nameof(CreditsRequested)] = $"{CreditsRequested}", 28 | [nameof(RequestedDetails)] = RequestedDetails ?? string.Empty, 29 | }, 30 | }; 31 | 32 | public static QuotaError FromResponseStatus(ResponseStatus status) 33 | { 34 | var to = new QuotaError 35 | { 36 | ErrorCode = status.ErrorCode, 37 | Message = status.Message, 38 | TimeRemaining = TimeSpan.Parse(status.Meta[nameof(TimeRemaining)]), 39 | DailyQuota = int.Parse(status.Meta[nameof(DailyQuota)]), 40 | CreditsUsed = int.Parse(status.Meta[nameof(CreditsUsed)]), 41 | CreditsRequested = int.Parse(status.Meta[nameof(CreditsRequested)]), 42 | RequestedDetails = status.Meta.TryGetValue(nameof(RequestedDetails), out var details) ? details : null, 43 | }; 44 | to.CreditsRemaining = to.DailyQuota - to.CreditsUsed; 45 | return to; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Tag.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorDiffusion.ServiceModel; 2 | 3 | public static class Tag 4 | { 5 | public const string Todos = nameof(Todos); 6 | public const string Albums = nameof(Albums); 7 | public const string Creatives = nameof(Creatives); 8 | public const string Artifacts = nameof(Artifacts); 9 | public const string Modifiers = nameof(Modifiers); 10 | public const string Artists = nameof(Artists); 11 | public const string Comments = nameof(Comments); 12 | public const string Admin = nameof(Admin); 13 | public const string User = nameof(User); 14 | public const string Analytics = nameof(Analytics); 15 | public const string Ssg = nameof(Ssg); 16 | public const string Tasks = nameof(Tasks); 17 | } -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Tasks.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Collections.Generic; 3 | using ServiceStack; 4 | using ServiceStack.DataAnnotations; 5 | 6 | namespace BlazorDiffusion.ServiceModel; 7 | 8 | [Tag(Tag.Tasks)] 9 | [ExcludeMetadata] 10 | [Restrict(InternalOnly = true)] 11 | public class BackgroundTasks 12 | { 13 | public Creative? NewCreative { get; set; } 14 | public int? RecordArtifactLikeId { get; set; } 15 | public int? RecordArtifactUnlikeId { get; set; } 16 | 17 | public int? RecordAlbumLikeId { get; set; } 18 | public int? RecordAlbumUnlikeId { get; set; } 19 | 20 | public RecordPrimaryArtifact? RecordPrimaryArtifact { get; set; } 21 | 22 | public List? ArtifactIdsAddedToAlbums { get; set; } 23 | public List? ArtifactIdsRemovedFromAlbums { get; set; } 24 | } 25 | public class RecordPrimaryArtifact 26 | { 27 | public int CreativeId { get; set; } 28 | public int? FromArtifactId { get; set; } 29 | public int? ToArtifactId { get; set; } 30 | } 31 | 32 | [Tag(Tag.Tasks)] 33 | [ExcludeMetadata] 34 | [Restrict(InternalOnly = true)] 35 | public class AnalyticsTasks 36 | { 37 | public SearchStat? RecordSearchStat { get; set; } 38 | public ArtifactStat? RecordArtifactStat { get; set; } 39 | } 40 | 41 | [Tag(Tag.Tasks)] 42 | [ExcludeMetadata] 43 | [ValidateIsAdmin] 44 | public class SyncTasks : IReturn 45 | { 46 | public bool? Periodic { get; set; } 47 | public bool? Daily { get; set; } 48 | } 49 | public class SyncTasksResponse 50 | { 51 | public List Results { get; set; } 52 | } 53 | 54 | [Tag(Tag.Tasks)] 55 | [ExcludeMetadata] 56 | [Restrict(InternalOnly = true)] 57 | public class DiskTasks : IReturnVoid 58 | { 59 | public int? SaveCreativeId { get; set; } 60 | public Creative? SaveCreative { get; set; } 61 | public SaveFile? SaveFile { get; set; } 62 | public List? CdnDeleteFiles { get; set; } 63 | } 64 | public class SaveFile 65 | { 66 | public string FilePath { get; set; } 67 | public Stream Stream { get; set; } 68 | } 69 | -------------------------------------------------------------------------------- /BlazorDiffusion.ServiceModel/Todos.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Model; 3 | using System.Collections.Generic; 4 | 5 | namespace BlazorDiffusion.ServiceModel; 6 | 7 | [Tag(Tag.Todos)] 8 | [Route("/todos", "GET")] 9 | public class QueryTodos : QueryData 10 | { 11 | public int? Id { get; set; } 12 | public List? Ids { get; set; } 13 | public string? TextContains { get; set; } 14 | } 15 | 16 | [Tag(Tag.Todos)] 17 | [Route("/todos", "POST")] 18 | public class CreateTodo : IPost, IReturn 19 | { 20 | [ValidateNotEmpty] 21 | public string Text { get; set; } 22 | } 23 | 24 | [Tag(Tag.Todos)] 25 | [Route("/todos/{Id}", "PUT")] 26 | public class UpdateTodo : IPut, IReturn 27 | { 28 | public long Id { get; set; } 29 | [ValidateNotEmpty] 30 | public string Text { get; set; } 31 | public bool IsFinished { get; set; } 32 | } 33 | 34 | [Tag(Tag.Todos)] 35 | [Route("/todos/{Id}", "DELETE")] 36 | public class DeleteTodo : IDelete, IReturnVoid 37 | { 38 | public long Id { get; set; } 39 | } 40 | 41 | [Tag(Tag.Todos)] 42 | [Route("/todos", "DELETE")] 43 | public class DeleteTodos : IDelete, IReturnVoid 44 | { 45 | public List Ids { get; set; } 46 | } 47 | 48 | public class Todo : IHasId 49 | { 50 | public long Id { get; set; } 51 | public string Text { get; set; } 52 | public bool IsFinished { get; set; } 53 | } 54 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Data/seed/album-artifacts.csv: -------------------------------------------------------------------------------- 1 | AlbumRefId,ArtifactRefId,Description 2 | 6638f764-c00e-4703-bc2d-48b717fa164b,993db8d6-d16c-4108-b248-9de8f29d7fd3, 3 | 6638f764-c00e-4703-bc2d-48b717fa164b,10c6ea65-6ccb-4139-afd4-d16d13373ae0, 4 | 6638f764-c00e-4703-bc2d-48b717fa164b,e577a30f-ab57-46be-a34b-db19ff69a460, 5 | 1c526c25-96db-4fae-ad77-8118e597e9a7,3a5f5eeb-10b5-402b-b837-3c2af26eff44, 6 | 1c526c25-96db-4fae-ad77-8118e597e9a7,89e03bf8-0f76-4a13-b1cc-61fcf3edfd7b, 7 | 1c526c25-96db-4fae-ad77-8118e597e9a7,bab7dbc7-a8ca-4524-a17c-4c7c690b17fd, 8 | 1c526c25-96db-4fae-ad77-8118e597e9a7,936e1a03-b3ff-42f2-8f89-de2cad09c796, 9 | 1c526c25-96db-4fae-ad77-8118e597e9a7,5bac027e-1882-483d-b1ff-3943d4a0ed44, 10 | efb71410-61a2-4005-b1ec-7f06c7bc4435,10f3f9c6-5857-4c52-851b-6fe32ff850ab, 11 | efb71410-61a2-4005-b1ec-7f06c7bc4435,7efb5330-6452-4c84-8198-d7e11ec099ea, 12 | efb71410-61a2-4005-b1ec-7f06c7bc4435,4733fc2c-1e70-4765-8a07-4ecf87d1ea94, 13 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Data/seed/album-likes.csv: -------------------------------------------------------------------------------- 1 | RefId,AppUserId,CreatedDate 2 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Data/seed/albums.csv: -------------------------------------------------------------------------------- 1 | RefId,OwnerId,Name,Description,Tags,PrimaryArtifactRef 2 | 6638f764-c00e-4703-bc2d-48b717fa164b,3,Minecraft,,, 3 | 1c526c25-96db-4fae-ad77-8118e597e9a7,3,Landscapes,,, 4 | efb71410-61a2-4005-b1ec-7f06c7bc4435,3,Ciri,,, 5 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1140958129.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1140958129.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1159930719.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1159930719.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1437184716.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_1437184716.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_199094354.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_199094354.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_2922589731.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_2922589731.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3282754617.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3282754617.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3486285260.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3486285260.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3529434359.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_3529434359.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_663104678.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/42753907/output_663104678.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1359909046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1359909046.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1946842138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1946842138.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1950710229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_1950710229.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_2102961529.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_2102961529.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_2296583688.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_2296583688.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_390010331.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_390010331.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_4022298746.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_4022298746.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_4266588710.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_4266588710.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_722875130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/47519396/output_722875130.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1290639599.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1290639599.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1312358835.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1312358835.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1515285252.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1515285252.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1676765929.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_1676765929.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_2248701330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_2248701330.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_2633527491.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_2633527491.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4023030651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4023030651.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4097140727.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4097140727.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4294945848.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/artifacts/2022/10/20/54857882/output_4294945848.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/avatars/ciri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/avatars/ciri.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/avatars/emma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/avatars/emma.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/avatars/out/ciri_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/avatars/out/ciri_128.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/avatars/out/emma_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetCoreApps/BlazorDiffusionWasm/88c27802064831764cef58e5811b56c1c267e25d/BlazorDiffusion.Tests/App_Files/avatars/out/emma_128.png -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/App_Files/sync.bat: -------------------------------------------------------------------------------- 1 | RD /q /s artifacts 2 | MD artifacts\2022\10\20 3 | 4 | XCOPY /Y /E /H /C /I ..\..\BlazorDiffusion\App_Files\artifacts\2022\10\20\42753907 artifacts\2022\10\20\42753907 5 | XCOPY /Y /E /H /C /I ..\..\BlazorDiffusion\App_Files\artifacts\2022\10\20\47519396 artifacts\2022\10\20\47519396 6 | XCOPY /Y /E /H /C /I ..\..\BlazorDiffusion\App_Files\artifacts\2022\10\20\54857882 artifacts\2022\10\20\54857882 7 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/ArtifactTests.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using NUnit.Framework; 3 | using ServiceStack; 4 | using ServiceStack.Data; 5 | using ServiceStack.OrmLite; 6 | using ServiceStack.Text; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace BlazorDiffusion.Tests; 14 | 15 | [Explicit] 16 | public class ArtifactTests 17 | { 18 | IDbConnectionFactory ResolveDbFactory() => new ConfigureDb().ConfigureAndResolve(); 19 | 20 | [Test] 21 | public async Task Can_update_Quality() 22 | { 23 | var client = new JsonApiClient("https://localhost:5001"); 24 | await client.PostAsync(new Authenticate 25 | { 26 | provider = "credentials", 27 | UserName = "admin@email.com", 28 | Password = "p@55wOrd", 29 | }); 30 | 31 | var api = await client.ApiAsync(new UpdateArtifact 32 | { 33 | Id = 571, 34 | Quality = -1, 35 | }); 36 | if (api.Succeeded) 37 | { 38 | api.Response.PrintDump(); 39 | } 40 | } 41 | 42 | [Test] 43 | public void Generate_all_artifact_slugs() 44 | { 45 | using var db = ResolveDbFactory().OpenDbConnection(); 46 | var artifactsCount = db.RowCount(db.From()); 47 | var emptyPrompts = db.RowCount(db.From().Where(x => x.Prompt == null || x.Prompt == "")); 48 | 49 | var prompts = db.ColumnDistinct(db.From().SelectDistinct(x => x.Prompt)); 50 | $"Checking {prompts.Count} unique prompts, {emptyPrompts} empty from {artifactsCount} artifacts...".Print(); 51 | 52 | foreach (var prompt in prompts) 53 | { 54 | try 55 | { 56 | var slug = Ssg.GenerateSlug(prompt); 57 | } 58 | catch (Exception e) 59 | { 60 | $"ERROR: {e.Message} for '{prompt}'".Print(); 61 | } 62 | } 63 | 64 | var artifacts = db.Select(db.From()); 65 | $"Checking {artifacts.Count} artifacts paths...".Print(); 66 | foreach (var artifact in artifacts) 67 | { 68 | try 69 | { 70 | var path = Ssg.GetArtifact(artifact, Ssg.GetSlug(artifact)); 71 | } 72 | catch (Exception e) 73 | { 74 | $"ERROR: {e.Message} for #{artifact.Id} '{artifact.Prompt}'".Print(); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/FtsTasks.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using ServiceStack.Data; 3 | using ServiceStack.Text; 4 | using ServiceStack; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using ServiceStack.OrmLite; 12 | using BlazorDiffusion.ServiceModel; 13 | using System.Diagnostics; 14 | using Amazon.DynamoDBv2.DocumentModel; 15 | using ServiceStack.OrmLite.Legacy; 16 | 17 | namespace BlazorDiffusion.Tests; 18 | 19 | [Explicit] 20 | public class FtsTasks 21 | { 22 | IDbConnectionFactory ResolveDbFactory() => new ConfigureDb().ConfigureAndResolve(); 23 | public string GetHostDir() 24 | { 25 | JsConfig.Init(new Config { 26 | TextCase = TextCase.CamelCase, 27 | }); 28 | 29 | var appSettings = JSON.parse(File.ReadAllText(Path.GetFullPath("appsettings.json"))); 30 | return appSettings.ToObjectDictionary()["HostDir"].ToString()!; 31 | } 32 | 33 | 34 | [Test] 35 | public void Can_populate_and_query_ArtifactFts() 36 | { 37 | var DbFactory = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider); 38 | var Db = DbFactory.OpenDbConnection(); 39 | 40 | var testHostDir = Path.GetFullPath("../../.."); 41 | TestDatabase.CreateDatabase(DbFactory, Db, testHostDir); 42 | 43 | var sw = Stopwatch.StartNew(); 44 | 45 | Db.ExecuteNonQuery($@"CREATE VIRTUAL TABLE {nameof(ArtifactFts)} 46 | USING FTS5( 47 | {nameof(ArtifactFts.Prompt)}, 48 | {nameof(ArtifactFts.CreativeId)}, 49 | {nameof(ArtifactFts.RefId)});" 50 | ); 51 | 52 | var rowsAdded = Db.ExecuteNonQuery($@"INSERT INTO {nameof(ArtifactFts)} 53 | (rowid, 54 | {nameof(ArtifactFts.Prompt)}, 55 | {nameof(ArtifactFts.CreativeId)}, 56 | {nameof(ArtifactFts.RefId)}) 57 | SELECT 58 | {nameof(Artifact.Id)}, 59 | {nameof(Artifact.Prompt)}, 60 | {nameof(Artifact.CreativeId)}, 61 | {nameof(Artifact.RefId)} FROM {nameof(Artifact)}"); 62 | 63 | $"ArtifactFts rowsAdded: {rowsAdded}, took {sw.ElapsedMilliseconds}ms".Print(); 64 | 65 | OrmLiteUtils.PrintSql(); 66 | 67 | var query = "\"salma\"*"; //escape 68 | 69 | var q = Db.From() 70 | .Join((a, c) => c.Id == a.CreativeId && a.Id == c.PrimaryArtifactId); 71 | 72 | q.OrderByDescending(x => x.Quality); 73 | 74 | //q.Where(x => x.Prompt.Contains(query)); 75 | q.Join((a,f) => a.Id == f.rowid); 76 | q.Where(q.Column(x => x.Prompt, prefixTable:true) + " match {0}", query); 77 | q.ThenBy(q.Column("Rank", prefixTable: true)); 78 | 79 | q.ThenByDescending(x => new { x.Score, x.Id }); 80 | // Need distinct else Blazor @key throws when returning dupes 81 | q.SelectDistinct((a, c) => new { a, c.UserPrompt, c.ArtistNames, c.ModifierNames, c.PrimaryArtifactId }); 82 | 83 | var results = Db.LoadSelect(q); 84 | results.PrintDump(); 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/IntegrationTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Funq; 3 | using ServiceStack; 4 | using BlazorDiffusion.ServiceInterface; 5 | using BlazorDiffusion.ServiceModel; 6 | 7 | namespace BlazorDiffusion.Tests; 8 | 9 | public class IntegrationTest 10 | { 11 | const string BaseUri = "http://localhost:2000/"; 12 | private readonly ServiceStackHost appHost; 13 | 14 | class AppHost : AppSelfHostBase 15 | { 16 | public AppHost() : base(nameof(IntegrationTest), typeof(MyServices).Assembly) { } 17 | 18 | public override void Configure(Container container) 19 | { 20 | } 21 | } 22 | 23 | public IntegrationTest() 24 | { 25 | appHost = new AppHost() 26 | .Init() 27 | .Start(BaseUri); 28 | } 29 | 30 | [OneTimeTearDown] 31 | public void OneTimeTearDown() => appHost.Dispose(); 32 | 33 | public IServiceClient CreateClient() => new JsonServiceClient(BaseUri); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/MigrationTasks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using ServiceStack.OrmLite; 4 | using BlazorDiffusion.Migrations; 5 | using ServiceStack; 6 | using ServiceStack.Data; 7 | 8 | namespace BlazorDiffusion.Tests; 9 | 10 | [TestFixture, Explicit, Category(nameof(MigrationTasks))] 11 | public class MigrationTasks 12 | { 13 | IDbConnectionFactory ResolveDbFactory() => new ConfigureDb().ConfigureAndResolve(); 14 | Migrator CreateMigrator() => new(ResolveDbFactory(), typeof(Migration1000).Assembly); 15 | 16 | [Test] 17 | public void Migrate() 18 | { 19 | var migrator = CreateMigrator(); 20 | migrator.Timeout = TimeSpan.Zero; 21 | var result = migrator.Run(); 22 | Assert.That(result.Succeeded); 23 | } 24 | 25 | [Test] 26 | public void Revert_All() 27 | { 28 | var migrator = CreateMigrator(); 29 | migrator.Timeout = TimeSpan.Zero; 30 | var result = migrator.Revert(Migrator.All); 31 | Assert.That(result.Succeeded); 32 | } 33 | 34 | [Test] 35 | public void Revert_Last() 36 | { 37 | var migrator = CreateMigrator(); 38 | var result = migrator.Revert(Migrator.Last); 39 | Assert.That(result.Succeeded); 40 | } 41 | 42 | [Test] 43 | public void Rerun_Last_Migration() 44 | { 45 | Revert_Last(); 46 | Migrate(); 47 | } 48 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/PrerenderTasks.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using NUnit.Framework; 4 | using ServiceStack; 5 | using ServiceStack.Text; 6 | using Microsoft.Extensions.Configuration; 7 | using System.Collections.Generic; 8 | using ServiceStack.Data; 9 | using ServiceStack.OrmLite; 10 | using BlazorDiffusion.ServiceModel; 11 | 12 | namespace BlazorDiffusion.Tests; 13 | 14 | [TestFixture, Category("prerender"), Explicit] 15 | public class PrerenderTasks 16 | { 17 | Bunit.TestContext Context; 18 | string ClientDir; 19 | string WwrootDir => ClientDir.CombineWith("wwwroot"); 20 | string PrerenderDir => WwrootDir.CombineWith("prerender"); 21 | 22 | public PrerenderTasks() 23 | { 24 | Context = new(); 25 | var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); 26 | ClientDir = config[nameof(ClientDir)] 27 | ?? throw new Exception($"{nameof(ClientDir)} not defined in appsettings.json"); 28 | // FileSystemVirtualFiles.RecreateDirectory(PrerenderDir); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "BlazorDiffusion.Tests": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "https://localhost:56616;http://localhost:56617" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/UnitTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using ServiceStack; 3 | using ServiceStack.Testing; 4 | using BlazorDiffusion.ServiceInterface; 5 | using BlazorDiffusion.ServiceModel; 6 | 7 | namespace BlazorDiffusion.Tests; 8 | 9 | public class UnitTest 10 | { 11 | private readonly ServiceStackHost appHost; 12 | 13 | public UnitTest() 14 | { 15 | appHost = new BasicAppHost().Init(); 16 | appHost.Container.AddTransient(); 17 | } 18 | 19 | [OneTimeTearDown] 20 | public void OneTimeTearDown() => appHost.Dispose(); 21 | 22 | [Test] 23 | public void Can_call_MyServices() 24 | { 25 | var service = appHost.Container.Resolve(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BlazorDiffusion.Tests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "HostDir": "../../../../BlazorDiffusion", 3 | "ClientDir": "../../../../BlazorDiffusion.Client" 4 | } 5 | -------------------------------------------------------------------------------- /BlazorDiffusion.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.31903.59 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDiffusion", "BlazorDiffusion\BlazorDiffusion.csproj", "{5F817400-1A3A-48DF-98A6-E7E5A3DC762F}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDiffusion.ServiceInterface", "BlazorDiffusion.ServiceInterface\BlazorDiffusion.ServiceInterface.csproj", "{5B8FFF01-1E0B-477D-9D7F-93016C128B23}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDiffusion.ServiceModel", "BlazorDiffusion.ServiceModel\BlazorDiffusion.ServiceModel.csproj", "{0127B6CA-1B79-46A6-8307-B36836D107F0}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDiffusion.Tests", "BlazorDiffusion.Tests\BlazorDiffusion.Tests.csproj", "{455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorDiffusion.Client", "BlazorDiffusion.Client\BlazorDiffusion.Client.csproj", "{B653A7DC-A85F-4A0B-B305-63F119979ED8}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {B653A7DC-A85F-4A0B-B305-63F119979ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {B653A7DC-A85F-4A0B-B305-63F119979ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {B653A7DC-A85F-4A0B-B305-63F119979ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {B653A7DC-A85F-4A0B-B305-63F119979ED8}.Release|Any CPU.Build.0 = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {02854F2A-8EF4-468E-80A3-CD64BBAF5D15} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /BlazorDiffusion/App_Data/seed/album-artifacts.csv: -------------------------------------------------------------------------------- 1 | AlbumRefId,ArtifactRefId,Description 2 | affd8c84-4581-46e1-8b79-56441f89e80e,80ade78e-d7cc-4e4b-a639-189379ca8e28, 3 | affd8c84-4581-46e1-8b79-56441f89e80e,d1f86bc0-ce4c-4672-ab1c-efb21bc6c9a2, 4 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,022b523a-75b3-41e5-aaee-ed9e27d512d8, 5 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,60511f0c-e20b-42bf-80db-3c0854df5cf5, 6 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,90131bdd-844c-4f7d-879e-3adc0d8e73d4, 7 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,3057d239-0ab3-4e8b-95d3-755ca9b14509, 8 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,28a0fa5d-d0ef-4b7f-91a0-85a19777364f, 9 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,74b7e6e8-06ad-4677-ba9e-ee1b8ac5fea4, 10 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,910a2936-7e6d-4620-8783-1343b2763857, 11 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,c1bf11d0-70f6-4017-b361-e6f2a8f402e1, 12 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,128681b5-2322-4725-a569-a9fbcdc5d76b, 13 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,da64e4ee-07a0-4d04-8016-2d4e03a037a6, 14 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,ba0c06b0-fdff-46c8-8205-df995ef2e688, 15 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,a0cc53a5-0fc9-4642-bbe6-6af8b1a30419, 16 | 2e298971-e776-4aaf-a295-33fa52f56634,18d40f96-fbb3-4669-81a9-1f69da9ed7c4, 17 | 2e298971-e776-4aaf-a295-33fa52f56634,f21dc3eb-e41f-442c-ad3d-d8e11869f650, 18 | 2e298971-e776-4aaf-a295-33fa52f56634,cfdee895-5929-4d22-9afc-1787cb925d25, 19 | 2e298971-e776-4aaf-a295-33fa52f56634,c8a00ea8-98d1-49d2-a9b8-5d5af240a009, 20 | 2e298971-e776-4aaf-a295-33fa52f56634,df93f956-b713-49cc-9cba-905235588e57, 21 | 2e298971-e776-4aaf-a295-33fa52f56634,c7e1bde2-b805-4943-befd-7cf9e650c558, 22 | 2e298971-e776-4aaf-a295-33fa52f56634,6095e90b-96dc-437a-be4d-1b8164903753, 23 | -------------------------------------------------------------------------------- /BlazorDiffusion/App_Data/seed/album-likes.csv: -------------------------------------------------------------------------------- 1 | RefId,AppUserId,CreatedDate 2 | -------------------------------------------------------------------------------- /BlazorDiffusion/App_Data/seed/albums.csv: -------------------------------------------------------------------------------- 1 | RefId,OwnerId,Name,Description,Tags,PrimaryArtifactRef 2 | affd8c84-4581-46e1-8b79-56441f89e80e,5,Steampunk,,,80ade78e-d7cc-4e4b-a639-189379ca8e28 3 | 7de2c76e-9b10-48e5-89cc-1620cebe67ba,5,Cassandra cain,,,022b523a-75b3-41e5-aaee-ed9e27d512d8 4 | 2e298971-e776-4aaf-a295-33fa52f56634,5,Nature,,,18d40f96-fbb3-4669-81a9-1f69da9ed7c4 5 | -------------------------------------------------------------------------------- /BlazorDiffusion/App_Data/seed/artifact-likes.csv: -------------------------------------------------------------------------------- 1 | RefId,ArtifactId,AppUserId,CreatedDate 2 | -------------------------------------------------------------------------------- /BlazorDiffusion/AssetUtils.cs: -------------------------------------------------------------------------------- 1 | using System.IO.Compression; 2 | using ServiceStack.OrmLite; 3 | 4 | namespace BlazorDiffusion; 5 | 6 | public static class AssetUtils 7 | { 8 | public static void FetchDemoAssets(this Migrator migrator) 9 | { 10 | using var zipStream = "https://github.com/NetCoreApps/BlazorDiffusionAssets/archive/refs/heads/master.zip" 11 | .GetStreamFromUrl(); 12 | var zipArchive = new ZipArchive(zipStream); 13 | var appFiles = new DirectoryInfo("./App_Files"); 14 | foreach (var entry in zipArchive.Entries) 15 | { 16 | using var stream = entry.Open(); 17 | var relPath = entry.FullName.SplitOnFirst("/")[1]; 18 | var path = Path.Combine(appFiles.FullName, relPath); 19 | if (entry.Length == 0) 20 | { 21 | if(!Directory.Exists(path)) 22 | Directory.CreateDirectory(path); 23 | continue; 24 | } 25 | File.WriteAllBytes(Path.Combine(appFiles.FullName,relPath), entry.Open().ReadFully()); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /BlazorDiffusion/Configure.AutoQuery.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Data; 3 | 4 | [assembly: HostingStartup(typeof(BlazorDiffusion.ConfigureAutoQuery))] 5 | 6 | namespace BlazorDiffusion; 7 | 8 | public class ConfigureAutoQuery : IHostingStartup 9 | { 10 | public void Configure(IWebHostBuilder builder) => builder 11 | .ConfigureServices(services => { 12 | // Enable Audit History 13 | services.AddSingleton(c => 14 | new OrmLiteCrudEvents(c.Resolve())); 15 | }) 16 | .ConfigureAppHost(appHost => { 17 | 18 | // For TodosService 19 | appHost.Plugins.Add(new AutoQueryDataFeature()); 20 | 21 | // For Bookings https://github.com/NetCoreApps/BookingsCrud 22 | appHost.Plugins.Add(new AutoQueryFeature { 23 | MaxLimit = 1000, 24 | //IncludeTotal = true, 25 | }); 26 | 27 | appHost.Resolve().InitSchema(); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /BlazorDiffusion/Configure.Db.Migrations.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.Data; 3 | using ServiceStack.OrmLite; 4 | using BlazorDiffusion.Migrations; 5 | using BlazorDiffusion.ServiceInterface; 6 | using BlazorDiffusion.ServiceModel; 7 | 8 | [assembly: HostingStartup(typeof(BlazorDiffusion.ConfigureDbMigrations))] 9 | 10 | namespace BlazorDiffusion; 11 | 12 | public class ConfigureDbMigrations : IHostingStartup 13 | { 14 | public void Configure(IWebHostBuilder builder) => builder 15 | .ConfigureAppHost(afterAppHostInit:appHost => { 16 | var migrator = new Migrator(appHost.Resolve(), typeof(Migration1000).Assembly); 17 | AppTasks.Register("migrate", _ => migrator.Run()); 18 | AppTasks.Register("migrate.revert", args => migrator.Revert(args[0])); 19 | AppTasks.Run(); 20 | 21 | using var db = migrator.DbFactory.OpenDbConnection(); 22 | Scores.Load(db); 23 | using var dbAnalytics = migrator.DbFactory.OpenDbConnection(Databases.Analytics); 24 | Scores.LoadAnalytics(dbAnalytics); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /BlazorDiffusion/Configure.Db.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceInterface; 2 | using BlazorDiffusion.ServiceModel; 3 | using CoenM.ImageHash; 4 | using Microsoft.Data.Sqlite; 5 | using ServiceStack.Data; 6 | using ServiceStack.OrmLite; 7 | using ServiceStack.OrmLite.Converters; 8 | 9 | [assembly: HostingStartup(typeof(BlazorDiffusion.ConfigureDb))] 10 | 11 | namespace BlazorDiffusion; 12 | 13 | // Database can be created with "dotnet run --AppTasks=migrate" 14 | public class ConfigureDb : IHostingStartup 15 | { 16 | public void Configure(IWebHostBuilder builder) => builder 17 | .ConfigureServices((context,services) => 18 | { 19 | var dbFactory = new OrmLiteConnectionFactory( 20 | context.Configuration.GetConnectionString("DefaultConnection") ?? "App_Data/db.sqlite", 21 | SqliteDialect.Provider); 22 | dbFactory.RegisterConnection(Databases.Analytics, 23 | context.Configuration.GetConnectionString("AnalyticsConnection") ?? "App_Data/analytics.sqlite", 24 | SqliteDialect.Provider); 25 | services.AddSingleton(dbFactory); 26 | ((DateTimeConverter)SqliteDialect.Provider.GetConverter()).DateStyle = DateTimeKind.Utc; 27 | }) 28 | .ConfigureAppHost(appHost => 29 | { 30 | using var db = appHost.Resolve().OpenDbConnection(); 31 | db.RegisterImgCompare(); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /BlazorDiffusion/Configure.Mq.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using ServiceStack; 5 | using ServiceStack.Messaging; 6 | 7 | [assembly: HostingStartup(typeof(BlazorDiffusion.ConfigureMq))] 8 | 9 | namespace BlazorDiffusion 10 | { 11 | /** 12 | Register Services you want available via MQ in your AppHost, e.g: 13 | var mqServer = container.Resolve(); 14 | mqServer.RegisterHandler(ExecuteMessage); 15 | */ 16 | public class ConfigureMq : IHostingStartup 17 | { 18 | public void Configure(IWebHostBuilder builder) => builder 19 | .ConfigureServices(services => { 20 | services.AddSingleton((Func)(c => new BackgroundMqService())); 21 | }) 22 | .ConfigureAppHost(afterAppHostInit: appHost => { 23 | var mqService = appHost.Resolve(); 24 | mqService.RegisterHandler(appHost.ExecuteMessage); 25 | mqService.RegisterHandler(appHost.ExecuteMessage); 26 | mqService.RegisterHandler(appHost.ExecuteMessage); 27 | mqService.Start(); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BlazorDiffusion/Configure.Profiling.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | 3 | [assembly: HostingStartup(typeof(BlazorDiffusion.ConfigureProfiling))] 4 | 5 | namespace BlazorDiffusion; 6 | 7 | public class ConfigureProfiling : IHostingStartup 8 | { 9 | public void Configure(IWebHostBuilder builder) 10 | { 11 | builder.ConfigureAppHost(host => { 12 | host.Plugins.AddIfDebug(new RequestLogsFeature { 13 | EnableResponseTracking = true, 14 | }); 15 | 16 | host.Plugins.AddIfDebug(new ProfilingFeature { 17 | IncludeStackTrace = true, 18 | }); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BlazorDiffusion/MarkdownUtils.cs: -------------------------------------------------------------------------------- 1 | using Markdig; 2 | using Markdig.Syntax; 3 | using ServiceStack; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorDiffusion; 10 | 11 | public class MarkdownFileInfo 12 | { 13 | public string? Path { get; set; } 14 | public string? FileName { get; set; } 15 | public string? Title { get; set; } 16 | public string? Summary { get; set; } 17 | public DateTime? Date { get; set; } 18 | public string? Content { get; set; } 19 | public string? Preview { get; set; } 20 | } 21 | 22 | public static class MarkdownUtils 23 | { 24 | public static Dictionary Cache = new(); 25 | 26 | public static async Task> LoadDocumentAsync(string name, Func> resolverAsync) 27 | { 28 | try 29 | { 30 | if (Cache.TryGetValue(name, out var cachedDoc)) 31 | return ApiResult.Create(cachedDoc); 32 | 33 | var pipeline = new MarkdownPipelineBuilder() 34 | .UseYamlFrontMatter() 35 | .UseAdvancedExtensions() 36 | .Build(); 37 | var writer = new System.IO.StringWriter(); 38 | var renderer = new Markdig.Renderers.HtmlRenderer(writer); 39 | pipeline.Setup(renderer); 40 | 41 | var doc = new MarkdownFileInfo 42 | { 43 | Path = name, 44 | FileName = $"{name}.md", 45 | }; 46 | doc.Content = await resolverAsync(doc); 47 | 48 | var document = Markdown.Parse(doc.Content!, pipeline); 49 | renderer.Render(document); 50 | var block = document 51 | .Descendants() 52 | .FirstOrDefault(); 53 | 54 | var metaObj = block? 55 | .Lines // StringLineGroup[] 56 | .Lines // StringLine[] 57 | .Select(x => $"{x}\n") 58 | .ToList() 59 | .Select(x => x.Replace("---", string.Empty)) 60 | .Where(x => !string.IsNullOrWhiteSpace(x)) 61 | .Select(x => KeyValuePairs.Create(x.LeftPart(':').Trim(), x.RightPart(':').Trim())) 62 | .ToObjectDictionary(); 63 | metaObj?.PopulateInstance(doc); 64 | 65 | await writer.FlushAsync(); 66 | doc.Preview = writer.ToString(); 67 | Cache[name] = doc; 68 | return ApiResult.Create(doc); 69 | } 70 | catch (Exception ex) 71 | { 72 | return ApiResult.CreateError(ex.AsResponseStatus()); 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /BlazorDiffusion/MigrationUtils.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorDiffusion; 2 | 3 | public static class MigrationUtils 4 | { 5 | public static T BySystemUser(this T row, DateTime? date = null) where T : AuditBase 6 | { 7 | var useDate = date ?? DateTime.Now; 8 | row.CreatedBy = "2"; 9 | row.CreatedDate = useDate; 10 | row.ModifiedBy = "2"; 11 | row.ModifiedDate = useDate; 12 | return row; 13 | } 14 | } -------------------------------------------------------------------------------- /BlazorDiffusion/Migrations/Migration1000.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack; 2 | using ServiceStack.DataAnnotations; 3 | using ServiceStack.OrmLite; 4 | 5 | namespace BlazorDiffusion.Migrations; 6 | 7 | public class Migration1000 : MigrationBase 8 | { 9 | public class Booking : AuditBase 10 | { 11 | [AutoIncrement] 12 | public int Id { get; set; } 13 | public string Name { get; set; } 14 | public RoomType RoomType { get; set; } 15 | public int RoomNumber { get; set; } 16 | public DateTime BookingStartDate { get; set; } 17 | public DateTime? BookingEndDate { get; set; } 18 | public decimal Cost { get; set; } 19 | public string? Notes { get; set; } 20 | public bool? Cancelled { get; set; } 21 | 22 | [References(typeof(Coupon))] 23 | public string? CouponId { get; set; } 24 | } 25 | 26 | public class Coupon 27 | { 28 | public string Id { get; set; } 29 | public string Description { get; set; } 30 | public int Discount { get; set; } 31 | public DateTime ExpiryDate { get; set; } 32 | } 33 | 34 | public enum RoomType 35 | { 36 | Queen, 37 | Double, 38 | Suite, 39 | } 40 | 41 | public override void Up() 42 | { 43 | Db.CreateTable(); 44 | Db.CreateTable(); 45 | 46 | new[] { 5, 10, 15, 20, 25, 30, 40, 50, 60, 70, }.Each(percent => { 47 | Db.Insert(new Coupon { Id = $"BOOK{percent}", Description = $"{percent}% off", Discount = 10, ExpiryDate = DateTime.UtcNow.AddDays(30) }); 48 | }); 49 | 50 | CreateBooking("First Booking!", RoomType.Queen, 10, 100, "BOOK10", "employee@email.com"); 51 | CreateBooking("Booking 2", RoomType.Double, 12, 120, "BOOK25", "manager@email.com"); 52 | CreateBooking("Booking the 3rd", RoomType.Suite, 13, 130, null, "employee@email.com"); 53 | } 54 | 55 | public void CreateBooking(string name, RoomType type, int roomNo, decimal cost, string? couponId, string by) => 56 | Db.Insert(new Booking { 57 | Name = name, 58 | RoomType = type, 59 | RoomNumber = roomNo, 60 | Cost = cost, 61 | BookingStartDate = DateTime.UtcNow.AddDays(roomNo), 62 | BookingEndDate = DateTime.UtcNow.AddDays(roomNo + 7), 63 | CouponId = couponId, 64 | CreatedBy = by, 65 | CreatedDate = DateTime.UtcNow, 66 | ModifiedBy = by, 67 | ModifiedDate = DateTime.UtcNow, 68 | }); 69 | 70 | public override void Down() 71 | { 72 | Db.DropTable(); 73 | Db.DropTable(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /BlazorDiffusion/Migrations/Migration1002.cs: -------------------------------------------------------------------------------- 1 | using ServiceStack.DataAnnotations; 2 | using ServiceStack.OrmLite; 3 | 4 | namespace BlazorDiffusion.Migrations; 5 | 6 | [Description("Create FTS Tables")] 7 | public class Migration1002 : MigrationBase 8 | { 9 | public class ArtifactFts 10 | { 11 | public int rowid { get; set; } 12 | public int CreativeId { get; set; } 13 | public string Prompt { get; set; } 14 | public string RefId { get; set; } 15 | } 16 | 17 | public override void Up() 18 | { 19 | // Create virtual tables for SQLite Full Text Search 20 | Db.ExecuteNonQuery($@"CREATE VIRTUAL TABLE {nameof(ArtifactFts)} 21 | USING FTS5( 22 | {nameof(ArtifactFts.Prompt)}, 23 | {nameof(ArtifactFts.CreativeId)}, 24 | {nameof(ArtifactFts.RefId)});" 25 | ); 26 | 27 | Db.ExecuteNonQuery($@"INSERT INTO {nameof(ArtifactFts)} 28 | (rowid, 29 | {nameof(ArtifactFts.Prompt)}, 30 | {nameof(ArtifactFts.CreativeId)}, 31 | {nameof(ArtifactFts.RefId)}) 32 | SELECT 33 | {nameof(Migration1001.Artifact.Id)}, 34 | {nameof(Migration1001.Artifact.Prompt)}, 35 | {nameof(Migration1001.Artifact.CreativeId)}, 36 | {nameof(Migration1001.Artifact.RefId)} FROM {nameof(Migration1001.Artifact)}"); 37 | } 38 | public override void Down() 39 | { 40 | Db.DropTable(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BlazorDiffusion/Migrations/Migration1003.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using ServiceStack.DataAnnotations; 3 | using ServiceStack.OrmLite; 4 | 5 | namespace BlazorDiffusion.Migrations; 6 | 7 | [Description("Add Analytics tables")] 8 | [NamedConnection(Databases.Analytics)] 9 | public class Migration1003 : MigrationBase 10 | { 11 | [NamedConnection(Databases.Analytics)] 12 | public class StatBase 13 | { 14 | public string RefId { get; set; } 15 | public int? AppUserId { get; set; } 16 | public string RawUrl { get; set; } 17 | public string RemoteIp { get; set; } 18 | public DateTime CreatedDate { get; set; } 19 | } 20 | 21 | public enum StatType 22 | { 23 | Download, 24 | } 25 | 26 | public class ArtifactStat : StatBase 27 | { 28 | [AutoIncrement] 29 | public int Id { get; set; } 30 | 31 | public StatType Type { get; set; } 32 | public int ArtifactId { get; set; } 33 | public string Source { get; set; } 34 | public string Version { get; set; } 35 | } 36 | 37 | public class SearchStat : StatBase 38 | { 39 | [AutoIncrement] 40 | public int Id { get; set; } 41 | public string? Query { get; set; } 42 | public string? Similar { get; set; } 43 | public string? User { get; set; } 44 | public string? Modifier { get; set; } 45 | public string? Artist { get; set; } 46 | public string? Album { get; set; } 47 | public string? Show { get; set; } 48 | public string? Source { get; set; } 49 | 50 | public int? ArtifactId { get; set; } 51 | public int? AlbumId { get; set; } 52 | public int? ModifierId { get; set; } 53 | public int? ArtistId { get; set; } 54 | } 55 | 56 | public enum SignupType 57 | { 58 | Updates, 59 | Beta, 60 | } 61 | public class Signup : StatBase 62 | { 63 | [AutoIncrement] 64 | public int Id { get; set; } 65 | public SignupType Type { get; set; } 66 | public string Email { get; set; } 67 | public string? Name { get; set; } 68 | public DateTime? CancelledDate { get; set; } 69 | } 70 | 71 | public override void Up() 72 | { 73 | Db.CreateTable(); 74 | Db.CreateTable(); 75 | Db.CreateTable(); 76 | } 77 | 78 | public override void Down() 79 | { 80 | Db.DropTable(); 81 | Db.DropTable(); 82 | Db.DropTable(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /BlazorDiffusion/Migrations/Migration1004.cs: -------------------------------------------------------------------------------- 1 | using BlazorDiffusion.ServiceModel; 2 | using ServiceStack.DataAnnotations; 3 | using ServiceStack.OrmLite; 4 | 5 | namespace BlazorDiffusion.Migrations; 6 | 7 | [Description("Add Comments")] 8 | public class Migration1004 : MigrationBase 9 | { 10 | public class ArtifactComment : AuditBase 11 | { 12 | [AutoIncrement] 13 | public int Id { get; set; } 14 | public int ArtifactId { get; set; } 15 | public int? ReplyId { get; set; } 16 | public string Content { get; set; } 17 | [Default(0)] 18 | public int UpVotes { get; set; } 19 | [Default(0)] 20 | public int DownVotes { get; set; } 21 | [Default(0)] 22 | public int Votes { get; set; } 23 | public string? FlagReason { get; set; } 24 | public string? Notes { get; set; } 25 | public string RefId { get; set; } 26 | public int AppUserId { get; set; } 27 | } 28 | 29 | [UniqueConstraint(nameof(ArtifactCommentId), nameof(AppUserId))] 30 | public class ArtifactCommentVote 31 | { 32 | [AutoIncrement] 33 | public long Id { get; set; } 34 | 35 | [References(typeof(ArtifactComment))] 36 | public int ArtifactCommentId { get; set; } 37 | [References(typeof(AppUser))] 38 | public int AppUserId { get; set; } 39 | public int Vote { get; set; } // -1 / 1 40 | public DateTime CreatedDate { get; set; } 41 | } 42 | 43 | public class ArtifactCommentReport 44 | { 45 | [AutoIncrement] 46 | public long Id { get; set; } 47 | 48 | [References(typeof(ArtifactComment))] 49 | public int ArtifactCommentId { get; set; } 50 | [References(typeof(AppUser))] 51 | public int AppUserId { get; set; } 52 | public PostReport PostReport { get; set; } 53 | public string Description { get; set; } 54 | public DateTime CreatedDate { get; set; } 55 | } 56 | 57 | public enum PostReport 58 | { 59 | Offensive, 60 | Spam, 61 | Nudity, 62 | Illegal, 63 | } 64 | 65 | public override void Up() 66 | { 67 | Db.CreateTable(); 68 | Db.CreateTable(); 69 | Db.CreateTable(); 70 | } 71 | 72 | public override void Down() 73 | { 74 | Db.DropTable(); 75 | Db.DropTable(); 76 | Db.DropTable(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorDiffusion.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |

Error.

19 |

An error occurred while processing your request.

20 | 21 | @if (Model.ShowRequestId) 22 | { 23 |

24 | Request ID: @Model.RequestId 25 |

26 | } 27 | 28 |

Development Mode

29 |

30 | Swapping to the Development environment displays detailed information about the error that occurred. 31 |

32 |

33 | The Development environment shouldn't be enabled for deployed applications. 34 | It can result in displaying sensitive information from exceptions to end users. 35 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 36 | and restarting the app. 37 |

38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace BlazorDiffusion.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string? RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @model IndexModel 3 | @{ 4 | //ViewData["Title"] = "Home page"; 5 | } 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | 4 | namespace BlazorDiffusion.Pages; 5 | 6 | public class IndexModel : PageModel 7 | { 8 | private readonly ILogger _logger; 9 | 10 | public IndexModel(ILogger logger) 11 | { 12 | _logger = logger; 13 | } 14 | 15 | public void OnGet() 16 | { 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Shared/_ArtifactImage.cshtml: -------------------------------------------------------------------------------- 1 | @model ArtifactImageParams 2 | @using BlazorDiffusion.UI 3 | @inject UserState UserState 4 | @{ 5 | var Artifact = Model.Artifact; 6 | @if (Artifact != null) 7 | { 8 | var MinSize = Model.MinSize; 9 | int width = MinSize == null 10 | ? Artifact.Width 11 | : (int)(Artifact.Width > Artifact.Height 12 | ? (Artifact.Width / (double)Artifact.Height) * MinSize.Value 13 | : MinSize.Value); 14 | 15 | int height = MinSize == null 16 | ? Artifact.Height 17 | : (int)(Artifact.Height > Artifact.Width 18 | ? (Artifact.Height / (double)Artifact.Width) * MinSize.Value 19 | : MinSize.Value); 20 | 21 |
22 | @Artifact.Prompt 24 |
25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Shared/_LayoutEmpty.cshtml: -------------------------------------------------------------------------------- 1 | @RenderBody() -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/Shared/_StaticGallery.cshtml: -------------------------------------------------------------------------------- 1 | @model GalleryResults 2 | @using BlazorDiffusion.UI 3 | @using BlazorDiffusion.ServiceModel 4 | 5 |
6 | @foreach (var artifact in Model.Artifacts.OrEmpty()) 7 | { 8 | @await Html.PartialAsync("_StaticImage", artifact) 9 | } 10 |
11 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/_EmptyLayout.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components 2 | @using Microsoft.AspNetCore.Components.Web 3 | @using System.Text.RegularExpressions 4 | @namespace BlazorDiffusion.Pages 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | 7 | 8 | @RenderBody() 9 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using BlazorDiffusion 2 | @namespace BlazorDiffusion.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/ssg/Albums.cshtml: -------------------------------------------------------------------------------- 1 | @page "/ssg/albums" 2 | @inherits ServiceStack.Mvc.RazorPage 3 | @{ 4 | Layout = "_LayoutStatic"; 5 | } 6 | @using BlazorDiffusion.UI 7 | @using BlazorDiffusion.ServiceModel 8 | @inject UserState UserState 9 | 10 | @{ 11 | int GridColumns = 5; 12 | 13 | List results = new(); 14 | 15 | var api = await Gateway.ApiAsync(new GetAlbumIds()); 16 | if (api.Succeeded) 17 | { 18 | results = await UserState.GetAlbumsByIdsAsync(api.Response!.Results.Take(UserState.StaticTake)); 19 | } 20 | } 21 | 22 |

Albums

23 | 24 | 59 | 60 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/ssg/Image.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using BlazorDiffusion.UI; 4 | using BlazorDiffusion.ServiceModel; 5 | using System.Linq; 6 | 7 | namespace BlazorDiffusion.Pages.ssg; 8 | 9 | public class ImageModel : PageModel, IGetPageModel 10 | { 11 | [FromQuery] public new int Id { get; set; } = 0; 12 | [FromQuery] public new string? Slug { get; set; } 13 | 14 | public string? UseSlug = null; 15 | public string? Title = null; 16 | public Creative? Creative = null; 17 | public Artifact? Artifact = null; 18 | public AlbumResult[] CreativeAlbums = Array.Empty(); 19 | public List CreativeAlbumArtifacts = new(); 20 | 21 | public IEnumerable GetArtifactAlbums() => CreativeAlbums.Where(x => x.ArtifactIds.Contains(Id)); 22 | public Artifact? GetAlbumCoverArtifact(AlbumResult album) => CreativeAlbumArtifacts.FirstOrDefault(x => x.Id == album.GetAlbumCoverArtifactId()); 23 | 24 | public async Task OnGetAsync() 25 | { 26 | var Gateway = HostContext.AppHost.GetServiceGateway(); 27 | Title = "Image Art View"; 28 | 29 | var request = new GetCreative { ArtifactId = Id }; 30 | var api = await Gateway.ApiAsync(request); 31 | if (api.Succeeded) 32 | { 33 | Creative = api.Response!.Result; 34 | Artifact = Creative.Artifacts?.FirstOrDefault(x => x.Id == Id) 35 | ?? Creative.Artifacts?.FirstOrDefault(); 36 | UseSlug = Slug ??= Creative.UserPrompt.GenerateSlug(); 37 | 38 | Title = Creative.UserPrompt; 39 | 40 | var apiAlbums = await Gateway.ApiAsync(new GetCreativesInAlbums { CreativeId = Creative.Id }); 41 | if (apiAlbums.Succeeded) 42 | { 43 | CreativeAlbums = (apiAlbums.Response!.Results ?? new()).ToArray(); 44 | 45 | var artifactIds = CreativeAlbums.Select(x => x.GetAlbumCoverArtifactId()).ToList(); 46 | var apiAlbumArtifacts = await Gateway.ApiAsync(new QueryArtifacts { Ids = artifactIds }); 47 | if (apiAlbumArtifacts.Succeeded) 48 | { 49 | CreativeAlbumArtifacts = apiAlbumArtifacts.Response?.Results ?? new(); 50 | } 51 | } 52 | } 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/ssg/Latest.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using BlazorDiffusion.UI; 4 | using BlazorDiffusion.ServiceModel; 5 | using ServiceStack.Logging; 6 | 7 | namespace BlazorDiffusion.Pages.ssg; 8 | 9 | public class LatestModel : PageModel, IGetPageModel 10 | { 11 | [FromQuery] public new int? Page { get; set; } 12 | 13 | public int UsePage; 14 | public int Total; 15 | public int Pages; 16 | public const int GridColumns = 4; 17 | public GalleryResults GalleryResults = new(); 18 | 19 | public async Task OnGetAsync() 20 | { 21 | var log = LogManager.GetLogger(GetType()); 22 | UsePage = Math.Max(Page ?? 1, 1); 23 | var Gateway = HostContext.AppHost.GetServiceGateway(); 24 | 25 | List results = new(); 26 | HashSet resultIds = new(); 27 | bool hasMore; 28 | 29 | GalleryResults = new() { GridColumns = GridColumns }; 30 | 31 | var request = new SearchArtifacts 32 | { 33 | Skip = (UsePage - 1) * UserState.StaticPagedTake, 34 | Take = UserState.StaticTake, 35 | Include = "total", 36 | Show = "latest" 37 | }; 38 | 39 | var api = await Gateway.ApiAsync(request); 40 | clearResults(); 41 | 42 | if (api.Succeeded) 43 | { 44 | if (api.Response?.Results != null) 45 | { 46 | addResults(api.Response.Results); 47 | } 48 | } 49 | 50 | void setResults(IEnumerable newResults) 51 | { 52 | results = newResults.ToList(); 53 | GalleryResults = X.Apply(GalleryResults.Clone(), x => x.Artifacts = results.ShuffleGridArtifacts(GridColumns).ToList()); 54 | } 55 | 56 | void clearResults() 57 | { 58 | results.Clear(); 59 | resultIds.Clear(); 60 | } 61 | 62 | void addResults(List artifacts, bool reset = false) 63 | { 64 | if (reset) 65 | clearResults(); 66 | 67 | var missingPrompts = new List(); 68 | 69 | hasMore = artifacts.Count >= request.Take; 70 | foreach (var artifact in artifacts) 71 | { 72 | if (resultIds.Contains(artifact.Id)) 73 | continue; 74 | 75 | if (string.IsNullOrEmpty(artifact.Prompt) && string.IsNullOrEmpty(artifact.UserPrompt)) 76 | { 77 | missingPrompts.Add(artifact.Id); 78 | continue; 79 | } 80 | 81 | resultIds.Add(artifact.Id); 82 | results.Add(artifact); 83 | } 84 | 85 | if (missingPrompts.Count > 0) 86 | { 87 | log.ErrorFormat("Artifacts with missing prompts: {0}", string.Join(", ", missingPrompts)); 88 | } 89 | 90 | setResults(results); 91 | } 92 | 93 | Total = api.Response?.Total ?? 0; 94 | Pages = (int)Math.Ceiling(Total / (double)UserState.StaticPagedTake); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /BlazorDiffusion/Pages/ssg/Top.cshtml: -------------------------------------------------------------------------------- 1 | @page "/ssg/top" 2 | @inherits ServiceStack.Mvc.RazorPage 3 | @{ 4 | Layout = "_LayoutStatic"; 5 | } 6 | @using BlazorDiffusion.UI 7 | @using BlazorDiffusion.ServiceModel 8 | @inject UserState UserState 9 | 10 | @{ 11 | const int GridColumns = 4; 12 | 13 | SearchArtifacts request = new(); 14 | List results = new(); 15 | HashSet resultIds = new(); 16 | bool hasMore; 17 | 18 | ApiResult> api = new(); 19 | GalleryResults GalleryResults = new() { GridColumns = GridColumns }; 20 | 21 | request.Skip = 0; 22 | request.Take = UserState.StaticTake; 23 | request.Show = "top"; 24 | api = await Gateway.ApiAsync(request); 25 | clearResults(); 26 | 27 | if (api.Succeeded) 28 | { 29 | if (api.Response?.Results != null) 30 | { 31 | addResults(api.Response.Results); 32 | } 33 | } 34 | 35 | void setResults(IEnumerable newResults) 36 | { 37 | results = newResults.ToList(); 38 | GalleryResults = X.Apply(GalleryResults.Clone(), x => x.Artifacts = results.ShuffleGridArtifacts(GridColumns).ToList()); 39 | } 40 | 41 | void clearResults() 42 | { 43 | results.Clear(); 44 | resultIds.Clear(); 45 | } 46 | 47 | void addResults(List artifacts, bool reset = false) 48 | { 49 | if (reset) 50 | clearResults(); 51 | 52 | hasMore = artifacts.Count >= request.Take; 53 | foreach (var artifact in artifacts) 54 | { 55 | if (resultIds.Contains(artifact.Id)) 56 | continue; 57 | 58 | resultIds.Add(artifact.Id); 59 | results.Add(artifact); 60 | } 61 | setResults(results); 62 | } 63 | } 64 | 65 |

Top Images

66 | 67 | @await Html.PartialAsync("_StaticGallery", GalleryResults) 68 | 69 | 70 | -------------------------------------------------------------------------------- /BlazorDiffusion/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Microsoft.AspNetCore.Components.Authorization; 3 | using ServiceStack.Blazor; 4 | using BlazorDiffusion.UI; 5 | using BlazorDiffusion.ServiceModel; 6 | using Ljbc1994.Blazor.IntersectionObserver; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | 9 | AppHost.RegisterKey(); 10 | 11 | var builder = WebApplication.CreateBuilder(args); 12 | 13 | // Add services to the container. 14 | builder.Services.AddLogging(); 15 | builder.Services.AddRazorPages(); 16 | builder.Services.AddServerSideBlazor(); 17 | 18 | builder.Services.AddScoped(); 19 | builder.Services.AddScoped(s => s.GetRequiredService()); 20 | 21 | var baseUrl = // builder.Configuration["oauth.RedirectUrl"] ?? // trying out UseInProcessClient 22 | (builder.Environment.IsDevelopment() ? "https://localhost:5001" : "http://" + IPAddress.Loopback); 23 | 24 | builder.Services.AddLocalStorage(); 25 | builder.Services.AddTransient(); // needed to use same WASM ServiceStackStateProvider 26 | builder.Services.AddBlazorServerApiClient(baseUrl); 27 | 28 | builder.Services.AddScoped(); 29 | builder.Services.AddScoped(); 30 | builder.Services.AddIntersectionObserver(); 31 | 32 | var app = builder.Build(); 33 | 34 | // Configure the HTTP request pipeline. 35 | if (app.Environment.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | app.UseWebAssemblyDebugging(); 39 | } 40 | else 41 | { 42 | app.UseExceptionHandler("/Error"); 43 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 44 | app.UseHsts(); 45 | } 46 | 47 | app.UseHttpsRedirection(); 48 | app.UseStaticFiles(); 49 | 50 | app.UseBlazorFrameworkFiles(); // WASM 51 | app.UseRouting(); 52 | app.MapRazorPages(); 53 | app.MapBlazorHub(); 54 | app.MapFallbackToPage("/Index"); 55 | 56 | app.UseServiceStack(new AppHost()); 57 | 58 | 59 | BlazorConfig.Set(new() 60 | { 61 | // This changes Blazor Components + ApiAsync APIs to use InProcessGateway instead of JsonApiClient 62 | // UseInProcessClient = true, 63 | 64 | Services = app.Services, 65 | JSParseObject = JS.ParseObject, 66 | EnableLogging = app.Environment.IsDevelopment(), 67 | EnableVerboseLogging = app.Environment.IsDevelopment(), 68 | ApiBaseUrl = AppConfig.Instance.ApiBaseUrl, 69 | AssetsBasePath = AppConfig.Instance.AssetsBasePath, 70 | FallbackAssetsBasePath = AppConfig.Instance.FallbackAssetsBasePath, 71 | DefaultProfileUrl = Icons.AnonUserUri, 72 | OnApiErrorAsync = (request, apiError) => 73 | { 74 | BlazorConfig.Instance.GetLog()?.LogDebug("\n\nOnApiErrorAsync(): {0}", apiError.Error.GetDetailedError()); 75 | return Task.CompletedTask; 76 | } 77 | }); 78 | 79 | app.Run(); 80 | -------------------------------------------------------------------------------- /BlazorDiffusion/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "IIS Express": { 4 | "commandName": "IISExpress", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 10 | }, 11 | "BlazorDiffusion": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "dotnetRunMessages": true, 18 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 19 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 20 | } 21 | }, 22 | "iisSettings": { 23 | "windowsAuthentication": false, 24 | "anonymousAuthentication": true, 25 | "iisExpress": { 26 | "applicationUrl": "http://localhost:8345", 27 | "sslPort": 44311 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /BlazorDiffusion/R2VirtualFilesProvider.cs: -------------------------------------------------------------------------------- 1 | using Amazon.S3; 2 | using Amazon.S3.Model; 3 | using ServiceStack.Aws; 4 | using ServiceStack.IO; 5 | using ServiceStack.Logging; 6 | using ServiceStack.Text; 7 | 8 | namespace BlazorDiffusion; 9 | 10 | public class R2VirtualFilesProvider : S3VirtualFiles 11 | { 12 | public static ILog Log = LogManager.GetLogger(typeof(R2VirtualFilesProvider)); 13 | 14 | public R2VirtualFilesProvider(IAmazonS3 client, string bucketName) : base(client, bucketName) {} 15 | 16 | public override void WriteFile(string filePath, Stream stream) 17 | { 18 | AmazonS3.PutObject(new PutObjectRequest 19 | { 20 | Key = SanitizePath(filePath), 21 | BucketName = BucketName, 22 | InputStream = stream, 23 | DisablePayloadSigning = true, 24 | }); 25 | } 26 | 27 | public override void WriteFile(string filePath, string contents) 28 | { 29 | AmazonS3.PutObject(new PutObjectRequest 30 | { 31 | Key = SanitizePath(filePath), 32 | BucketName = BucketName, 33 | ContentBody = contents, 34 | DisablePayloadSigning = true, 35 | }); 36 | } 37 | 38 | public override async Task WriteFileAsync(string path, object contents, CancellationToken token = default) 39 | { 40 | try 41 | { 42 | // need to buffer otherwise hangs when trying to send an uploaded file stream (depends on provider) 43 | var buffer = contents is not MemoryStream; 44 | var fileContents = await FileContents.GetAsync(contents, buffer); 45 | if (fileContents?.Stream != null) 46 | { 47 | await AmazonS3.PutObjectAsync(new PutObjectRequest 48 | { 49 | Key = SanitizePath(path), 50 | BucketName = BucketName, 51 | InputStream = fileContents.Stream, 52 | DisablePayloadSigning = true, 53 | }, token).ConfigAwait(); 54 | } 55 | else if (fileContents?.Text != null) 56 | { 57 | await AmazonS3.PutObjectAsync(new PutObjectRequest 58 | { 59 | Key = SanitizePath(path), 60 | BucketName = BucketName, 61 | ContentBody = fileContents.Text, 62 | DisablePayloadSigning = true, 63 | }, token).ConfigAwait(); 64 | } 65 | else throw new NotSupportedException($"Unknown File Content Type: {contents.GetType().Name}"); 66 | 67 | if (buffer && fileContents.Stream != null) 68 | using (fileContents.Stream) {} 69 | } 70 | catch (Exception e) 71 | { 72 | Log.Error(e, "Could not write file to {0}", path); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /BlazorDiffusion/TrackingCircuitHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Server.Circuits; 2 | 3 | namespace BlazorDiffusion; 4 | 5 | public class TrackingCircuitHandler : CircuitHandler 6 | { 7 | private HashSet circuits = new(); 8 | 9 | ILogger log; 10 | public TrackingCircuitHandler(ILogger log) 11 | { 12 | this.log = log; 13 | } 14 | 15 | public override Task OnConnectionUpAsync(Circuit circuit, 16 | CancellationToken cancellationToken) 17 | { 18 | log.LogDebug("Circuit Connection {0} Opened", circuit.Id); 19 | circuits.Add(circuit); 20 | 21 | return Task.CompletedTask; 22 | } 23 | 24 | public override Task OnConnectionDownAsync(Circuit circuit, 25 | CancellationToken cancellationToken) 26 | { 27 | log.LogDebug("Circuit Connection {0} Closed", circuit.Id); 28 | circuits.Remove(circuit); 29 | 30 | return Task.CompletedTask; 31 | } 32 | 33 | public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken) 34 | { 35 | log.LogDebug("Circuit {0} Opened", circuit.Id); 36 | return base.OnCircuitOpenedAsync(circuit, cancellationToken); 37 | } 38 | 39 | public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken) 40 | { 41 | log.LogDebug("Circuit {0} Closed", circuit.Id); 42 | return base.OnCircuitClosedAsync(circuit, cancellationToken); 43 | } 44 | 45 | public int ConnectedCircuits => circuits.Count; 46 | } 47 | -------------------------------------------------------------------------------- /BlazorDiffusion/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DebugMode": true, 3 | "BlazorWebRoot": "../BlazorDiffusion.Client/wwwroot", 4 | "DetailedErrors": true, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "Debug": { 8 | "LogLevel": { 9 | "Default": "Debug", 10 | "System": "Information", 11 | "Microsoft": "Information", 12 | "HubConnection": "Debug" 13 | } 14 | }, 15 | "Console": { 16 | "LogLevel": { 17 | "Default": "Debug", 18 | "System": "Information", 19 | "Microsoft": "Information", 20 | "HubConnection": "Debug" 21 | } 22 | } 23 | }, 24 | 25 | "oauth.RedirectUrl": "https://localhost:5001/", 26 | "oauth.CallbackUrl": "https://localhost:5001/auth/{0}", 27 | "oauth.facebook.Permissions": [ "email", "public_profile" ], 28 | "oauth.facebook.AppId": "505529004846883", 29 | "oauth.facebook.AppSecret": "15cf0bfb56b1c70e5d91e7c8991e5beb", 30 | "oauth.google.ConsumerKey": "1102689471-722v6ct4d4t05npu15ca78vc5etd0cak.apps.googleusercontent.com", 31 | "oauth.google.ConsumerSecret": "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", 32 | "oauth.microsoftgraph.AppId": "d686ba27-19e5-4a12-b756-c40efb2d04f2", 33 | "oauth.microsoftgraph.AppSecret": "g3~8Q~FpS6SZDZoVdhecLoTDfPlT.aLFg1JFOaN~", 34 | "oauth.microsoftgraph.SavePhoto": "true", 35 | "oauth.microsoftgraph.SavePhotoSize": "96x96" 36 | } 37 | -------------------------------------------------------------------------------- /BlazorDiffusion/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "Debug": { 5 | "LogLevel": { 6 | "Default": "Warning" 7 | } 8 | }, 9 | "Console": { 10 | "LogLevel": { 11 | "Default": "Warning" 12 | } 13 | } 14 | }, 15 | "oauth.RedirectUrl": "https://blazordiffusion.com/", 16 | "oauth.CallbackUrl": "https://blazordiffusion.com/auth/{0}", 17 | "oauth.facebook.AppId": "1881378095539436", 18 | "oauth.facebook.Permissions": [ "email", "public_profile" ], 19 | "oauth.google.ConsumerKey": "1102689471-tgo6e7bt34chcal8euncm6kgth805tr8.apps.googleusercontent.com", 20 | "oauth.microsoftgraph.AppId": "edfd58af-6d17-4745-82fa-f74497edfe9e", 21 | "oauth.microsoftgraph.SavePhoto": "true", 22 | "oauth.microsoftgraph.SavePhotoSize": "96x96", 23 | "AllowedHosts": "*" 24 | } 25 | -------------------------------------------------------------------------------- /BlazorDiffusion/proto/engines.proto: -------------------------------------------------------------------------------- 1 | syntax = 'proto3'; 2 | package gooseai; 3 | option go_package = "./;engines"; 4 | 5 | // Possible engine type 6 | enum EngineType { 7 | TEXT = 0; 8 | PICTURE = 1; 9 | AUDIO = 2; 10 | VIDEO = 3; 11 | CLASSIFICATION = 4; 12 | STORAGE = 5; 13 | } 14 | 15 | enum EngineTokenizer { 16 | GPT2 = 0; 17 | PILE = 1; 18 | } 19 | 20 | // Engine info struct 21 | message EngineInfo { 22 | string id = 1; 23 | string owner = 2; 24 | bool ready = 3; 25 | EngineType type = 4; 26 | EngineTokenizer tokenizer = 5; 27 | string name = 6; 28 | string description = 7; 29 | } 30 | 31 | message ListEnginesRequest { 32 | // Empty 33 | } 34 | 35 | // Engine info list 36 | message Engines { 37 | repeated EngineInfo engine = 1; 38 | } 39 | 40 | service EnginesService { 41 | rpc ListEngines (ListEnginesRequest) returns (Engines) {}; 42 | } -------------------------------------------------------------------------------- /BlazorDiffusion/proto/tensors.proto: -------------------------------------------------------------------------------- 1 | syntax = 'proto3'; 2 | package tensors; 3 | option go_package = "github.com/coreweave/tensorizer/tensors"; 4 | 5 | enum Dtype { 6 | DT_INVALID = 0; 7 | DT_FLOAT32 = 1; 8 | DT_FLOAT64 = 2; 9 | DT_FLOAT16 = 3; 10 | DT_BFLOAT16 = 4; 11 | DT_COMPLEX32 = 5; 12 | DT_COMPLEX64 = 6; 13 | DT_COMPLEX128 = 7; 14 | DT_UINT8 = 8; 15 | DT_INT8 = 9; 16 | DT_INT16 = 10; 17 | DT_INT32 = 11; 18 | DT_INT64 = 12; 19 | DT_BOOL = 13; 20 | DT_QUINT8 = 14; 21 | DT_QINT8 = 15; 22 | DT_QINT32 = 16; 23 | DT_QUINT4_2 = 17; 24 | } 25 | 26 | enum AttributeType { 27 | AT_PARAMETER = 0; 28 | AT_BUFFER = 1; 29 | } 30 | 31 | message Tensor { 32 | Dtype dtype = 1; 33 | repeated int64 shape = 2; 34 | bytes data = 3; 35 | optional AttributeType attr_type = 4; 36 | } 37 | 38 | message Attribute { 39 | string name = 1; 40 | oneof value { 41 | Module module = 3; 42 | Tensor tensor = 4; 43 | string string = 5; 44 | int64 int64 = 6; 45 | float float = 7; 46 | bool bool = 8; 47 | } 48 | } 49 | 50 | message Module { 51 | string name = 1; 52 | repeated string names = 2; 53 | repeated Attribute attributes = 3; 54 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS build 2 | WORKDIR /app 3 | COPY ./ . 4 | 5 | RUN --mount=type=secret,id=github_actor \ 6 | --mount=type=secret,id=github_token \ 7 | export github_actor=$(cat /run/secrets/github_actor) && \ 8 | export github_token=$(cat /run/secrets/github_token) && \ 9 | dotnet nuget add source "https://nuget.pkg.github.com/ServiceStack/index.json" --username $github_actor --password $github_token --store-password-in-clear-text --name github && \ 10 | dotnet restore 11 | 12 | WORKDIR /app/BlazorDiffusion 13 | RUN dotnet publish -c release -o /out --no-restore /p:SkipTailwindBuild=true 14 | 15 | FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal AS runtime 16 | WORKDIR /app 17 | COPY --from=build /out . 18 | ENTRYPOINT ["dotnet", "BlazorDiffusion.dll"] 19 | -------------------------------------------------------------------------------- /sync.bat: -------------------------------------------------------------------------------- 1 | RD /Q /S BlazorDiffusion.Client\Pages 2 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\Pages BlazorDiffusion.Client\Pages 3 | DEL BlazorDiffusion.Client\Pages\*.cshtml 4 | 5 | RD /Q /S BlazorDiffusion.Client\Shared 6 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\Shared BlazorDiffusion.Client\Shared 7 | 8 | RD /Q /S BlazorDiffusion.Client\UI 9 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\UI BlazorDiffusion.Client\UI 10 | 11 | RD /Q /S BlazorDiffusion.Client\wwwroot\img 12 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\wwwroot\img BlazorDiffusion.Client\wwwroot\img 13 | 14 | RD /Q /S BlazorDiffusion.Client\wwwroot\lib 15 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\wwwroot\lib BlazorDiffusion.Client\wwwroot\lib 16 | 17 | COPY ..\BlazorDiffusion\BlazorDiffusion\wwwroot\js\*.* BlazorDiffusion.Client\wwwroot\js\ 18 | COPY ..\BlazorDiffusion\BlazorDiffusion\wwwroot\content\*.* BlazorDiffusion.Client\wwwroot\content\ 19 | COPY ..\BlazorDiffusion\BlazorDiffusion\wwwroot\_index.html BlazorDiffusion\ 20 | REM powershell -Command "(Get-Content ..\BlazorDiffusion\BlazorDiffusion\wwwroot\_index.html) -replace 'blazor.server.js', 'blazor.webassembly.js' | Out-File -encoding ASCII BlazorDiffusion\_index.html" 21 | 22 | COPY ..\BlazorDiffusion\BlazorDiffusion\tailwind.* BlazorDiffusion.Client\ 23 | 24 | RD /Q /S BlazorDiffusion\Pages\Shared 25 | MOVE BlazorDiffusion.Client\Pages\Shared BlazorDiffusion\Pages\ 26 | RD /Q /S BlazorDiffusion\Pages\ssg 27 | MOVE BlazorDiffusion.Client\Pages\ssg BlazorDiffusion\Pages\ 28 | 29 | RD /Q /S BlazorDiffusion\App_Data 30 | MD BlazorDiffusion\App_Data 31 | 32 | RD /Q /S BlazorDiffusion\proto 33 | MD BlazorDiffusion\proto 34 | 35 | MOVE BlazorDiffusion\Program.cs . 36 | COPY ..\BlazorDiffusion\BlazorDiffusion\*.cs BlazorDiffusion\ 37 | DEL TrackingCircuitHandler.cs 38 | MOVE Program.cs BlazorDiffusion\ 39 | 40 | REM COPY ..\BlazorDiffusion\BlazorDiffusion\appsettings.* BlazorDiffusion\ 41 | COPY ..\BlazorDiffusion\BlazorDiffusion\Migrations\*.cs BlazorDiffusion\Migrations\ 42 | COPY ..\BlazorDiffusion\BlazorDiffusion\proto\*.* BlazorDiffusion\proto\ 43 | 44 | RD /Q /S BlazorDiffusion\App_Data 45 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion\App_Data BlazorDiffusion\App_Data 46 | 47 | MOVE BlazorDiffusion.ServiceInterface\BlazorDiffusion.ServiceInterface.csproj . 48 | RD /Q /S BlazorDiffusion.ServiceInterface 49 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion.ServiceInterface BlazorDiffusion.ServiceInterface 50 | MOVE BlazorDiffusion.ServiceInterface.csproj BlazorDiffusion.ServiceInterface\ 51 | 52 | MOVE BlazorDiffusion.ServiceModel\BlazorDiffusion.ServiceModel.csproj . 53 | RD /Q /S BlazorDiffusion.ServiceModel 54 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion.ServiceModel BlazorDiffusion.ServiceModel 55 | MOVE BlazorDiffusion.ServiceModel.csproj BlazorDiffusion.ServiceModel\ 56 | 57 | MOVE BlazorDiffusion.Tests\BlazorDiffusion.Tests.csproj . 58 | MOVE BlazorDiffusion.Tests\appsettings.json . 59 | RD /Q /S BlazorDiffusion.Tests 60 | XCOPY /Y /E /H /C /I ..\BlazorDiffusion\BlazorDiffusion.Tests BlazorDiffusion.Tests 61 | MOVE BlazorDiffusion.Tests.csproj BlazorDiffusion.Tests\ 62 | MOVE appsettings.json BlazorDiffusion.Tests\ 63 | 64 | --------------------------------------------------------------------------------