Compare commits

..

3 commits

Author SHA1 Message Date
Daniel Balk 2b85ffd93b implemented register + email verify requests 2023-11-02 19:42:20 +01:00
Daniel Balk c0533d78fb login api | + bugfixes 2023-11-01 22:49:53 +01:00
Daniel Balk 8dc1130d2a added the api client 2023-11-01 19:14:47 +01:00
685 changed files with 14166 additions and 22525 deletions

8
.gitattributes vendored
View file

@ -1,2 +1,10 @@
# Auto detect text files and perform LF normalization # Auto detect text files and perform LF normalization
* text=auto * text=auto
Moonlight/wwwroot/** linguist-vendored
Moonlight/wwwroot/assets/js/scripts.bundle.js linguist-vendored
Moonlight/wwwroot/assets/js/widgets.bundle.js linguist-vendored
Moonlight/wwwroot/assets/js/theme.js linguist-vendored
Moonlight/wwwroot/assets/css/boxicons.min.css linguist-vendored
Moonlight/wwwroot/assets/css/style.bundle.css linguist-vendored
Moonlight/wwwroot/assets/plugins/** linguist-vendored
Moonlight/wwwroot/assets/fonts/** linguist-vendored

2
.github/FUNDING.yml vendored
View file

@ -1,2 +0,0 @@
# These are supported funding model platforms
ko_fi: masuowo

View file

@ -1,87 +0,0 @@
name: Bug Report
description: Something isn't working quite right in the software.
labels: [not confirmed]
body:
- type: markdown
attributes:
value: |
Bug reports should only be used for reporting issues with how the software works. For assistance installing this software, as well as debugging issues with dependencies, please use our [Discord server](https://discord.gg/TJaspT7A8p).
- type: textarea
attributes:
label: Current Behavior
description: Please provide a clear & concise description of the issue.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: Please describe what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Please be as detailed as possible when providing steps to reproduce, failure to provide steps will result in this issue being closed.
validations:
required: true
- type: input
id: panel-version
attributes:
label: Panel Version
description: Version number of your Panel (latest is not a version)
placeholder: v2 EA
validations:
required: true
- type: input
id: daemon-version
attributes:
label: Daemon Version
description: Version number of your Daemon (latest is not a version)
placeholder: v2 EA
validations:
required: true
- type: input
id: image-details
attributes:
label: Games and/or Images Affected
description: Please include the specific game(s) or Image(s) you are running into this bug with.
placeholder: Minecraft (Paper), Minecraft (Forge)
- type: input
id: docker-image
attributes:
label: Docker Image
description: The specific Docker image you are using for the game(s) above.
placeholder: ghcr.io/xxx/yolks:java_17
- type: textarea
id: panel-logs
attributes:
label: Error Logs
description: |
Run the following command to collect logs on your system.
Panel: `docker logs moonlight`
Wings: `sudo wings diagnostics`
placeholder: logs here
render: bash
validations:
required: false
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please [search here](https://github.com/Moonlight-Panel/Moonlight/issues) to see if an issue already exists for your problem.
options:
- label: I have searched the existing issues before opening this issue.
required: true
- label: I have provided all relevant details, including the specific game and Docker images I am using if this issue is related to running a server.
required: true
- label: I have checked in the Discord server and believe this is a bug with the software, and not a configuration issue with my specific system.
required: true

View file

@ -1,8 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Installation Help
url: https://discord.gg/TJaspT7A8p
about: Please visit our Discord for help with your installation.
- name: General Question
url: https://discord.gg/TJaspT7A8p
about: Please visit our Discord for general questions about Moonlight Panel.

View file

@ -1,32 +0,0 @@
name: Feature Request
description: Suggest a new feature or improvement for the software.
labels: [feature request]
body:
- type: checkboxes
attributes:
label: Is there an existing feature request for this?
description: Please [search here](https://github.com/Moonlight-Panel/Moonlight/issues?q=is%3Aissue) to see if someone else has already suggested this.
options:
- label: I have searched the existing issues before opening this feature request.
required: true
- type: textarea
attributes:
label: Describe the feature you would like to see.
description: "A clear & concise description of the feature you'd like to have added, and what issues it would solve."
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like.
description: "You must explain how you'd like to see this feature implemented. Technical implementation details are not necessary, rather an idea of how you'd like to see this feature used."
validations:
required: true
- type: textarea
attributes:
label: Additional context to this request.
description: "Add any other context or screenshots about the feature request."
validations:
required: false

430
.gitignore vendored
View file

@ -1,404 +1,44 @@
## Ignore Visual Studio temporary files, build results, and  Common IntelliJ Platform excludes
## files generated by popular Visual Studio add-ons.
## # User specific
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore **/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/httpRequests/
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
Moonlight/[Bb]in/
Moonlight/[Oo]bj/
Moonlight/_UpgradeReport_Files/
Moonlight/[Pp]ackages/
# User-specific files
*.rsuser
*.suo *.suo
*.user *.user
*.userosscache .vs/
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/ _UpgradeReport_Files/
Backup*/ [Pp]ackages/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files Thumbs.db
*.mdf Desktop.ini
*.ldf .DS_Store
*.ndf .idea/.idea.Moonlight/.idea/discord.xml
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
storage/
.idea/.idea.Moonlight/.idea/dataSources.xml
Moonlight/wwwroot/css/theme.css Moonlight/wwwroot/css/theme.css
Moonlight/wwwroot/css/theme.css.map Moonlight/wwwroot/css/theme.css.map
.idea/.idea.Moonlight/.idea/discord.xml storage/

View file

@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.Moonlight.iml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
<option name="theme" value="material" />
<option name="button1Title" value="" />
<option name="button1Url" value="" />
<option name="button2Title" value="" />
<option name="button2Url" value="" />
</component>
</project>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EfCoreCommonOptions">
<option name="migrationsToStartupProjects">
<map>
<entry key="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" value="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" />
</map>
</option>
<option name="solutionLevelOptions">
<map>
<entry key="migrationsProject" value="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" />
<entry key="startupProject" value="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" />
</map>
</option>
<option name="startupToMigrationsProjects">
<map>
<entry key="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" value="691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da" />
</map>
</option>
</component>
</project>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EfCoreDialogsState">
<option name="keyValueStorage">
<map>
<entry key="Common:691e5ec2-4b4f-4bd1-9cbc-7d3c6efc12da:dbContext" value="Moonlight.App.Database.DataContext" />
<entry key="Common:buildConfiguration" value="Debug" />
<entry key="Common:noBuild" value="false" />
<entry key="Common:outputFolder" value="App/Database/Migrations" />
<entry key="Common:useDefaultConnection" value="true" />
</map>
</option>
</component>
</project>

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="52a374ed:18c1029d858:-8000" />
<option name="version" value="8.13.2" />
</MTProjectMetadataState>
</option>
</component>
</project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RiderProjectSettingsUpdater">
<option name="vcsConfiguration" value="2" />
</component>
</project>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>

121
LICENSE
View file

@ -1,121 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View file

@ -1,6 +1,6 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight", "Moonlight\Moonlight.csproj", "{B9AD26D2-AAA7-4C57-884D-5AACAA54D5B6}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moonlight", "Moonlight\Moonlight.csproj", "{691E5EC2-4B4F-4BD1-9CBC-7D3C6EFC12DA}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -8,11 +8,9 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B9AD26D2-AAA7-4C57-884D-5AACAA54D5B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {691E5EC2-4B4F-4BD1-9CBC-7D3C6EFC12DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9AD26D2-AAA7-4C57-884D-5AACAA54D5B6}.Debug|Any CPU.Build.0 = Debug|Any CPU {691E5EC2-4B4F-4BD1-9CBC-7D3C6EFC12DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9AD26D2-AAA7-4C57-884D-5AACAA54D5B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {691E5EC2-4B4F-4BD1-9CBC-7D3C6EFC12DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9AD26D2-AAA7-4C57-884D-5AACAA54D5B6}.Release|Any CPU.Build.0 = Release|Any CPU {691E5EC2-4B4F-4BD1-9CBC-7D3C6EFC12DA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

6
Moonlight/.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

1
Moonlight/.idea/.name Normal file
View file

@ -0,0 +1 @@
data.sqlite

6
Moonlight/.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View file

@ -1,26 +0,0 @@
@using Moonlight.Core.UI.Layouts
@using Moonlight.Core.Services
@inject FeatureService FeatureService
@{
var assemblies = FeatureService.UiContext.RouteAssemblies;
var firstAssembly = assemblies.First();
var additionalAssemblies = assemblies.Skip(1).ToArray();
}
<Router AppAssembly="@firstAssembly" AdditionalAssemblies="@additionalAssemblies">
<Found Context="routeData">
<CascadingValue TValue="Type" Name="TargetPageType" Value="@routeData.PageType">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
</CascadingValue>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<IconAlert Title="Unknown page" Icon="bx-search">
The address you are trying to access is not associated with any page. To resume please go back or to the <a href="/">homepage</a>
</IconAlert>
</LayoutView>
</NotFound>
</Router>

View file

@ -0,0 +1,22 @@
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Models.Abstractions;
namespace Moonlight.App.Actions.Dummy;
public class DummyActions : ServiceActions
{
public override Task Create(IServiceProvider provider, Service service)
{
return Task.CompletedTask;
}
public override Task Update(IServiceProvider provider, Service service)
{
return Task.CompletedTask;
}
public override Task Delete(IServiceProvider provider, Service service)
{
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,10 @@
namespace Moonlight.App.Api;
public abstract class AbstractRequest
{
public IServiceProvider ServiceProvider { get; set; }
public ApiUserContext? Context { get; set; }
public abstract void ReadData(RequestDataContext dataContext);
public abstract Task ProcessRequest();
public abstract ResponseDataBuilder CreateResponse(ResponseDataBuilder builder);
}

View file

@ -0,0 +1,63 @@
using System.Net.WebSockets;
using System.Reflection;
namespace Moonlight.App.Api;
public class ApiManagementService
{
public Dictionary<int, Type> Requests;
public List<ApiUserContext> Contexts;
private readonly IServiceProvider ServiceProvider;
public ApiManagementService(IServiceProvider serviceProvider)
{
Requests = new Dictionary<int, Type>();
Contexts = new List<ApiUserContext>();
ServiceProvider = serviceProvider;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.ExportedTypes;
foreach (var type in types)
{
var attribute = type.GetCustomAttribute<ApiRequestAttribute>();
if(attribute == null)
continue;
var id = attribute.Id;
Requests[id] = type;
}
}
}
public AbstractRequest GetRequest(int id, ApiUserContext context)
{
var type = Requests[id];
var obj = Activator.CreateInstance(type) as AbstractRequest;
obj!.Context = context;
obj!.ServiceProvider = ServiceProvider.CreateScope().ServiceProvider;
return obj!;
}
public async Task HandleRequest(ApiUserContext context, byte[] data)
{
var rqd = new RequestDataContext(data);
var id = rqd.ReadInt();
var request = GetRequest(id, context);
request.ReadData(rqd);
await request.ProcessRequest();
var rbd = new ResponseDataBuilder();
rbd = request.CreateResponse(rbd);
CancellationToken t = new CancellationToken();
var bytes = rbd.ToBytes();
await context.WebSocket.SendAsync(bytes, WebSocketMessageType.Binary, true, t);
}
}

View file

@ -0,0 +1,11 @@
namespace Moonlight.App.Api;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class ApiRequestAttribute : Attribute
{
public int Id { get; set; }
public ApiRequestAttribute(int id)
{
Id = id;
}
}

View file

@ -0,0 +1,15 @@
using System.Net.WebSockets;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Api;
public class ApiUserContext
{
public ApiUserContext(WebSocket webSocket)
{
WebSocket = webSocket;
}
public User? User { get; set; }
public WebSocket WebSocket { get; }
}

View file

@ -0,0 +1,51 @@
using System.Buffers.Binary;
using System.Text;
namespace Moonlight.App.Api;
public class RequestDataContext
{
private List<byte> Data;
public RequestDataContext(byte[] data)
{
Data = data.ToList();
}
public int ReadInt()
{
var bytes = Data.Take(4).ToList();
Data.RemoveRange(0, 4);
if (BitConverter.IsLittleEndian) // because of java (the app needing the api is written in java/kotlin) we need to use big endian
{
bytes.Reverse();
}
return BitConverter.ToInt32(bytes.ToArray());
}
public byte ReadByte()
{
var b = Data[0];
Data.RemoveAt(0);
return b;
}
public bool ReadBoolean()
{
var b = ReadByte();
return b == 255;
}
public String ReadString()
{
var len = ReadInt();
var bytes = Data.Take(len).ToList();
Data.RemoveRange(0, len);
return Encoding.UTF8.GetString(bytes.ToArray());
}
}

View file

@ -0,0 +1,141 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Abstractions;
using Moonlight.App.Models.Enums;
using Moonlight.App.Repositories;
using Moonlight.App.Services.Utils;
using OtpNet;
namespace Moonlight.App.Api.Requests.Auth;
[ApiRequest(3)]
public class CredentialBasedLoginRequest : AbstractRequest
{
public string Email { get; set; }
public string Password { get; set; }
public string Code { get; set; }
public bool Success { get; set; }
public bool RequireTotp { get; set; }
/// <summary>
/// 0: all fine
/// 1: wrong credentials
/// 2: Totp enabled
/// 3: TotpKey missing
/// 4: wrong totp code
/// </summary>
public int ErrorId { get; set; }
public string Token { get; set; } = "";
public override ResponseDataBuilder CreateResponse(ResponseDataBuilder builder)
{
builder.WriteBoolean(Success);
builder.WriteBoolean(RequireTotp);
builder.WriteInt(ErrorId);
builder.WriteString(Token);
return builder;
}
public override async Task ProcessRequest()
{
var userRepository = ServiceProvider.GetService<Repository<User>>();
var user = userRepository
.Get()
.FirstOrDefault(x => x.Email == Email);
if (user == null)
{
Success = false;
RequireTotp = false;
ErrorId = 1;
Token = "";
return;
}
if (!HashHelper.Verify(Password, user.Password))
{
Success = false;
RequireTotp = false;
ErrorId = 1;
Token = "";
return;
}
var flags = new FlagStorage(user.Flags); // Construct FlagStorage to check for 2fa
if (!flags[UserFlag.TotpEnabled])
{
// No 2fa found on this user so were done here
Success = true;
RequireTotp = false;
ErrorId = 0;
Token = await GenerateToken(user);
Context!.User = user;
return;
}
// If we reach this point, 2fa is enabled so we need to continue validating
if (string.IsNullOrEmpty(Code))
{
// This will show an additional 2fa login field
Success = false;
RequireTotp = true;
ErrorId = 2;
Token = "";
return;
}
if (user.TotpKey == null)
{
// Hopefully we will never fulfill this check ;)
Success = false;
RequireTotp = false;
ErrorId = 3;
Token = "";
return;
throw new DisplayException("2FA key is missing. Please contact the support to fix your account");
}
// Calculate server side code
var totp = new Totp(Base32Encoding.ToBytes(user.TotpKey));
var codeServerSide = totp.ComputeTotp();
if (codeServerSide == Code)
{
Success = true;
RequireTotp = false;
ErrorId = 0;
Token = await GenerateToken(user);
Context!.User = user;
return;
}
Success = false;
RequireTotp = false;
ErrorId = 4;
Token = "";
}
public async Task<string> GenerateToken(User user)
{
var jwtService = ServiceProvider.GetService<JwtService>();
var token = await jwtService.Create(data =>
{
data.Add("userId", user.Id.ToString());
data.Add("issuedAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
}, TimeSpan.FromDays(365));
return token;
}
public override void ReadData(RequestDataContext dataContext)
{
Email = dataContext.ReadString();
Password = dataContext.ReadString();
Code = dataContext.ReadString();
}
}

View file

@ -0,0 +1,50 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Enums;
using Moonlight.App.Repositories;
using Moonlight.App.Services;
using Moonlight.App.Services.Users;
namespace Moonlight.App.Api.Requests.Auth;
[ApiRequest(5)]
public class IsEmailVerifiedRequest : AbstractRequest
{
public bool SendMail { get; set; }
public bool MailVerified { get; set; }
public override async Task ProcessRequest()
{
if(Context.User == null)
return;
var userRepository = ServiceProvider.GetRequiredService<Repository<User>>();
Context.User = userRepository.Get().Where(x => x.Id == Context.User.Id).ToArray()[0];
if (SendMail && Context.User != null)
{
var userAuthService = ServiceProvider.GetRequiredService<UserAuthService>();
await userAuthService.SendVerification(Context!.User);
}
var configService = ServiceProvider.GetRequiredService<ConfigService>();
var reqEmailVerify = configService.Get().Security.EnableEmailVerify;
if (Context?.User?.Flags!.Contains(UserFlag.MailVerified.ToString()) ?? false)
MailVerified = true;
else
MailVerified = !reqEmailVerify;
await Task.Delay(200);
}
public override ResponseDataBuilder CreateResponse(ResponseDataBuilder builder)
{
builder.WriteBoolean(MailVerified);
return builder;
}
public override void ReadData(RequestDataContext dataContext)
{
SendMail = dataContext.ReadBoolean();
}
}

View file

@ -0,0 +1,181 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using Moonlight.App.Database.Entities;
using Moonlight.App.Event;
using Moonlight.App.Exceptions;
using Moonlight.App.Extensions;
using Moonlight.App.Helpers;
using Moonlight.App.Repositories;
using Moonlight.App.Services;
using Moonlight.App.Services.Utils;
namespace Moonlight.App.Api.Requests.Auth;
[ApiRequest(4)]
public class RegisterRequest: AbstractRequest
{
[Required(ErrorMessage = "9")]
[EmailAddress(ErrorMessage = "10")]
public string Email { get; set; } = "";
[Required(ErrorMessage = "8")]
[MinLength(7, ErrorMessage = "7")]
[MaxLength(20, ErrorMessage = "7")]
[RegularExpression("^[a-z][a-z0-9]*$", ErrorMessage = "6")]
public string Username { get; set; } = "";
[Required(ErrorMessage = "4")]
[MinLength(8, ErrorMessage = "5")]
[MaxLength(256, ErrorMessage = "5")]
public string Password { get; set; } = "";
public string PasswordConfirm { get; set; } = "";
public bool Success { get; set; }
public bool RequireEmailVerify { get; set; }
/// <summary>
/// Error Codes:
/// - 0 all successful
/// - 1 email exists
/// - 2 username exists
/// - 3 passwords do not match
/// - 4 password needs to be provided
/// - 5 password needs to be between 8 and 256 characters
/// - 6 Usernames can only contain lowercase characters and numbers
/// - 7 username has to be between 7 and 20 chars
/// - 8 username required
/// - 9 email required
/// - 10 email invalid
/// </summary>
public int ErrorCode { get; set; }
public string Token { get; set; } = "";
public override ResponseDataBuilder CreateResponse(ResponseDataBuilder builder)
{
builder.WriteBoolean(Success);
builder.WriteBoolean(RequireEmailVerify);
builder.WriteInt(ErrorCode);
builder.WriteString(Token);
return builder;
}
public override void ReadData(RequestDataContext dataContext)
{
Email = dataContext.ReadString();
Username = dataContext.ReadString();
Password = dataContext.ReadString();
PasswordConfirm = dataContext.ReadString();
}
public override async Task ProcessRequest()
{
var userRepository = ServiceProvider.GetRequiredService<Repository<User>>();
var configService = ServiceProvider.GetRequiredService<ConfigService>();
var reqEmailVerify = configService.Get().Security.EnableEmailVerify;
// Event though we have form validation i want to
// ensure that at least these basic formatting things are done
Email = Email.ToLower().Trim();
Username = Username.ToLower().Trim();
if (PasswordConfirm != Password)
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = 3;
return;
}
if (!IsPropertyValid(this.GetProperty(x => x.Email)!, out var errorCd))
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = errorCd;
return;
}
if (!IsPropertyValid(this.GetProperty(x => x.Password)!, out var errorCd1))
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = errorCd1;
return;
}
if (!IsPropertyValid(this.GetProperty(x => x.Username)!, out var errorCd2))
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = errorCd2;
return;
}
// Prevent duplication or username and/or email
if (userRepository.Get().Any(x => x.Email == Email))
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = 1;
return;
}
if (userRepository.Get().Any(x => x.Username == Username))
{
Token = "";
Success = false;
RequireEmailVerify = false;
ErrorCode = 2;
return;
}
var user = new User()
{
Username = Username,
Email = Email,
Password = HashHelper.HashToString(Password)
};
var result = userRepository.Add(user);
await Events.OnUserRegistered.InvokeAsync(result);
Token = await GenerateToken(user);
Success = true;
RequireEmailVerify = reqEmailVerify;
ErrorCode = 0;
}
public async Task<string> GenerateToken(User user)
{
var jwtService = ServiceProvider.GetService<JwtService>();
var token = await jwtService.Create(data =>
{
data.Add("userId", user.Id.ToString());
data.Add("issuedAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
}, TimeSpan.FromDays(365));
return token;
}
private bool IsPropertyValid(PropertyInfo property, out int errorCode)
{
var attribs = property.GetCustomAttributes<ValidationAttribute>();
foreach (var a in attribs)
{
if (!a.IsValid(property.GetValue(this)))
{
errorCode = int.Parse(a.ErrorMessage);
return false;
}
}
errorCode = 0;
return true;
}
}

View file

@ -0,0 +1,65 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Repositories;
using Moonlight.App.Services.Utils;
namespace Moonlight.App.Api.Requests.Auth;
[ApiRequest(2)]
public class TokenBasedLoginRequest: AbstractRequest
{
private String Token { get; set; }
private bool Success { get; set; } = false;
public override ResponseDataBuilder CreateResponse(ResponseDataBuilder builder)
{
builder.WriteBoolean(Success);
return builder;
}
public override async Task ProcessRequest()
{
var jwtService = ServiceProvider.GetRequiredService<JwtService>();
var userRepository = ServiceProvider.GetRequiredService<Repository<User>>();
if (string.IsNullOrEmpty(Token))
return;
if (!await jwtService.Validate(Token))
return;
var data = await jwtService.Decode(Token);
if (!data.ContainsKey("userId"))
return;
var userId = int.Parse(data["userId"]);
var user = userRepository
.Get()
.FirstOrDefault(x => x.Id == userId);
if (user == null)
return;
if (!data.ContainsKey("issuedAt"))
return;
var issuedAt = long.Parse(data["issuedAt"]);
var issuedAtDateTime = DateTimeOffset.FromUnixTimeSeconds(issuedAt).DateTime;
// If the valid time is newer then when the token was issued, the token is not longer valid
if (user.TokenValidTimestamp > issuedAtDateTime)
return;
Context!.User = user;
if (Context.User == null) // If the current user is null, stop loading additional data
return;
Success = true;
}
public override void ReadData(RequestDataContext dataContext)
{
Token = dataContext.ReadString();
}
}

View file

@ -0,0 +1,21 @@
namespace Moonlight.App.Api.Requests;
[ApiRequest(1)]
public class PingRequest : AbstractRequest
{
public override void ReadData(RequestDataContext dataContext)
{
var chunk = dataContext.ReadInt();
}
public override ResponseDataBuilder CreateResponse(ResponseDataBuilder builder)
{
builder.WriteInt(10324);
return builder;
}
public override async Task ProcessRequest()
{
}
}

View file

@ -0,0 +1,49 @@
using System.Text;
namespace Moonlight.App.Api;
public class ResponseDataBuilder
{
private List<byte> Data;
public ResponseDataBuilder()
{
Data = new List<byte>();
}
public void WriteInt(int data)
{
var bytes = BitConverter.GetBytes(data);
if (BitConverter.IsLittleEndian) // because of java (the app needing th api is written in java/kotlin) we need to use big endian
{
bytes = bytes.Reverse().ToArray();
}
Data.AddRange(bytes);
}
public void WriteByte(byte data)
{
Data.Add(data);
}
public void WriteBoolean(bool data)
{
WriteByte(data ? (byte)255 : (byte)0);
}
public void WriteString(String data)
{
var bytes = Encoding.UTF8.GetBytes(data);
var len = bytes.Length;
WriteInt(len);
Data.AddRange(bytes);
}
public byte[] ToBytes()
{
return Data.ToArray();
}
}

View file

@ -0,0 +1,73 @@
using System.ComponentModel;
using Moonlight.App.Helpers;
using Newtonsoft.Json;
namespace Moonlight.App.Configuration;
public class ConfigV1
{
[JsonProperty("AppUrl")]
[Description("The url with which moonlight is accessible from the internet. It must not end with a /")]
public string AppUrl { get; set; } = "http://your-moonlight-instance-without-slash.owo";
[JsonProperty("Security")] public SecurityData Security { get; set; } = new();
[JsonProperty("Database")] public DatabaseData Database { get; set; } = new();
[JsonProperty("MailServer")] public MailServerData MailServer { get; set; } = new();
[JsonProperty("Store")] public StoreData Store { get; set; } = new();
public class StoreData
{
[JsonProperty("Currency")]
[Description("A string value representing the currency which will be shown to a user")]
public string Currency { get; set; } = "€";
}
public class SecurityData
{
[JsonProperty("Token")]
[Description("The security token helio will use to encrypt various things like tokens")]
public string Token { get; set; } = Guid.NewGuid().ToString().Replace("-", "");
[JsonProperty("EnableEmailVerify")]
[Description("This will users force to verify their email address if they havent already")]
public bool EnableEmailVerify { get; set; } = false;
}
public class DatabaseData
{
[JsonProperty("UseSqlite")]
public bool UseSqlite { get; set; } = false;
[JsonProperty("SqlitePath")]
public string SqlitePath { get; set; } = PathBuilder.File("storage", "data.sqlite");
[JsonProperty("Host")]
public string Host { get; set; } = "your.db.host";
[JsonProperty("Port")]
public int Port { get; set; } = 3306;
[JsonProperty("Username")]
public string Username { get; set; } = "moonlight_user";
[JsonProperty("Password")]
public string Password { get; set; } = "s3cr3t";
[JsonProperty("Database")]
public string Database { get; set; } = "moonlight_db";
}
public class MailServerData
{
[JsonProperty("Host")] public string Host { get; set; } = "your.email.host";
[JsonProperty("Port")] public int Port { get; set; } = 465;
[JsonProperty("Email")] public string Email { get; set; } = "noreply@your.email.host";
[JsonProperty("Password")] public string Password { get; set; } = "s3cr3t";
[JsonProperty("UseSsl")] public bool UseSsl { get; set; } = true;
}
}

View file

@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Community;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Database.Entities.Tickets;
using Moonlight.App.Services;
namespace Moonlight.App.Database;
public class DataContext : DbContext
{
private readonly ConfigService ConfigService;
public DbSet<User> Users { get; set; }
//public DbSet<Ticket> Tickets { get; set; }
//public DbSet<TicketMessage> TicketMessages { get; set; }
// Store
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Service> Services { get; set; }
public DbSet<ServiceShare> ServiceShares { get; set; }
public DbSet<GiftCode> GiftCodes { get; set; }
public DbSet<GiftCodeUse> GiftCodeUses { get; set; }
public DbSet<Coupon> Coupons { get; set; }
public DbSet<CouponUse> CouponUses { get; set; }
// Community
public DbSet<Post> Posts { get; set; }
public DbSet<PostComment> PostComments { get; set; }
public DbSet<PostLike> PostLikes { get; set; }
public DbSet<WordFilter> WordFilters { get; set; }
public DataContext(ConfigService configService)
{
ConfigService = configService;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var config = ConfigService.Get().Database;
if (config.UseSqlite)
optionsBuilder.UseSqlite($"Data Source={config.SqlitePath}");
else
{
var connectionString = $"host={config.Host};" +
$"port={config.Port};" +
$"database={config.Database};" +
$"uid={config.Username};" +
$"pwd={config.Password}";
optionsBuilder.UseMySql(
connectionString,
ServerVersion.AutoDetect(connectionString),
builder => builder.EnableRetryOnFailure(5)
);
}
}
}
}

View file

@ -0,0 +1,16 @@
using Moonlight.App.Database.Enums;
namespace Moonlight.App.Database.Entities.Community;
public class Post
{
public int Id { get; set; }
public string Title { get; set; } = "";
public string Content { get; set; } = "";
public User Author { get; set; }
public PostType Type { get; set; }
public List<PostComment> Comments { get; set; } = new();
public List<PostLike> Likes { get; set; } = new();
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,10 @@
namespace Moonlight.App.Database.Entities.Community;
public class PostComment
{
public int Id { get; set; }
public string Content { get; set; } = "";
public User Author { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Database.Entities.Community;
public class PostLike
{
public int Id { get; set; }
public User User { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,7 @@
namespace Moonlight.App.Database.Entities.Community;
public class WordFilter
{
public int Id { get; set; }
public string Filter { get; set; } = "";
}

View file

@ -0,0 +1,9 @@
namespace Moonlight.App.Database.Entities.Store;
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string Slug { get; set; } = "";
}

View file

@ -0,0 +1,9 @@
namespace Moonlight.App.Database.Entities.Store;
public class Coupon
{
public int Id { get; set; }
public string Code { get; set; } = "";
public int Percent { get; set; }
public int Amount { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace Moonlight.App.Database.Entities.Store;
public class CouponUse
{
public int Id { get; set; }
public Coupon Coupon { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace Moonlight.App.Database.Entities.Store;
public class GiftCode
{
public int Id { get; set; }
public string Code { get; set; } = "";
public double Value { get; set; }
public int Amount { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace Moonlight.App.Database.Entities.Store;
public class GiftCodeUse
{
public int Id { get; set; }
public GiftCode GiftCode { get; set; }
}

View file

@ -0,0 +1,23 @@
using Moonlight.App.Database.Enums;
namespace Moonlight.App.Database.Entities.Store;
public class Product
{
public int Id { get; set; }
public Category Category { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string Slug { get; set; } = "";
public double Price { get; set; }
public int Stock { get; set; }
public int MaxPerUser { get; set; }
public int Duration { get; set; }
public ServiceType Type { get; set; }
public string ConfigJson { get; set; } = "{}";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,18 @@
namespace Moonlight.App.Database.Entities.Store;
public class Service
{
public int Id { get; set; }
public string? Nickname { get; set; }
public bool Suspended { get; set; } = false;
public Product Product { get; set; }
public string? ConfigJsonOverride { get; set; }
public User Owner { get; set; }
public List<ServiceShare> Shares { get; set; } = new();
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime RenewAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,7 @@
namespace Moonlight.App.Database.Entities.Store;
public class ServiceShare
{
public int Id { get; set; }
public User User { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Database.Entities.Store;
public class Transaction
{
public int Id { get; set; }
public double Price { get; set; }
public string Text { get; set; } = "";
}

View file

@ -0,0 +1,18 @@
using Moonlight.App.Database.Enums;
namespace Moonlight.App.Database.Entities.Tickets;
public class Ticket
{
public int Id { get; set; }
public User Creator { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string Tries { get; set; } = "";
public TicketPriority Priority { get; set; } = TicketPriority.Low;
public bool Open { get; set; } = true;
public List<TicketMessage> Messages = new();
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,11 @@
namespace Moonlight.App.Database.Entities.Tickets;
public class TicketMessage
{
public int Id { get; set; }
public User? Sender { get; set; }
public bool IsSupport { get; set; }
public string Content { get; set; } = "";
public string? Attachment { get; set; }
public DateTime CreatedAt { get; set; }
}

View file

@ -0,0 +1,28 @@
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Database.Entities;
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string? Avatar { get; set; } = null;
public string? TotpKey { get; set; } = null;
// Store
public double Balance { get; set; }
public List<Transaction> Transactions { get; set; } = new();
public List<CouponUse> CouponUses { get; set; } = new();
public List<GiftCodeUse> GiftCodeUses { get; set; } = new();
// Meta data
public string Flags { get; set; } = "";
public int Permissions { get; set; } = 0;
// Timestamps
public DateTime TokenValidTimestamp { get; set; } = DateTime.UtcNow.AddMinutes(-10);
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Database.Enums;
public enum PostType
{
Project = 0,
Announcement = 1,
Event = 2
}

View file

@ -0,0 +1,9 @@
namespace Moonlight.App.Database.Enums;
public enum ServiceType
{
Server,
Webspace,
Database,
Domain
}

View file

@ -0,0 +1,9 @@
namespace Moonlight.App.Database.Enums;
public enum TicketPriority
{
Low,
Medium,
High,
Critical
}

View file

@ -0,0 +1,67 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231013200303_AddedUser")]
partial class AddedUser
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,43 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Username = table.Column<string>(type: "TEXT", nullable: false),
Email = table.Column<string>(type: "TEXT", nullable: false),
Password = table.Column<string>(type: "TEXT", nullable: false),
Avatar = table.Column<string>(type: "TEXT", nullable: true),
TotpKey = table.Column<string>(type: "TEXT", nullable: true),
Flags = table.Column<string>(type: "TEXT", nullable: false),
Permissions = table.Column<int>(type: "INTEGER", nullable: false),
TokenValidTimestamp = table.Column<DateTime>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Users");
}
}
}

View file

@ -0,0 +1,341 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231017075519_AddStoreModels")]
partial class AddStoreModels
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId");
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,245 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddStoreModels : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Categories",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: false),
Slug = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Categories", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Coupons",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Code = table.Column<string>(type: "TEXT", nullable: false),
Percent = table.Column<int>(type: "INTEGER", nullable: false),
Amount = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Coupons", x => x.Id);
});
migrationBuilder.CreateTable(
name: "GiftCodes",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Code = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<double>(type: "REAL", nullable: false),
Amount = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GiftCodes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Products",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CategoryId = table.Column<int>(type: "INTEGER", nullable: true),
Name = table.Column<string>(type: "TEXT", nullable: false),
Description = table.Column<string>(type: "TEXT", nullable: false),
Slug = table.Column<string>(type: "TEXT", nullable: false),
Price = table.Column<double>(type: "REAL", nullable: false),
Stock = table.Column<int>(type: "INTEGER", nullable: false),
MaxPerUser = table.Column<int>(type: "INTEGER", nullable: false),
Duration = table.Column<int>(type: "INTEGER", nullable: false),
Type = table.Column<int>(type: "INTEGER", nullable: false),
ConfigJson = table.Column<string>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.Id);
table.ForeignKey(
name: "FK_Products_Categories_CategoryId",
column: x => x.CategoryId,
principalTable: "Categories",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "CouponUses",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CouponId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CouponUses", x => x.Id);
table.ForeignKey(
name: "FK_CouponUses_Coupons_CouponId",
column: x => x.CouponId,
principalTable: "Coupons",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "GiftCodeUses",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GiftCodeId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_GiftCodeUses", x => x.Id);
table.ForeignKey(
name: "FK_GiftCodeUses_GiftCodes_GiftCodeId",
column: x => x.GiftCodeId,
principalTable: "GiftCodes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Services",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Nickname = table.Column<string>(type: "TEXT", nullable: true),
Suspended = table.Column<bool>(type: "INTEGER", nullable: false),
ProductId = table.Column<int>(type: "INTEGER", nullable: false),
ConfigJsonOverride = table.Column<string>(type: "TEXT", nullable: true),
OwnerId = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
RenewAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Services", x => x.Id);
table.ForeignKey(
name: "FK_Services_Products_ProductId",
column: x => x.ProductId,
principalTable: "Products",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Services_Users_OwnerId",
column: x => x.OwnerId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ServiceShares",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<int>(type: "INTEGER", nullable: false),
ServiceId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceShares", x => x.Id);
table.ForeignKey(
name: "FK_ServiceShares_Services_ServiceId",
column: x => x.ServiceId,
principalTable: "Services",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ServiceShares_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_CouponUses_CouponId",
table: "CouponUses",
column: "CouponId");
migrationBuilder.CreateIndex(
name: "IX_GiftCodeUses_GiftCodeId",
table: "GiftCodeUses",
column: "GiftCodeId");
migrationBuilder.CreateIndex(
name: "IX_Products_CategoryId",
table: "Products",
column: "CategoryId");
migrationBuilder.CreateIndex(
name: "IX_Services_OwnerId",
table: "Services",
column: "OwnerId");
migrationBuilder.CreateIndex(
name: "IX_Services_ProductId",
table: "Services",
column: "ProductId");
migrationBuilder.CreateIndex(
name: "IX_ServiceShares_ServiceId",
table: "ServiceShares",
column: "ServiceId");
migrationBuilder.CreateIndex(
name: "IX_ServiceShares_UserId",
table: "ServiceShares",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CouponUses");
migrationBuilder.DropTable(
name: "GiftCodeUses");
migrationBuilder.DropTable(
name: "ServiceShares");
migrationBuilder.DropTable(
name: "Coupons");
migrationBuilder.DropTable(
name: "GiftCodes");
migrationBuilder.DropTable(
name: "Services");
migrationBuilder.DropTable(
name: "Products");
migrationBuilder.DropTable(
name: "Categories");
}
}
}

View file

@ -0,0 +1,371 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231018203522_AddedUserStoreData")]
partial class AddedUserStoreData
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.HasIndex("UserId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.HasIndex("UserId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<double>("Balance")
.HasColumnType("REAL");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("CouponUses")
.HasForeignKey("UserId");
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("GiftCodeUses")
.HasForeignKey("UserId");
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Navigation("CouponUses");
b.Navigation("GiftCodeUses");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,130 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedUserStoreData : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Products_Categories_CategoryId",
table: "Products");
migrationBuilder.AddColumn<double>(
name: "Balance",
table: "Users",
type: "REAL",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AlterColumn<int>(
name: "CategoryId",
table: "Products",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER",
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "UserId",
table: "GiftCodeUses",
type: "INTEGER",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "UserId",
table: "CouponUses",
type: "INTEGER",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_GiftCodeUses_UserId",
table: "GiftCodeUses",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_CouponUses_UserId",
table: "CouponUses",
column: "UserId");
migrationBuilder.AddForeignKey(
name: "FK_CouponUses_Users_UserId",
table: "CouponUses",
column: "UserId",
principalTable: "Users",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_GiftCodeUses_Users_UserId",
table: "GiftCodeUses",
column: "UserId",
principalTable: "Users",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_Products_Categories_CategoryId",
table: "Products",
column: "CategoryId",
principalTable: "Categories",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_CouponUses_Users_UserId",
table: "CouponUses");
migrationBuilder.DropForeignKey(
name: "FK_GiftCodeUses_Users_UserId",
table: "GiftCodeUses");
migrationBuilder.DropForeignKey(
name: "FK_Products_Categories_CategoryId",
table: "Products");
migrationBuilder.DropIndex(
name: "IX_GiftCodeUses_UserId",
table: "GiftCodeUses");
migrationBuilder.DropIndex(
name: "IX_CouponUses_UserId",
table: "CouponUses");
migrationBuilder.DropColumn(
name: "Balance",
table: "Users");
migrationBuilder.DropColumn(
name: "UserId",
table: "GiftCodeUses");
migrationBuilder.DropColumn(
name: "UserId",
table: "CouponUses");
migrationBuilder.AlterColumn<int>(
name: "CategoryId",
table: "Products",
type: "INTEGER",
nullable: true,
oldClrType: typeof(int),
oldType: "INTEGER");
migrationBuilder.AddForeignKey(
name: "FK_Products_Categories_CategoryId",
table: "Products",
column: "CategoryId",
principalTable: "Categories",
principalColumn: "Id");
}
}
}

View file

@ -0,0 +1,403 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231018204737_AddedTransactions")]
partial class AddedTransactions
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.HasIndex("UserId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.HasIndex("UserId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Transaction");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<double>("Balance")
.HasColumnType("REAL");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("CouponUses")
.HasForeignKey("UserId");
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("GiftCodeUses")
.HasForeignKey("UserId");
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("Transactions")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Navigation("CouponUses");
b.Navigation("GiftCodeUses");
b.Navigation("Transactions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,46 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedTransactions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Transaction",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Price = table.Column<double>(type: "REAL", nullable: false),
Text = table.Column<string>(type: "TEXT", nullable: false),
UserId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Transaction", x => x.Id);
table.ForeignKey(
name: "FK_Transaction_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_Transaction_UserId",
table: "Transaction",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Transaction");
}
}
}

View file

@ -0,0 +1,539 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231027105412_AddPostsModels")]
partial class AddPostsModels
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.ToTable("Posts");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("PostId");
b.ToTable("PostComments");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PostId");
b.HasIndex("UserId");
b.ToTable("PostLikes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.HasIndex("UserId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.HasIndex("UserId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Transaction");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<double>("Balance")
.HasColumnType("REAL");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Comments")
.HasForeignKey("PostId");
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Likes")
.HasForeignKey("PostId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("CouponUses")
.HasForeignKey("UserId");
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("GiftCodeUses")
.HasForeignKey("UserId");
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("Transactions")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Navigation("Comments");
b.Navigation("Likes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Navigation("CouponUses");
b.Navigation("GiftCodeUses");
b.Navigation("Transactions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,131 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddPostsModels : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Posts",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Title = table.Column<string>(type: "TEXT", nullable: false),
Content = table.Column<string>(type: "TEXT", nullable: false),
AuthorId = table.Column<int>(type: "INTEGER", nullable: false),
Type = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Posts", x => x.Id);
table.ForeignKey(
name: "FK_Posts_Users_AuthorId",
column: x => x.AuthorId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PostComments",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Content = table.Column<string>(type: "TEXT", nullable: false),
AuthorId = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
PostId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PostComments", x => x.Id);
table.ForeignKey(
name: "FK_PostComments_Posts_PostId",
column: x => x.PostId,
principalTable: "Posts",
principalColumn: "Id");
table.ForeignKey(
name: "FK_PostComments_Users_AuthorId",
column: x => x.AuthorId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "PostLikes",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
PostId = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PostLikes", x => x.Id);
table.ForeignKey(
name: "FK_PostLikes_Posts_PostId",
column: x => x.PostId,
principalTable: "Posts",
principalColumn: "Id");
table.ForeignKey(
name: "FK_PostLikes_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_PostComments_AuthorId",
table: "PostComments",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_PostComments_PostId",
table: "PostComments",
column: "PostId");
migrationBuilder.CreateIndex(
name: "IX_PostLikes_PostId",
table: "PostLikes",
column: "PostId");
migrationBuilder.CreateIndex(
name: "IX_PostLikes_UserId",
table: "PostLikes",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Posts_AuthorId",
table: "Posts",
column: "AuthorId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PostComments");
migrationBuilder.DropTable(
name: "PostLikes");
migrationBuilder.DropTable(
name: "Posts");
}
}
}

View file

@ -0,0 +1,554 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20231028214520_AddedWordFilter")]
partial class AddedWordFilter
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.ToTable("Posts");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("PostId");
b.ToTable("PostComments");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PostId");
b.HasIndex("UserId");
b.ToTable("PostLikes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.WordFilter", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Filter")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("WordFilters");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.HasIndex("UserId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.HasIndex("UserId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Transaction");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<double>("Balance")
.HasColumnType("REAL");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Comments")
.HasForeignKey("PostId");
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Likes")
.HasForeignKey("PostId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("CouponUses")
.HasForeignKey("UserId");
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("GiftCodeUses")
.HasForeignKey("UserId");
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("Transactions")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Navigation("Comments");
b.Navigation("Likes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Navigation("CouponUses");
b.Navigation("GiftCodeUses");
b.Navigation("Transactions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedWordFilter : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "WordFilters",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Filter = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_WordFilters", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "WordFilters");
}
}
}

View file

@ -0,0 +1,551 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
partial class DataContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.ToTable("Posts");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AuthorId")
.HasColumnType("INTEGER");
b.Property<string>("Content")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.HasIndex("PostId");
b.ToTable("PostComments");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<int?>("PostId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("PostId");
b.HasIndex("UserId");
b.ToTable("PostLikes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.WordFilter", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Filter")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("WordFilters");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Category", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Categories");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Coupon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Percent")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("Coupons");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CouponId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CouponId");
b.HasIndex("UserId");
b.ToTable("CouponUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCode", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Amount")
.HasColumnType("INTEGER");
b.Property<string>("Code")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Value")
.HasColumnType("REAL");
b.HasKey("Id");
b.ToTable("GiftCodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("GiftCodeId")
.HasColumnType("INTEGER");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GiftCodeId");
b.HasIndex("UserId");
b.ToTable("GiftCodeUses");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CategoryId")
.HasColumnType("INTEGER");
b.Property<string>("ConfigJson")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Duration")
.HasColumnType("INTEGER");
b.Property<int>("MaxPerUser")
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Stock")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CategoryId");
b.ToTable("Products");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConfigJsonOverride")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Nickname")
.HasColumnType("TEXT");
b.Property<int>("OwnerId")
.HasColumnType("INTEGER");
b.Property<int>("ProductId")
.HasColumnType("INTEGER");
b.Property<DateTime>("RenewAt")
.HasColumnType("TEXT");
b.Property<bool>("Suspended")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("ProductId");
b.ToTable("Services");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int?>("ServiceId")
.HasColumnType("INTEGER");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("UserId");
b.ToTable("ServiceShares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<double>("Price")
.HasColumnType("REAL");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Transaction");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Avatar")
.HasColumnType("TEXT");
b.Property<double>("Balance")
.HasColumnType("REAL");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Flags")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Permissions")
.HasColumnType("INTEGER");
b.Property<DateTime>("TokenValidTimestamp")
.HasColumnType("TEXT");
b.Property<string>("TotpKey")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostComment", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Author")
.WithMany()
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Comments")
.HasForeignKey("PostId");
b.Navigation("Author");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.PostLike", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Community.Post", null)
.WithMany("Likes")
.HasForeignKey("PostId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.CouponUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Coupon", "Coupon")
.WithMany()
.HasForeignKey("CouponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("CouponUses")
.HasForeignKey("UserId");
b.Navigation("Coupon");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.GiftCodeUse", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.GiftCode", "GiftCode")
.WithMany()
.HasForeignKey("GiftCodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("GiftCodeUses")
.HasForeignKey("UserId");
b.Navigation("GiftCode");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Product", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Category", "Category")
.WithMany()
.HasForeignKey("CategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Category");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Store.Product", "Product")
.WithMany()
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("Product");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.ServiceShare", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Store.Service", null)
.WithMany("Shares")
.HasForeignKey("ServiceId");
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Transaction", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", null)
.WithMany("Transactions")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Community.Post", b =>
{
b.Navigation("Comments");
b.Navigation("Likes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Store.Service", b =>
{
b.Navigation("Shares");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Navigation("CouponUses");
b.Navigation("GiftCodeUses");
b.Navigation("Transactions");
});
#pragma warning restore 612, 618
}
}
}

View file

@ -0,0 +1,9 @@
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Event.Args;
public class MailVerificationEventArgs
{
public User User { get; set; }
public string Jwt { get; set; }
}

View file

@ -0,0 +1,10 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Event.Args;
public class TransactionCreatedEventArgs
{
public Transaction Transaction { get; set; }
public User User { get; set; }
}

View file

@ -0,0 +1,22 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Community;
using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Event.Args;
namespace Moonlight.App.Event;
public class Events
{
public static EventHandler<User> OnUserRegistered;
public static EventHandler<User> OnUserPasswordChanged;
public static EventHandler<User> OnUserTotpSet;
public static EventHandler<MailVerificationEventArgs> OnUserMailVerify;
public static EventHandler<Service> OnServiceOrdered;
public static EventHandler<TransactionCreatedEventArgs> OnTransactionCreated;
public static EventHandler<Post> OnPostCreated;
public static EventHandler<Post> OnPostUpdated;
public static EventHandler<Post> OnPostDeleted;
public static EventHandler<Post> OnPostLiked;
public static EventHandler<PostComment> OnPostCommentCreated;
public static EventHandler<PostComment> OnPostCommentDeleted;
}

View file

@ -0,0 +1,16 @@
namespace Moonlight.App.Exceptions;
public class DisplayException : Exception
{
public DisplayException()
{
}
public DisplayException(string message) : base(message)
{
}
public DisplayException(string message, Exception inner) : base(message, inner)
{
}
}

View file

@ -0,0 +1,20 @@
using Moonlight.App.Models.Enums;
namespace Moonlight.App.Extensions.Attributes;
public class RequirePermissionAttribute : Attribute
{
public int PermissionInteger = 0;
public RequirePermissionAttribute(){}
public RequirePermissionAttribute(int perms)
{
PermissionInteger = perms;
}
public RequirePermissionAttribute(Permission permission)
{
PermissionInteger = (int)permission;
}
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Extensions.Attributes;
public class SelectorAttribute : Attribute
{
public string SelectorProp { get; set; } = "";
public string DisplayProp { get; set; } = "";
public bool UseDropdown { get; set; } = false;
}

View file

@ -0,0 +1,13 @@
using System.Text;
namespace Moonlight.App.Extensions;
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddJsonString(this IConfigurationBuilder configurationBuilder, string json)
{
var bytes = Encoding.UTF8.GetBytes(json);
var stream = new MemoryStream(bytes);
return configurationBuilder.AddJsonStream(stream);
}
}

View file

@ -0,0 +1,37 @@
namespace Moonlight.App.Extensions;
public static class EventHandlerExtensions
{
public static async Task InvokeAsync(this EventHandler handler)
{
var tasks = handler
.GetInvocationList()
.Select(x => new Task(() => x.DynamicInvoke(null, null)))
.ToArray();
foreach (var task in tasks)
{
task.Start();
}
await Task.WhenAll(tasks);
}
public static async Task InvokeAsync<T>(this EventHandler<T>? handler, T? data = default(T))
{
if(handler == null)
return;
var tasks = handler
.GetInvocationList()
.Select(x => new Task(() => x.DynamicInvoke(null, data)))
.ToArray();
foreach (var task in tasks)
{
task.Start();
}
await Task.WhenAll(tasks);
}
}

View file

@ -0,0 +1,17 @@
using System.Linq.Expressions;
using System.Reflection;
namespace Moonlight.App.Extensions;
public static class TypeExtensions
{
public static PropertyInfo? GetProperty<T, TValue>(this T type, Expression<Func<T, TValue>> selector)
where T : class
{
Expression expression = selector.Body;
return expression.NodeType == ExpressionType.MemberAccess
? (PropertyInfo) ((MemberExpression) expression).Member
: null;
}
}

View file

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Helpers;
public static class ComponentHelper
{
public static RenderFragment FromType(Type type) => builder =>
{
builder.OpenComponent(0, type);
builder.CloseComponent();
};
}

View file

@ -0,0 +1,280 @@
using System.Text;
using Microsoft.AspNetCore.Components;
namespace Moonlight.App.Helpers;
public static class Formatter
{
public static string GenerateString(int length)
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var stringBuilder = new StringBuilder();
var random = new Random();
for (int i = 0; i < length; i++)
{
stringBuilder.Append(chars[random.Next(chars.Length)]);
}
return stringBuilder.ToString();
}
public static string IntToStringWithLeadingZeros(int number, int n)
{
string result = number.ToString();
int length = result.Length;
for (int i = length; i < n; i++)
{
result = "0" + result;
}
return result;
}
public static string CapitalizeFirstCharacter(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}
char firstChar = char.ToUpper(input[0]);
string restOfString = input.Substring(1);
return firstChar + restOfString;
}
public static string CutInHalf(string input)
{
if (string.IsNullOrEmpty(input))
return input;
int length = input.Length;
int halfLength = length / 2;
return input.Substring(0, halfLength);
}
public static bool EndsInOneOf(string suffix, IEnumerable<string> strings)
{
foreach (string str in strings)
{
if (suffix.EndsWith(str))
{
return true;
}
}
return false;
}
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings, out string foundText)
{
foreach (string str in strings)
{
if (textToSearch.Contains(str))
{
foundText = str;
return true;
}
}
foundText = "";
return false;
}
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings)
{
return ContainsOneOf(textToSearch, strings, out _);
}
public static string FormatSize(long bytes)
{
var i = Math.Abs(bytes) / 1024D;
if (i < 1)
{
return bytes + " B";
}
else if (i / 1024D < 1)
{
return i.Round(2) + " KB";
}
else if (i / (1024D * 1024D) < 1)
{
return (i / 1024D).Round(2) + " MB";
}
else
{
return (i / (1024D * 1024D)).Round(2) + " GB";
}
}
private static double Round(this double d, int decimals)
{
return Math.Round(d, decimals);
}
public static string ReplaceEnd(string input, string substringToReplace, string newSubstring)
{
int lastIndexOfSubstring = input.LastIndexOf(substringToReplace);
if (lastIndexOfSubstring >= 0)
{
input = input.Remove(lastIndexOfSubstring, substringToReplace.Length)
.Insert(lastIndexOfSubstring, newSubstring);
}
return input;
}
public static string ConvertCamelCaseToSpaces(string input)
{
StringBuilder output = new StringBuilder();
foreach (char c in input)
{
if (char.IsUpper(c))
{
output.Append(' ');
}
output.Append(c);
}
return output.ToString().Trim();
}
public static string FormatUptime(double uptime)
{
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
return FormatUptime(t);
}
public static string FormatUptime(TimeSpan t)
{
if (t.Days > 0)
{
return $"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s";
}
else
{
return $"{t.Hours}h {t.Minutes}m {t.Seconds}s";
}
}
public static string FormatDate(DateTime e)
{
string i2s(int i)
{
if (i.ToString().Length < 2)
return "0" + i;
return i.ToString();
}
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year} {i2s(e.Hour)}:{i2s(e.Minute)}";
}
public static string FormatDateOnly(DateTime e)
{
string i2s(int i)
{
if (i.ToString().Length < 2)
return "0" + i;
return i.ToString();
}
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year}";
}
public static string FormatSize(double bytes)
{
var i = Math.Abs(bytes) / 1024D;
if (i < 1)
{
return bytes + " B";
}
else if (i / 1024D < 1)
{
return i.Round(2) + " KB";
}
else if (i / (1024D * 1024D) < 1)
{
return (i / 1024D).Round(2) + " MB";
}
else
{
return (i / (1024D * 1024D)).Round(2) + " GB";
}
}
public static RenderFragment FormatLineBreaks(string content)
{
return builder =>
{
int i = 0;
var arr = content.Split("\n");
foreach (var line in arr)
{
builder.AddContent(i, line);
if (i++ != arr.Length - 1)
{
builder.AddMarkupContent(i, "<br/>");
}
}
};
}
public static string FormatAgoFromDateTime(DateTime dt)
{
TimeSpan timeSince = DateTime.UtcNow.Subtract(dt);
if (timeSince.TotalMilliseconds < 1)
return "just now";
if (timeSince.TotalMinutes < 1)
return "less than a minute ago";
if (timeSince.TotalMinutes < 2)
return "1 minute ago";
if (timeSince.TotalMinutes < 60)
return Math.Round(timeSince.TotalMinutes) + " minutes ago";
if (timeSince.TotalHours < 2)
return "1 hour ago";
if (timeSince.TotalHours < 24)
return Math.Round(timeSince.TotalHours) + " hours ago";
if (timeSince.TotalDays < 2)
return "1 day ago";
return Math.Round(timeSince.TotalDays) + " days ago";
}
// This will replace every placeholder with the respective value if specified in the model
// For example:
// A instance of the user model has been passed in the 'models' parameter of the function.
// So the placeholder {{User.Email}} will be replaced by the value of the Email property of the model
public static string ProcessTemplating(string text, params object[] models)
{
foreach (var model in models)
{
foreach (var property in model.GetType().GetProperties())
{
var value = property.GetValue(model);
if(value == null)
continue;
var placeholder = "{{" + $"{model.GetType().Name}.{property.Name}" + "}}";
text = text.Replace(placeholder, value.ToString());
}
}
return text;
}
}

View file

@ -0,0 +1,172 @@
using System.Security.Cryptography;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
namespace Moonlight.App.Helpers;
// Src: https://codereview.stackexchange.com/questions/176697/net-core-mvc-future-proof-hashing-of-passwords
public static class HashHelper
{
/// <summary>
/// The default number of Iterations
/// </summary>
private const int DefaultIterations = 10000;
/// <summary>
/// Provides Information about a specific Hash Version
/// </summary>
private class HashVersion
{
public short Version { get; set; }
public int SaltSize { get; set; }
public int HashSize { get; set; }
public KeyDerivationPrf KeyDerivation { get; set; }
}
/// <summary>
/// Holds all possible Hash Versions
/// </summary>
private static readonly Dictionary<short, HashVersion> _versions = new Dictionary<short, HashVersion>
{
{
1, new HashVersion
{
Version = 1,
KeyDerivation = KeyDerivationPrf.HMACSHA512,
HashSize = 256 / 8,
SaltSize = 128 / 8
}
}
};
/// <summary>
/// The default Hash Version, which should be used, if a new Hash is Created
/// </summary>
private static HashVersion DefaultVersion => _versions[1];
/// <summary>
/// Checks if a given hash uses the latest version
/// </summary>
/// <param name="data">The hash</param>
/// <returns>Is the hash of the latest version?</returns>
public static bool IsLatestHashVersion(byte[] data)
{
var version = BitConverter.ToInt16(data, 0);
return version == DefaultVersion.Version;
}
/// <summary>
/// Checks if a given hash uses the latest version
/// </summary>
/// <param name="data">The hash</param>
/// <returns>Is the hash of the latest version?</returns>
public static bool IsLatestHashVersion(string data)
{
var dataBytes = Convert.FromBase64String(data);
return IsLatestHashVersion(dataBytes);
}
/// <summary>
/// Gets a random byte array
/// </summary>
/// <param name="length">The length of the byte array</param>
/// <returns>The random byte array</returns>
public static byte[] GetRandomBytes(int length)
{
var data = new byte[length];
using (var randomNumberGenerator = RandomNumberGenerator.Create())
{
randomNumberGenerator.GetBytes(data);
}
return data;
}
/// <summary>
/// Creates a Hash of a clear text
/// </summary>
/// <param name="clearText">the clear text</param>
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
/// <returns>the Hash</returns>
public static byte[] Hash(string clearText, int iterations = DefaultIterations)
{
//get current version
var currentVersion = DefaultVersion;
//get the byte arrays of the hash and meta information
var saltBytes = GetRandomBytes(currentVersion.SaltSize);
var versionBytes = BitConverter.GetBytes(currentVersion.Version);
var iterationBytes = BitConverter.GetBytes(iterations);
var hashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iterations,
currentVersion.HashSize);
//calculate the indexes for the combined hash
var indexVersion = 0;
var indexIteration = indexVersion + 2;
var indexSalt = indexIteration + 4;
var indexHash = indexSalt + currentVersion.SaltSize;
//combine all data to one result hash
var resultBytes = new byte[2 + 4 + currentVersion.SaltSize + currentVersion.HashSize];
Array.Copy(versionBytes, 0, resultBytes, indexVersion, 2);
Array.Copy(iterationBytes, 0, resultBytes, indexIteration, 4);
Array.Copy(saltBytes, 0, resultBytes, indexSalt, currentVersion.SaltSize);
Array.Copy(hashBytes, 0, resultBytes, indexHash, currentVersion.HashSize);
return resultBytes;
}
/// <summary>
/// Creates a Hash of a clear text and convert it to a Base64 String representation
/// </summary>
/// <param name="clearText">the clear text</param>
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
/// <returns>the Hash</returns>
public static string HashToString(string clearText, int iterations = DefaultIterations)
{
var data = Hash(clearText, iterations);
return Convert.ToBase64String(data);
}
/// <summary>
/// Verifies a given clear Text against a hash
/// </summary>
/// <param name="clearText">The clear text</param>
/// <param name="data">The hash</param>
/// <returns>Is the hash equal to the clear text?</returns>
public static bool Verify(string clearText, byte[] data)
{
//Get the current version and number of iterations
var currentVersion = _versions[BitConverter.ToInt16(data, 0)];
var iteration = BitConverter.ToInt32(data, 2);
//Create the byte arrays for the salt and hash
var saltBytes = new byte[currentVersion.SaltSize];
var hashBytes = new byte[currentVersion.HashSize];
//Calculate the indexes of the salt and the hash
var indexSalt = 2 + 4; // Int16 (Version) and Int32 (Iteration)
var indexHash = indexSalt + currentVersion.SaltSize;
//Fill the byte arrays with salt and hash
Array.Copy(data, indexSalt, saltBytes, 0, currentVersion.SaltSize);
Array.Copy(data, indexHash, hashBytes, 0, currentVersion.HashSize);
//Hash the current clearText with the parameters given via the data
var verificationHashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iteration,
currentVersion.HashSize);
//Check if generated hashes are equal
return hashBytes.SequenceEqual(verificationHashBytes);
}
/// <summary>
/// Verifies a given clear Text against a hash
/// </summary>
/// <param name="clearText">The clear text</param>
/// <param name="data">The hash</param>
/// <returns>Is the hash equal to the clear text?</returns>
public static bool Verify(string clearText, string data)
{
var dataBytes = Convert.FromBase64String(data);
return Verify(clearText, dataBytes);
}
}

View file

@ -0,0 +1,11 @@
namespace Moonlight.App.Helpers.LogMigrator;
public class LogMigrateProvider : ILoggerProvider
{
public void Dispose() {}
public ILogger CreateLogger(string categoryName)
{
return new MigrateLogger();
}
}

View file

@ -0,0 +1,53 @@
namespace Moonlight.App.Helpers.LogMigrator;
public class MigrateLogger : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
switch (logLevel)
{
case LogLevel.Critical:
Logger.Fatal(formatter(state, exception));
if(exception != null)
Logger.Fatal(exception);
break;
case LogLevel.Warning:
Logger.Warn(formatter(state, exception));
if(exception != null)
Logger.Warn(exception);
break;
case LogLevel.Debug:
Logger.Debug(formatter(state, exception));
if(exception != null)
Logger.Debug(exception);
break;
case LogLevel.Error:
Logger.Error(formatter(state, exception));
if(exception != null)
Logger.Error(exception);
break;
case LogLevel.Information:
Logger.Info(formatter(state, exception));
if(exception != null)
Logger.Info(exception);
break;
}
}
}

View file

@ -0,0 +1,112 @@
using System.Diagnostics;
using System.Reflection;
using Serilog;
namespace Moonlight.App.Helpers;
public class Logger
{
#region String logger
public static void Verbose(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Verbose("{Message}", message);
}
public static void Info(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Information("{Message}", message);
}
public static void Debug(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Debug("{Message}", message);
}
public static void Error(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Error("{Message}", message);
}
public static void Warn(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Warning("{Message}", message);
}
public static void Fatal(string message, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Fatal("{Message}", message);
}
#endregion
#region Exception method calls
public static void Verbose(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Verbose(exception, "");
}
public static void Info(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Information(exception, "");
}
public static void Debug(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Debug(exception, "");
}
public static void Error(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Error(exception, "");
}
public static void Warn(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Warning(exception, "");
}
public static void Fatal(Exception exception, string channel = "default")
{
Log.ForContext("SourceContext", GetNameOfCallingClass())
.Fatal(exception, "");
}
#endregion
private static string GetNameOfCallingClass(int skipFrames = 4)
{
string fullName;
Type declaringType;
do
{
MethodBase method = new StackFrame(skipFrames, false).GetMethod();
declaringType = method.DeclaringType;
if (declaringType == null)
{
return method.Name;
}
skipFrames++;
if (declaringType.Name.Contains("<"))
fullName = declaringType.ReflectedType.Name;
else
fullName = declaringType.Name;
} while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase) |
fullName.Contains("Logger"));
return fullName;
}
}

View file

@ -0,0 +1,34 @@
namespace Moonlight.App.Helpers;
public static class PathBuilder
{
public static string Dir(params string[] parts)
{
var res = "";
foreach (var part in parts)
{
res += part + Path.DirectorySeparatorChar;
}
return res.Replace(
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
$"{Path.DirectorySeparatorChar}"
);
}
public static string File(params string[] parts)
{
var res = "";
foreach (var part in parts)
{
res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar);
}
return res.Replace(
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
$"{Path.DirectorySeparatorChar}"
);
}
}

View file

@ -0,0 +1,57 @@
using System.Reflection;
namespace Moonlight.App.Helpers;
public class PropBinder<T>
{
private PropertyInfo PropertyInfo;
private object DataObject;
public PropBinder(PropertyInfo propertyInfo, object dataObject)
{
PropertyInfo = propertyInfo;
DataObject = dataObject;
}
public string StringValue
{
get => (string)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public int IntValue
{
get => (int)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public long LongValue
{
get => (long)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public bool BoolValue
{
get => (bool)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public DateTime DateTimeValue
{
get => (DateTime)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public T Class
{
get => (T)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
public double DoubleValue
{
get => (double)PropertyInfo.GetValue(DataObject)!;
set => PropertyInfo.SetValue(DataObject, value);
}
}

View file

@ -0,0 +1,64 @@
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Enums;
using Moonlight.App.Repositories;
using Moonlight.App.Services;
using Moonlight.App.Services.Utils;
namespace Moonlight.App.Http.Controllers.Api.Auth;
[ApiController]
[Route("api/auth/reset")]
public class ResetController : Controller
{
private readonly Repository<User> UserRepository;
private readonly IdentityService IdentityService;
private readonly JwtService JwtService;
public ResetController(Repository<User> userRepository, IdentityService identityService, JwtService jwtService)
{
UserRepository = userRepository;
IdentityService = identityService;
JwtService = jwtService;
}
[HttpGet]
public async Task<ActionResult> Get([FromQuery] string token)
{
// Validate token
if (!await JwtService.Validate(token))
return Redirect("/password-reset");
var data = await JwtService.Decode(token);
if (!data.ContainsKey("accountToReset"))
return Redirect("/password-reset");
var userId = int.Parse(data["accountToReset"]);
var user = UserRepository
.Get()
.FirstOrDefault(x => x.Id == userId);
// User may have been deleted, so we check here
if (user == null)
return Redirect("/password-reset");
// In order to allow the user to get access to the change password screen
// we need to authenticate him so we can read his flags.
// That's why we are creating a session here
var sessionToken = await IdentityService.GenerateToken(user);
// Authenticate the current identity service instance in order to
// get access to the flags field.
await IdentityService.Authenticate(sessionToken);
IdentityService.Flags[UserFlag.PasswordPending] = true;
await IdentityService.SaveFlags();
// Make the user login so he can reach the change password screen
Response.Cookies.Append("token", sessionToken);
return Redirect("/");
}
}

View file

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Enums;
using Moonlight.App.Services;
using Moonlight.App.Services.Utils;
namespace Moonlight.App.Http.Controllers.Api.Auth;
[ApiController]
[Route("api/auth/verify")]
public class VerifyController : Controller
{
private readonly IdentityService IdentityService;
private readonly JwtService JwtService;
public VerifyController(IdentityService identityService, JwtService jwtService)
{
IdentityService = identityService;
JwtService = jwtService;
}
[HttpGet]
public async Task<ActionResult> Get([FromQuery] string token)
{
await IdentityService.Authenticate(Request);
if (!IdentityService.IsSignedIn)
return Redirect("/login");
if (!await JwtService.Validate(token))
return Redirect("/login");
var data = await JwtService.Decode(token);
if (!data.ContainsKey("mailToVerify"))
return Redirect("/login");
var mailToVerify = data["mailToVerify"];
if (mailToVerify != IdentityService.CurrentUser.Email)
{
Logger.Warn($"User {IdentityService.CurrentUser.Email} tried to mail verify {mailToVerify} via verify api endpoint", "security");
return Redirect("/login");
}
IdentityService.Flags[UserFlag.MailVerified] = true;
await IdentityService.SaveFlags();
return Redirect("/");
}
}

View file

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Helpers;
using Moonlight.App.Services;
namespace Moonlight.App.Http.Controllers.Api;
[ApiController]
[Route("api/bucket")]
public class BucketController : Controller
{
private readonly BucketService BucketService;
public BucketController(BucketService bucketService)
{
BucketService = bucketService;
}
[HttpGet("{bucket}/{file}")]
public async Task<ActionResult> Get([FromRoute] string bucket, [FromRoute] string file) // TODO: Implement auth
{
if (bucket.Contains("..") || file.Contains(".."))
{
Logger.Warn($"Detected path transversal attack ({Request.HttpContext.Connection.RemoteIpAddress}).", "security");
return NotFound();
}
try
{
var stream = await BucketService.Pull(bucket, file);
return File(stream, MimeTypes.GetMimeType(file));
}
catch (FileNotFoundException)
{
return NotFound();
}
}
}

View file

@ -0,0 +1,59 @@
using System.Net.WebSockets;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Api;
namespace Moonlight.App.Http.Controllers.Api;
public class WebsocketController : Controller
{
private readonly ApiManagementService ApiManagementService;
public WebsocketController(ApiManagementService apiManagementService)
{
ApiManagementService = apiManagementService;
}
[Route("/api/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using (var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync())
{
await Echo(webSocket);
}
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
public async Task Echo(WebSocket webSocket)
{
var context = new ApiUserContext(webSocket);
ApiManagementService.Contexts.Add(context);
await webSocket.SendAsync(Encoding.UTF8.GetBytes("Hello World"), WebSocketMessageType.Text,
true, CancellationToken.None);
try
{
while (webSocket.State == WebSocketState.Open)
{
var buffer = new byte[1024 * 10];
var data = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
buffer = buffer[..data.Count];
await ApiManagementService.HandleRequest(context, buffer);
}
}
catch (Exception)
{
}
ApiManagementService.Contexts.Remove(context);
}
}

View file

@ -0,0 +1,50 @@
using Moonlight.App.Models.Enums;
namespace Moonlight.App.Models.Abstractions;
public class FlagStorage
{
private readonly List<string> FlagList;
public UserFlag[] Flags => FlagList
.Select(x => Enum.Parse(typeof(UserFlag), x))
.Select(x => (UserFlag)x)
.ToArray();
public string[] RawFlags => FlagList.ToArray();
public string RawFlagString => string.Join(";", FlagList);
public bool this[UserFlag flag]
{
get => Flags.Contains(flag);
set => Set(flag.ToString(), value);
}
public bool this[string flagName]
{
get => FlagList.Contains(flagName);
set => Set(flagName, value);
}
public FlagStorage(string flagString)
{
FlagList = flagString
.Split(";")
.Where(x => !string.IsNullOrEmpty(x))
.ToList();
}
public void Set(string flagName, bool shouldAdd)
{
if (shouldAdd)
{
if(!FlagList.Contains(flagName))
FlagList.Add(flagName);
}
else
{
if (FlagList.Contains(flagName))
FlagList.Remove(flagName);
}
}
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Models.Abstractions;
public abstract class PaymentGateway
{
public abstract string Name { get; }
public abstract string Icon { get; }
public abstract Task<string> Start(double price);
}

View file

@ -0,0 +1,34 @@
using Moonlight.App.Models.Enums;
namespace Moonlight.App.Models.Abstractions;
public class PermissionStorage
{
public readonly int PermissionInteger;
public PermissionStorage(int permissionInteger)
{
PermissionInteger = permissionInteger;
}
public Permission[] Permissions => GetPermissions();
public Permission[] GetPermissions()
{
return GetAllPermissions()
.Where(x => (int)x <= PermissionInteger)
.ToArray();
}
public static Permission[] GetAllPermissions()
{
return Enum.GetValues<Permission>();
}
public static Permission GetFromInteger(int id)
{
return GetAllPermissions().First(x => (int)x == id);
}
public bool this[Permission permission] => Permissions.Contains(permission);
}

View file

@ -0,0 +1,10 @@
using Moonlight.App.Database.Entities.Store;
namespace Moonlight.App.Models.Abstractions;
public abstract class ServiceActions
{
public abstract Task Create(IServiceProvider provider, Service service);
public abstract Task Update(IServiceProvider provider, Service service);
public abstract Task Delete(IServiceProvider provider, Service service);
}

View file

@ -0,0 +1,12 @@
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Models.Abstractions;
public class Session
{
public string Ip { get; set; } = "N/A";
public string Url { get; set; } = "N/A";
public User? User { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; // To remove inactive sessions
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Models.Abstractions;
public class Subscriber
{
public string Id { get; set; }
public object Action { get; set; }
public object Handle { get; set; }
}

View file

@ -0,0 +1,16 @@
namespace Moonlight.App.Models.Enums;
public enum Permission
{
Default = 0,
AdminMenu = 999,
AdminOverview = 1000,
AdminUsers = 1001,
AdminSessions = 1002,
AdminUsersEdit = 1003,
AdminTickets = 1004,
AdminCommunity = 1030,
AdminStore = 1900,
AdminViewExceptions = 1999,
AdminRoot = 2000
}

View file

@ -0,0 +1,8 @@
namespace Moonlight.App.Models.Enums;
public enum UserFlag
{
MailVerified,
PasswordPending,
TotpEnabled
}

View file

@ -0,0 +1,12 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Community;
public class AddWordFilter
{
[Required(ErrorMessage = "You need to specify a filter")]
[Description(
"This filters all posts and comments created using this regex. If any match is found it will block the action")]
public string Filter { get; set; } = "";
}

View file

@ -0,0 +1,12 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Community;
public class EditWordFilter
{
[Required(ErrorMessage = "You need to specify a filter")]
[Description(
"This filters all posts and comments created using this regex. If any match is found it will block the action")]
public string Filter { get; set; } = "";
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Store;
public class AddCouponForm
{
[MinLength(5, ErrorMessage = "The code needs to be longer than 4")]
[MaxLength(15, ErrorMessage = "The code should not be longer than 15 characters")]
public string Code { get; set; } = "";
[Range(1, 99, ErrorMessage = "The percent needs to be between 1 and 99")]
public int Percent { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "The amount needs to be equals or greater than 0")]
public int Amount { get; set; }
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Store;
public class AddGiftCodeForm
{
[MinLength(5, ErrorMessage = "The code needs to be longer than 4")]
[MaxLength(15, ErrorMessage = "The code should not be longer than 15 characters")]
public string Code { get; set; } = "";
[Range(0, int.MaxValue, ErrorMessage = "The value needs to be equals or greater than 0")]
public double Value { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "The amount needs to be equals or greater than 0")]
public int Amount { get; set; }
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Store;
public class EditCouponForm
{
[MinLength(5, ErrorMessage = "The code needs to be longer than 4")]
[MaxLength(15, ErrorMessage = "The code should not be longer than 15 characters")]
public string Code { get; set; } = "";
[Range(1, 99, ErrorMessage = "The percent needs to be between 1 and 99")]
public int Percent { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "The amount needs to be equals or greater than 0")]
public int Amount { get; set; }
}

View file

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms.Admin.Store;
public class EditGiftCodeForm
{
[MinLength(5, ErrorMessage = "The code needs to be longer than 4")]
[MaxLength(15, ErrorMessage = "The code should not be longer than 15 characters")]
public string Code { get; set; } = "";
[Range(0, int.MaxValue, ErrorMessage = "The value needs to be equals or greater than 0")]
public double Value { get; set; }
[Range(0, int.MaxValue, ErrorMessage = "The amount needs to be equals or greater than 0")]
public int Amount { get; set; }
}

Some files were not shown because too many files have changed in this diff Show more