├── .dockerignore ├── .editorconfig ├── .gitignore ├── App.razor ├── BlazorServerApp.csproj ├── BlazorServerApp.sln ├── Data ├── Base │ └── GlobalVariables.cs ├── Blogger │ ├── AdverUserInfoDto.cs │ ├── BloggerArticleDto.cs │ └── BloggerInfoDto.cs ├── DataChartService.cs ├── WeatherForecast.cs └── WeatherForecastService.cs ├── Dockerfile ├── Global ├── ApplicationBuilderExtensions.cs ├── Base │ ├── BaseContst.cs │ ├── Enums │ │ └── ApiResultEnum.cs │ └── ProCompontentBase.cs ├── Config │ ├── AppSetting.cs │ ├── BrowserStorageProvider.cs │ ├── CookiesStorageService.cs │ ├── GlobalConfig.cs │ ├── GlobalConfigChangedEvent.cs │ └── IStorageProvider.cs ├── Middleware │ └── CookieMiddleware.cs ├── Nav │ ├── Model │ │ └── NavModel.cs │ ├── NavHelper.cs │ └── NavServiceCollectionExtensions.cs └── ServiceCollectionExtensions.cs ├── GlobalSuppressions.cs ├── HttpServers ├── CustomerHttpException.cs ├── Dtos │ ├── ApiResponce.cs │ ├── ApiSignDto.cs │ └── LoginReqDto.cs ├── HttpClientHelper.cs └── UserInfoServers.cs ├── Pages ├── Authentication │ ├── Login.razor │ └── Register.razor ├── Error.cshtml ├── Error.cshtml.cs ├── Home │ ├── DataChart.razor │ ├── DataChart.razor.cs │ ├── DataDetail.razor │ ├── DataDetail.razor.cs │ ├── DataList.razor │ ├── DataList.razor.cs │ └── Index.razor ├── _Host.cshtml └── _Layout.cshtml ├── Program.cs ├── Properties └── launchSettings.json ├── README.md ├── Servers └── Authentication │ ├── Login.razor.cs │ └── Register.razor.cs ├── Shared ├── EmptyLayout.razor ├── MainLayout.razor ├── MainLayout.razor.cs ├── MainLayout.razor.css ├── Search.razor └── Search.razor.cs ├── _Imports.cs ├── _Imports.razor ├── appsettings.Production.json ├── azure-pipelines.yml ├── docker ├── .dockerignore └── Dockerfile └── wwwroot ├── css ├── app.css ├── icons │ ├── iconfont.css │ └── iconfont.ttf ├── masa-blazor-pro.css ├── normalize.css ├── prism-coy.min.css └── prism.css ├── favicon.ico ├── favicon1.ico ├── html └── WeatherSDK.html ├── i18n ├── en-US.json ├── languageConfig.json └── zh-CN.json ├── icon-192.png ├── images ├── bg.jpg ├── witeem.png └── witeem1.png ├── img └── avatar │ ├── 1.svg │ ├── 10.svg │ ├── 11.svg │ ├── 12.svg │ ├── 13.svg │ ├── 14.svg │ ├── 2.svg │ ├── 3.svg │ ├── 4.svg │ ├── 5.jpg │ ├── 7.svg │ ├── 8.svg │ └── 9.svg ├── js ├── Common.min.js ├── CookieStorage.min.js └── prism.js └── nav └── nav.json /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS8618: 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 4 | dotnet_diagnostic.CS8618.severity = none 5 | 6 | # SYSLIB0020: 类型或成员已过时 7 | dotnet_diagnostic.SYSLIB0020.severity = none 8 | 9 | # CS8601: 引用类型赋值可能为 null。 10 | dotnet_diagnostic.CS8601.severity = none 11 | 12 | # IDE0059: 不需要赋值 13 | dotnet_diagnostic.IDE0059.severity = none 14 | 15 | # CS0168: 声明了变量,但从未使用过 16 | dotnet_diagnostic.CS0168.severity = none 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | appsettings.json 352 | appsettings.Development.json 353 | azure-pipelines.yml 354 | -------------------------------------------------------------------------------- /App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /BlazorServerApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | disable 5 | enable 6 | 866e99bd-93c1-4267-99a8-63f7d26241f5 7 | Linux 8 | . 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | 23 | 24 | PreserveNewest 25 | 26 | 27 | PreserveNewest 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /BlazorServerApp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServerApp", "BlazorServerApp.csproj", "{BDC3D3AD-A5E9-4B71-AD7F-F8505DAC2896}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FEFE6337-1B76-4C95-BCEC-5A3BAF71A0E6}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {BDC3D3AD-A5E9-4B71-AD7F-F8505DAC2896}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {BDC3D3AD-A5E9-4B71-AD7F-F8505DAC2896}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {BDC3D3AD-A5E9-4B71-AD7F-F8505DAC2896}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {BDC3D3AD-A5E9-4B71-AD7F-F8505DAC2896}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {B354025A-1597-4F7E-ADC8-0B52666E9FCE} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /Data/Base/GlobalVariables.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data; 2 | public static class GlobalVariables 3 | { 4 | public const string DefaultRoute = "/home/index"; 5 | } 6 | -------------------------------------------------------------------------------- /Data/Blogger/AdverUserInfoDto.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data.Blogger; 2 | public struct AdverUserInfoDto 3 | { 4 | /// 5 | /// 名称 6 | /// 7 | public string Name { get; set; } 8 | 9 | /// 10 | /// 昵称 11 | /// 12 | public string NickName { get; set; } 13 | 14 | /// 15 | /// 手机号 16 | /// 17 | public string Phone { get; set; } 18 | 19 | /// 20 | /// 密码 21 | /// 22 | public string Password { get; set; } 23 | 24 | /// 25 | /// 头像 26 | /// 27 | public string Picture { get; set; } 28 | 29 | /// 30 | /// 出生日期 31 | /// 32 | public DateTime BirthDay { get; set; } 33 | 34 | /// 35 | /// 年龄 36 | /// 37 | public int Age { get; set; } 38 | 39 | /// 40 | /// 性别 41 | /// 42 | public int Sex { get; set; } 43 | 44 | /// 45 | /// 用户权限(最多存在5种,使用英文逗号隔开) 46 | /// 47 | public string RoleCodes { get; set; } 48 | } 49 | -------------------------------------------------------------------------------- /Data/Blogger/BloggerArticleDto.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data.Blogger 2 | { 3 | public struct BloggerArticleDto 4 | { 5 | public long Id { get; set; } 6 | 7 | public long BloggerId { get; set; } 8 | 9 | public string ArticleTitle { get; set; } 10 | 11 | public string Introduction { get; set; } 12 | 13 | public string Article { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Data/Blogger/BloggerInfoDto.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data.Blogger; 2 | public struct BloggerInfoDto 3 | { 4 | public long Id { get; set; } 5 | 6 | /// 7 | /// 用户ID 8 | /// 9 | public long UserId { get; set; } 10 | 11 | /// 12 | /// 博主 13 | /// 14 | public string Blogger { get; set; } 15 | 16 | /// 17 | /// 博主简介 18 | /// 19 | public string Details { get; set; } 20 | 21 | /// 22 | /// 头像 23 | /// 24 | public string Avatar { get; set; } 25 | 26 | /// 27 | /// 等级 28 | /// 29 | public int Level { get; set; } 30 | 31 | /// 32 | /// 电子邮箱 33 | /// 34 | public string Email { get; set; } 35 | } 36 | -------------------------------------------------------------------------------- /Data/DataChartService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data; 2 | public static class DataChartService 3 | { 4 | public static int[][] GetOrderChartData() => new int[][] { new[] { 0, 0 }, new[] { 1, 2 }, new[] { 2, 1 }, new[] { 3, 3 }, new[] { 4, 2 }, new[] { 5, 4 } }; 5 | 6 | public static int[] GetProfitChartData() => new[] { 2, 6, 4, 2, 4 }; 7 | 8 | public static int[] GetEarningsChartData() => new[] { 53, 31, 16 }; 9 | 10 | public static int[][] GetRevenueReportChartData() => new int[][] { new[] { 100, 180, 300, 250, 100, 50, 200, 140, 80 }, new[] { -180, -100, -70, -250, -130, -100, -90, -120 } }; 11 | 12 | public static int[] GetBudgetChartData() => new[] { 150, 260, 160, 200, 150, 100, 200, 140 }; 13 | } 14 | -------------------------------------------------------------------------------- /Data/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data 2 | { 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string Summary { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Data/WeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Data 2 | { 3 | public class WeatherForecastService 4 | { 5 | private static readonly string[] Summaries = new[] 6 | { 7 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 8 | }; 9 | 10 | public Task GetForecastAsync(DateTime startDate) 11 | { 12 | return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast 13 | { 14 | Date = startDate.AddDays(index), 15 | TemperatureC = Random.Shared.Next(-20, 55), 16 | Summary = Summaries[Random.Shared.Next(Summaries.Length)] 17 | }).ToArray()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | EXPOSE 443 7 | 8 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 9 | WORKDIR /src 10 | COPY ["BlazorServerApp.csproj", "."] 11 | RUN dotnet restore "./BlazorServerApp.csproj" 12 | COPY . . 13 | WORKDIR "/src/." 14 | RUN dotnet build "BlazorServerApp.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "BlazorServerApp.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "BlazorServerApp.dll"] -------------------------------------------------------------------------------- /Global/ApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public static class ApplicationBuilderExtensions 3 | { 4 | public static IApplicationBuilder UseGlobal(this IApplicationBuilder app) 5 | { 6 | app.UseMiddleware(); 7 | return app; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Global/Base/BaseContst.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global.Base 2 | { 3 | public class BaseContst 4 | { 5 | public const string AppSetting = "AppSetting"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Global/Base/Enums/ApiResultEnum.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global.Base.Enums 2 | { 3 | public enum ApiResultEnum 4 | { 5 | Succeed = 200, 6 | UnAuthorization = 1401, 7 | Undefind = 1404, 8 | Error = 1502, 9 | ParamsError = 1504, 10 | SignError = 1999 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Global/Base/ProCompontentBase.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public abstract class ProCompontentBase : ComponentBase 3 | { 4 | private I18n _languageProvider; 5 | 6 | [Inject] 7 | public I18n LanguageProvider 8 | { 9 | get 10 | { 11 | return _languageProvider ?? throw new Exception("please Inject I18n!"); 12 | } 13 | set 14 | { 15 | _languageProvider = value; 16 | } 17 | } 18 | 19 | public string T(string key) 20 | { 21 | return LanguageProvider.T(key) ?? key; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Global/Config/AppSetting.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global.Config 2 | { 3 | public class AppSetting 4 | { 5 | /// 6 | /// 网关地址 7 | /// 8 | public string GatewayUri { get; set; } 9 | 10 | public string ApiUri { get; set; } 11 | 12 | public string ConnKey { get; set; } 13 | 14 | public string ConnIV { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Global/Config/BrowserStorageProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | namespace BlazorServerApp.Global.Config; 3 | public class BrowserStorageProvider : IStorageProvider 4 | { 5 | private readonly IJSRuntime _jSRuntime; 6 | 7 | public BrowserStorageProvider(IJSRuntime jSRuntime) 8 | { 9 | _jSRuntime = jSRuntime; 10 | } 11 | 12 | /// 13 | /// 根据Key值获取Cookie 14 | /// 15 | /// key 16 | public async ValueTask GetCookiesItemAsync(string key, CancellationToken? cancellationToken = null) 17 | => await _jSRuntime.InvokeAsync("Cookies.get", cancellationToken ?? CancellationToken.None, key); 18 | 19 | /// 20 | /// 根据Key值移除Cookie 21 | /// 22 | /// key 23 | public ValueTask RemoveCookiesItemAsync(string key, CancellationToken? cancellationToken = null) 24 | => _jSRuntime.InvokeVoidAsync("Cookies.expire", cancellationToken ?? CancellationToken.None, key); 25 | 26 | /// 27 | /// 设置Cookie 28 | /// 29 | /// key 30 | /// data 31 | public ValueTask SetCookiesItemAsync(string key, string data, CancellationToken? cancellationToken = null) 32 | => _jSRuntime.InvokeVoidAsync("Cookies.set", cancellationToken ?? CancellationToken.None, key, data); 33 | 34 | /// 35 | /// 设置Cookie在某域名下 36 | /// 37 | /// key 38 | /// data 39 | /// 域名 40 | /// secure 41 | public ValueTask SetCookiesItemAsync(string key, string data, string domain, bool secure, CancellationToken? cancellationToken = null) 42 | => _jSRuntime.InvokeVoidAsync("Cookies.set", cancellationToken ?? CancellationToken.None, key, data, new { domain, secure }); 43 | 44 | public ValueTask SetCookiesItemAsync(string key, string data, TimeSpan exprires, CancellationToken? cancellationToken = null) 45 | => _jSRuntime.InvokeVoidAsync("Cookies.set", cancellationToken ?? CancellationToken.None, key, data, new { exprires }); 46 | 47 | public ValueTask SetCookiesItemAsync(string key, string data, DateTime exprires, CancellationToken? cancellationToken = null) 48 | => _jSRuntime.InvokeVoidAsync("Cookies.set", cancellationToken ?? CancellationToken.None, key, data, new { exprires }); 49 | } 50 | -------------------------------------------------------------------------------- /Global/Config/CookiesStorageService.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace BlazorServerApp.Global.Config 4 | { 5 | public class CookiesStorageService 6 | { 7 | private readonly IStorageProvider _storageProvider; 8 | 9 | public CookiesStorageService(IStorageProvider storageProvider) 10 | { 11 | _storageProvider = storageProvider; 12 | } 13 | 14 | public async ValueTask GetItemAsync(string key, CancellationToken? cancellationToken = null) 15 | { 16 | if (string.IsNullOrWhiteSpace(key)) 17 | throw new ArgumentNullException(nameof(key)); 18 | 19 | return await _storageProvider.GetCookiesItemAsync(key, cancellationToken); 20 | } 21 | 22 | public ValueTask RemoveItemAsync(string key, CancellationToken? cancellationToken = null) 23 | { 24 | if (string.IsNullOrWhiteSpace(key)) 25 | throw new ArgumentNullException(nameof(key)); 26 | 27 | return _storageProvider.RemoveCookiesItemAsync(key, cancellationToken); 28 | } 29 | 30 | public async ValueTask SetItemAsync(string key, string data, CancellationToken? cancellationToken = null) 31 | { 32 | if (string.IsNullOrWhiteSpace(key)) 33 | throw new ArgumentNullException(nameof(key)); 34 | 35 | await _storageProvider.SetCookiesItemAsync(key, data, cancellationToken).ConfigureAwait(false); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Global/Config/GlobalConfig.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public class GlobalConfig 3 | { 4 | #region Field 5 | 6 | private bool _isDark; 7 | private bool _expandOnHover; 8 | private bool _navigationMini; 9 | private string _favorite; 10 | private string _token; 11 | private CookieStorage _cookieStorage; 12 | #endregion 13 | 14 | #region Property 15 | 16 | public static string IsDarkCookieKey { get; set; } = "GlobalConfig_IsDark"; 17 | 18 | public static string NavigationMiniCookieKey { get; set; } = "GlobalConfig_NavigationMini"; 19 | 20 | public static string ExpandOnHoverCookieKey { get; set; } = "GlobalConfig_ExpandOnHover"; 21 | 22 | public static string FavoriteCookieKey { get; set; } = "GlobalConfig_Favorite"; 23 | 24 | public static string TokenKey { get; set; } = "GlobalConfig_Token"; 25 | 26 | public I18nConfig I18nConfig { get; set; } 27 | 28 | public bool IsDark 29 | { 30 | get => _isDark; 31 | set 32 | { 33 | _isDark = value; 34 | _cookieStorage?.SetItemAsync(IsDarkCookieKey, value); 35 | } 36 | } 37 | 38 | public bool NavigationMini 39 | { 40 | get => _navigationMini; 41 | set 42 | { 43 | _navigationMini = value; 44 | _cookieStorage?.SetItemAsync(NavigationMiniCookieKey, value); 45 | } 46 | } 47 | 48 | public bool ExpandOnHover 49 | { 50 | get => _expandOnHover; 51 | set 52 | { 53 | _expandOnHover = value; 54 | _cookieStorage?.SetItemAsync(ExpandOnHoverCookieKey, value); 55 | } 56 | } 57 | 58 | public string Favorite 59 | { 60 | get => _favorite; 61 | set 62 | { 63 | _favorite = value; 64 | _cookieStorage?.SetItemAsync(FavoriteCookieKey, value); 65 | } 66 | } 67 | 68 | public string Token 69 | { 70 | get => _token; 71 | set 72 | { 73 | _token = value; 74 | _cookieStorage?.SetItemAsync(TokenKey, value); 75 | } 76 | } 77 | 78 | #endregion 79 | 80 | /// 81 | /// 别删,大锅 82 | /// 83 | public GlobalConfig() { } 84 | 85 | public GlobalConfig(CookieStorage cookieStorage, I18nConfig i18nConfig) 86 | { 87 | _cookieStorage = cookieStorage; 88 | I18nConfig = i18nConfig; 89 | } 90 | 91 | #region Method 92 | 93 | public void Initialization(IRequestCookieCollection cookies) 94 | { 95 | _isDark = Convert.ToBoolean(cookies[IsDarkCookieKey]); 96 | _navigationMini = Convert.ToBoolean(cookies[NavigationMiniCookieKey]); 97 | _expandOnHover = Convert.ToBoolean(cookies[ExpandOnHoverCookieKey]); 98 | _favorite = cookies[FavoriteCookieKey]; 99 | _token = cookies[TokenKey]; 100 | } 101 | 102 | public async Task Initialization() 103 | { 104 | if (_cookieStorage is not null) 105 | { 106 | _isDark = Convert.ToBoolean(await _cookieStorage.GetCookie(IsDarkCookieKey)); 107 | _navigationMini = Convert.ToBoolean(await _cookieStorage.GetCookie(NavigationMiniCookieKey)); 108 | _expandOnHover = Convert.ToBoolean(await _cookieStorage.GetCookie(ExpandOnHoverCookieKey)); 109 | _favorite = await _cookieStorage.GetCookie(FavoriteCookieKey); 110 | } 111 | } 112 | 113 | public void Bind(GlobalConfig globalConfig) 114 | { 115 | _isDark = globalConfig.IsDark; 116 | _navigationMini = globalConfig.NavigationMini; 117 | _expandOnHover = globalConfig.ExpandOnHover; 118 | _favorite = globalConfig.Favorite; 119 | } 120 | 121 | #endregion 122 | } 123 | -------------------------------------------------------------------------------- /Global/Config/GlobalConfigChangedEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public partial class GlobalConfigChangedEvent 3 | { 4 | public delegate void GlobalConfigChanged(); 5 | public event GlobalConfigChanged OnGlobalConfigChanged; 6 | public event GlobalConfigChanged NavigationStateHasChanged; 7 | 8 | public void Invoke() 9 | { 10 | OnGlobalConfigChanged?.Invoke(); 11 | } 12 | 13 | public void InvokeNavigationStateHasChanged() 14 | { 15 | NavigationStateHasChanged?.Invoke(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Global/Config/IStorageProvider.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global.Config; 2 | public interface IStorageProvider 3 | { 4 | ValueTask GetCookiesItemAsync(string key, CancellationToken? cancellationToken = null); 5 | 6 | ValueTask RemoveCookiesItemAsync(string key, CancellationToken? cancellationToken = null); 7 | 8 | ValueTask SetCookiesItemAsync(string key, string data, CancellationToken? cancellationToken = null); 9 | 10 | ValueTask SetCookiesItemAsync(string key, string data, string domain, bool secure, CancellationToken? cancellationToken = null); 11 | 12 | ValueTask SetCookiesItemAsync(string key, string data, TimeSpan exprires, CancellationToken? cancellationToken = null); 13 | 14 | ValueTask SetCookiesItemAsync(string key, string data, DateTime exprires, CancellationToken? cancellationToken = null); 15 | } -------------------------------------------------------------------------------- /Global/Middleware/CookieMiddleware.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public class CookieMiddleware 3 | { 4 | private readonly RequestDelegate _next; 5 | 6 | public CookieMiddleware(RequestDelegate next) 7 | { 8 | _next = next; 9 | } 10 | 11 | public async Task InvokeAsync(HttpContext context, GlobalConfig globalConfig) 12 | { 13 | var cookies = context.Request.Cookies; 14 | globalConfig.Initialization(cookies); 15 | await _next(context); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Global/Nav/Model/NavModel.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public class NavModel 3 | { 4 | public int Id { get; set; } 5 | 6 | public int ParentId { get; set; } 7 | 8 | public string Href { get; set; } 9 | 10 | public string Icon { get; set; } 11 | 12 | public string ParentIcon { get; set; } 13 | 14 | public string Title { get; set; } 15 | 16 | public string FullTitle { get; set; } 17 | 18 | public bool Hide { get; set; } 19 | 20 | public bool Active { get; set; } 21 | 22 | public NavModel[] Children { get; set; } 23 | 24 | public NavModel(int id, string href, string icon, string title, NavModel[] children) 25 | { 26 | Id = id; 27 | Href = href; 28 | Icon = icon; 29 | ParentIcon = icon; 30 | Title = title; 31 | FullTitle = title; 32 | Children = children; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Global/Nav/NavHelper.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public partial class NavHelper 3 | { 4 | private List _navList; 5 | private NavigationManager _navigationManager; 6 | private GlobalConfigChangedEvent _globalConfigChangedEvent; 7 | 8 | public List Navs { get; } 9 | 10 | public List SameLevelNavs { get; } 11 | 12 | public NavHelper(List navList, NavigationManager navigationManager, GlobalConfigChangedEvent globalEvent) 13 | { 14 | _navList = navList; 15 | _navigationManager = navigationManager; 16 | _globalConfigChangedEvent = globalEvent; 17 | Navs = new List(); 18 | SameLevelNavs = new List(); 19 | Initialization(); 20 | } 21 | 22 | private void Initialization() 23 | { 24 | _navList.ForEach(nav => 25 | { 26 | if (nav.Hide is false) Navs.Add(nav); 27 | 28 | if (nav.Children is not null) 29 | { 30 | nav.Children = nav.Children.Where(c => c.Hide is false).ToArray(); 31 | 32 | nav.Children.ForEach(child => 33 | { 34 | child.ParentId = nav.Id; 35 | child.FullTitle = $"{nav.Title} {child.Title}"; 36 | child.ParentIcon = nav.Icon; 37 | }); 38 | } 39 | }); 40 | 41 | Navs.ForEach(nav => 42 | { 43 | SameLevelNavs.Add(nav); 44 | if (nav.Children is not null) SameLevelNavs.AddRange(nav.Children); 45 | }); 46 | } 47 | 48 | public void NavigateTo(NavModel nav) 49 | { 50 | Active(nav); 51 | _navigationManager.NavigateTo(nav.Href ?? ""); 52 | } 53 | 54 | public void NavigateTo(string href) 55 | { 56 | var nav = SameLevelNavs.FirstOrDefault(n => n.Href == href); 57 | if (nav is not null) Active(nav); 58 | _navigationManager.NavigateTo(href); 59 | } 60 | 61 | public void RefreshRender(NavModel nav) 62 | { 63 | Active(nav); 64 | _globalConfigChangedEvent.Invoke(); 65 | } 66 | 67 | public void NavigateToByEvent(NavModel nav) 68 | { 69 | RefreshRender(nav); 70 | _navigationManager.NavigateTo(nav.Href ?? ""); 71 | } 72 | 73 | public void Active(NavModel nav) 74 | { 75 | SameLevelNavs.ForEach(n => n.Active = false); 76 | nav.Active = true; 77 | if (nav.ParentId != 0) SameLevelNavs.First(n => n.Id == nav.ParentId).Active = true; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Global/Nav/NavServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Global; 2 | public static class NavServiceCollectionExtensions 3 | { 4 | public static IServiceCollection AddNav(this IServiceCollection services, List navList) 5 | { 6 | services.AddSingleton(navList); 7 | services.AddScoped(); 8 | 9 | return services; 10 | } 11 | 12 | public static IServiceCollection AddNav(this IServiceCollection services, string navSettingsFile) 13 | { 14 | var navList = JsonSerializer.Deserialize>(File.ReadAllText(navSettingsFile)); 15 | 16 | if (navList is null) throw new Exception("Please configure the navigation first!"); 17 | 18 | services.AddNav(navList); 19 | 20 | return services; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Global/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Global.Base; 2 | 3 | namespace BlazorServerApp.Global; 4 | 5 | public static class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddGlobalForServer(this IServiceCollection services) 8 | { 9 | services.AddMasaI18nForServer("wwwroot/i18n"); 10 | var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Get the assembly root directory exception!"); 11 | services.AddNav(Path.Combine(basePath, $"wwwroot/nav/nav.json")); 12 | services.AddScoped(); 13 | services.AddScoped(); 14 | services.AddScoped(); 15 | services.AddScoped(); 16 | services.AddScoped(); 17 | services.AddScoped(); 18 | return services; 19 | } 20 | 21 | public static async Task AddGlobalForWasmAsync(this IServiceCollection services, string baseUri) 22 | { 23 | await services.AddMasaI18nForWasmAsync(Path.Combine(baseUri, $"i18n")); 24 | using var httpclient = new HttpClient(); 25 | var navList = await httpclient.GetFromJsonAsync>(Path.Combine(baseUri, $"nav/nav.json")) ?? throw new Exception("please configure the Navigation!"); 26 | services.AddNav(navList); 27 | services.AddScoped(); 28 | services.AddScoped(); 29 | services.AddScoped(); 30 | services.AddScoped(); 31 | services.AddScoped(); 32 | services.AddScoped(); 33 | return services; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Usage", "CA2245:请勿将属性分配给其自身", Justification = "<挂起>", Scope = "member", Target = "~M:BlazorServerApp.HttpServers.CustomerHttpException.#ctor(System.String,System.String)")] 9 | -------------------------------------------------------------------------------- /HttpServers/CustomerHttpException.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.HttpServers 2 | { 3 | public class CustomerHttpException : Exception 4 | { 5 | public string ErrorCode { get; set; } 6 | 7 | public string ErrorMessage { get; set; } 8 | 9 | public CustomerHttpException() : base() { } 10 | 11 | public CustomerHttpException(string errorCode, string errorMessage) : base() 12 | { 13 | this.ErrorCode = errorCode; 14 | this.ErrorMessage = ErrorMessage; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /HttpServers/Dtos/ApiResponce.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Global.Base.Enums; 2 | 3 | namespace BlazorServerApp.HttpServers.Dtos 4 | { 5 | public class ApiResponce 6 | { 7 | public int Code { get; set; } 8 | 9 | public T Data { get; set; } 10 | 11 | public string Msg { get; set; } 12 | 13 | public bool Successed 14 | { 15 | get 16 | { 17 | return Code == (int)ApiResultEnum.Succeed; 18 | } 19 | } 20 | 21 | public static ApiResponce Success(T datas, string msg = "操作成功") 22 | { 23 | ApiResponce responce = new ApiResponce() 24 | { 25 | Code = (int)ApiResultEnum.Succeed, 26 | Data = datas, 27 | Msg = msg 28 | }; 29 | return responce; 30 | } 31 | 32 | public static ApiResponce Fail(int code, string msg) 33 | { 34 | ApiResponce responce = new ApiResponce() 35 | { 36 | Code = code, 37 | Data = default(T), 38 | Msg = msg 39 | }; 40 | return responce; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /HttpServers/Dtos/ApiSignDto.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.HttpServers.Dtos 2 | { 3 | public class ApiSignDto 4 | { 5 | /// 6 | /// 时间擢 7 | /// 8 | public string Timespan { get; set; } 9 | 10 | /// 11 | /// 随机字符串 12 | /// 13 | public string Nonce { get; set; } 14 | 15 | /// 16 | /// 签名 17 | /// 18 | public string Sign { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /HttpServers/Dtos/LoginReqDto.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.HttpServers.Dtos 2 | { 3 | public class LoginReqDto : ApiSignDto 4 | { 5 | public string UserName { get; set; } 6 | 7 | public string Password { get; set; } 8 | } 9 | 10 | public class LoginResult 11 | { 12 | /// 13 | /// 请求成功与否 14 | /// 15 | public bool success { get; set; } 16 | 17 | public string access_token { get; set; } 18 | 19 | public string token_type { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /HttpServers/HttpClientHelper.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using BlazorServerApp.Global.Base.Enums; 3 | using BlazorServerApp.HttpServers.Dtos; 4 | using Microsoft.Extensions.Options; 5 | using Newtonsoft.Json; 6 | using RestSharp; 7 | 8 | namespace BlazorServerApp.HttpServers; 9 | 10 | public class HttpClientHelper 11 | { 12 | private readonly ILogger _logger; 13 | private readonly AppSetting _appSetting; 14 | private readonly CookiesStorageService _cookiesStorage; 15 | private readonly ILocalStorageService _localStorage; 16 | 17 | private string _baseUri = string.Empty; 18 | private bool _isSign = true; 19 | private bool _withAccToken = false; 20 | public RestClient _client = null; 21 | 22 | public HttpClientHelper(ILogger logger, IOptionsMonitor appsetting, CookiesStorageService cookiesStorage, ILocalStorageService localStorage) 23 | { 24 | _logger = logger; 25 | _appSetting = appsetting.CurrentValue; 26 | _cookiesStorage = cookiesStorage; 27 | _localStorage = localStorage; 28 | } 29 | 30 | public RestClient CreateClient() 31 | { 32 | _client = new RestClient(BaseUri); 33 | return _client; 34 | } 35 | 36 | public string BaseUri 37 | { 38 | get { return _baseUri; } 39 | set { _baseUri = value; } 40 | } 41 | 42 | public bool IsSign 43 | { 44 | get { return _isSign; } 45 | set { _isSign = value; } 46 | } 47 | 48 | public bool WithAccToken 49 | { 50 | get { return _withAccToken; } 51 | set { _withAccToken = value; } 52 | } 53 | 54 | public async Task> GetAsync(string serverMethod, Dictionary queryParams, int timeoutSecond = 180, CancellationToken cancellationToken = default) 55 | { 56 | return await GetAsync(serverMethod, queryParams, null, timeoutSecond, cancellationToken); 57 | } 58 | 59 | public async Task> GetAsync(string serverMethod, Dictionary queryParams, ICollection> headerParams, int timeoutSecond = 180, CancellationToken cancellationToken = default) 60 | { 61 | try 62 | { 63 | _client = CreateClient(); 64 | var request = new RestRequest(serverMethod); 65 | if (_isSign) queryParams = CreateSign(queryParams, null); 66 | if (_withAccToken) await AddAccToken(request); 67 | 68 | if (queryParams != null) 69 | { 70 | foreach (var item in queryParams) 71 | { 72 | request.AddQueryParameter(item.Key, item.Value); 73 | } 74 | } 75 | 76 | 77 | if (headerParams != null) 78 | { 79 | request.AddOrUpdateHeaders(headerParams); 80 | } 81 | 82 | request.Timeout = timeoutSecond * 1000; 83 | var response = await _client.GetAsync(request, cancellationToken); 84 | if (response.StatusCode == System.Net.HttpStatusCode.OK) 85 | { 86 | var data = response.Content.ToObject>(); 87 | return data; 88 | } 89 | else 90 | { 91 | return ApiResponce.Fail((int)response.StatusCode, response.ErrorMessage); 92 | } 93 | } 94 | catch (Exception ex) 95 | { 96 | _logger.LogError($"HttpGet:{serverMethod} Error:{ex.ToString()}"); 97 | return ApiResponce.Fail((int)ApiResultEnum.Error, ex.Message); 98 | } 99 | } 100 | 101 | public async Task> PostAsync(string serverMethod, N requestBody, int timeoutSecond = 180, CancellationToken cancellationToken = default) 102 | { 103 | return await PostAsync(serverMethod, requestBody, null, null, timeoutSecond, cancellationToken); 104 | } 105 | 106 | public async Task> PostAsync(string serverMethod, N requestBody, Dictionary queryParams, int timeoutSecond = 180, CancellationToken cancellationToken = default) 107 | { 108 | return await PostAsync(serverMethod, requestBody, queryParams, null, timeoutSecond, cancellationToken); 109 | } 110 | 111 | public async Task> PostAsync(string serverMethod, N requestBody, Dictionary queryParams, ICollection> headerParams, int timeoutSecond = 180, CancellationToken cancellationToken = default) 112 | { 113 | try 114 | { 115 | _client = CreateClient(); 116 | var request = new RestRequest(serverMethod); 117 | request.Timeout = timeoutSecond * 1000; 118 | if (_isSign) queryParams = CreateSign(queryParams, requestBody); 119 | if (_withAccToken) await AddAccToken(request); 120 | 121 | if (queryParams != null) 122 | { 123 | foreach (var item in queryParams) 124 | { 125 | request.AddQueryParameter(item.Key, item.Value); 126 | } 127 | } 128 | 129 | 130 | if (headerParams != null) 131 | { 132 | request.AddOrUpdateHeaders(headerParams); 133 | } 134 | 135 | request.AddStringBody(JsonConvert.SerializeObject(requestBody), "application/json"); 136 | var response = await _client.PostAsync(request, cancellationToken); 137 | if (response.StatusCode == System.Net.HttpStatusCode.OK) 138 | { 139 | var data = response.Content.ToObject>(); 140 | return data; 141 | } 142 | else 143 | { 144 | return ApiResponce.Fail((int)response.StatusCode, response.ErrorMessage); 145 | } 146 | } 147 | catch (Exception ex) 148 | { 149 | _logger.LogError($"HttpGet:{serverMethod} Error:{ex.ToString()}"); 150 | return ApiResponce.Fail((int)ApiResultEnum.Error, ex.Message); 151 | } 152 | } 153 | 154 | private Dictionary CreateSign(Dictionary queryParams, T requestBody) 155 | { 156 | DateTimeOffset offTime = new DateTimeOffset(DateTime.Now); 157 | string timeSpan = offTime.ToUnixTimeMilliseconds().ToString(); 158 | string nonce = Guid.NewGuid().ToString(); 159 | string secretKey = string.Empty; 160 | if (requestBody == null) 161 | secretKey = GetSecretKey(queryParams); 162 | else 163 | secretKey = GetSecretKey(requestBody); 164 | 165 | string sign = EncyptHelper.MD5(secretKey + timeSpan + nonce); 166 | if (queryParams == null) 167 | queryParams = new Dictionary(); 168 | queryParams.Add("timespan", timeSpan); 169 | queryParams.Add("nonce", nonce); 170 | queryParams.Add("sign", sign); 171 | return queryParams; 172 | } 173 | 174 | private async Task AddAccToken(RestRequest restRequest) 175 | { 176 | 177 | // var accToken = await _localStorage.GetItemAsync(GlobalConfig.TokenKey); 178 | var accToken = await _cookiesStorage.GetItemAsync(GlobalConfig.TokenKey); 179 | if (!string.IsNullOrEmpty(accToken)) 180 | { 181 | restRequest.AddOrUpdateHeader("X-Token", accToken); 182 | } 183 | 184 | await Task.CompletedTask; 185 | } 186 | 187 | private string GetSecretKey(Dictionary queryParams) 188 | { 189 | if (queryParams != null) 190 | { 191 | return EncyptHelper.AESEncrypt(JsonConvert.SerializeObject(queryParams), _appSetting.ConnKey, _appSetting.ConnIV); 192 | } 193 | else 194 | { 195 | return EncyptHelper.AESEncrypt("{}", _appSetting.ConnKey, _appSetting.ConnIV); 196 | } 197 | } 198 | 199 | private string GetSecretKey(T t) 200 | { 201 | if (t != null) 202 | { 203 | return EncyptHelper.AESEncrypt(JsonConvert.SerializeObject(t), _appSetting.ConnKey, _appSetting.ConnIV); 204 | } 205 | else 206 | { 207 | return EncyptHelper.AESEncrypt("{}", _appSetting.ConnKey, _appSetting.ConnIV); 208 | } 209 | } 210 | 211 | private string GetSecretKey(string requestBody) 212 | { 213 | if (!string.IsNullOrEmpty(requestBody)) 214 | { 215 | return EncyptHelper.AESEncrypt(requestBody, _appSetting.ConnKey, _appSetting.ConnIV); 216 | } 217 | else 218 | { 219 | return EncyptHelper.AESEncrypt("{}", _appSetting.ConnKey, _appSetting.ConnIV); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /HttpServers/UserInfoServers.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Data.Blogger; 2 | using BlazorServerApp.Global.Base.Enums; 3 | using BlazorServerApp.HttpServers.Dtos; 4 | using Newtonsoft.Json; 5 | 6 | namespace BlazorServerApp.HttpServers; 7 | public class UserInfoServers 8 | { 9 | private readonly ILogger _logger; 10 | private readonly AppSetting _appSetting; 11 | private readonly HttpClientHelper _httpClientHelper; 12 | 13 | public UserInfoServers(ILogger logger, IOptionsMonitor appsetting, HttpClientHelper httpClientHelper) 14 | { 15 | _logger = logger; 16 | _appSetting = appsetting.CurrentValue; 17 | _httpClientHelper = httpClientHelper; 18 | } 19 | 20 | /// 21 | /// 获取登录Token 22 | /// 23 | /// 24 | public async Task> GetAuthToken(string account, string passwd) 25 | { 26 | // 网关地址,请求时 Kong API 网关会根据路由匹配转发到对应的应用服务上 27 | _httpClientHelper.BaseUri = _appSetting.GatewayUri; 28 | Dictionary reqParams = new Dictionary(); 29 | reqParams.Add("UserName", account); 30 | reqParams.Add("Password", passwd); 31 | var authInfo = await _httpClientHelper.GetAsync("/api/oAuth/Login", reqParams); 32 | return authInfo; 33 | } 34 | 35 | /// 36 | /// 注册 37 | /// 38 | /// 39 | public async Task> GetUserInfoAsync(string account, string passwd) 40 | { 41 | try 42 | { 43 | // 网关地址,请求时 Kong API 网关会根据路由匹配转发到对应的应用服务上 44 | _httpClientHelper.BaseUri = _appSetting.GatewayUri; 45 | _httpClientHelper.WithAccToken = true; 46 | AdverUserInfoDto userInfoDto = new AdverUserInfoDto() 47 | { 48 | Name = account, 49 | NickName = account, 50 | Password = passwd, 51 | Phone = passwd, 52 | BirthDay = new DateTime(1999, 1, 1), 53 | Age = 1, 54 | Sex = 1, 55 | RoleCodes = "10003", 56 | Picture = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Finews.gtimg.com%2Fnewsapp_bt%2F0%2F14165370162%2F1000&refer=http%3A%2F%2Finews.gtimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653370854&t=db13fd7e61f120642e912ff15d52ee69" 57 | }; 58 | 59 | var authInfo = await _httpClientHelper.PostAsync("/api/oAuth/AddUserInfo", userInfoDto); 60 | if (authInfo.Successed == true) 61 | { 62 | var tokenResult = await GetAuthToken(account, passwd); 63 | return tokenResult; 64 | } 65 | 66 | } 67 | catch (Exception ex) 68 | { 69 | _logger.LogError($"HttpGet:{GetUserInfoAsync} Error:{ex.ToString()}"); 70 | return ApiResponce.Fail((int)ApiResultEnum.Error, ex.Message); 71 | } 72 | 73 | return ApiResponce.Fail((int)ApiResultEnum.Error, "注册失败"); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Pages/Authentication/Login.razor: -------------------------------------------------------------------------------- 1 | @page "/Login" 2 | @layout EmptyLayout 3 | 4 |
5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 |
欢迎来到Witeem博客基地! 👋
13 | 14 | 19 | 20 | 29 | 30 | 31 | 32 | @*@onclick=@(()=>Navigation.NavigateTo(Navigation.BaseUri+ForgotPasswordRoute))*@ 33 | 忘记密码? 34 | 35 | @if (!_isPass) 36 | { 37 | 38 | @(_verifyMsg) 39 | 40 | } 41 | Login 42 | 43 |
44 | 没有基地账号?   45 | 注册 46 |
47 |
48 |
49 | 第三方登录 50 |
51 |
52 |
53 | 54 | 55 | fab fa-github 56 | 57 | 58 | GitHub 59 | 60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 | 68 | 粤ICP备2022003323号-1 69 | 70 | 71 | -------------------------------------------------------------------------------- /Pages/Authentication/Register.razor: -------------------------------------------------------------------------------- 1 | @page "/Register" 2 | @layout EmptyLayout 3 | 4 |
5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 |
欢迎来到Witeem博客基地! 👋
13 | 14 | 19 | 20 | 29 | 30 | 39 | 40 | 41 | @if (!_isPass) 42 | { 43 | 44 | @(_verifyMsg) 45 | 46 | } 47 | 注册 48 |
49 |
50 |
51 | 52 | 53 | 54 | 粤ICP备2022003323号-1 55 | 56 | 57 | -------------------------------------------------------------------------------- /Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Masa.Server.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Error.

18 |

An error occurred while processing your request.

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

23 | Request ID: @Model.RequestId 24 |

25 | } 26 | 27 |

Development Mode

28 |

29 | Swapping to the Development environment displays detailed information about the error 30 | 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 36 | ASPNETCORE_ENVIRONMENT environment variable to Development 37 | and restarting the app. 38 |

39 |
40 |
41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace Masa.Server.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 | } -------------------------------------------------------------------------------- /Pages/Home/DataChart.razor: -------------------------------------------------------------------------------- 1 | @inherits ProCompontentBase 2 | @inject MasaBlazor MasaBlazor 3 | @inject GlobalConfig GlobalConfig; 4 | @implements IDisposable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 浏览人数 12 |
$230k
13 |
14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 点赞人数 27 |
$682.5k
28 |
29 | 30 | 31 | 32 |
33 |
34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 文章分类 42 |
$9745
43 | 68% more earning than last month. 44 |
45 | 46 | 47 | 48 |
49 |
50 |
51 |
52 |
53 | -------------------------------------------------------------------------------- /Pages/Home/DataChart.razor.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Pages.Home; 2 | public partial class DataChart : ProCompontentBase 3 | { 4 | private object _orderChart; 5 | private object _profitChart; 6 | private object _earningsChart; 7 | private object _revenueReportChart; 8 | private object _budgetChart; 9 | 10 | [Inject] 11 | public MasaBlazor Masa { get; set; } = default!; 12 | private string GetEchartKey() 13 | { 14 | return GlobalConfig.NavigationMini.ToString() + MasaBlazor.Breakpoint.Width.ToString(); 15 | } 16 | 17 | protected override void OnInitialized() 18 | { 19 | Masa.Application.PropertyChanged += OnPropertyChanged; 20 | 21 | _orderChart = new 22 | { 23 | Tooltip = new 24 | { 25 | Trigger = "axis" 26 | }, 27 | XAxis = new 28 | { 29 | axisLine = new 30 | { 31 | Show = false 32 | }, 33 | splitLine = new 34 | { 35 | Show = true, 36 | LineStyle = new 37 | { 38 | Color = new[] { "#F0F3FA" } 39 | } 40 | }, 41 | axisLabel = new 42 | { 43 | Show = false 44 | }, 45 | axisTick = new 46 | { 47 | Show = false 48 | }, 49 | }, 50 | YAxis = new 51 | { 52 | axisLine = new 53 | { 54 | Show = false 55 | }, 56 | axisLabel = new 57 | { 58 | Show = false 59 | }, 60 | axisTick = new 61 | { 62 | Show = false 63 | }, 64 | splitLine = new 65 | { 66 | Show = false 67 | }, 68 | }, 69 | Series = new[] 70 | { 71 | new 72 | { 73 | name= "series-1", 74 | Type= "line", 75 | Data= DataChartService.GetOrderChartData(), 76 | Color= "#4318FF", 77 | SymbolSize= 6, 78 | Symbol= "circle", 79 | } 80 | }, 81 | Grid = new 82 | { 83 | x = 3, 84 | x2 = 3, 85 | y = 3, 86 | y2 = 3 87 | } 88 | }; 89 | 90 | _profitChart = new 91 | { 92 | Tooltip = new 93 | { 94 | Trigger = "axis", 95 | axisPointer = new 96 | { 97 | Type = "shadow" 98 | } 99 | }, 100 | XAxis = new 101 | { 102 | Data = new[] { "", "", "", "", "" }, 103 | axisLine = new 104 | { 105 | Show = false 106 | }, 107 | axisLabel = new 108 | { 109 | Show = false 110 | }, 111 | axisTick = new 112 | { 113 | Show = false 114 | }, 115 | splitLine = new 116 | { 117 | Show = false 118 | }, 119 | }, 120 | YAxis = new 121 | { 122 | axisLine = new 123 | { 124 | Show = false 125 | }, 126 | axisLabel = new 127 | { 128 | Show = false 129 | }, 130 | axisTick = new 131 | { 132 | Show = false 133 | }, 134 | splitLine = new 135 | { 136 | Show = false 137 | }, 138 | }, 139 | Series = new[] 140 | { 141 | new 142 | { 143 | Type= "bar", 144 | Data= DataChartService.GetProfitChartData(), 145 | Color= "#4318FF" 146 | } 147 | }, 148 | Grid = new 149 | { 150 | x = 3, 151 | x2 = 3, 152 | y = 3, 153 | y2 = 3 154 | } 155 | }; 156 | 157 | int[] _earningsChartData = DataChartService.GetEarningsChartData(); 158 | 159 | _earningsChart = new 160 | { 161 | Tooltip = new 162 | { 163 | Trigger = "item", 164 | }, 165 | Series = new[] 166 | { 167 | new 168 | { 169 | Type= "pie", 170 | Radius= "90%", 171 | Label=new 172 | { 173 | Show=false 174 | }, 175 | Data=new[] 176 | { 177 | new 178 | { 179 | value= _earningsChartData[0], 180 | Name= "Product", 181 | ItemStyle=new 182 | { 183 | Color= "#4318FF" 184 | } 185 | }, 186 | new 187 | { 188 | value= _earningsChartData[1], 189 | Name= "App", 190 | ItemStyle=new 191 | { 192 | Color= "#05CD99" 193 | } 194 | }, 195 | new 196 | { 197 | value= _earningsChartData[2], 198 | Name= "Service", 199 | ItemStyle = new 200 | { 201 | Color= "#FFB547" 202 | } 203 | }, 204 | } 205 | } 206 | } 207 | }; 208 | 209 | var _revenueReportChartData = DataChartService.GetRevenueReportChartData(); 210 | 211 | _revenueReportChart = new 212 | { 213 | Title = new 214 | { 215 | Text = "Revenue Report", 216 | TextStyle = new 217 | { 218 | Color = "#1B2559" 219 | } 220 | }, 221 | Tooltip = new 222 | { 223 | Trigger = "axis", 224 | axisPointer = new 225 | { 226 | Type = "shadow" 227 | } 228 | }, 229 | Legend = new 230 | { 231 | Data = new[] { "Earning", "Expense" }, 232 | Right = "5px", 233 | TextStyle = new 234 | { 235 | Color = "#485585", 236 | } 237 | }, 238 | XAxis = new 239 | { 240 | Data = new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Aug", "Sep" }, 241 | axisLine = new 242 | { 243 | Show = false, 244 | LineStyle = new 245 | { 246 | Color = "#485585" 247 | } 248 | }, 249 | axisTick = new 250 | { 251 | Show = false 252 | }, 253 | splitLine = new 254 | { 255 | Show = false 256 | }, 257 | TextStyle = new 258 | { 259 | Color = "#485585" 260 | } 261 | }, 262 | YAxis = new 263 | { 264 | axisLine = new 265 | { 266 | Show = false, 267 | LineStyle = new 268 | { 269 | Color = "#485585" 270 | } 271 | }, 272 | axisTick = new 273 | { 274 | Show = false 275 | }, 276 | splitLine = new 277 | { 278 | Show = false 279 | } 280 | }, 281 | Series = new[] 282 | { 283 | new 284 | { 285 | Name="Earning", 286 | Type= "bar", 287 | Data= _revenueReportChartData[0], 288 | Color="#4318FF", 289 | Stack="x", 290 | BarWidth="15" 291 | }, 292 | new 293 | { 294 | Name="Expense", 295 | Type= "bar", 296 | Data= _revenueReportChartData[1], 297 | Color="#A18BFF", 298 | Stack="x", 299 | BarWidth="15" 300 | } 301 | }, 302 | Grid = new 303 | { 304 | y2 = 25 305 | } 306 | }; 307 | 308 | _budgetChart = new 309 | { 310 | Radar = new[] 311 | { 312 | new 313 | { 314 | Indicator=new [] 315 | { 316 | new 317 | { 318 | Text="Jan",Max=300 319 | }, 320 | new 321 | { 322 | Text="Feb",Max=300 323 | }, 324 | new 325 | { 326 | Text="Mar",Max=300 327 | }, 328 | new 329 | { 330 | Text="Apr",Max=300 331 | }, 332 | new 333 | { 334 | Text="May",Max=300 335 | }, 336 | new 337 | { 338 | Text="Jun",Max=300 339 | }, 340 | new 341 | { 342 | Text="Aug",Max=300 343 | }, 344 | new 345 | { 346 | Text="Sep",Max=300 347 | }, 348 | }, 349 | Radius=70 350 | } 351 | }, 352 | Series = new[] 353 | { 354 | new 355 | { 356 | Type= "radar", 357 | Data= new [] 358 | { 359 | new 360 | { 361 | Value=DataChartService.GetBudgetChartData(), 362 | } 363 | }, 364 | Color="#4318FF", 365 | SymbolSize=6, 366 | Symbol="circle", 367 | } 368 | } 369 | }; 370 | } 371 | 372 | private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) 373 | { 374 | if (e.PropertyName == nameof(Application.Left)) 375 | { 376 | InvokeAsync(StateHasChanged); 377 | } 378 | } 379 | 380 | public void Dispose() 381 | { 382 | Masa.Application.PropertyChanged -= OnPropertyChanged; 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /Pages/Home/DataDetail.razor: -------------------------------------------------------------------------------- 1 | @page "/DataDetail" 2 | @page "/DataDetail/{Id}" 3 | @layout EmptyLayout 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | Witeem 19 | 20 | 21 | 22 |
23 | 24 | 12345 25 |
26 |
27 | 28 | 12345 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | @if (_hasXss) 38 | { 39 | @_postHtmlContent.ToString() 40 | ; 41 | } 42 | else 43 | { 44 | @_postHtmlContent 45 | } 46 |
47 |
-------------------------------------------------------------------------------- /Pages/Home/DataDetail.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Data.Blogger; 2 | using BlazorServerApp.HttpServers.Dtos; 3 | using Microsoft.JSInterop; 4 | using Markdig; 5 | 6 | namespace BlazorServerApp.Pages.Home; 7 | public partial class DataDetail 8 | { 9 | private string markdownValue = ""; 10 | private MarkupString _postHtmlContent; 11 | public bool _hasXss { get; set; } 12 | 13 | [Parameter] 14 | public string Id { get; set; } 15 | 16 | [Inject] public HttpClientHelper _httpClientHelper { get; set; } 17 | [Inject] public IOptionsMonitor _appSetting { get; set; } 18 | [Inject] public IJSRuntime _jsRuntime { get; set; } 19 | 20 | protected override async Task OnInitializedAsync() 21 | { 22 | await base.OnInitializedAsync(); 23 | } 24 | 25 | /// 26 | /// 组件呈现之后 27 | /// 28 | /// 29 | /// 30 | protected override async Task OnAfterRenderAsync(bool firstRender) 31 | { 32 | try 33 | { 34 | if (firstRender) 35 | { 36 | var ipAddress = await _jsRuntime.InvokeAsync("getIpAddress"); 37 | var datas = await GetBloggerArticleAsync(Id); 38 | if (datas.Successed) 39 | { 40 | HtmlToMarkdown(datas); 41 | if (!string.IsNullOrEmpty(ipAddress)) 42 | { 43 | _ = await AddViews(Id, ipAddress); 44 | } 45 | } 46 | 47 | StateHasChanged(); 48 | } 49 | 50 | await _jsRuntime.InvokeVoidAsync("Prism.highlightAll"); 51 | } 52 | catch (Exception ex) 53 | { 54 | } 55 | 56 | await base.OnAfterRenderAsync(firstRender); 57 | } 58 | 59 | /// 60 | /// 文章内容转成MarkDown 61 | /// 62 | /// 63 | private void HtmlToMarkdown(ApiResponce datas) 64 | { 65 | markdownValue = datas.Data.Article; 66 | var htmlData = Markdown.ToHtml(markdownValue ?? string.Empty); 67 | // 转为 prism 支持的语言标记(不是必须,可以删除) 68 | htmlData = htmlData.Replace("language-golang", "language-go"); 69 | 70 | // TODO: 使用 https://github.com/mganss/HtmlSanitizer 清洗html中的xss 71 | if (htmlData.Contains(" 80 | /// 获取文章详情 81 | /// 82 | private async Task> GetBloggerArticleAsync(string Id) 83 | { 84 | #if DEBUG 85 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.ApiUri; 86 | #else 87 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 88 | #endif 89 | Dictionary reqParams = new Dictionary(); 90 | reqParams.Add("Type", "1"); 91 | reqParams.Add("Id", Id); 92 | var bloggerInfo = await _httpClientHelper.GetAsync("/api/AdverUser/GetArticleDetails", reqParams); 93 | return bloggerInfo; 94 | } 95 | 96 | /// 97 | /// 新增文章浏览数量 98 | /// 99 | /// 100 | /// 101 | private async Task AddViews(string Id, string Ip) 102 | { 103 | #if DEBUG 104 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.ApiUri; 105 | #else 106 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 107 | #endif 108 | Dictionary reqParams = new Dictionary(); 109 | reqParams.Add("Id", Id); 110 | reqParams.Add("IP", Ip); 111 | var responce = await _httpClientHelper.GetAsync("/api/Article/ArticleAddViews", reqParams); 112 | return responce.Data; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Pages/Home/DataList.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | @foreach (var item in _items) 4 | { 5 | 6 | 7 | 8 | 9 | 10 | 11 | @item.Title 12 | 13 |
@item?.SubTtile 14 | 15 | 12345 16 | 17 | 18 | 44435 19 | 20 |
21 |
22 | } 23 |
24 |
-------------------------------------------------------------------------------- /Pages/Home/DataList.razor.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Pages.Home; 2 | using BlazorServerApp.Data.Blogger; 3 | using BlazorServerApp.HttpServers.Dtos; 4 | 5 | public partial class DataList 6 | { 7 | [Inject] public HttpClientHelper _httpClientHelper { get; set; } 8 | [Inject] public IOptionsMonitor _appSetting { get; set; } 9 | 10 | /// 11 | /// 组件呈现之后 12 | /// 13 | /// 14 | /// 15 | protected override async Task OnAfterRenderAsync(bool firstRender) 16 | { 17 | try 18 | { 19 | if (firstRender) 20 | { 21 | var datas = await GetBloggerArticlesAsync(); 22 | if (datas.Successed) 23 | { 24 | foreach (var item in datas.Data) 25 | { 26 | _items.Add(new DataDemo { 27 | Id = item.Id, 28 | Title = item.ArticleTitle, 29 | SubTtile = item.Introduction, 30 | Avatar = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic2%2Fcover%2F00%2F32%2F66%2F5810fec833d03_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1652262505&t=b71a8a1c448b8149147f788ff6e2ad6e" 31 | }); 32 | } 33 | } 34 | StateHasChanged(); 35 | } 36 | } 37 | catch (Exception ex) 38 | { 39 | } 40 | 41 | await base.OnAfterRenderAsync(firstRender); 42 | } 43 | 44 | private class DataDemo 45 | { 46 | public long Id { get; set; } 47 | public string Header { get; set; } 48 | public string Avatar { get; set; } 49 | public string Title { get; set; } 50 | public string SubTtile { get; set; } 51 | public bool Divider { get; set; } 52 | public bool Inset { get; set; } 53 | } 54 | 55 | private List _items = new List(); 56 | 57 | /// 58 | /// 获取博主文章列表 59 | /// 60 | /// 61 | private async Task>> GetBloggerArticlesAsync() 62 | { 63 | #if DEBUG 64 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.ApiUri; 65 | #else 66 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 67 | #endif 68 | _httpClientHelper.WithAccToken = true; 69 | Dictionary reqParams = new Dictionary(); 70 | reqParams.Add("Type", "1"); 71 | var bloggerInfo = await _httpClientHelper.GetAsync>("/api/AdverUser/GetBloggerArticles", reqParams); 72 | return bloggerInfo; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Pages/Home/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @inherits LayoutComponentBase 3 | @inject NavigationManager Nav 4 | 5 | -------------------------------------------------------------------------------- /Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @namespace BlazorServerApp.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | @{ 5 | Layout = "_Layout"; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /Pages/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @namespace BlazorServerApp.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Witeem 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 | 37 | 38 | 39 | @RenderBody() 40 | 41 | 42 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using BlazorServerApp.Global.Base; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | builder.Services.AddMasaBlazor(builder => 6 | { 7 | builder.UseTheme(option => 8 | { 9 | option.Primary = "#4318FF"; 10 | option.Accent = "#4318FF"; 11 | } 12 | ); 13 | }); 14 | // Add services to the container. 15 | builder.Services.AddRazorPages(); 16 | builder.Services.AddServerSideBlazor(); 17 | builder.Services.AddSingleton(); 18 | builder.Services.Configure(builder.Configuration.GetSection(BaseContst.AppSetting)); 19 | builder.Services.AddBlazoredLocalStorage(); 20 | builder.Services.AddGlobalForServer(); 21 | builder.Services.AddCors(x => x.AddPolicy("externalRequests", policy => policy.WithOrigins("https://jsonip.com"))); 22 | var app = builder.Build(); 23 | 24 | // Configure the HTTP request pipeline. 25 | if (!app.Environment.IsDevelopment()) 26 | { 27 | app.UseExceptionHandler("/Error"); 28 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 29 | app.UseHsts(); 30 | } 31 | 32 | app.UseHttpsRedirection(); 33 | 34 | app.UseStaticFiles(); 35 | app.UseCors("externalRequests"); 36 | app.UseRouting(); 37 | app.UseGlobal(); 38 | app.MapBlazorHub(); 39 | app.MapFallbackToPage("/_Host"); 40 | app.Run(); 41 | -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:9775", 7 | "sslPort": 44656 8 | } 9 | }, 10 | "profiles": { 11 | "BlazorServerApp": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "applicationUrl": "https://localhost:7194;http://localhost:5315", 18 | "dotnetRunMessages": true 19 | }, 20 | "IIS Express": { 21 | "commandName": "IISExpress", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | }, 27 | "Docker": { 28 | "commandName": "Docker", 29 | "launchBrowser": true, 30 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 31 | "publishAllPorts": true, 32 | "useSSL": true 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 项目介绍 2 | 3 | ------ 4 | 5 | 个人简易博客(前端Web项目): 6 | 效果演示:https://www.witeemv.cn/ 7 | 8 | 博客功能如下: 9 | 10 | - 展示博主信息以及所在地天气信息(已完成) 11 | - 展示博主文章列表(已完成) 12 | - 博主编辑博客文章(开发中) 13 | - 注册,登录与注销(已完成) 14 | - 浏览人数展示(开发中) 15 | - 点赞人数展示(开发中) 16 | - 文章数量展示(开发中) 17 | 18 | 19 | 20 | ### 技术支持 21 | 22 | ------ 23 | 24 | 此项目使用的是 Masa Blazor + Blazor Server 25 | 26 | C# 开发员使用Blazor的好处: Blazor 允许您使用 C# 而不是 JavaScript 构建交互式 Web UI。Blazor 应用由使用 C#、HTML 和 CSS 实现的可重用 Web UI 组件组成。客户端和服务器代码都是用 C# 编写的,允许您共享代码和库。 27 | 28 | Blazor 相关资料:https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor 29 | 30 | Masa UI 相关资料:https://blazor.masastack.com/getting-started/installation 31 | 32 | 33 | 34 | ### 相关项目 35 | 36 | ------ 37 | 38 | 个人简易博客(服务端项目):https://github.com/witeem/BlogCore.API 39 | -------------------------------------------------------------------------------- /Servers/Authentication/Login.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Global.Base.Enums; 2 | using BlazorServerApp.HttpServers.Dtos; 3 | 4 | namespace BlazorServerApp.Pages.Authentication; 5 | 6 | public partial class Login 7 | { 8 | private bool _show; 9 | private bool _isPass = true; 10 | private string _account = ""; 11 | private string _passwd = ""; 12 | private bool _loading = false; 13 | private string _verifyMsg = "账号密码错误,请重试"; 14 | 15 | [Inject] public NavigationManager _navigation { get; set; } 16 | [Inject] public HttpClientHelper _httpClientHelper { get; set; } 17 | [Inject] public UserInfoServers _userInfoServers { get; set; } 18 | [Inject] public IStorageProvider _localStorage { get; set; } 19 | [Inject] public IOptionsMonitor _appSetting { get; set; } 20 | 21 | /// 22 | /// 判断传入的值是否为空 23 | /// 24 | private Func _requiredRule = value => !string.IsNullOrEmpty(value) ? true : "Required."; 25 | // private Func _phoneRule = value => System.Text.RegularExpressions.Regex.Match(value, "^1[3456789]\\d{9}$").Success ? true : "Invalid Phone."; 26 | 27 | private IEnumerable> _accountRules => new List> 28 | { 29 | _requiredRule 30 | }; 31 | 32 | private IEnumerable> _passwdRules => new List> 33 | { 34 | _requiredRule 35 | }; 36 | 37 | 38 | /// 39 | /// 执行登录 40 | /// 41 | /// 42 | private async Task OnLogin() 43 | { 44 | try 45 | { 46 | if (_requiredRule(_account) != true) 47 | { 48 | _isPass = false; 49 | _verifyMsg = "账号密码错误,请重试"; 50 | return; 51 | } 52 | if (_requiredRule(_passwd) != true) 53 | { 54 | _isPass = false; 55 | _verifyMsg = "账号密码错误,请重试"; 56 | return; 57 | } 58 | ApiResponce authInfo = await _userInfoServers.GetAuthToken(_account, _passwd); 59 | if (authInfo.Code == (int)ApiResultEnum.Succeed && authInfo.Data.success) 60 | { 61 | // 存Cookie 62 | #if DEBUG 63 | await _localStorage.SetCookiesItemAsync(GlobalConfig.TokenKey, authInfo.Data.access_token, default); 64 | #else 65 | await _localStorage.SetCookiesItemAsync(GlobalConfig.TokenKey, authInfo.Data.access_token, ".witeemv.cn", true, default); 66 | #endif 67 | 68 | // 存浏览器本地缓存 69 | // await _localStorage.SetItemAsStringAsync(GlobalConfig.TokenKey, authInfo.Data.access_token); 70 | _navigation.NavigateTo($"{_navigation.BaseUri}"); // 跳转到首页,可以优化成根据返回的地址路径进行跳转 71 | } 72 | else 73 | { 74 | _isPass = false; 75 | _verifyMsg = "账号密码错误,请重试"; 76 | return; 77 | } 78 | } 79 | catch (Exception ex) 80 | { 81 | _isPass = false; 82 | _verifyMsg = "账号密码错误,请重试"; 83 | return; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Servers/Authentication/Register.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorServerApp.Data.Blogger; 2 | using BlazorServerApp.Global.Base.Enums; 3 | using BlazorServerApp.HttpServers.Dtos; 4 | 5 | namespace BlazorServerApp.Pages.Authentication; 6 | public partial class Register 7 | { 8 | private bool _show; 9 | private bool _isPass = true; 10 | private string _account = ""; 11 | private string _passwd = ""; 12 | private string _passwdVerify = ""; 13 | private bool _loading = false; 14 | private string _verifyMsg = "账号密码错误,请重试"; 15 | 16 | [Inject] public NavigationManager _navigation { get; set; } 17 | [Inject] public HttpClientHelper _httpClientHelper { get; set; } 18 | [Inject] public UserInfoServers _userInfoServers { get; set; } 19 | [Inject] public IStorageProvider _localStorage { get; set; } 20 | [Inject] public IOptionsMonitor _appSetting { get; set; } 21 | 22 | // private Func _phoneRule = value => System.Text.RegularExpressions.Regex.Match(value, "^1[3456789]\\d{9}$").Success ? true : "Invalid Phone."; 23 | private Func accountVerify = value => string.IsNullOrEmpty(value) ? false : true; 24 | private Func passwdVerify = value => string.IsNullOrEmpty(value) ? false : true; 25 | 26 | private IEnumerable> verifyRules => new List> 27 | { 28 | 29 | }; 30 | 31 | /// 32 | /// 注册 33 | /// 34 | /// 35 | private async Task Login() 36 | { 37 | try 38 | { 39 | await Task.CompletedTask; 40 | if (!accountVerify(_account)) 41 | { 42 | _isPass = false; 43 | _verifyMsg = "账号不能为空,请重试"; 44 | return; 45 | } 46 | 47 | if (!passwdVerify(_passwd)) 48 | { 49 | _isPass = false; 50 | _verifyMsg = "密码不能为空,请重试"; 51 | return; 52 | } 53 | 54 | if (!_passwdVerify.Equals(_passwd)) 55 | { 56 | _isPass = false; 57 | _verifyMsg = "密码匹对不正确,请重试"; 58 | return; 59 | } 60 | 61 | ApiResponce authInfo = await _userInfoServers.GetUserInfoAsync(_account, _passwd); 62 | if (authInfo.Code == (int)ApiResultEnum.Succeed) 63 | { 64 | 65 | #if DEBUG 66 | await _localStorage.SetCookiesItemAsync(GlobalConfig.TokenKey, authInfo.Data.access_token, default); 67 | #else 68 | await _localStorage.SetCookiesItemAsync(GlobalConfig.TokenKey, authInfo.Data.access_token, ".witeemv.cn", true, default); 69 | #endif 70 | 71 | // 存浏览器本地缓存 72 | // await _localStorage.SetItemAsStringAsync(GlobalConfig.TokenKey, authInfo.Data.access_token); 73 | _navigation.NavigateTo($"{_navigation.BaseUri}"); // 跳转到首页,可以优化成根据返回的地址路径进行跳转 74 | } 75 | else 76 | { 77 | _isPass = false; 78 | _verifyMsg = "账号密码错误,请重试"; 79 | return; 80 | } 81 | } 82 | catch (Exception ex) 83 | { 84 | _isPass = false; 85 | _verifyMsg = "账号密码错误,请重试"; 86 | return; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Shared/EmptyLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | @Body 5 |
6 |
7 | -------------------------------------------------------------------------------- /Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @inject NavHelper NavHelper 3 | 4 | 5 | 6 | 7 | 8 | 9 | 工欲善其事,必先利其器 10 | 11 | @if (_isLogin) 12 | { 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 |
@_userInfo.Name
29 |

@_userInfo.NickName

30 | 31 | Logout 32 |
33 |
34 |
35 |
36 |
37 | } 38 | else 39 | { 40 | 41 | 42 | 43 | 44 | 45 | } 46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | @(_bloggerInfo.Blogger ?? "") 55 | 56 |
@(_bloggerInfo.Details ?? "")
57 |
58 |
59 |
60 | 61 | 62 | 63 |
64 | 65 |
66 | @Body 67 |
68 |
69 |
-------------------------------------------------------------------------------- /Shared/MainLayout.razor.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using BlazorServerApp.Data.Blogger; 3 | using BlazorServerApp.HttpServers.Dtos; 4 | 5 | namespace BlazorServerApp.Shared; 6 | public partial class MainLayout 7 | { 8 | private string _searchContent; 9 | private bool _isLogin = false; 10 | private string _localToken; 11 | private BloggerInfoDto _bloggerInfo; 12 | private AdverUserInfoDto _userInfo; 13 | 14 | [Inject] public NavigationManager _navigation { get; set; } 15 | 16 | [Inject] public HttpClientHelper _httpClientHelper { get; set; } 17 | 18 | [Inject] public IOptionsMonitor _appSetting { get; set; } 19 | 20 | [Inject] public IStorageProvider _localStorage { get; set; } 21 | 22 | public class ItemDemo 23 | { 24 | public string Title { get; set; } 25 | public string Icon { get; set; } 26 | } 27 | 28 | /// 29 | /// 初始化组件 30 | /// 31 | /// 32 | protected override async Task OnInitializedAsync() 33 | { 34 | await base.OnInitializedAsync(); 35 | } 36 | 37 | /// 38 | /// 设置参数之后 39 | /// 40 | /// 41 | protected override async Task OnParametersSetAsync() 42 | { 43 | await base.OnParametersSetAsync(); 44 | } 45 | 46 | /// 47 | /// 组件呈现之后 48 | /// 49 | /// 50 | /// 51 | protected override async Task OnAfterRenderAsync(bool firstRender) 52 | { 53 | try 54 | { 55 | if (firstRender) 56 | { 57 | _localToken = await _localStorage.GetCookiesItemAsync(GlobalConfig.TokenKey); 58 | ApiResponce bloggerInfo = await GetBloggerInfoAsync(); 59 | if (bloggerInfo.Successed) 60 | _bloggerInfo = bloggerInfo.Data; 61 | 62 | if (!string.IsNullOrEmpty(_localToken)) 63 | { 64 | ApiResponce userInfo = await GetUserInfoAsync(); 65 | if (userInfo.Successed) 66 | { 67 | _isLogin = true; 68 | _userInfo = userInfo.Data; 69 | } 70 | else 71 | _isLogin = false; 72 | } 73 | 74 | StateHasChanged(); 75 | } 76 | } 77 | catch (Exception ex) 78 | { 79 | _isLogin = false; 80 | } 81 | 82 | await base.OnAfterRenderAsync(firstRender); 83 | } 84 | 85 | public List GetMenus() 86 | { 87 | List items = new List 88 | { 89 | new ItemDemo { Title = "Home", Icon = "mdi-view-dashboard" }, 90 | new ItemDemo { Title = "About", Icon = "mdi-forum" } 91 | }; 92 | 93 | return items; 94 | } 95 | 96 | public async Task SearchBlogArticle() 97 | { 98 | _searchContent = "这里可以搜索"; 99 | await Task.CompletedTask; 100 | } 101 | 102 | /// 103 | /// 登录 104 | /// 105 | /// 106 | public async Task Login() 107 | { 108 | await _localStorage.RemoveCookiesItemAsync(GlobalConfig.TokenKey); 109 | _navigation.NavigateTo($"{_navigation.BaseUri}Login"); 110 | } 111 | 112 | /// 113 | /// 退出 114 | /// 115 | /// 116 | public async Task Logout() 117 | { 118 | try 119 | { 120 | var localToken = await _localStorage.GetCookiesItemAsync(GlobalConfig.TokenKey); 121 | if (!string.IsNullOrEmpty(localToken)) 122 | { 123 | await ClearLoginAsync(); 124 | _isLogin = true; 125 | } 126 | 127 | _navigation.NavigateTo($"{_navigation.BaseUri}Login"); 128 | } 129 | catch (Exception ex) 130 | { 131 | _navigation.NavigateTo($"{_navigation.BaseUri}Login"); 132 | } 133 | } 134 | 135 | 136 | /// 137 | /// 清除Redis登录状态 138 | /// 139 | /// 140 | private async Task> ClearLoginAsync() 141 | { 142 | // 网关地址,请求时 Kong API 网关会根据路由匹配转发到对应的应用服务上 143 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 144 | _httpClientHelper.WithAccToken = true; 145 | Dictionary reqParams = new Dictionary(); 146 | var authInfo = await _httpClientHelper.GetAsync("/api/oAuth/LogOut", reqParams); 147 | if (authInfo.Data) 148 | { 149 | await _localStorage.RemoveCookiesItemAsync(GlobalConfig.TokenKey); 150 | } 151 | 152 | return authInfo; 153 | } 154 | 155 | /// 156 | /// 获取博主信息 157 | /// 158 | /// 159 | private async Task> GetBloggerInfoAsync() 160 | { 161 | #if DEBUG 162 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.ApiUri; 163 | #else 164 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 165 | #endif 166 | _httpClientHelper.WithAccToken = true; 167 | Dictionary reqParams = new Dictionary(); 168 | reqParams.Add("Type", "1"); 169 | var bloggerInfo = await _httpClientHelper.GetAsync("/api/AdverUser/GetBloggerInfo", reqParams); 170 | return bloggerInfo; 171 | } 172 | 173 | /// 174 | /// 获取登录用户信息 175 | /// 176 | /// 177 | private async Task> GetUserInfoAsync() 178 | { 179 | _httpClientHelper.BaseUri = _appSetting.CurrentValue.GatewayUri; 180 | _httpClientHelper.WithAccToken = true; 181 | Dictionary reqParams = new Dictionary(); 182 | var bloggerInfo = await _httpClientHelper.GetAsync("/api/oAuth/GetAuthenticate", reqParams); 183 | return bloggerInfo; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Shared/Search.razor: -------------------------------------------------------------------------------- 1 | @inherits ProCompontentBase 2 | @inject NavHelper NavHelper 3 | @{ 4 | var navs = GetNavs(SearchContent); 5 | } 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | mdi-magnify 14 | 15 | 16 | 17 |
18 |
19 |
-------------------------------------------------------------------------------- /Shared/Search.razor.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorServerApp.Shared; 2 | public partial class Search 3 | { 4 | [Parameter] 5 | public string SearchContent { get; set; } 6 | 7 | List GetNavs(string search) 8 | { 9 | var output = new List(); 10 | if (search is not null && search != "") 11 | { 12 | output.AddRange(NavHelper.SameLevelNavs.Where(n => n.Href is not null && GetI18nFullTitle(n.FullTitle).Contains(search, StringComparison.OrdinalIgnoreCase))); 13 | } 14 | return output; 15 | } 16 | 17 | string GetI18nFullTitle(string fullTitle) 18 | { 19 | var arr = fullTitle.Split(' ').ToList(); 20 | if (arr.Count == 1) return T(fullTitle); 21 | else 22 | { 23 | var parent = arr[0]; 24 | arr.RemoveAt(0); 25 | return $"{T(parent)} {T(string.Join(' ', arr))}"; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /_Imports.cs: -------------------------------------------------------------------------------- 1 | global using BlazorServerApp.Data; 2 | global using BlazorServerApp.Global; 3 | global using BlazorServerApp.Global.Config; 4 | global using Microsoft.AspNetCore.Components; 5 | global using BlazorComponent; 6 | global using BlazorComponent.I18n; 7 | global using System.Reflection; 8 | global using System.Text.Json; 9 | global using Masa.Blazor; 10 | global using System.ComponentModel; 11 | global using BlazorServerApp.HttpServers; 12 | global using witeem.CoreHelper.ExtensionTools.CommonTools; 13 | global using Microsoft.Extensions.Options; 14 | global using BlazorServerApp.Global.Base; -------------------------------------------------------------------------------- /_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorComponent 10 | @using Masa.Blazor 11 | @using BlazorServerApp.Data 12 | @using BlazorServerApp.Shared 13 | @using BlazorServerApp.Global 14 | @using System.Security.Claims 15 | @using Blazored.LocalStorage 16 | -------------------------------------------------------------------------------- /appsettings.Production.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/appsettings.Production.json -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | tags: 3 | include: [ prod-* ] 4 | 5 | resources: 6 | - repo: 'self' 7 | 8 | parameters: 9 | - name: appsettings 10 | type: string 11 | values: 12 | - appsettings.Production.json 13 | 14 | variables: 15 | - group: witeem-blog 16 | - name: Key 17 | value: $[variables.ConnKey] 18 | - name: IV 19 | value: $[variables.ConnIV] 20 | 21 | stages: 22 | - stage: Build 23 | displayName: Build image 24 | jobs: 25 | - job: Build 26 | displayName: Build 27 | pool: 28 | vmImage: ubuntu-18.04 29 | steps: 30 | - script: | 31 | ls ${{parameters.appsettings}} -l 32 | chmod 666 ${{parameters.appsettings}} 33 | sed -i 's/{ConnKey}/$(Key)/g' ${{parameters.appsettings}} 34 | sed -i 's/{ConnIV}/$(IV)/g' ${{parameters.appsettings}} 35 | echo "================= print file ${{parameters.appsettings}} start ====================" 36 | cat ${{parameters.appsettings}} | while read line; do echo $line; done 37 | echo "================= print file ${{parameters.appsettings}} end ====================" 38 | echo "command executed" 39 | 40 | - task: Docker@2 41 | inputs: 42 | containerRegistry: 'mydocker' 43 | repository: 'witeem/blazorserverapp' 44 | command: 'buildAndPush' 45 | Dockerfile: '**/Dockerfile' 46 | tags: latest 47 | - task: SSH@0 48 | inputs: 49 | sshEndpoint: 'myssh' 50 | runOptions: 'inline' 51 | inline: | 52 | sudo docker kill blogweb 53 | sudo docker rm blogweb 54 | sudo docker kill myweb 55 | sudo docker rm myweb 56 | sudo docker rmi witeem/blazorserverapp 57 | sudo docker pull witeem/blazorserverapp 58 | sudo docker run -it --name blogweb -p 9080:80 -d witeem/blazorserverapp 59 | sudo docker run -it --name myweb -p 9081:80 -d witeem/blazorserverapp 60 | failOnStdErr: false 61 | interactiveSession: true 62 | readyTimeout: '20000' 63 | -------------------------------------------------------------------------------- /docker/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | EXPOSE 443 7 | 8 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 9 | WORKDIR /src 10 | COPY ["BlazorServerApp.csproj", "."] 11 | RUN dotnet restore "./BlazorServerApp.csproj" 12 | COPY . . 13 | WORKDIR "/src/." 14 | RUN dotnet build "BlazorServerApp.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "BlazorServerApp.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS final 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "BlazorServerApp.dll"] -------------------------------------------------------------------------------- /wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | /*scrollbar*/ 2 | ::-webkit-scrollbar-track { 3 | border-radius: 15px; 4 | margin: 5px 0; 5 | } 6 | 7 | ::-webkit-scrollbar { 8 | width: 5px; 9 | height: 5px; 10 | margin: 5px 0; 11 | } 12 | 13 | ::-webkit-scrollbar-thumb { 14 | margin: 5px 0; 15 | border-radius: 15px; 16 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.44, rgb(170 168 168)), color-stop(0.72, rgb(170 168 168)), color-stop(0.86, rgb(170 168 168))); 17 | } 18 | 19 | #blazor-error-ui { 20 | background: lightyellow; 21 | bottom: 0; 22 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 23 | display: none; 24 | left: 0; 25 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 26 | position: fixed; 27 | width: 100%; 28 | z-index: 1000; 29 | } 30 | 31 | #blazor-error-ui .dismiss { 32 | cursor: pointer; 33 | position: absolute; 34 | right: 0.75rem; 35 | top: 0.5rem; 36 | } 37 | 38 | .fill-lighten-1 { 39 | background: #f0f3fa !important; 40 | } 41 | 42 | .hide-shadow { 43 | box-shadow: none !important; 44 | border: none; 45 | } 46 | 47 | .rounded-2 { 48 | border-radius: 10px !important; 49 | } 50 | 51 | .rounded-4 { 52 | border-radius: 20px !important; 53 | } 54 | 55 | .neutral-lighten-1--text { 56 | color: #323D6F !important; 57 | caret-color: #323D6F !important; 58 | } 59 | 60 | .hover-pointer { 61 | cursor: pointer; 62 | } 63 | 64 | .text-s1 { 65 | font-size:1rem; 66 | } 67 | 68 | .text-s2 { 69 | font-size: 1.25rem; 70 | } 71 | 72 | .text-s3 { 73 | font-size: 1.5rem; 74 | } 75 | 76 | .text-s4 { 77 | font-size: 1.75rem; 78 | } 79 | 80 | .text-m1 { 81 | font-size: 2rem; 82 | } 83 | 84 | .text-m2 { 85 | font-size: 2.25rem; 86 | } 87 | 88 | .text-m3 { 89 | font-size: 2.5rem; 90 | } 91 | 92 | .text-m4 { 93 | font-size: 2.75rem; 94 | } 95 | 96 | .text-l1 { 97 | font-size: 3rem; 98 | } 99 | 100 | .text-l2 { 101 | font-size: 3.25rem; 102 | } 103 | 104 | .text-l3 { 105 | font-size: 3.5rem; 106 | } 107 | 108 | .text-l4 { 109 | font-size: 3.75rem; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /wwwroot/css/icons/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "iconfont"; /* Project id */ 3 | src: url('iconfont.ttf?t=1651218218808') format('truetype'); 4 | } 5 | 6 | .iconfont { 7 | font-family: "iconfont" !important; 8 | font-size: 18px; 9 | font-style: normal; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | .iconfont-m { 15 | font-size: 22px; 16 | } 17 | 18 | .iconfont-l { 19 | font-size: 28px; 20 | } 21 | 22 | .iconfont:hover { 23 | color: cornflowerblue; 24 | } 25 | 26 | .num-hover:hover { 27 | color: cornflowerblue; 28 | } 29 | 30 | .icon-shoucang:before { 31 | content: "\e86d"; 32 | } 33 | 34 | .icon-sousuo:before { 35 | content: "\e86e"; 36 | } 37 | 38 | .icon-fenxiang:before { 39 | content: "\e86f"; 40 | } 41 | 42 | .icon-xiaoxi:before { 43 | content: "\e870"; 44 | } 45 | 46 | .icon-xihuan:before { 47 | content: "\e871"; 48 | } 49 | 50 | .icon-xihuan1:before { 51 | content: "\e872"; 52 | } 53 | 54 | .icon-zan:before { 55 | content: "\e873"; 56 | } 57 | 58 | .icon-zan1:before { 59 | content: "\e874"; 60 | } 61 | 62 | .icon-yueduxiao:before { 63 | content: "\e87d"; 64 | } 65 | 66 | .icon-tishi:before { 67 | content: "\e881"; 68 | } 69 | 70 | .icon-zanxiao:before { 71 | content: "\e885"; 72 | } 73 | 74 | .icon-neirong2:before { 75 | content: "\e889"; 76 | } 77 | 78 | .icon-fujian:before { 79 | content: "\e88a"; 80 | } 81 | 82 | .icon-pinglun:before { 83 | content: "\e891"; 84 | } 85 | 86 | .icon-saoyisao:before { 87 | content: "\e892"; 88 | } 89 | 90 | .icon-shouqijiantouxiao:before { 91 | content: "\e893"; 92 | } 93 | 94 | .icon-gouwuche:before { 95 | content: "\e899"; 96 | } 97 | 98 | .icon-wode:before { 99 | content: "\e8a0"; 100 | } 101 | 102 | .icon-jia:before { 103 | content: "\e8a6"; 104 | } 105 | 106 | .icon-shibai:before { 107 | content: "\e8a9"; 108 | } 109 | 110 | .icon-shuaxin:before { 111 | content: "\e8aa"; 112 | } 113 | 114 | .icon-zhengque:before { 115 | content: "\e8ad"; 116 | } 117 | 118 | .icon-fanhuijiantou:before { 119 | content: "\e8b5"; 120 | } 121 | 122 | .icon-lishixiao:before { 123 | content: "\e8bd"; 124 | } 125 | 126 | .icon-chaxunfancheng:before { 127 | content: "\e8c8"; 128 | } 129 | 130 | .icon-zhifubao:before { 131 | content: "\e8e4"; 132 | } 133 | 134 | .icon-xialajiantouxiao:before { 135 | content: "\e907"; 136 | } 137 | 138 | .icon-shouqijiantouxiao1:before { 139 | content: "\e908"; 140 | } 141 | 142 | .icon-quxiaodingdanxiao:before { 143 | content: "\e933"; 144 | } 145 | 146 | .icon-baocun:before { 147 | content: "\e936"; 148 | } 149 | 150 | .icon-wodexiao:before { 151 | content: "\e941"; 152 | } 153 | -------------------------------------------------------------------------------- /wwwroot/css/icons/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/css/icons/iconfont.ttf -------------------------------------------------------------------------------- /wwwroot/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | html { 4 | line-height: 1.15; 5 | -webkit-text-size-adjust: 100% 6 | } 7 | 8 | body { 9 | margin: 0 10 | } 11 | 12 | main { 13 | display: block 14 | } 15 | 16 | h1 { 17 | font-size: 2em; 18 | margin: .67em 0 19 | } 20 | 21 | hr { 22 | box-sizing: content-box; 23 | height: 0; 24 | overflow: visible 25 | } 26 | 27 | pre { 28 | font-family: monospace,monospace; 29 | font-size: 1em 30 | } 31 | 32 | a { 33 | background-color: transparent 34 | } 35 | 36 | abbr[title] { 37 | border-bottom: 0; 38 | text-decoration: underline; 39 | text-decoration: underline dotted 40 | } 41 | 42 | b, strong { 43 | font-weight: bolder 44 | } 45 | 46 | code, kbd, samp { 47 | font-family: monospace,monospace; 48 | font-size: 1em 49 | } 50 | 51 | small { 52 | font-size: 80% 53 | } 54 | 55 | sub, sup { 56 | font-size: 75%; 57 | line-height: 0; 58 | position: relative; 59 | vertical-align: baseline 60 | } 61 | 62 | sub { 63 | bottom: -0.25em 64 | } 65 | 66 | sup { 67 | top: -0.5em 68 | } 69 | 70 | img { 71 | border-style: none 72 | } 73 | 74 | button, input, optgroup, select, textarea { 75 | font-family: inherit; 76 | font-size: 100%; 77 | line-height: 1.15; 78 | margin: 0 79 | } 80 | 81 | button, input { 82 | overflow: visible 83 | } 84 | 85 | button, select { 86 | text-transform: none 87 | } 88 | 89 | button, [type="button"], [type="reset"], [type="submit"] { 90 | -webkit-appearance: button 91 | } 92 | 93 | button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { 94 | border-style: none; 95 | padding: 0 96 | } 97 | 98 | button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { 99 | outline: 1px dotted ButtonText 100 | } 101 | 102 | fieldset { 103 | padding: .35em .75em .625em 104 | } 105 | 106 | legend { 107 | box-sizing: border-box; 108 | color: inherit; 109 | display: table; 110 | max-width: 100%; 111 | padding: 0; 112 | white-space: normal 113 | } 114 | 115 | progress { 116 | vertical-align: baseline 117 | } 118 | 119 | textarea { 120 | overflow: auto 121 | } 122 | 123 | [type="checkbox"], [type="radio"] { 124 | box-sizing: border-box; 125 | padding: 0 126 | } 127 | 128 | [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { 129 | height: auto 130 | } 131 | 132 | [type="search"] { 133 | -webkit-appearance: textfield; 134 | outline-offset: -2px 135 | } 136 | 137 | [type="search"]::-webkit-search-decoration { 138 | -webkit-appearance: none 139 | } 140 | 141 | ::-webkit-file-upload-button { 142 | -webkit-appearance: button; 143 | font: inherit 144 | } 145 | 146 | details { 147 | display: block 148 | } 149 | 150 | summary { 151 | display: list-item 152 | } 153 | 154 | template { 155 | display: none 156 | } 157 | 158 | [hidden] { 159 | display: none 160 | } 161 | -------------------------------------------------------------------------------- /wwwroot/css/prism-coy.min.css: -------------------------------------------------------------------------------- 1 | code[class*=language-], pre[class*=language-] { 2 | background: 0 0; 3 | color: #000; 4 | text-align: left; 5 | white-space: pre; 6 | word-spacing: normal; 7 | word-wrap: normal; 8 | -moz-tab-size: 4; 9 | -o-tab-size: 4; 10 | tab-size: 4; 11 | font-size: 1em; 12 | font-family: Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace; 13 | line-height: 1.5; 14 | word-break: normal; 15 | -webkit-hyphens: none; 16 | -moz-hyphens: none; 17 | -ms-hyphens: none; 18 | hyphens: none 19 | } 20 | 21 | pre[class*=language-] { 22 | position: relative; 23 | overflow: visible; 24 | margin: .5em 0; 25 | padding: 1px 26 | } 27 | 28 | pre[class*=language-] > code { 29 | position: relative; 30 | z-index: 1; 31 | border-left: 10px solid #358ccb; 32 | background-color: #fdfdfd; 33 | background-image: linear-gradient(transparent 50%,rgba(69,142,209,.04) 0); 34 | background-size: 3em 3em; 35 | background-attachment: local; 36 | background-origin: content-box; 37 | box-shadow: -1px 0 0 0 #358ccb,0 0 0 1px #dfdfdf 38 | } 39 | 40 | code[class*=language-] { 41 | display: block; 42 | overflow: auto; 43 | padding: 0 1em; 44 | height: inherit; 45 | max-height: inherit 46 | } 47 | 48 | :not(pre) > code[class*=language-], pre[class*=language-] { 49 | -webkit-box-sizing: border-box; 50 | -moz-box-sizing: border-box; 51 | box-sizing: border-box; 52 | margin-bottom: 1em; 53 | background-color: #fdfdfd 54 | } 55 | 56 | :not(pre) > code[class*=language-] { 57 | position: relative; 58 | display: inline; 59 | padding: .2em; 60 | border: 1px solid rgba(0,0,0,.1); 61 | border-radius: .3em; 62 | color: #c92c2c; 63 | white-space: normal 64 | } 65 | 66 | pre[class*=language-]:after, pre[class*=language-]:before { 67 | position: absolute; 68 | bottom: .75em; 69 | left: .18em; 70 | display: block; 71 | width: 40%; 72 | height: 20%; 73 | max-height: 13em; 74 | box-shadow: 0 13px 8px #979797 75 | } 76 | 77 | pre[class*=language-]:after { 78 | right: .75em; 79 | left: auto 80 | } 81 | 82 | .token.block-comment, .token.cdata, .token.comment, .token.doctype, .token.prolog { 83 | color: #7d8b99 84 | } 85 | 86 | .token.punctuation { 87 | color: #5f6364 88 | } 89 | 90 | .token.boolean, .token.constant, .token.deleted, .token.function-name, .token.number, .token.property, .token.symbol, .token.tag { 91 | color: #c92c2c 92 | } 93 | 94 | .token.attr-name, .token.builtin, .token.char, .token.function, .token.inserted, .token.selector, .token.string { 95 | color: #2f9c0a 96 | } 97 | 98 | .token.entity, .token.operator, .token.url, .token.variable { 99 | background: hsla(0,0%,100%,.5); 100 | color: #a67f59 101 | } 102 | 103 | .token.atrule, .token.attr-value, .token.class-name, .token.keyword { 104 | color: #1990b8 105 | } 106 | 107 | .token.important, .token.regex { 108 | color: #e90 109 | } 110 | 111 | .language-css .token.string, .style .token.string { 112 | background: hsla(0,0%,100%,.5); 113 | color: #a67f59 114 | } 115 | 116 | .token.important { 117 | font-weight: 400 118 | } 119 | 120 | .token.bold { 121 | font-weight: 700 122 | } 123 | 124 | .token.italic { 125 | font-style: italic 126 | } 127 | 128 | .token.entity { 129 | cursor: help 130 | } 131 | 132 | .token.namespace { 133 | opacity: .7 134 | } 135 | 136 | @media screen and (max-width:767px) { 137 | pre[class*=language-]:after, pre[class*=language-]:before { 138 | bottom: 14px; 139 | box-shadow: none 140 | } 141 | } 142 | 143 | pre[class*=language-].line-numbers.line-numbers { 144 | padding-left: 0 145 | } 146 | 147 | pre[class*=language-].line-numbers.line-numbers code { 148 | padding-left: 3.8em 149 | } 150 | 151 | pre[class*=language-].line-numbers.line-numbers .line-numbers-rows { 152 | left: 0 153 | } 154 | 155 | pre[class*=language-][data-line] { 156 | padding-top: 0; 157 | padding-bottom: 0; 158 | padding-left: 0 159 | } 160 | 161 | pre[data-line] code { 162 | position: relative; 163 | padding-left: 4em 164 | } 165 | 166 | pre .line-highlight { 167 | margin-top: 0 168 | } 169 | -------------------------------------------------------------------------------- /wwwroot/css/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.28.0 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+aspnet+bash+c+csharp+css-extras+diff+docker+git+go+java+json+lua+markup-templating+nginx+php+plsql+python+sql&plugins=line-highlight+line-numbers+autolinker+show-language+jsonp-highlight+highlight-keywords+inline-color+autoloader+toolbar+diff-highlight+filter-highlight-all+treeview */ 3 | code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} 4 | pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)} 5 | pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right} 6 | .token a{color:inherit} 7 | div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none} 8 | span.inline-color-wrapper{background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4=);background-position:center;background-size:110%;display:inline-block;height:1.333ch;width:1.333ch;margin:0 .333ch;box-sizing:border-box;border:1px solid #fff;outline:1px solid rgba(0,0,0,.5);overflow:hidden}span.inline-color{display:block;height:120%;width:120%} 9 | pre.diff-highlight>code .token.deleted:not(.prefix),pre>code.diff-highlight .token.deleted:not(.prefix){background-color:rgba(255,0,0,.1);color:inherit;display:block}pre.diff-highlight>code .token.inserted:not(.prefix),pre>code.diff-highlight .token.inserted:not(.prefix){background-color:rgba(0,255,128,.1);color:inherit;display:block} 10 | .token.treeview-part .entry-line{position:relative;text-indent:-99em;display:inline-block;vertical-align:top;width:1.2em}.token.treeview-part .entry-line:before,.token.treeview-part .line-h:after{content:"";position:absolute;top:0;left:50%;width:50%;height:100%}.token.treeview-part .line-h:before,.token.treeview-part .line-v:before{border-left:1px solid #ccc}.token.treeview-part .line-v-last:before{height:50%;border-left:1px solid #ccc;border-bottom:1px solid #ccc}.token.treeview-part .line-h:after{height:50%;border-bottom:1px solid #ccc}.token.treeview-part .entry-name{position:relative;display:inline-block;vertical-align:top}.token.treeview-part .entry-name.dotfile{opacity:.5}@font-face{font-family:PrismTreeview;src:url(data:application/font-woff;base64,d09GRgABAAAAAAgYAAsAAAAAEGAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADsAAABUIIslek9TLzIAAAFEAAAAPwAAAFY1UkH9Y21hcAAAAYQAAAB/AAACCtvO7yxnbHlmAAACBAAAA+MAAAlACm1VqmhlYWQAAAXoAAAAKgAAADZfxj5jaGhlYQAABhQAAAAYAAAAJAFbAMFobXR4AAAGLAAAAA4AAAA0CGQAAGxvY2EAAAY8AAAAHAAAABwM9A9CbWF4cAAABlgAAAAfAAAAIAEgAHZuYW1lAAAGeAAAATcAAAJSfUrk+HBvc3QAAAewAAAAZgAAAIka0DSfeJxjYGRgYOBiMGCwY2BycfMJYeDLSSzJY5BiYGGAAJA8MpsxJzM9kYEDxgPKsYBpDiBmg4gCACY7BUgAeJxjYGRYyjiBgZWBgaGQoRZISkLpUAYOBj0GBiYGVmYGrCAgzTWFweEV4ysehs1ArgDDFgZGIA3CDAB2tQjAAHic7ZHLEcMwCESfLCz/VEoKSEE5parURxMOC4c0Ec283WGFdABgBXrwCAzam4bOK9KWeefM3Hhmjyn3ed+hTRq1pS7Ra/HjYGPniHcXMy4G/zNTP7/KW5HTXArkvdBW3ArN19dCG/NRIN8K5HuB/CiQn4U26VeBfBbML9NEH78AeJyVVc1u20YQ3pn905JcSgr/YsuSDTEg3cR1bFEkYyS1HQcQ2jQF2hot6vYSoECKnnPLA/SWUy9NTr31Bfp+6azsNI0SGiolzu7ODnfn+2Z2lnHG3rxhr9nfLGKbLGesncAYYnUHpsVnMG/uwyzNdFIVd6HI6twp8+R3LpT4TSglLoTHwwJgG2/dFvKrl9yI507/p5CCq4LTxB/PlPjkFaMHnWB/0S9je7RTPS+utnGtom1T2q5pk/e3H0M1S18rsXAL7wgpxQuhAmteGGvNjmcfGXuwnFNOPCXxeOGmnjrBLWNyBeNtVq2Hs03yus1aPS3mzSyNVSfu588iW1Q93x/4fjcHn+5EkS2tMxr4xIRa8ese+4L9uKZnxEqs8+ldyN9atU02a5t5uQ8hZGms1QTKpaKYqnipiNNOAIeIADC0JNEOYY+jtSgFoOchiAjRGFACpUTRje8bwIYWGCDEgENY8MEu9bnCYCdAxftoNg0KiSpUtPaHcanYwzXRu6T4r40b5npal3V7UHWCPJW9niyl1vIHgoujEXZjudBkeWkOeMQBRmbEPhKzij1i52t6/TadL+3q7H0U1eq4E8cG4gIIwQLx8VX7ToPXgPrehVc5QXHR7gMSmwjKfaYAP4KvZV+yn9bE18y2IY37LvtyrSg3i7ZK++B603ndlg/gBJpZRsfpBI6hyiaQ6FjlnThz8lAC3LgBIMnXDOAXxBQ4SIgiEhx2AcGCAwAhwjXRpCQms42bwAUt75BvAwgONzdgOfWEwzk4Ylzj4mz+5YEzzXzWX9aNlk7ot65y5QnBHsNlm6zDTu7sspRqG4V+fgJ1lVBZ07Nm7s5nemo3Lf3PO7iwtnroQ5/YDGwPRUip6fV6L+27p+wCHwSvPs85UnHqId8NAn5IBsKdv95KrL9m31Gsf2a/rluDslk1y1J9GE+LUmmVT/OyOHaFKGnapt2H5XeJTmKd6qYNoVVZOy+pWzr7rMip3ndG/4mQSoUcMbAqG/YNIAdXhkAqTVruXhocSKN0iS4Rwj7vSS4fcF/La07BfeQSuRAcFeW+9igjwPhhYPpGCBCBHhxiKMyFMFT7ziRH7RtfIWdiha+TdW+Rqs7bLHdN2ZJIKl0um0x3op9saYr0REeRdj09pl43pMzz4tjztrY8L4o8bzT+oLY27PR/eFtXs/YY5vtwB5Iqad14eYN0ujveMaGWqkdU3TKbQSC5Uvxaf4fA7SAQ3r2tEfIhd4duld91bwMisjqBw22orthNcroXl7KqO1329HBgAexgoCfGAwiDPoBnriki3lmNojrzvD0tjo6E3vPYP6E2BMIAeJxjYGRgYADiY8t3FsTz23xl4GbYzIAB/v9nWM6wBcjgYGAC8QH+QQhZAAB4nGNgZGBg2MzAACeXMzAyoAJeADPyAh14nGNgAILNpGEA0fgIZQAAAAAAAAA2AHIAvgE+AZgCCAKMAv4DlgPsBEYEoHicY2BkYGDgZchi4GQAASYg5gJCBob/YD4DABTSAZcAeJx9kU1uwjAQhV/4qwpqhdSqi67cTTeVEmBXDgBbhBD7AHYISuLUMSD2PUdP0HNwjp6i676k3qQS9Ujjb968mYUNoI8zPJTHw02Vy9PAFatfbpLuHbfIT47b6MF33KH+6riLF0wc93CHN27wWtdUHvHuuIFbfDhuUv903CKfHbfxgC/HHerfjrtYen3HPTx7ambiIl0YKQ+xPM5ltE9CU9NqxVKaItaZGPqDmj6VmTShlRuxOoniEI2sVUIZnYqJzqxMEi1yo3dybf2ttfk4CJTT/bVOMYNBjAIpFiTJOLCWOGLOHGGPBCE7l32XO0tmw04MjQwCQ7774B//lDmrZkJY3hvOrHBiLuiJMKJqoVgrejQ3CP5Yubt0JwxNJa96Oypr6j621VSOMQKG+uP36eKmHylcb0MAeJxtwdEOgjAMBdBeWEFR/Mdl7bTJtMsygc/nwVfPoYF+QP+tGDAigDFhxgVXLLjhjhUPCtmKTtmLaGN7x6dy/Io5bybqoevRQ3LRObb0sk3HKpn1SFqW6ru26vbpYfcmRCccJhqsAAA=) format("woff")}.token.treeview-part .entry-name:before{content:"\ea01";font-family:PrismTreeview;font-size:inherit;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:2.5ex;display:inline-block}.token.treeview-part .entry-name.dir:before{content:"\ea02"}.token.treeview-part .entry-name.ext-bmp:before,.token.treeview-part .entry-name.ext-eps:before,.token.treeview-part .entry-name.ext-gif:before,.token.treeview-part .entry-name.ext-jpe:before,.token.treeview-part .entry-name.ext-jpeg:before,.token.treeview-part .entry-name.ext-jpg:before,.token.treeview-part .entry-name.ext-png:before,.token.treeview-part .entry-name.ext-svg:before,.token.treeview-part .entry-name.ext-tiff:before{content:"\ea03"}.token.treeview-part .entry-name.ext-cfg:before,.token.treeview-part .entry-name.ext-conf:before,.token.treeview-part .entry-name.ext-config:before,.token.treeview-part .entry-name.ext-csv:before,.token.treeview-part .entry-name.ext-ini:before,.token.treeview-part .entry-name.ext-log:before,.token.treeview-part .entry-name.ext-md:before,.token.treeview-part .entry-name.ext-nfo:before,.token.treeview-part .entry-name.ext-txt:before{content:"\ea06"}.token.treeview-part .entry-name.ext-asp:before,.token.treeview-part .entry-name.ext-aspx:before,.token.treeview-part .entry-name.ext-c:before,.token.treeview-part .entry-name.ext-cc:before,.token.treeview-part .entry-name.ext-cpp:before,.token.treeview-part .entry-name.ext-cs:before,.token.treeview-part .entry-name.ext-css:before,.token.treeview-part .entry-name.ext-h:before,.token.treeview-part .entry-name.ext-hh:before,.token.treeview-part .entry-name.ext-htm:before,.token.treeview-part .entry-name.ext-html:before,.token.treeview-part .entry-name.ext-jav:before,.token.treeview-part .entry-name.ext-java:before,.token.treeview-part .entry-name.ext-js:before,.token.treeview-part .entry-name.ext-php:before,.token.treeview-part .entry-name.ext-rb:before,.token.treeview-part .entry-name.ext-xml:before{content:"\ea07"}.token.treeview-part .entry-name.ext-7z:before,.token.treeview-part .entry-name.ext-bz2:before,.token.treeview-part .entry-name.ext-bz:before,.token.treeview-part .entry-name.ext-gz:before,.token.treeview-part .entry-name.ext-rar:before,.token.treeview-part .entry-name.ext-tar:before,.token.treeview-part .entry-name.ext-tgz:before,.token.treeview-part .entry-name.ext-zip:before{content:"\ea08"}.token.treeview-part .entry-name.ext-aac:before,.token.treeview-part .entry-name.ext-au:before,.token.treeview-part .entry-name.ext-cda:before,.token.treeview-part .entry-name.ext-flac:before,.token.treeview-part .entry-name.ext-mp3:before,.token.treeview-part .entry-name.ext-oga:before,.token.treeview-part .entry-name.ext-ogg:before,.token.treeview-part .entry-name.ext-wav:before,.token.treeview-part .entry-name.ext-wma:before{content:"\ea04"}.token.treeview-part .entry-name.ext-avi:before,.token.treeview-part .entry-name.ext-flv:before,.token.treeview-part .entry-name.ext-mkv:before,.token.treeview-part .entry-name.ext-mov:before,.token.treeview-part .entry-name.ext-mp4:before,.token.treeview-part .entry-name.ext-mpeg:before,.token.treeview-part .entry-name.ext-mpg:before,.token.treeview-part .entry-name.ext-ogv:before,.token.treeview-part .entry-name.ext-webm:before{content:"\ea05"}.token.treeview-part .entry-name.ext-pdf:before{content:"\ea09"}.token.treeview-part .entry-name.ext-xls:before,.token.treeview-part .entry-name.ext-xlsx:before{content:"\ea0a"}.token.treeview-part .entry-name.ext-doc:before,.token.treeview-part .entry-name.ext-docm:before,.token.treeview-part .entry-name.ext-docx:before{content:"\ea0c"}.token.treeview-part .entry-name.ext-pps:before,.token.treeview-part .entry-name.ext-ppt:before,.token.treeview-part .entry-name.ext-pptx:before{content:"\ea0b"} 11 | -------------------------------------------------------------------------------- /wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/favicon.ico -------------------------------------------------------------------------------- /wwwroot/favicon1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/favicon1.ico -------------------------------------------------------------------------------- /wwwroot/html/WeatherSDK.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /wwwroot/i18n/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "$Text": "English", 3 | "Dashboard": "Dashboard", 4 | "eCommerce": "eCommerce", 5 | "Analytics": "Analytics", 6 | "Apps": "Apps", 7 | "Shop": "Shop", 8 | "Details": "Details", 9 | "Order": "Order", 10 | "Todo": "Todo", 11 | "Invoice": "Invoice", 12 | "List": "List", 13 | "Preview": "Preview", 14 | "Edit": "Edit", 15 | "Add": "Add", 16 | "User": "User", 17 | "View": "View", 18 | "Pages": "Pages", 19 | "Auth": "Auth", 20 | "Login v1": "Login v1", 21 | "Login v2": "Login v2", 22 | "Not Authorized": "Not Authorized", 23 | "Register v1": "Register v1", 24 | "Register v2": "Register v2", 25 | "Forgot Password v1": "Forgot Password v1", 26 | "Forgot Password v2": "Forgot Password v2", 27 | "Reset Password v1": "Reset Password v1", 28 | "Reset Password v2": "Reset Password v2", 29 | "Others": "Others", 30 | "Account Settings": "Account Settings", 31 | "Favorite": "Favorite" 32 | } -------------------------------------------------------------------------------- /wwwroot/i18n/languageConfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | "zh-CN", 3 | "en-US" 4 | ] 5 | 6 | 7 | -------------------------------------------------------------------------------- /wwwroot/i18n/zh-CN.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/i18n/zh-CN.json -------------------------------------------------------------------------------- /wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/icon-192.png -------------------------------------------------------------------------------- /wwwroot/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/images/bg.jpg -------------------------------------------------------------------------------- /wwwroot/images/witeem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/images/witeem.png -------------------------------------------------------------------------------- /wwwroot/images/witeem1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/images/witeem1.png -------------------------------------------------------------------------------- /wwwroot/img/avatar/1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/10.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/11.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/12.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/13.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/14.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/witeem/Blazor-Blog-Web/a101cb2fe8ebaa5f45fbf5972cf80547569036b8/wwwroot/img/avatar/5.jpg -------------------------------------------------------------------------------- /wwwroot/img/avatar/7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /wwwroot/img/avatar/9.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 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 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /wwwroot/js/Common.min.js: -------------------------------------------------------------------------------- 1 | eval(function (p, a, c, k, e, r) { e = String; if ('0'.replace(0, e) == 0) { while (c--) r[e(c)] = k[c]; k = [function (e) { return r[e] || e }]; e = function () { return '[0-3]' }; c = 1 }; while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); return p }('function getIpAddress(){0 fetch(\'https://jsonip.com/\').1((2)=>2.json()).1((3)=>{0 3.ip})}', [], 4, 'return|then|response|data'.split('|'), 0, {})) -------------------------------------------------------------------------------- /wwwroot/js/CookieStorage.min.js: -------------------------------------------------------------------------------- 1 | eval(function (p, a, c, k, e, r) { e = function (c) { return (c < 62 ? '' : e(parseInt(c / 62))) + ((c = c % 62) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) }; if ('0'.replace(0, e) == 0) { while (c--) r[e(c)] = k[c]; k = [function (e) { return r[e] || e }]; e = function () { return '([2-9b-dfhj-zA-Z]|1\\w)' }; c = 1 }; while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]); return p }('(6(q,f){\'use strict\';7 E=6(l){c(d l.w!==\'h\'){P m Q(\'2.R requires a `l` S a `w` h\');}7 2=6(4,8,3){9 arguments.F===1?2.G(4):2.x(4,8,3)};2.r=l.w;2.y=\'cookey.\';2.T=m n(\'Fri, 31 Dec 9999 23:U:U UTC\');2.s={j:\'/\',k:false};2.G=6(4){c(2.V!==2.r.t){2.W()}7 8=2.X[2.y+4];9 8===f?f:Y(8)};2.x=6(4,8,3){3=2.Z(3);3.5=2.10(8===f?-1:3.5);2.r.t=2.11(4,8,3);9 2};2.12=6(4,3){9 2.x(4,f,3)};2.Z=6(3){9{j:3&&3.j||2.s.j,o:3&&3.o||2.s.o,5:3&&3.5||2.s.5,k:3&&3.k!==f?3.k:2.s.k}};2.13=6(H){9 Object.prototype.toString.call(H)===\'[h n]\'&&!isNaN(H.14())};2.10=6(5,z){z=z||m n();c(d 5===\'number\'){5=5===Infinity?2.T:m n(z.14()+5*1000)}I c(d 5===\'string\'){5=m n(5)}c(5&&!2.13(5)){P m Q(\'`5` parameter cannot be converted to a valid n instance\');}9 5};2.11=6(4,8,3){4=4.A(/[^#$&+\\^`|]/g,15);4=4.A(/\\(/g,\'%28\').A(/\\)/g,\'%29\');8=(8+\'\').A(/[^!#$&-+\\--:<-\\[\\]-~]/g,15);3=3||{};7 b=4+\'=\'+8;b+=3.j?\';j=\'+3.j:\'\';b+=3.o?\';o=\'+3.o:\'\';b+=3.5?\';5=\'+3.5.toUTCString():\'\';b+=3.k?\';k\':\'\';9 b};2.16=6(J){7 B={};7 K=J?J.split(\'; \'):[];for(7 i=0;i